summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSuraj Kandpal <suraj.kandpal@intel.com>2024-10-28 10:22:46 +0530
committerSuraj Kandpal <suraj.kandpal@intel.com>2024-10-28 10:22:46 +0530
commita092bc531da99903b88e7306ad45ee1f92efef9d (patch)
tree73abc4f7c725fc60d58d130cd80ae03c4057377a
parent12837c251571546470056761ee0c7b944c89edc3 (diff)
2024y-10m-28d-04h-51m-10s UTC: drm-tip rerere cache update
git version 2.34.1
-rw-r--r--rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/postimage.22870
-rw-r--r--rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage2956
-rw-r--r--rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage.12956
-rw-r--r--rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage.22956
-rw-r--r--rr-cache/0c1c391f6e92e878a32995cabf64a0f916149ff2/postimage2104
-rw-r--r--rr-cache/0c1c391f6e92e878a32995cabf64a0f916149ff2/preimage2105
-rw-r--r--rr-cache/184514ddd9867f1a8f27d1e7de47d36242b6ad90/postimage12343
-rw-r--r--rr-cache/184514ddd9867f1a8f27d1e7de47d36242b6ad90/preimage12348
-rw-r--r--rr-cache/3b5fa8b49e38141113421fdc2503fb1e1928096c/preimage36
-rw-r--r--rr-cache/41549dd6cc337627199acbe6749c5685c7e927d3/preimage2234
-rw-r--r--rr-cache/41549dd6cc337627199acbe6749c5685c7e927d3/preimage.12234
-rw-r--r--rr-cache/44bc583546c40f3144ba90b705854f1ee09d13ea/preimage1231
-rw-r--r--rr-cache/44bc583546c40f3144ba90b705854f1ee09d13ea/preimage.11231
-rw-r--r--rr-cache/607de968990f3d5df977e832f76f7e44097ae59c/preimage2073
-rw-r--r--rr-cache/6e989852f5454d81ebf331bbd2c55116dc711575/preimage.2982
-rw-r--r--rr-cache/6e989852f5454d81ebf331bbd2c55116dc711575/preimage.3982
-rw-r--r--rr-cache/882012b4af13c77de852328870770019afd5505e/postimage33
-rw-r--r--rr-cache/882012b4af13c77de852328870770019afd5505e/preimage36
-rw-r--r--rr-cache/e7c9aafc2297a37f89715cfeed48ccbfb82f76bb/preimage.52244
-rw-r--r--rr-cache/e7c9aafc2297a37f89715cfeed48ccbfb82f76bb/preimage.62244
-rw-r--r--rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/postimage.2377
-rw-r--r--rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage384
-rw-r--r--rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage.1384
-rw-r--r--rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage.2384
-rw-r--r--rr-cache/f013472cad3d9dd7cdbe39311eced4b9997712a7/preimage1974
-rw-r--r--rr-cache/f013472cad3d9dd7cdbe39311eced4b9997712a7/preimage.11974
-rw-r--r--rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/postimage.21927
-rw-r--r--rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage1930
-rw-r--r--rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage.11930
-rw-r--r--rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage.21930
30 files changed, 2462 insertions, 66930 deletions
diff --git a/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/postimage.2 b/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/postimage.2
deleted file mode 100644
index a31fae5feedf..000000000000
--- a/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/postimage.2
+++ /dev/null
@@ -1,2870 +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.
- *
- */
-
-#define SWSMU_CODE_LAYER_L2
-
-#include <linux/firmware.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include "amdgpu.h"
-#include "amdgpu_smu.h"
-#include "atomfirmware.h"
-#include "amdgpu_atomfirmware.h"
-#include "amdgpu_atombios.h"
-#include "smu_v14_0.h"
-#include "smu14_driver_if_v14_0.h"
-#include "soc15_common.h"
-#include "atom.h"
-#include "smu_v14_0_2_ppt.h"
-#include "smu_v14_0_2_pptable.h"
-#include "smu_v14_0_2_ppsmc.h"
-#include "mp/mp_14_0_2_offset.h"
-#include "mp/mp_14_0_2_sh_mask.h"
-
-#include "smu_cmn.h"
-#include "amdgpu_ras.h"
-
-/*
- * DO NOT use these for err/warn/info/debug messages.
- * Use dev_err, dev_warn, dev_info and dev_dbg instead.
- * They are more MGPU friendly.
- */
-#undef pr_err
-#undef pr_warn
-#undef pr_info
-#undef pr_debug
-
-#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
-
-#define FEATURE_MASK(feature) (1ULL << feature)
-#define SMC_DPM_FEATURE ( \
- FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_UCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_LINK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_FCLK_BIT))
-
-#define MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE 0x4000
-#define DEBUGSMC_MSG_Mode1Reset 2
-#define LINK_SPEED_MAX 3
-
-#define PP_OD_FEATURE_GFXCLK_FMIN 0
-#define PP_OD_FEATURE_GFXCLK_FMAX 1
-#define PP_OD_FEATURE_UCLK_FMIN 2
-#define PP_OD_FEATURE_UCLK_FMAX 3
-#define PP_OD_FEATURE_GFX_VF_CURVE 4
-#define PP_OD_FEATURE_FAN_CURVE_TEMP 5
-#define PP_OD_FEATURE_FAN_CURVE_PWM 6
-#define PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT 7
-#define PP_OD_FEATURE_FAN_ACOUSTIC_TARGET 8
-#define PP_OD_FEATURE_FAN_TARGET_TEMPERATURE 9
-#define PP_OD_FEATURE_FAN_MINIMUM_PWM 10
-
-static struct cmn2asic_msg_mapping smu_v14_0_2_message_map[SMU_MSG_MAX_COUNT] = {
- MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
- MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
- MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
- MSG_MAP(SetAllowedFeaturesMaskLow, PPSMC_MSG_SetAllowedFeaturesMaskLow, 0),
- MSG_MAP(SetAllowedFeaturesMaskHigh, PPSMC_MSG_SetAllowedFeaturesMaskHigh, 0),
- MSG_MAP(EnableAllSmuFeatures, PPSMC_MSG_EnableAllSmuFeatures, 0),
- MSG_MAP(DisableAllSmuFeatures, PPSMC_MSG_DisableAllSmuFeatures, 0),
- MSG_MAP(EnableSmuFeaturesLow, PPSMC_MSG_EnableSmuFeaturesLow, 1),
- MSG_MAP(EnableSmuFeaturesHigh, PPSMC_MSG_EnableSmuFeaturesHigh, 1),
- MSG_MAP(DisableSmuFeaturesLow, PPSMC_MSG_DisableSmuFeaturesLow, 1),
- MSG_MAP(DisableSmuFeaturesHigh, PPSMC_MSG_DisableSmuFeaturesHigh, 1),
- MSG_MAP(GetEnabledSmuFeaturesLow, PPSMC_MSG_GetRunningSmuFeaturesLow, 1),
- MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetRunningSmuFeaturesHigh, 1),
- MSG_MAP(SetWorkloadMask, PPSMC_MSG_SetWorkloadMask, 1),
- MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0),
- MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
- MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
- MSG_MAP(SetToolsDramAddrHigh, PPSMC_MSG_SetToolsDramAddrHigh, 0),
- MSG_MAP(SetToolsDramAddrLow, PPSMC_MSG_SetToolsDramAddrLow, 0),
- MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
- MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 0),
- MSG_MAP(UseDefaultPPTable, PPSMC_MSG_UseDefaultPPTable, 0),
- MSG_MAP(RunDcBtc, PPSMC_MSG_RunDcBtc, 0),
- MSG_MAP(EnterBaco, PPSMC_MSG_EnterBaco, 0),
- MSG_MAP(ExitBaco, PPSMC_MSG_ExitBaco, 0),
- MSG_MAP(SetSoftMinByFreq, PPSMC_MSG_SetSoftMinByFreq, 1),
- MSG_MAP(SetSoftMaxByFreq, PPSMC_MSG_SetSoftMaxByFreq, 1),
- MSG_MAP(SetHardMinByFreq, PPSMC_MSG_SetHardMinByFreq, 1),
- MSG_MAP(SetHardMaxByFreq, PPSMC_MSG_SetHardMaxByFreq, 0),
- MSG_MAP(GetMinDpmFreq, PPSMC_MSG_GetMinDpmFreq, 1),
- MSG_MAP(GetMaxDpmFreq, PPSMC_MSG_GetMaxDpmFreq, 1),
- MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1),
- MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 0),
- MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 0),
- MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 0),
- MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 0),
- MSG_MAP(GetDcModeMaxDpmFreq, PPSMC_MSG_GetDcModeMaxDpmFreq, 1),
- MSG_MAP(OverridePcieParameters, PPSMC_MSG_OverridePcieParameters, 0),
- MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0),
- MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0),
- MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0),
- MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 0),
- MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 0),
- MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm, 0),
- MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0),
- MSG_MAP(NotifyPowerSource, PPSMC_MSG_NotifyPowerSource, 0),
- MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 0),
- MSG_MAP(DFCstateControl, PPSMC_MSG_SetExternalClientDfCstateAllow, 0),
- MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0),
- MSG_MAP(SetNumBadMemoryPagesRetired, PPSMC_MSG_SetNumBadMemoryPagesRetired, 0),
- MSG_MAP(SetBadMemoryPagesRetiredFlagsPerChannel,
- PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel, 0),
- MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0),
- MSG_MAP(ReenableAcDcInterrupt, PPSMC_MSG_ReenableAcDcInterrupt, 0),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_clk_map[SMU_CLK_COUNT] = {
- CLK_MAP(GFXCLK, PPCLK_GFXCLK),
- CLK_MAP(SCLK, PPCLK_GFXCLK),
- CLK_MAP(SOCCLK, PPCLK_SOCCLK),
- CLK_MAP(FCLK, PPCLK_FCLK),
- CLK_MAP(UCLK, PPCLK_UCLK),
- CLK_MAP(MCLK, PPCLK_UCLK),
- CLK_MAP(VCLK, PPCLK_VCLK_0),
- CLK_MAP(DCLK, PPCLK_DCLK_0),
- CLK_MAP(DCEFCLK, PPCLK_DCFCLK),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_feature_mask_map[SMU_FEATURE_COUNT] = {
- FEA_MAP(FW_DATA_READ),
- FEA_MAP(DPM_GFXCLK),
- FEA_MAP(DPM_GFX_POWER_OPTIMIZER),
- FEA_MAP(DPM_UCLK),
- FEA_MAP(DPM_FCLK),
- FEA_MAP(DPM_SOCCLK),
- FEA_MAP(DPM_LINK),
- FEA_MAP(DPM_DCN),
- FEA_MAP(VMEMP_SCALING),
- FEA_MAP(VDDIO_MEM_SCALING),
- FEA_MAP(DS_GFXCLK),
- FEA_MAP(DS_SOCCLK),
- FEA_MAP(DS_FCLK),
- FEA_MAP(DS_LCLK),
- FEA_MAP(DS_DCFCLK),
- FEA_MAP(DS_UCLK),
- FEA_MAP(GFX_ULV),
- FEA_MAP(FW_DSTATE),
- FEA_MAP(GFXOFF),
- FEA_MAP(BACO),
- FEA_MAP(MM_DPM),
- FEA_MAP(SOC_MPCLK_DS),
- FEA_MAP(BACO_MPCLK_DS),
- FEA_MAP(THROTTLERS),
- FEA_MAP(SMARTSHIFT),
- FEA_MAP(GTHR),
- FEA_MAP(ACDC),
- FEA_MAP(VR0HOT),
- FEA_MAP(FW_CTF),
- FEA_MAP(FAN_CONTROL),
- FEA_MAP(GFX_DCS),
- FEA_MAP(GFX_READ_MARGIN),
- FEA_MAP(LED_DISPLAY),
- FEA_MAP(GFXCLK_SPREAD_SPECTRUM),
- FEA_MAP(OUT_OF_BAND_MONITOR),
- FEA_MAP(OPTIMIZED_VMIN),
- FEA_MAP(GFX_IMU),
- FEA_MAP(BOOT_TIME_CAL),
- FEA_MAP(GFX_PCC_DFLL),
- FEA_MAP(SOC_CG),
- FEA_MAP(DF_CSTATE),
- FEA_MAP(GFX_EDC),
- FEA_MAP(BOOT_POWER_OPT),
- FEA_MAP(CLOCK_POWER_DOWN_BYPASS),
- FEA_MAP(DS_VCN),
- FEA_MAP(BACO_CG),
- FEA_MAP(MEM_TEMP_READ),
- FEA_MAP(ATHUB_MMHUB_PG),
- FEA_MAP(SOC_PCC),
- [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
- [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
- [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_table_map[SMU_TABLE_COUNT] = {
- TAB_MAP(PPTABLE),
- TAB_MAP(WATERMARKS),
- TAB_MAP(AVFS_PSM_DEBUG),
- TAB_MAP(PMSTATUSLOG),
- TAB_MAP(SMU_METRICS),
- TAB_MAP(DRIVER_SMU_CONFIG),
- TAB_MAP(ACTIVITY_MONITOR_COEFF),
- [SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
- TAB_MAP(I2C_COMMANDS),
- TAB_MAP(ECCINFO),
- TAB_MAP(OVERDRIVE),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
- PWR_MAP(AC),
- PWR_MAP(DC),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_workload_map[PP_SMC_POWER_PROFILE_COUNT] = {
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT, WORKLOAD_PPLIB_DEFAULT_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_FULLSCREEN3D, WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_POWERSAVING, WORKLOAD_PPLIB_POWER_SAVING_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VIDEO, WORKLOAD_PPLIB_VIDEO_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VR, WORKLOAD_PPLIB_VR_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_COMPUTE, WORKLOAD_PPLIB_COMPUTE_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_CUSTOM, WORKLOAD_PPLIB_CUSTOM_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_WINDOW3D, WORKLOAD_PPLIB_WINDOW_3D_BIT),
-};
-
-static const uint8_t smu_v14_0_2_throttler_map[] = {
- [THROTTLER_PPT0_BIT] = (SMU_THROTTLER_PPT0_BIT),
- [THROTTLER_PPT1_BIT] = (SMU_THROTTLER_PPT1_BIT),
- [THROTTLER_PPT2_BIT] = (SMU_THROTTLER_PPT2_BIT),
- [THROTTLER_PPT3_BIT] = (SMU_THROTTLER_PPT3_BIT),
- [THROTTLER_TDC_GFX_BIT] = (SMU_THROTTLER_TDC_GFX_BIT),
- [THROTTLER_TDC_SOC_BIT] = (SMU_THROTTLER_TDC_SOC_BIT),
- [THROTTLER_TEMP_EDGE_BIT] = (SMU_THROTTLER_TEMP_EDGE_BIT),
- [THROTTLER_TEMP_HOTSPOT_BIT] = (SMU_THROTTLER_TEMP_HOTSPOT_BIT),
- [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
- [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
- [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
- [THROTTLER_TEMP_VR_MEM0_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
- [THROTTLER_TEMP_VR_MEM1_BIT] = (SMU_THROTTLER_TEMP_VR_MEM1_BIT),
- [THROTTLER_TEMP_LIQUID0_BIT] = (SMU_THROTTLER_TEMP_LIQUID0_BIT),
- [THROTTLER_TEMP_LIQUID1_BIT] = (SMU_THROTTLER_TEMP_LIQUID1_BIT),
- [THROTTLER_GFX_APCC_PLUS_BIT] = (SMU_THROTTLER_APCC_BIT),
- [THROTTLER_FIT_BIT] = (SMU_THROTTLER_FIT_BIT),
-};
-
-static int
-smu_v14_0_2_get_allowed_feature_mask(struct smu_context *smu,
- uint32_t *feature_mask, uint32_t num)
-{
- struct amdgpu_device *adev = smu->adev;
- /*u32 smu_version;*/
-
- if (num > 2)
- return -EINVAL;
-
- memset(feature_mask, 0xff, sizeof(uint32_t) * num);
-
- if (adev->pm.pp_feature & PP_SCLK_DPM_MASK) {
- *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT);
- *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_IMU_BIT);
- }
-#if 0
- if (!(adev->pg_flags & AMD_PG_SUPPORT_ATHUB) ||
- !(adev->pg_flags & AMD_PG_SUPPORT_MMHUB))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_ATHUB_MMHUB_PG_BIT);
-
- if (!(adev->pm.pp_feature & PP_SOCCLK_DPM_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT);
-
- /* PMFW 78.58 contains a critical fix for gfxoff feature */
- smu_cmn_get_smc_version(smu, NULL, &smu_version);
- if ((smu_version < 0x004e3a00) ||
- !(adev->pm.pp_feature & PP_GFXOFF_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFXOFF_BIT);
-
- if (!(adev->pm.pp_feature & PP_MCLK_DPM_MASK)) {
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_UCLK_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VMEMP_SCALING_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VDDIO_MEM_SCALING_BIT);
- }
-
- if (!(adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_GFXCLK_BIT);
-
- if (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK)) {
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_LINK_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_LCLK_BIT);
- }
-
- if (!(adev->pm.pp_feature & PP_ULV_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFX_ULV_BIT);
-#endif
-
- return 0;
-}
-
-static int smu_v14_0_2_check_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
- struct smu_baco_context *smu_baco = &smu->smu_baco;
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
- const OverDriveLimits_t * const overdrive_lowerlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMin;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_HARDWAREDC)
- smu->dc_controlled_by_gpio = true;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_BACO) {
- smu_baco->platform_support = true;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_MACO)
- smu_baco->maco_support = true;
- }
-
- if (!overdrive_lowerlimits->FeatureCtrlMask ||
- !overdrive_upperlimits->FeatureCtrlMask)
- smu->od_enabled = false;
-
- table_context->thermal_controller_type =
- powerplay_table->thermal_controller_type;
-
- /*
- * Instead of having its own buffer space and get overdrive_table copied,
- * smu->od_settings just points to the actual overdrive_table
- */
- smu->od_settings = &powerplay_table->overdrive_table;
-
- smu->adev->pm.no_fan =
- !(pptable->PFE_Settings.FeaturesToRun[0] & (1 << FEATURE_FAN_CONTROL_BIT));
-
- return 0;
-}
-
-static int smu_v14_0_2_store_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
-
- memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
- sizeof(PPTable_t));
-
- return 0;
-}
-
-#ifndef atom_smc_dpm_info_table_14_0_0
-struct atom_smc_dpm_info_table_14_0_0 {
- struct atom_common_table_header table_header;
- BoardTable_t BoardTable;
-};
-#endif
-
-static int smu_v14_0_2_append_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *smc_pptable = table_context->driver_pptable;
- struct atom_smc_dpm_info_table_14_0_0 *smc_dpm_table;
- BoardTable_t *BoardTable = &smc_pptable->BoardTable;
- int index, ret;
-
- index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
- smc_dpm_info);
-
- ret = amdgpu_atombios_get_data_table(smu->adev, index, NULL, NULL, NULL,
- (uint8_t **)&smc_dpm_table);
- if (ret)
- return ret;
-
- memcpy(BoardTable, &smc_dpm_table->BoardTable, sizeof(BoardTable_t));
-
- return 0;
-}
-
-#if 0
-static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
- void **table,
- uint32_t *size)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- void *combo_pptable = smu_table->combo_pptable;
- int ret = 0;
-
- ret = smu_cmn_get_combo_pptable(smu);
- if (ret)
- return ret;
-
- *table = combo_pptable;
- *size = sizeof(struct smu_14_0_powerplay_table);
-
- return 0;
-}
-#endif
-
-static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
- void **table,
- uint32_t *size)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- void *combo_pptable = smu_table->combo_pptable;
- int ret = 0;
-
- ret = smu_cmn_get_combo_pptable(smu);
- if (ret)
- return ret;
-
- *table = combo_pptable;
- *size = sizeof(struct smu_14_0_2_powerplay_table);
-
- return 0;
-}
-
-static int smu_v14_0_2_setup_pptable(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct amdgpu_device *adev = smu->adev;
- int ret = 0;
-
- if (amdgpu_sriov_vf(smu->adev))
- return 0;
-
- if (!adev->scpm_enabled)
- ret = smu_v14_0_setup_pptable(smu);
- else
- ret = smu_v14_0_2_get_pptable_from_pmfw(smu,
- &smu_table->power_play_table,
- &smu_table->power_play_table_size);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_store_powerplay_table(smu);
- if (ret)
- return ret;
-
- /*
- * With SCPM enabled, the operation below will be handled
- * by PSP. Driver involvment is unnecessary and useless.
- */
- if (!adev->scpm_enabled) {
- ret = smu_v14_0_2_append_powerplay_table(smu);
- if (ret)
- return ret;
- }
-
- ret = smu_v14_0_2_check_powerplay_table(smu);
- if (ret)
- return ret;
-
- return ret;
-}
-
-static int smu_v14_0_2_tables_init(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct smu_table *tables = smu_table->tables;
-
- SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetricsExternal_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU14_TOOL_SIZE,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- sizeof(DpmActivityMonitorCoeffIntExternal_t), PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_COMBO_PPTABLE, MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_ECCINFO, sizeof(EccInfoTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
-
- smu_table->metrics_table = kzalloc(sizeof(SmuMetricsExternal_t), GFP_KERNEL);
- if (!smu_table->metrics_table)
- goto err0_out;
- smu_table->metrics_time = 0;
-
- smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
- smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
- if (!smu_table->gpu_metrics_table)
- goto err1_out;
-
- smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
- if (!smu_table->watermarks_table)
- goto err2_out;
-
- smu_table->ecc_table = kzalloc(tables[SMU_TABLE_ECCINFO].size, GFP_KERNEL);
- if (!smu_table->ecc_table)
- goto err3_out;
-
- return 0;
-
-err3_out:
- kfree(smu_table->watermarks_table);
-err2_out:
- kfree(smu_table->gpu_metrics_table);
-err1_out:
- kfree(smu_table->metrics_table);
-err0_out:
- return -ENOMEM;
-}
-
-static int smu_v14_0_2_allocate_dpm_context(struct smu_context *smu)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
-
- smu_dpm->dpm_context = kzalloc(sizeof(struct smu_14_0_dpm_context),
- GFP_KERNEL);
- if (!smu_dpm->dpm_context)
- return -ENOMEM;
-
- smu_dpm->dpm_context_size = sizeof(struct smu_14_0_dpm_context);
-
- return 0;
-}
-
-static int smu_v14_0_2_init_smc_tables(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_v14_0_2_tables_init(smu);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_allocate_dpm_context(smu);
- if (ret)
- return ret;
-
- return smu_v14_0_init_smc_tables(smu);
-}
-
-static int smu_v14_0_2_set_default_dpm_table(struct smu_context *smu)
-{
- struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- SkuTable_t *skutable = &pptable->SkuTable;
- struct smu_14_0_dpm_table *dpm_table;
- struct smu_14_0_pcie_table *pcie_table;
- uint32_t link_level;
- int ret = 0;
-
- /* socclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.soc_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_SOCCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* gfxclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.gfx_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_GFXCLK,
- dpm_table);
- if (ret)
- return ret;
-
- /*
- * Update the reported maximum shader clock to the value
- * which can be guarded to be achieved on all cards. This
- * is aligned with Window setting. And considering that value
- * might be not the peak frequency the card can achieve, it
- * is normal some real-time clock frequency can overtake this
- * labelled maximum clock frequency(for example in pp_dpm_sclk
- * sysfs output).
- */
- if (skutable->DriverReportedClocks.GameClockAc &&
- (dpm_table->dpm_levels[dpm_table->count - 1].value >
- skutable->DriverReportedClocks.GameClockAc)) {
- dpm_table->dpm_levels[dpm_table->count - 1].value =
- skutable->DriverReportedClocks.GameClockAc;
- dpm_table->max = skutable->DriverReportedClocks.GameClockAc;
- }
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* uclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.uclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_UCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* fclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.fclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_FCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* vclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.vclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_VCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_VCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* dclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.dclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_DCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* lclk dpm table setup */
- pcie_table = &dpm_context->dpm_tables.pcie_table;
- pcie_table->num_of_link_levels = 0;
- for (link_level = 0; link_level < NUM_LINK_LEVELS; link_level++) {
- if (!skutable->PcieGenSpeed[link_level] &&
- !skutable->PcieLaneCount[link_level] &&
- !skutable->LclkFreq[link_level])
- continue;
-
- pcie_table->pcie_gen[pcie_table->num_of_link_levels] =
- skutable->PcieGenSpeed[link_level];
- pcie_table->pcie_lane[pcie_table->num_of_link_levels] =
- skutable->PcieLaneCount[link_level];
- pcie_table->clk_freq[pcie_table->num_of_link_levels] =
- skutable->LclkFreq[link_level];
- pcie_table->num_of_link_levels++;
- }
-
- /* dcefclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.dcef_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCN_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_DCEFCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- return 0;
-}
-
-static bool smu_v14_0_2_is_dpm_running(struct smu_context *smu)
-{
- int ret = 0;
- uint64_t feature_enabled;
-
- ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
- if (ret)
- return false;
-
- return !!(feature_enabled & SMC_DPM_FEATURE);
-}
-
-static void smu_v14_0_2_dump_pptable(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- PFE_Settings_t *PFEsettings = &pptable->PFE_Settings;
-
- dev_info(smu->adev->dev, "Dumped PPTable:\n");
-
- dev_info(smu->adev->dev, "Version = 0x%08x\n", PFEsettings->Version);
- dev_info(smu->adev->dev, "FeaturesToRun[0] = 0x%08x\n", PFEsettings->FeaturesToRun[0]);
- dev_info(smu->adev->dev, "FeaturesToRun[1] = 0x%08x\n", PFEsettings->FeaturesToRun[1]);
-}
-
-static uint32_t smu_v14_0_2_get_throttler_status(SmuMetrics_t *metrics)
-{
- uint32_t throttler_status = 0;
- int i;
-
- for (i = 0; i < THROTTLER_COUNT; i++)
- throttler_status |=
- (metrics->ThrottlingPercentage[i] ? 1U << i : 0);
-
- return throttler_status;
-}
-
-#define SMU_14_0_2_BUSY_THRESHOLD 5
-static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu,
- MetricsMember_t member,
- uint32_t *value)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- SmuMetrics_t *metrics =
- &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- NULL,
- false);
- if (ret)
- return ret;
-
- switch (member) {
- case METRICS_CURR_GFXCLK:
- *value = metrics->CurrClock[PPCLK_GFXCLK];
- break;
- case METRICS_CURR_SOCCLK:
- *value = metrics->CurrClock[PPCLK_SOCCLK];
- break;
- case METRICS_CURR_UCLK:
- *value = metrics->CurrClock[PPCLK_UCLK];
- break;
- case METRICS_CURR_VCLK:
- *value = metrics->CurrClock[PPCLK_VCLK_0];
- break;
- case METRICS_CURR_DCLK:
- *value = metrics->CurrClock[PPCLK_DCLK_0];
- break;
- case METRICS_CURR_FCLK:
- *value = metrics->CurrClock[PPCLK_FCLK];
- break;
- case METRICS_CURR_DCEFCLK:
- *value = metrics->CurrClock[PPCLK_DCFCLK];
- break;
- case METRICS_AVERAGE_GFXCLK:
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageGfxclkFrequencyPostDs;
- else
- *value = metrics->AverageGfxclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_FCLK:
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageFclkFrequencyPostDs;
- else
- *value = metrics->AverageFclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_UCLK:
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageMemclkFrequencyPostDs;
- else
- *value = metrics->AverageMemclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_VCLK:
- *value = metrics->AverageVclk0Frequency;
- break;
- case METRICS_AVERAGE_DCLK:
- *value = metrics->AverageDclk0Frequency;
- break;
- case METRICS_AVERAGE_VCLK1:
- *value = metrics->AverageVclk1Frequency;
- break;
- case METRICS_AVERAGE_DCLK1:
- *value = metrics->AverageDclk1Frequency;
- break;
- case METRICS_AVERAGE_GFXACTIVITY:
- *value = metrics->AverageGfxActivity;
- break;
- case METRICS_AVERAGE_MEMACTIVITY:
- *value = metrics->AverageUclkActivity;
- break;
- case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
- break;
- case METRICS_TEMPERATURE_EDGE:
- *value = metrics->AvgTemperature[TEMP_EDGE] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_HOTSPOT:
- *value = metrics->AvgTemperature[TEMP_HOTSPOT] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_MEM:
- *value = metrics->AvgTemperature[TEMP_MEM] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_VRGFX:
- *value = metrics->AvgTemperature[TEMP_VR_GFX] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_VRSOC:
- *value = metrics->AvgTemperature[TEMP_VR_SOC] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_THROTTLER_STATUS:
- *value = smu_v14_0_2_get_throttler_status(metrics);
- break;
- case METRICS_CURR_FANSPEED:
- *value = metrics->AvgFanRpm;
- break;
- case METRICS_CURR_FANPWM:
- *value = metrics->AvgFanPwm;
- break;
- case METRICS_VOLTAGE_VDDGFX:
- *value = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- break;
- case METRICS_PCIE_RATE:
- *value = metrics->PcieRate;
- break;
- case METRICS_PCIE_WIDTH:
- *value = metrics->PcieWidth;
- break;
- default:
- *value = UINT_MAX;
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_get_dpm_ultimate_freq(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t *min,
- uint32_t *max)
-{
- struct smu_14_0_dpm_context *dpm_context =
- smu->smu_dpm.dpm_context;
- struct smu_14_0_dpm_table *dpm_table;
-
- switch (clk_type) {
- case SMU_MCLK:
- case SMU_UCLK:
- /* uclk dpm table */
- dpm_table = &dpm_context->dpm_tables.uclk_table;
- break;
- case SMU_GFXCLK:
- case SMU_SCLK:
- /* gfxclk dpm table */
- dpm_table = &dpm_context->dpm_tables.gfx_table;
- break;
- case SMU_SOCCLK:
- /* socclk dpm table */
- dpm_table = &dpm_context->dpm_tables.soc_table;
- break;
- case SMU_FCLK:
- /* fclk dpm table */
- dpm_table = &dpm_context->dpm_tables.fclk_table;
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- /* vclk dpm table */
- dpm_table = &dpm_context->dpm_tables.vclk_table;
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- /* dclk dpm table */
- dpm_table = &dpm_context->dpm_tables.dclk_table;
- break;
- default:
- dev_err(smu->adev->dev, "Unsupported clock type!\n");
- return -EINVAL;
- }
-
- if (min)
- *min = dpm_table->min;
- if (max)
- *max = dpm_table->max;
-
- return 0;
-}
-
-static int smu_v14_0_2_read_sensor(struct smu_context *smu,
- enum amd_pp_sensors sensor,
- void *data,
- uint32_t *size)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *smc_pptable = table_context->driver_pptable;
- int ret = 0;
-
- switch (sensor) {
- case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
- *(uint16_t *)data = smc_pptable->CustomSkuTable.FanMaximumRpm;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_MEM_LOAD:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_MEMACTIVITY,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GPU_LOAD:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_GFXACTIVITY,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_SOCKETPOWER,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_HOTSPOT,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_EDGE_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_EDGE,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_MEM_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_MEM,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GFX_MCLK:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_CURR_UCLK,
- (uint32_t *)data);
- *(uint32_t *)data *= 100;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GFX_SCLK:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_GFXCLK,
- (uint32_t *)data);
- *(uint32_t *)data *= 100;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_VDDGFX:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_VOLTAGE_VDDGFX,
- (uint32_t *)data);
- *size = 4;
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_get_current_clk_freq_by_table(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t *value)
-{
- MetricsMember_t member_type;
- int clk_id = 0;
-
- clk_id = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_CLK,
- clk_type);
- if (clk_id < 0)
- return -EINVAL;
-
- switch (clk_id) {
- case PPCLK_GFXCLK:
- member_type = METRICS_AVERAGE_GFXCLK;
- break;
- case PPCLK_UCLK:
- member_type = METRICS_CURR_UCLK;
- break;
- case PPCLK_FCLK:
- member_type = METRICS_CURR_FCLK;
- break;
- case PPCLK_SOCCLK:
- member_type = METRICS_CURR_SOCCLK;
- break;
- case PPCLK_VCLK_0:
- member_type = METRICS_AVERAGE_VCLK;
- break;
- case PPCLK_DCLK_0:
- member_type = METRICS_AVERAGE_DCLK;
- break;
- case PPCLK_DCFCLK:
- member_type = METRICS_CURR_DCEFCLK;
- break;
- default:
- return -EINVAL;
- }
-
- return smu_v14_0_2_get_smu_metrics_data(smu,
- member_type,
- value);
-}
-
-static bool smu_v14_0_2_is_od_feature_supported(struct smu_context *smu,
- int od_feature_bit)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
-
- return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
-}
-
-static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
- int od_feature_bit,
- int32_t *min,
- int32_t *max)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
- const OverDriveLimits_t * const overdrive_lowerlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMin;
- int32_t od_min_setting, od_max_setting;
-
- switch (od_feature_bit) {
- case PP_OD_FEATURE_GFXCLK_FMIN:
- od_min_setting = overdrive_lowerlimits->GfxclkFmin;
- od_max_setting = overdrive_upperlimits->GfxclkFmin;
- break;
- case PP_OD_FEATURE_GFXCLK_FMAX:
- od_min_setting = overdrive_lowerlimits->GfxclkFmax;
- od_max_setting = overdrive_upperlimits->GfxclkFmax;
- break;
- case PP_OD_FEATURE_UCLK_FMIN:
- od_min_setting = overdrive_lowerlimits->UclkFmin;
- od_max_setting = overdrive_upperlimits->UclkFmin;
- break;
- case PP_OD_FEATURE_UCLK_FMAX:
- od_min_setting = overdrive_lowerlimits->UclkFmax;
- od_max_setting = overdrive_upperlimits->UclkFmax;
- break;
- case PP_OD_FEATURE_GFX_VF_CURVE:
- od_min_setting = overdrive_lowerlimits->VoltageOffsetPerZoneBoundary[0];
- od_max_setting = overdrive_upperlimits->VoltageOffsetPerZoneBoundary[0];
- break;
- case PP_OD_FEATURE_FAN_CURVE_TEMP:
- od_min_setting = overdrive_lowerlimits->FanLinearTempPoints[0];
- od_max_setting = overdrive_upperlimits->FanLinearTempPoints[0];
- break;
- case PP_OD_FEATURE_FAN_CURVE_PWM:
- od_min_setting = overdrive_lowerlimits->FanLinearPwmPoints[0];
- od_max_setting = overdrive_upperlimits->FanLinearPwmPoints[0];
- break;
- case PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT:
- od_min_setting = overdrive_lowerlimits->AcousticLimitRpmThreshold;
- od_max_setting = overdrive_upperlimits->AcousticLimitRpmThreshold;
- break;
- case PP_OD_FEATURE_FAN_ACOUSTIC_TARGET:
- od_min_setting = overdrive_lowerlimits->AcousticTargetRpmThreshold;
- od_max_setting = overdrive_upperlimits->AcousticTargetRpmThreshold;
- break;
- case PP_OD_FEATURE_FAN_TARGET_TEMPERATURE:
- od_min_setting = overdrive_lowerlimits->FanTargetTemperature;
- od_max_setting = overdrive_upperlimits->FanTargetTemperature;
- break;
- case PP_OD_FEATURE_FAN_MINIMUM_PWM:
- od_min_setting = overdrive_lowerlimits->FanMinimumPwm;
- od_max_setting = overdrive_upperlimits->FanMinimumPwm;
- break;
- default:
- od_min_setting = od_max_setting = INT_MAX;
- break;
- }
-
- if (min)
- *min = od_min_setting;
- if (max)
- *max = od_max_setting;
-}
-
-static int smu_v14_0_2_print_clk_levels(struct smu_context *smu,
- enum smu_clk_type clk_type,
- char *buf)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
- struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
- struct smu_14_0_dpm_table *single_dpm_table;
- struct smu_14_0_pcie_table *pcie_table;
- uint32_t gen_speed, lane_width;
- int i, curr_freq, size = 0;
- int32_t min_value, max_value;
- int ret = 0;
-
- smu_cmn_get_sysfs_buf(&buf, &size);
-
- if (amdgpu_ras_intr_triggered()) {
- size += sysfs_emit_at(buf, size, "unavailable\n");
- return size;
- }
-
- switch (clk_type) {
- case SMU_SCLK:
- single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
- break;
- case SMU_MCLK:
- single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- break;
- case SMU_SOCCLK:
- single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- break;
- case SMU_FCLK:
- single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- break;
- case SMU_DCEFCLK:
- single_dpm_table = &(dpm_context->dpm_tables.dcef_table);
- break;
- default:
- break;
- }
-
- switch (clk_type) {
- case SMU_SCLK:
- case SMU_MCLK:
- case SMU_SOCCLK:
- case SMU_FCLK:
- case SMU_VCLK:
- case SMU_VCLK1:
- case SMU_DCLK:
- case SMU_DCLK1:
- case SMU_DCEFCLK:
- ret = smu_v14_0_2_get_current_clk_freq_by_table(smu, clk_type, &curr_freq);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to get current clock freq!");
- return ret;
- }
-
- if (single_dpm_table->is_fine_grained) {
- /*
- * For fine grained dpms, there are only two dpm levels:
- * - level 0 -> min clock freq
- * - level 1 -> max clock freq
- * And the current clock frequency can be any value between them.
- * So, if the current clock frequency is not at level 0 or level 1,
- * we will fake it as three dpm levels:
- * - level 0 -> min clock freq
- * - level 1 -> current actual clock freq
- * - level 2 -> max clock freq
- */
- if ((single_dpm_table->dpm_levels[0].value != curr_freq) &&
- (single_dpm_table->dpm_levels[1].value != curr_freq)) {
- size += sysfs_emit_at(buf, size, "0: %uMhz\n",
- single_dpm_table->dpm_levels[0].value);
- size += sysfs_emit_at(buf, size, "1: %uMhz *\n",
- curr_freq);
- size += sysfs_emit_at(buf, size, "2: %uMhz\n",
- single_dpm_table->dpm_levels[1].value);
- } else {
- size += sysfs_emit_at(buf, size, "0: %uMhz %s\n",
- single_dpm_table->dpm_levels[0].value,
- single_dpm_table->dpm_levels[0].value == curr_freq ? "*" : "");
- size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
- single_dpm_table->dpm_levels[1].value,
- single_dpm_table->dpm_levels[1].value == curr_freq ? "*" : "");
- }
- } else {
- for (i = 0; i < single_dpm_table->count; i++)
- size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
- i, single_dpm_table->dpm_levels[i].value,
- single_dpm_table->dpm_levels[i].value == curr_freq ? "*" : "");
- }
- break;
- case SMU_PCIE:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_PCIE_RATE,
- &gen_speed);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_PCIE_WIDTH,
- &lane_width);
- if (ret)
- return ret;
-
- pcie_table = &(dpm_context->dpm_tables.pcie_table);
- for (i = 0; i < pcie_table->num_of_link_levels; i++)
- size += sysfs_emit_at(buf, size, "%d: %s %s %dMhz %s\n", i,
- (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s," :
- (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s," :
- (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s," :
- (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," : "",
- (pcie_table->pcie_lane[i] == 1) ? "x1" :
- (pcie_table->pcie_lane[i] == 2) ? "x2" :
- (pcie_table->pcie_lane[i] == 3) ? "x4" :
- (pcie_table->pcie_lane[i] == 4) ? "x8" :
- (pcie_table->pcie_lane[i] == 5) ? "x12" :
- (pcie_table->pcie_lane[i] == 6) ? "x16" : "",
- pcie_table->clk_freq[i],
- (gen_speed == DECODE_GEN_SPEED(pcie_table->pcie_gen[i])) &&
- (lane_width == DECODE_LANE_WIDTH(pcie_table->pcie_lane[i])) ?
- "*" : "");
- break;
-
- case SMU_OD_SCLK:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_GFXCLK_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_SCLK:\n");
- size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMhz\n",
- od_table->OverDriveTable.GfxclkFmin,
- od_table->OverDriveTable.GfxclkFmax);
- break;
-
- case SMU_OD_MCLK:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_UCLK_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_MCLK:\n");
- size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMHz\n",
- od_table->OverDriveTable.UclkFmin,
- od_table->OverDriveTable.UclkFmax);
- break;
-
- case SMU_OD_VDDGFX_OFFSET:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_GFX_VF_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_VDDGFX_OFFSET:\n");
- size += sysfs_emit_at(buf, size, "%dmV\n",
- od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[0]);
- break;
-
- case SMU_OD_FAN_CURVE:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_FAN_CURVE:\n");
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++)
- size += sysfs_emit_at(buf, size, "%d: %dC %d%%\n",
- i,
- (int)od_table->OverDriveTable.FanLinearTempPoints[i],
- (int)od_table->OverDriveTable.FanLinearPwmPoints[i]);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_TEMP,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "FAN_CURVE(hotspot temp): %uC %uC\n",
- min_value, max_value);
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_PWM,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "FAN_CURVE(fan speed): %u%% %u%%\n",
- min_value, max_value);
-
- break;
-
- case SMU_OD_ACOUSTIC_LIMIT:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_LIMIT:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.AcousticLimitRpmThreshold);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "ACOUSTIC_LIMIT: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_ACOUSTIC_TARGET:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_TARGET:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.AcousticTargetRpmThreshold);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "ACOUSTIC_TARGET: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_FAN_TARGET_TEMPERATURE:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "FAN_TARGET_TEMPERATURE:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.FanTargetTemperature);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "TARGET_TEMPERATURE: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_FAN_MINIMUM_PWM:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "FAN_MINIMUM_PWM:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.FanMinimumPwm);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_MINIMUM_PWM,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "MINIMUM_PWM: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_RANGE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT) &&
- !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT) &&
- !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMIN,
- &min_value,
- NULL);
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMAX,
- NULL,
- &max_value);
- size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
- min_value, max_value);
- }
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMIN,
- &min_value,
- NULL);
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMAX,
- NULL,
- &max_value);
- size += sysfs_emit_at(buf, size, "MCLK: %7uMhz %10uMhz\n",
- min_value, max_value);
- }
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFX_VF_CURVE,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "VDDGFX_OFFSET: %7dmv %10dmv\n",
- min_value, max_value);
- }
- break;
-
- default:
- break;
- }
-
- return size;
-}
-
-static int smu_v14_0_2_force_clk_levels(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t mask)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
- struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
- struct smu_14_0_dpm_table *single_dpm_table;
- uint32_t soft_min_level, soft_max_level;
- uint32_t min_freq, max_freq;
- int ret = 0;
-
- soft_min_level = mask ? (ffs(mask) - 1) : 0;
- soft_max_level = mask ? (fls(mask) - 1) : 0;
-
- switch (clk_type) {
- case SMU_GFXCLK:
- case SMU_SCLK:
- single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
- break;
- case SMU_MCLK:
- case SMU_UCLK:
- single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- break;
- case SMU_SOCCLK:
- single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- break;
- case SMU_FCLK:
- single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- break;
- default:
- break;
- }
-
- switch (clk_type) {
- case SMU_GFXCLK:
- case SMU_SCLK:
- case SMU_MCLK:
- case SMU_UCLK:
- case SMU_SOCCLK:
- case SMU_FCLK:
- case SMU_VCLK:
- case SMU_VCLK1:
- case SMU_DCLK:
- case SMU_DCLK1:
- if (single_dpm_table->is_fine_grained) {
- /* There is only 2 levels for fine grained DPM */
- soft_max_level = (soft_max_level >= 1 ? 1 : 0);
- soft_min_level = (soft_min_level >= 1 ? 1 : 0);
- } else {
- if ((soft_max_level >= single_dpm_table->count) ||
- (soft_min_level >= single_dpm_table->count))
- return -EINVAL;
- }
-
- min_freq = single_dpm_table->dpm_levels[soft_min_level].value;
- max_freq = single_dpm_table->dpm_levels[soft_max_level].value;
-
- ret = smu_v14_0_set_soft_freq_limited_range(smu,
- clk_type,
- min_freq,
- max_freq);
- break;
- case SMU_DCEFCLK:
- case SMU_PCIE:
- default:
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_update_pcie_parameters(struct smu_context *smu,
- uint8_t pcie_gen_cap,
- uint8_t pcie_width_cap)
-{
- struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
- struct smu_14_0_pcie_table *pcie_table =
- &dpm_context->dpm_tables.pcie_table;
- uint32_t smu_pcie_arg;
- int ret, i;
-
- for (i = 0; i < pcie_table->num_of_link_levels; i++) {
- if (pcie_table->pcie_gen[i] > pcie_gen_cap)
- pcie_table->pcie_gen[i] = pcie_gen_cap;
- if (pcie_table->pcie_lane[i] > pcie_width_cap)
- pcie_table->pcie_lane[i] = pcie_width_cap;
-
- smu_pcie_arg = i << 16;
- smu_pcie_arg |= pcie_table->pcie_gen[i] << 8;
- smu_pcie_arg |= pcie_table->pcie_lane[i];
-
- ret = smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_OverridePcieParameters,
- smu_pcie_arg,
- NULL);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static const struct smu_temperature_range smu14_thermal_policy[] = {
- {-273150, 99000, 99000, -273150, 99000, 99000, -273150, 99000, 99000},
- { 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000},
-};
-
-static int smu_v14_0_2_get_thermal_temperature_range(struct smu_context *smu,
- struct smu_temperature_range *range)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
- PPTable_t *pptable = smu->smu_table.driver_pptable;
-
- if (amdgpu_sriov_vf(smu->adev))
- return 0;
-
- if (!range)
- return -EINVAL;
-
- memcpy(range, &smu14_thermal_policy[0], sizeof(struct smu_temperature_range));
-
- range->max = pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->edge_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] + CTF_OFFSET_EDGE) *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->hotspot_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->hotspot_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] + CTF_OFFSET_HOTSPOT) *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->mem_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->mem_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] + CTF_OFFSET_MEM)*
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->software_shutdown_temp = powerplay_table->software_shutdown_temp;
- range->software_shutdown_temp_offset = pptable->CustomSkuTable.FanAbnormalTempLimitOffset;
-
- return 0;
-}
-
-static int smu_v14_0_2_populate_umd_state_clk(struct smu_context *smu)
-{
- struct smu_14_0_dpm_context *dpm_context =
- smu->smu_dpm.dpm_context;
- struct smu_14_0_dpm_table *gfx_table =
- &dpm_context->dpm_tables.gfx_table;
- struct smu_14_0_dpm_table *mem_table =
- &dpm_context->dpm_tables.uclk_table;
- struct smu_14_0_dpm_table *soc_table =
- &dpm_context->dpm_tables.soc_table;
- struct smu_14_0_dpm_table *vclk_table =
- &dpm_context->dpm_tables.vclk_table;
- struct smu_14_0_dpm_table *dclk_table =
- &dpm_context->dpm_tables.dclk_table;
- struct smu_14_0_dpm_table *fclk_table =
- &dpm_context->dpm_tables.fclk_table;
- struct smu_umd_pstate_table *pstate_table =
- &smu->pstate_table;
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- DriverReportedClocks_t driver_clocks =
- pptable->SkuTable.DriverReportedClocks;
-
- pstate_table->gfxclk_pstate.min = gfx_table->min;
- if (driver_clocks.GameClockAc &&
- (driver_clocks.GameClockAc < gfx_table->max))
- pstate_table->gfxclk_pstate.peak = driver_clocks.GameClockAc;
- else
- pstate_table->gfxclk_pstate.peak = gfx_table->max;
-
- pstate_table->uclk_pstate.min = mem_table->min;
- pstate_table->uclk_pstate.peak = mem_table->max;
-
- pstate_table->socclk_pstate.min = soc_table->min;
- pstate_table->socclk_pstate.peak = soc_table->max;
-
- pstate_table->vclk_pstate.min = vclk_table->min;
- pstate_table->vclk_pstate.peak = vclk_table->max;
-
- pstate_table->dclk_pstate.min = dclk_table->min;
- pstate_table->dclk_pstate.peak = dclk_table->max;
-
- pstate_table->fclk_pstate.min = fclk_table->min;
- pstate_table->fclk_pstate.peak = fclk_table->max;
-
- if (driver_clocks.BaseClockAc &&
- driver_clocks.BaseClockAc < gfx_table->max)
- pstate_table->gfxclk_pstate.standard = driver_clocks.BaseClockAc;
- else
- pstate_table->gfxclk_pstate.standard = gfx_table->max;
- pstate_table->uclk_pstate.standard = mem_table->max;
- pstate_table->socclk_pstate.standard = soc_table->min;
- pstate_table->vclk_pstate.standard = vclk_table->min;
- pstate_table->dclk_pstate.standard = dclk_table->min;
- pstate_table->fclk_pstate.standard = fclk_table->min;
-
- return 0;
-}
-
-static void smu_v14_0_2_get_unique_id(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- SmuMetrics_t *metrics =
- &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
- struct amdgpu_device *adev = smu->adev;
- uint32_t upper32 = 0, lower32 = 0;
- int ret;
-
- ret = smu_cmn_get_metrics_table(smu, NULL, false);
- if (ret)
- goto out;
-
- upper32 = metrics->PublicSerialNumberUpper;
- lower32 = metrics->PublicSerialNumberLower;
-
-out:
- adev->unique_id = ((uint64_t)upper32 << 32) | lower32;
-}
-
-static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
- uint32_t *current_power_limit,
- uint32_t *default_power_limit,
- uint32_t *max_power_limit,
- uint32_t *min_power_limit)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- CustomSkuTable_t *skutable = &pptable->CustomSkuTable;
- uint32_t power_limit;
- uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
-
- if (smu_v14_0_get_current_power_limit(smu, &power_limit))
- power_limit = smu->adev->pm.ac_power ?
- skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
- skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
-
- if (current_power_limit)
- *current_power_limit = power_limit;
- if (default_power_limit)
- *default_power_limit = power_limit;
-
- if (max_power_limit)
- *max_power_limit = msg_limit;
-
- if (min_power_limit)
- *min_power_limit = 0;
-
- return 0;
-}
-
-static int smu_v14_0_2_get_power_profile_mode(struct smu_context *smu,
- char *buf)
-{
- DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
- DpmActivityMonitorCoeffInt_t *activity_monitor =
- &(activity_monitor_external.DpmActivityMonitorCoeffInt);
- static const char *title[] = {
- "PROFILE_INDEX(NAME)",
- "CLOCK_TYPE(NAME)",
- "FPS",
- "MinActiveFreqType",
- "MinActiveFreq",
- "BoosterFreqType",
- "BoosterFreq",
- "PD_Data_limit_c",
- "PD_Data_error_coeff",
- "PD_Data_error_rate_coeff"};
- int16_t workload_type = 0;
- uint32_t i, size = 0;
- int result = 0;
-
- if (!buf)
- return -EINVAL;
-
- size += sysfs_emit_at(buf, size, "%16s %s %s %s %s %s %s %s %s %s\n",
- title[0], title[1], title[2], title[3], title[4], title[5],
- title[6], title[7], title[8], title[9]);
-
- for (i = 0; i < PP_SMC_POWER_PROFILE_COUNT; i++) {
- /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
- workload_type = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_WORKLOAD,
- i);
- if (workload_type == -ENOTSUPP)
- continue;
- else if (workload_type < 0)
- return -EINVAL;
-
- result = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- workload_type,
- (void *)(&activity_monitor_external),
- false);
- if (result) {
- dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
- return result;
- }
-
- size += sysfs_emit_at(buf, size, "%2d %14s%s:\n",
- i, amdgpu_pp_profile_name[i], (i == smu->power_profile_mode) ? "*" : " ");
-
- size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
- " ",
- 0,
- "GFXCLK",
- activity_monitor->Gfx_FPS,
- activity_monitor->Gfx_MinActiveFreqType,
- activity_monitor->Gfx_MinActiveFreq,
- activity_monitor->Gfx_BoosterFreqType,
- activity_monitor->Gfx_BoosterFreq,
- activity_monitor->Gfx_PD_Data_limit_c,
- activity_monitor->Gfx_PD_Data_error_coeff,
- activity_monitor->Gfx_PD_Data_error_rate_coeff);
-
- size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
- " ",
- 1,
- "FCLK",
- activity_monitor->Fclk_FPS,
- activity_monitor->Fclk_MinActiveFreqType,
- activity_monitor->Fclk_MinActiveFreq,
- activity_monitor->Fclk_BoosterFreqType,
- activity_monitor->Fclk_BoosterFreq,
- activity_monitor->Fclk_PD_Data_limit_c,
- activity_monitor->Fclk_PD_Data_error_coeff,
- activity_monitor->Fclk_PD_Data_error_rate_coeff);
- }
-
- return size;
-}
-
-static int smu_v14_0_2_set_power_profile_mode(struct smu_context *smu,
- long *input,
- uint32_t size)
-{
- DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
- DpmActivityMonitorCoeffInt_t *activity_monitor =
- &(activity_monitor_external.DpmActivityMonitorCoeffInt);
- int workload_type, ret = 0;
-
- smu->power_profile_mode = input[size];
-
- if (smu->power_profile_mode >= PP_SMC_POWER_PROFILE_COUNT) {
- dev_err(smu->adev->dev, "Invalid power profile mode %d\n", smu->power_profile_mode);
- return -EINVAL;
- }
-
- if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
- if (size != 9)
- return -EINVAL;
-
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- WORKLOAD_PPLIB_CUSTOM_BIT,
- (void *)(&activity_monitor_external),
- false);
- if (ret) {
- dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
- return ret;
- }
-
- switch (input[0]) {
- case 0: /* Gfxclk */
- activity_monitor->Gfx_FPS = input[1];
- activity_monitor->Gfx_MinActiveFreqType = input[2];
- activity_monitor->Gfx_MinActiveFreq = input[3];
- activity_monitor->Gfx_BoosterFreqType = input[4];
- activity_monitor->Gfx_BoosterFreq = input[5];
- activity_monitor->Gfx_PD_Data_limit_c = input[6];
- activity_monitor->Gfx_PD_Data_error_coeff = input[7];
- activity_monitor->Gfx_PD_Data_error_rate_coeff = input[8];
- break;
- case 1: /* Fclk */
- activity_monitor->Fclk_FPS = input[1];
- activity_monitor->Fclk_MinActiveFreqType = input[2];
- activity_monitor->Fclk_MinActiveFreq = input[3];
- activity_monitor->Fclk_BoosterFreqType = input[4];
- activity_monitor->Fclk_BoosterFreq = input[5];
- activity_monitor->Fclk_PD_Data_limit_c = input[6];
- activity_monitor->Fclk_PD_Data_error_coeff = input[7];
- activity_monitor->Fclk_PD_Data_error_rate_coeff = input[8];
- break;
- default:
- return -EINVAL;
- }
-
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- WORKLOAD_PPLIB_CUSTOM_BIT,
- (void *)(&activity_monitor_external),
- true);
- if (ret) {
- dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
- return ret;
- }
- }
-
- /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
- workload_type = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_WORKLOAD,
- smu->power_profile_mode);
- if (workload_type < 0)
- return -EINVAL;
-
- return smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_SetWorkloadMask,
- 1 << workload_type,
- NULL);
-}
-
-static int smu_v14_0_2_baco_enter(struct smu_context *smu)
-{
- struct smu_baco_context *smu_baco = &smu->smu_baco;
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev))
- return smu_v14_0_baco_set_armd3_sequence(smu,
- smu_baco->maco_support ? BACO_SEQ_BAMACO : BACO_SEQ_BACO);
- else
- return smu_v14_0_baco_enter(smu);
-}
-
-static int smu_v14_0_2_baco_exit(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev)) {
- /* Wait for PMFW handling for the Dstate change */
- usleep_range(10000, 11000);
- return smu_v14_0_baco_set_armd3_sequence(smu, BACO_SEQ_ULPS);
- } else {
- return smu_v14_0_baco_exit(smu);
- }
-}
-
-static bool smu_v14_0_2_is_mode1_reset_supported(struct smu_context *smu)
-{
- // TODO
-
- return true;
-}
-
-static int smu_v14_0_2_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msg, int num_msgs)
-{
- struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(i2c_adap);
- struct amdgpu_device *adev = smu_i2c->adev;
- struct smu_context *smu = adev->powerplay.pp_handle;
- struct smu_table_context *smu_table = &smu->smu_table;
- struct smu_table *table = &smu_table->driver_table;
- SwI2cRequest_t *req, *res = (SwI2cRequest_t *)table->cpu_addr;
- int i, j, r, c;
- u16 dir;
-
- if (!adev->pm.dpm_enabled)
- return -EBUSY;
-
- req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- req->I2CcontrollerPort = smu_i2c->port;
- req->I2CSpeed = I2C_SPEED_FAST_400K;
- req->SlaveAddress = msg[0].addr << 1; /* wants an 8-bit address */
- dir = msg[0].flags & I2C_M_RD;
-
- for (c = i = 0; i < num_msgs; i++) {
- for (j = 0; j < msg[i].len; j++, c++) {
- SwI2cCmd_t *cmd = &req->SwI2cCmds[c];
-
- if (!(msg[i].flags & I2C_M_RD)) {
- /* write */
- cmd->CmdConfig |= CMDCONFIG_READWRITE_MASK;
- cmd->ReadWriteData = msg[i].buf[j];
- }
-
- if ((dir ^ msg[i].flags) & I2C_M_RD) {
- /* The direction changes.
- */
- dir = msg[i].flags & I2C_M_RD;
- cmd->CmdConfig |= CMDCONFIG_RESTART_MASK;
- }
-
- req->NumCmds++;
-
- /*
- * Insert STOP if we are at the last byte of either last
- * message for the transaction or the client explicitly
- * requires a STOP at this particular message.
- */
- if ((j == msg[i].len - 1) &&
- ((i == num_msgs - 1) || (msg[i].flags & I2C_M_STOP))) {
- cmd->CmdConfig &= ~CMDCONFIG_RESTART_MASK;
- cmd->CmdConfig |= CMDCONFIG_STOP_MASK;
- }
- }
- }
- mutex_lock(&adev->pm.mutex);
- r = smu_cmn_update_table(smu, SMU_TABLE_I2C_COMMANDS, 0, req, true);
- mutex_unlock(&adev->pm.mutex);
- if (r)
- goto fail;
-
- for (c = i = 0; i < num_msgs; i++) {
- if (!(msg[i].flags & I2C_M_RD)) {
- c += msg[i].len;
- continue;
- }
- for (j = 0; j < msg[i].len; j++, c++) {
- SwI2cCmd_t *cmd = &res->SwI2cCmds[c];
-
- msg[i].buf[j] = cmd->ReadWriteData;
- }
- }
- r = num_msgs;
-fail:
- kfree(req);
- return r;
-}
-
-static u32 smu_v14_0_2_i2c_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm smu_v14_0_2_i2c_algo = {
- .master_xfer = smu_v14_0_2_i2c_xfer,
- .functionality = smu_v14_0_2_i2c_func,
-};
-
-static const struct i2c_adapter_quirks smu_v14_0_2_i2c_control_quirks = {
- .flags = I2C_AQ_COMB | I2C_AQ_COMB_SAME_ADDR | I2C_AQ_NO_ZERO_LEN,
- .max_read_len = MAX_SW_I2C_COMMANDS,
- .max_write_len = MAX_SW_I2C_COMMANDS,
- .max_comb_1st_msg_len = 2,
- .max_comb_2nd_msg_len = MAX_SW_I2C_COMMANDS - 2,
-};
-
-static int smu_v14_0_2_i2c_control_init(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
- int res, i;
-
- for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- smu_i2c->adev = adev;
- smu_i2c->port = i;
- mutex_init(&smu_i2c->mutex);
- control->owner = THIS_MODULE;
- control->dev.parent = &adev->pdev->dev;
- control->algo = &smu_v14_0_2_i2c_algo;
- snprintf(control->name, sizeof(control->name), "AMDGPU SMU %d", i);
- control->quirks = &smu_v14_0_2_i2c_control_quirks;
- i2c_set_adapdata(control, smu_i2c);
-
- res = i2c_add_adapter(control);
- if (res) {
- DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
- goto Out_err;
- }
- }
-
- /* assign the buses used for the FRU EEPROM and RAS EEPROM */
- /* XXX ideally this would be something in a vbios data table */
- adev->pm.ras_eeprom_i2c_bus = &adev->pm.smu_i2c[1].adapter;
- adev->pm.fru_eeprom_i2c_bus = &adev->pm.smu_i2c[0].adapter;
-
- return 0;
-Out_err:
- for ( ; i >= 0; i--) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- i2c_del_adapter(control);
- }
- return res;
-}
-
-static void smu_v14_0_2_i2c_control_fini(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
- int i;
-
- for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- i2c_del_adapter(control);
- }
- adev->pm.ras_eeprom_i2c_bus = NULL;
- adev->pm.fru_eeprom_i2c_bus = NULL;
-}
-
-static int smu_v14_0_2_set_mp1_state(struct smu_context *smu,
- enum pp_mp1_state mp1_state)
-{
- int ret;
-
- switch (mp1_state) {
- case PP_MP1_STATE_UNLOAD:
- ret = smu_cmn_set_mp1_state(smu, mp1_state);
- break;
- default:
- /* Ignore others */
- ret = 0;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_set_df_cstate(struct smu_context *smu,
- enum pp_df_cstate state)
-{
- return smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_DFCstateControl,
- state,
- NULL);
-}
-
-static int smu_v14_0_2_mode1_reset(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_cmn_send_debug_smc_msg(smu, DEBUGSMC_MSG_Mode1Reset);
- if (!ret) {
- if (amdgpu_emu_mode == 1)
- msleep(50000);
- else
- msleep(1000);
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_mode2_reset(struct smu_context *smu)
-{
- int ret = 0;
-
- // TODO
-
- return ret;
-}
-
-static int smu_v14_0_2_enable_gfx_features(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(14, 0, 2))
- return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_EnableAllSmuFeatures,
- FEATURE_PWR_GFX, NULL);
- else
- return -EOPNOTSUPP;
-}
-
-static void smu_v14_0_2_set_smu_mailbox_registers(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- smu->param_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_82);
- smu->msg_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_66);
- smu->resp_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_90);
-
- smu->debug_param_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_53);
- smu->debug_msg_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_75);
- smu->debug_resp_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_54);
-}
-
-static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
- void **table)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_3 *gpu_metrics =
- (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
- SmuMetricsExternal_t metrics_ext;
- SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- &metrics_ext,
- true);
- if (ret)
- return ret;
-
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
-
- gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
- gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
- gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
- gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
- gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
- gpu_metrics->temperature_vrmem = max(metrics->AvgTemperature[TEMP_VR_MEM0],
- metrics->AvgTemperature[TEMP_VR_MEM1]);
-
- gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
- gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
- gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage,
- metrics->Vcn1ActivityPercentage);
-
- gpu_metrics->average_socket_power = metrics->AverageSocketPower;
- gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
-
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
- else
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
-
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
- else
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
-
- gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
- gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
- gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
- gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
-
- gpu_metrics->current_gfxclk = gpu_metrics->average_gfxclk_frequency;
- gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK];
- gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK];
- gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
- gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_0];
-
- gpu_metrics->throttle_status =
- smu_v14_0_2_get_throttler_status(metrics);
- gpu_metrics->indep_throttle_status =
- smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
- smu_v14_0_2_throttler_map);
-
- gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
-
- gpu_metrics->pcie_link_width = metrics->PcieWidth;
- if ((metrics->PcieRate - 1) > LINK_SPEED_MAX)
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(1);
- else
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(metrics->PcieRate);
-
- gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
-
- gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_VDD_SOC];
- gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VDDIO_MEM];
-
- *table = (void *)gpu_metrics;
-
- return sizeof(struct gpu_metrics_v1_3);
-}
-
-static void smu_v14_0_2_dump_od_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- struct amdgpu_device *adev = smu->adev;
-
- dev_dbg(adev->dev, "OD: Gfxclk: (%d, %d)\n", od_table->OverDriveTable.GfxclkFmin,
- od_table->OverDriveTable.GfxclkFmax);
- dev_dbg(adev->dev, "OD: Uclk: (%d, %d)\n", od_table->OverDriveTable.UclkFmin,
- od_table->OverDriveTable.UclkFmax);
-}
-
-static int smu_v14_0_2_upload_overdrive_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- int ret;
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_OVERDRIVE,
- 0,
- (void *)od_table,
- true);
- if (ret)
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
-
- return ret;
-}
-
-static void smu_v14_0_2_set_supported_od_feature_mask(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- adev->pm.od_feature_mask |= OD_OPS_SUPPORT_FAN_CURVE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_CURVE_SET |
- OD_OPS_SUPPORT_ACOUSTIC_LIMIT_THRESHOLD_RETRIEVE |
- OD_OPS_SUPPORT_ACOUSTIC_LIMIT_THRESHOLD_SET |
- OD_OPS_SUPPORT_ACOUSTIC_TARGET_THRESHOLD_RETRIEVE |
- OD_OPS_SUPPORT_ACOUSTIC_TARGET_THRESHOLD_SET |
- OD_OPS_SUPPORT_FAN_TARGET_TEMPERATURE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_TARGET_TEMPERATURE_SET |
- OD_OPS_SUPPORT_FAN_MINIMUM_PWM_RETRIEVE |
- OD_OPS_SUPPORT_FAN_MINIMUM_PWM_SET;
-}
-
-static int smu_v14_0_2_get_overdrive_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- int ret;
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_OVERDRIVE,
- 0,
- (void *)od_table,
- false);
- if (ret)
- dev_err(smu->adev->dev, "Failed to get overdrive table!\n");
-
- return ret;
-}
-
-static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
-{
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
- OverDriveTableExternal_t *boot_od_table =
- (OverDriveTableExternal_t *)smu->smu_table.boot_overdrive_table;
- OverDriveTableExternal_t *user_od_table =
- (OverDriveTableExternal_t *)smu->smu_table.user_overdrive_table;
- OverDriveTableExternal_t user_od_table_bak;
- int ret;
- int i;
-
- ret = smu_v14_0_2_get_overdrive_table(smu, boot_od_table);
- if (ret)
- return ret;
-
- smu_v14_0_2_dump_od_table(smu, boot_od_table);
-
- memcpy(od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
-
- /*
- * For S3/S4/Runpm resume, we need to setup those overdrive tables again,
- * but we have to preserve user defined values in "user_od_table".
- */
- if (!smu->adev->in_suspend) {
- memcpy(user_od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
- smu->user_dpm_profile.user_od = false;
- } else if (smu->user_dpm_profile.user_od) {
- memcpy(&user_od_table_bak,
- user_od_table,
- sizeof(OverDriveTableExternal_t));
- memcpy(user_od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
- user_od_table->OverDriveTable.GfxclkFmin =
- user_od_table_bak.OverDriveTable.GfxclkFmin;
- user_od_table->OverDriveTable.GfxclkFmax =
- user_od_table_bak.OverDriveTable.GfxclkFmax;
- user_od_table->OverDriveTable.UclkFmin =
- user_od_table_bak.OverDriveTable.UclkFmin;
- user_od_table->OverDriveTable.UclkFmax =
- user_od_table_bak.OverDriveTable.UclkFmax;
- for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++)
- user_od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[i] =
- user_od_table_bak.OverDriveTable.VoltageOffsetPerZoneBoundary[i];
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++) {
- user_od_table->OverDriveTable.FanLinearTempPoints[i] =
- user_od_table_bak.OverDriveTable.FanLinearTempPoints[i];
- user_od_table->OverDriveTable.FanLinearPwmPoints[i] =
- user_od_table_bak.OverDriveTable.FanLinearPwmPoints[i];
- }
- user_od_table->OverDriveTable.AcousticLimitRpmThreshold =
- user_od_table_bak.OverDriveTable.AcousticLimitRpmThreshold;
- user_od_table->OverDriveTable.AcousticTargetRpmThreshold =
- user_od_table_bak.OverDriveTable.AcousticTargetRpmThreshold;
- user_od_table->OverDriveTable.FanTargetTemperature =
- user_od_table_bak.OverDriveTable.FanTargetTemperature;
- user_od_table->OverDriveTable.FanMinimumPwm =
- user_od_table_bak.OverDriveTable.FanMinimumPwm;
- }
-
- smu_v14_0_2_set_supported_od_feature_mask(smu);
-
- return 0;
-}
-
-static int smu_v14_0_2_restore_user_od_settings(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table = table_context->overdrive_table;
- OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
- int res;
-
- user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
- BIT(PP_OD_FEATURE_UCLK_BIT) |
- BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
- BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
- memcpy(od_table, user_od_table, sizeof(OverDriveTableExternal_t));
-
- return res;
-}
-
-static int smu_v14_0_2_od_restore_table_single(struct smu_context *smu, long input)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *boot_overdrive_table =
- (OverDriveTableExternal_t *)table_context->boot_overdrive_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- struct amdgpu_device *adev = smu->adev;
- int i;
-
- switch (input) {
- case PP_OD_EDIT_FAN_CURVE:
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS; i++) {
- od_table->OverDriveTable.FanLinearTempPoints[i] =
- boot_overdrive_table->OverDriveTable.FanLinearTempPoints[i];
- od_table->OverDriveTable.FanLinearPwmPoints[i] =
- boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
- }
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_ACOUSTIC_LIMIT:
- od_table->OverDriveTable.AcousticLimitRpmThreshold =
- boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_ACOUSTIC_TARGET:
- od_table->OverDriveTable.AcousticTargetRpmThreshold =
- boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
- od_table->OverDriveTable.FanTargetTemperature =
- boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_FAN_MINIMUM_PWM:
- od_table->OverDriveTable.FanMinimumPwm =
- boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- default:
- dev_info(adev->dev, "Invalid table index: %ld\n", input);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
- enum PP_OD_DPM_TABLE_COMMAND type,
- long input[],
- uint32_t size)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- struct amdgpu_device *adev = smu->adev;
- uint32_t offset_of_voltageoffset;
- int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
- int i, ret = 0;
-
- switch (type) {
- case PP_OD_EDIT_SCLK_VDDC_TABLE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
- dev_warn(adev->dev, "GFXCLK_LIMITS setting not supported!\n");
- return -ENOTSUPP;
- }
-
- for (i = 0; i < size; i += 2) {
- if (i + 2 > size) {
- dev_info(adev->dev, "invalid number of input parameters %d\n", size);
- return -EINVAL;
- }
-
- switch (input[i]) {
- case 0:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMIN,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "GfxclkFmin (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.GfxclkFmin = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_GFXCLK_BIT;
- break;
-
- case 1:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMAX,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "GfxclkFmax (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.GfxclkFmax = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_GFXCLK_BIT;
- break;
-
- default:
- dev_info(adev->dev, "Invalid SCLK_VDDC_TABLE index: %ld\n", input[i]);
- dev_info(adev->dev, "Supported indices: [0:min,1:max]\n");
- return -EINVAL;
- }
- }
-
- if (od_table->OverDriveTable.GfxclkFmin > od_table->OverDriveTable.GfxclkFmax) {
- dev_err(adev->dev,
- "Invalid setting: GfxclkFmin(%u) is bigger than GfxclkFmax(%u)\n",
- (uint32_t)od_table->OverDriveTable.GfxclkFmin,
- (uint32_t)od_table->OverDriveTable.GfxclkFmax);
- return -EINVAL;
- }
- break;
-
- case PP_OD_EDIT_MCLK_VDDC_TABLE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
- dev_warn(adev->dev, "UCLK_LIMITS setting not supported!\n");
- return -ENOTSUPP;
- }
-
- for (i = 0; i < size; i += 2) {
- if (i + 2 > size) {
- dev_info(adev->dev, "invalid number of input parameters %d\n", size);
- return -EINVAL;
- }
-
- switch (input[i]) {
- case 0:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMIN,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "UclkFmin (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.UclkFmin = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_UCLK_BIT;
- break;
-
- case 1:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMAX,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "UclkFmax (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.UclkFmax = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_UCLK_BIT;
- break;
-
- default:
- dev_info(adev->dev, "Invalid MCLK_VDDC_TABLE index: %ld\n", input[i]);
- dev_info(adev->dev, "Supported indices: [0:min,1:max]\n");
- return -EINVAL;
- }
- }
-
- if (od_table->OverDriveTable.UclkFmin > od_table->OverDriveTable.UclkFmax) {
- dev_err(adev->dev,
- "Invalid setting: UclkFmin(%u) is bigger than UclkFmax(%u)\n",
- (uint32_t)od_table->OverDriveTable.UclkFmin,
- (uint32_t)od_table->OverDriveTable.UclkFmax);
- return -EINVAL;
- }
- break;
-
- case PP_OD_EDIT_VDDGFX_OFFSET:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
- dev_warn(adev->dev, "Gfx offset setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFX_VF_CURVE,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "Voltage offset (%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++)
- od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[i] = input[0];
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_CURVE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- if (input[0] >= NUM_OD_FAN_MAX_POINTS - 1 ||
- input[0] < 0)
- return -EINVAL;
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_TEMP,
- &minimum,
- &maximum);
- if (input[1] < minimum ||
- input[1] > maximum) {
- dev_info(adev->dev, "Fan curve temp setting(%ld) must be within [%d, %d]!\n",
- input[1], minimum, maximum);
- return -EINVAL;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_PWM,
- &minimum,
- &maximum);
- if (input[2] < minimum ||
- input[2] > maximum) {
- dev_info(adev->dev, "Fan curve pwm setting(%ld) must be within [%d, %d]!\n",
- input[2], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanLinearTempPoints[input[0]] = input[1];
- od_table->OverDriveTable.FanLinearPwmPoints[input[0]] = input[2];
- od_table->OverDriveTable.FanMode = FAN_MODE_MANUAL_LINEAR;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_ACOUSTIC_LIMIT:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "acoustic limit threshold setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.AcousticLimitRpmThreshold = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_ACOUSTIC_TARGET:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "acoustic target threshold setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.AcousticTargetRpmThreshold = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "fan target temperature setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanTargetTemperature = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_MINIMUM_PWM:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_MINIMUM_PWM,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "fan minimum pwm setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanMinimumPwm = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_RESTORE_DEFAULT_TABLE:
- if (size == 1) {
- ret = smu_v14_0_2_od_restore_table_single(smu, input[0]);
- if (ret)
- return ret;
- } else {
- feature_ctrlmask = od_table->OverDriveTable.FeatureCtrlMask;
- memcpy(od_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t));
- od_table->OverDriveTable.FeatureCtrlMask = feature_ctrlmask;
- }
- fallthrough;
- case PP_OD_COMMIT_DPM_TABLE:
- /*
- * The member below instructs PMFW the settings focused in
- * this single operation.
- * `uint32_t FeatureCtrlMask;`
- * It does not contain actual informations about user's custom
- * settings. Thus we do not cache it.
- */
- offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset)) {
- smu_v14_0_2_dump_od_table(smu, od_table);
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
-
- od_table->OverDriveTable.FeatureCtrlMask = 0;
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
- (u8 *)od_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
-
- if (!memcmp(table_context->user_overdrive_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t)))
- smu->user_dpm_profile.user_od = false;
- else
- smu->user_dpm_profile.user_od = true;
- }
- break;
-
- default:
- return -ENOSYS;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_set_power_limit(struct smu_context *smu,
- enum smu_ppt_limit_type limit_type,
- uint32_t limit)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- int ret = 0;
-
- if (limit_type != SMU_DEFAULT_PPT_LIMIT)
- return -EINVAL;
-
- if (limit <= msg_limit) {
- if (smu->current_power_limit > msg_limit) {
- od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
- }
- return smu_v14_0_set_power_limit(smu, limit_type, limit);
- } else if (smu->od_enabled) {
- ret = smu_v14_0_set_power_limit(smu, limit_type, msg_limit);
- if (ret)
- return ret;
-
- od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
-
- smu->current_power_limit = limit;
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct pptable_funcs smu_v14_0_2_ppt_funcs = {
- .get_allowed_feature_mask = smu_v14_0_2_get_allowed_feature_mask,
- .set_default_dpm_table = smu_v14_0_2_set_default_dpm_table,
- .i2c_init = smu_v14_0_2_i2c_control_init,
- .i2c_fini = smu_v14_0_2_i2c_control_fini,
- .is_dpm_running = smu_v14_0_2_is_dpm_running,
- .dump_pptable = smu_v14_0_2_dump_pptable,
- .init_microcode = smu_v14_0_init_microcode,
- .load_microcode = smu_v14_0_load_microcode,
- .fini_microcode = smu_v14_0_fini_microcode,
- .init_smc_tables = smu_v14_0_2_init_smc_tables,
- .fini_smc_tables = smu_v14_0_fini_smc_tables,
- .init_power = smu_v14_0_init_power,
- .fini_power = smu_v14_0_fini_power,
- .check_fw_status = smu_v14_0_check_fw_status,
- .setup_pptable = smu_v14_0_2_setup_pptable,
- .check_fw_version = smu_v14_0_check_fw_version,
- .write_pptable = smu_cmn_write_pptable,
- .set_driver_table_location = smu_v14_0_set_driver_table_location,
- .system_features_control = smu_v14_0_system_features_control,
- .set_allowed_mask = smu_v14_0_set_allowed_mask,
- .get_enabled_mask = smu_cmn_get_enabled_mask,
- .dpm_set_vcn_enable = smu_v14_0_set_vcn_enable,
- .dpm_set_jpeg_enable = smu_v14_0_set_jpeg_enable,
- .get_dpm_ultimate_freq = smu_v14_0_2_get_dpm_ultimate_freq,
- .get_vbios_bootup_values = smu_v14_0_get_vbios_bootup_values,
- .read_sensor = smu_v14_0_2_read_sensor,
- .feature_is_enabled = smu_cmn_feature_is_enabled,
- .print_clk_levels = smu_v14_0_2_print_clk_levels,
- .force_clk_levels = smu_v14_0_2_force_clk_levels,
- .update_pcie_parameters = smu_v14_0_2_update_pcie_parameters,
- .get_thermal_temperature_range = smu_v14_0_2_get_thermal_temperature_range,
- .register_irq_handler = smu_v14_0_register_irq_handler,
- .enable_thermal_alert = smu_v14_0_enable_thermal_alert,
- .disable_thermal_alert = smu_v14_0_disable_thermal_alert,
- .notify_memory_pool_location = smu_v14_0_notify_memory_pool_location,
- .get_gpu_metrics = smu_v14_0_2_get_gpu_metrics,
- .set_soft_freq_limited_range = smu_v14_0_set_soft_freq_limited_range,
- .set_default_od_settings = smu_v14_0_2_set_default_od_settings,
- .restore_user_od_settings = smu_v14_0_2_restore_user_od_settings,
- .od_edit_dpm_table = smu_v14_0_2_od_edit_dpm_table,
- .init_pptable_microcode = smu_v14_0_init_pptable_microcode,
- .populate_umd_state_clk = smu_v14_0_2_populate_umd_state_clk,
- .set_performance_level = smu_v14_0_set_performance_level,
- .gfx_off_control = smu_v14_0_gfx_off_control,
- .get_unique_id = smu_v14_0_2_get_unique_id,
- .get_power_limit = smu_v14_0_2_get_power_limit,
- .set_power_limit = smu_v14_0_2_set_power_limit,
- .set_power_source = smu_v14_0_set_power_source,
- .get_power_profile_mode = smu_v14_0_2_get_power_profile_mode,
- .set_power_profile_mode = smu_v14_0_2_set_power_profile_mode,
- .run_btc = smu_v14_0_run_btc,
- .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
- .set_pp_feature_mask = smu_cmn_set_pp_feature_mask,
- .set_tool_table_location = smu_v14_0_set_tool_table_location,
- .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,
- .mode1_reset = smu_v14_0_2_mode1_reset,
- .mode2_reset = smu_v14_0_2_mode2_reset,
- .enable_gfx_features = smu_v14_0_2_enable_gfx_features,
- .set_mp1_state = smu_v14_0_2_set_mp1_state,
- .set_df_cstate = smu_v14_0_2_set_df_cstate,
-#if 0
- .gpo_control = smu_v14_0_gpo_control,
-#endif
-};
-
-void smu_v14_0_2_set_ppt_funcs(struct smu_context *smu)
-{
- smu->ppt_funcs = &smu_v14_0_2_ppt_funcs;
- smu->message_map = smu_v14_0_2_message_map;
- smu->clock_map = smu_v14_0_2_clk_map;
- smu->feature_map = smu_v14_0_2_feature_mask_map;
- smu->table_map = smu_v14_0_2_table_map;
- smu->pwr_src_map = smu_v14_0_2_pwr_src_map;
- smu->workload_map = smu_v14_0_2_workload_map;
- smu_v14_0_2_set_smu_mailbox_registers(smu);
-}
diff --git a/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage b/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage
deleted file mode 100644
index 6878263c313c..000000000000
--- a/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage
+++ /dev/null
@@ -1,2956 +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.
- *
- */
-
-#define SWSMU_CODE_LAYER_L2
-
-#include <linux/firmware.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include "amdgpu.h"
-#include "amdgpu_smu.h"
-#include "atomfirmware.h"
-#include "amdgpu_atomfirmware.h"
-#include "amdgpu_atombios.h"
-#include "smu_v14_0.h"
-#include "smu14_driver_if_v14_0.h"
-#include "soc15_common.h"
-#include "atom.h"
-#include "smu_v14_0_2_ppt.h"
-#include "smu_v14_0_2_pptable.h"
-#include "smu_v14_0_2_ppsmc.h"
-#include "mp/mp_14_0_2_offset.h"
-#include "mp/mp_14_0_2_sh_mask.h"
-
-#include "smu_cmn.h"
-#include "amdgpu_ras.h"
-
-/*
- * DO NOT use these for err/warn/info/debug messages.
- * Use dev_err, dev_warn, dev_info and dev_dbg instead.
- * They are more MGPU friendly.
- */
-#undef pr_err
-#undef pr_warn
-#undef pr_info
-#undef pr_debug
-
-#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
-
-#define FEATURE_MASK(feature) (1ULL << feature)
-#define SMC_DPM_FEATURE ( \
- FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_UCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_LINK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_FCLK_BIT))
-
-#define MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE 0x4000
-#define DEBUGSMC_MSG_Mode1Reset 2
-#define LINK_SPEED_MAX 3
-<<<<<<<
-=======
-
-#define PP_OD_FEATURE_GFXCLK_FMIN 0
-#define PP_OD_FEATURE_GFXCLK_FMAX 1
-#define PP_OD_FEATURE_UCLK_FMIN 2
-#define PP_OD_FEATURE_UCLK_FMAX 3
-#define PP_OD_FEATURE_GFX_VF_CURVE 4
-#define PP_OD_FEATURE_FAN_CURVE_TEMP 5
-#define PP_OD_FEATURE_FAN_CURVE_PWM 6
-#define PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT 7
-#define PP_OD_FEATURE_FAN_ACOUSTIC_TARGET 8
-#define PP_OD_FEATURE_FAN_TARGET_TEMPERATURE 9
-#define PP_OD_FEATURE_FAN_MINIMUM_PWM 10
->>>>>>>
-
-static struct cmn2asic_msg_mapping smu_v14_0_2_message_map[SMU_MSG_MAX_COUNT] = {
- MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
- MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
- MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
- MSG_MAP(SetAllowedFeaturesMaskLow, PPSMC_MSG_SetAllowedFeaturesMaskLow, 0),
- MSG_MAP(SetAllowedFeaturesMaskHigh, PPSMC_MSG_SetAllowedFeaturesMaskHigh, 0),
- MSG_MAP(EnableAllSmuFeatures, PPSMC_MSG_EnableAllSmuFeatures, 0),
- MSG_MAP(DisableAllSmuFeatures, PPSMC_MSG_DisableAllSmuFeatures, 0),
- MSG_MAP(EnableSmuFeaturesLow, PPSMC_MSG_EnableSmuFeaturesLow, 1),
- MSG_MAP(EnableSmuFeaturesHigh, PPSMC_MSG_EnableSmuFeaturesHigh, 1),
- MSG_MAP(DisableSmuFeaturesLow, PPSMC_MSG_DisableSmuFeaturesLow, 1),
- MSG_MAP(DisableSmuFeaturesHigh, PPSMC_MSG_DisableSmuFeaturesHigh, 1),
- MSG_MAP(GetEnabledSmuFeaturesLow, PPSMC_MSG_GetRunningSmuFeaturesLow, 1),
- MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetRunningSmuFeaturesHigh, 1),
- MSG_MAP(SetWorkloadMask, PPSMC_MSG_SetWorkloadMask, 1),
- MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0),
- MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
- MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
- MSG_MAP(SetToolsDramAddrHigh, PPSMC_MSG_SetToolsDramAddrHigh, 0),
- MSG_MAP(SetToolsDramAddrLow, PPSMC_MSG_SetToolsDramAddrLow, 0),
- MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
- MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 0),
- MSG_MAP(UseDefaultPPTable, PPSMC_MSG_UseDefaultPPTable, 0),
- MSG_MAP(RunDcBtc, PPSMC_MSG_RunDcBtc, 0),
- MSG_MAP(EnterBaco, PPSMC_MSG_EnterBaco, 0),
- MSG_MAP(ExitBaco, PPSMC_MSG_ExitBaco, 0),
- MSG_MAP(SetSoftMinByFreq, PPSMC_MSG_SetSoftMinByFreq, 1),
- MSG_MAP(SetSoftMaxByFreq, PPSMC_MSG_SetSoftMaxByFreq, 1),
- MSG_MAP(SetHardMinByFreq, PPSMC_MSG_SetHardMinByFreq, 1),
- MSG_MAP(SetHardMaxByFreq, PPSMC_MSG_SetHardMaxByFreq, 0),
- MSG_MAP(GetMinDpmFreq, PPSMC_MSG_GetMinDpmFreq, 1),
- MSG_MAP(GetMaxDpmFreq, PPSMC_MSG_GetMaxDpmFreq, 1),
- MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1),
- MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 0),
- MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 0),
- MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 0),
- MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 0),
- MSG_MAP(GetDcModeMaxDpmFreq, PPSMC_MSG_GetDcModeMaxDpmFreq, 1),
- MSG_MAP(OverridePcieParameters, PPSMC_MSG_OverridePcieParameters, 0),
- MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0),
- MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0),
- MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0),
- MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 0),
- MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 0),
- MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm, 0),
- MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0),
- MSG_MAP(NotifyPowerSource, PPSMC_MSG_NotifyPowerSource, 0),
- MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 0),
- MSG_MAP(DFCstateControl, PPSMC_MSG_SetExternalClientDfCstateAllow, 0),
- MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0),
- MSG_MAP(SetNumBadMemoryPagesRetired, PPSMC_MSG_SetNumBadMemoryPagesRetired, 0),
- MSG_MAP(SetBadMemoryPagesRetiredFlagsPerChannel,
- PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel, 0),
- MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0),
- MSG_MAP(ReenableAcDcInterrupt, PPSMC_MSG_ReenableAcDcInterrupt, 0),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_clk_map[SMU_CLK_COUNT] = {
- CLK_MAP(GFXCLK, PPCLK_GFXCLK),
- CLK_MAP(SCLK, PPCLK_GFXCLK),
- CLK_MAP(SOCCLK, PPCLK_SOCCLK),
- CLK_MAP(FCLK, PPCLK_FCLK),
- CLK_MAP(UCLK, PPCLK_UCLK),
- CLK_MAP(MCLK, PPCLK_UCLK),
- CLK_MAP(VCLK, PPCLK_VCLK_0),
- CLK_MAP(DCLK, PPCLK_DCLK_0),
- CLK_MAP(DCEFCLK, PPCLK_DCFCLK),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_feature_mask_map[SMU_FEATURE_COUNT] = {
- FEA_MAP(FW_DATA_READ),
- FEA_MAP(DPM_GFXCLK),
- FEA_MAP(DPM_GFX_POWER_OPTIMIZER),
- FEA_MAP(DPM_UCLK),
- FEA_MAP(DPM_FCLK),
- FEA_MAP(DPM_SOCCLK),
- FEA_MAP(DPM_LINK),
- FEA_MAP(DPM_DCN),
- FEA_MAP(VMEMP_SCALING),
- FEA_MAP(VDDIO_MEM_SCALING),
- FEA_MAP(DS_GFXCLK),
- FEA_MAP(DS_SOCCLK),
- FEA_MAP(DS_FCLK),
- FEA_MAP(DS_LCLK),
- FEA_MAP(DS_DCFCLK),
- FEA_MAP(DS_UCLK),
- FEA_MAP(GFX_ULV),
- FEA_MAP(FW_DSTATE),
- FEA_MAP(GFXOFF),
- FEA_MAP(BACO),
- FEA_MAP(MM_DPM),
- FEA_MAP(SOC_MPCLK_DS),
- FEA_MAP(BACO_MPCLK_DS),
- FEA_MAP(THROTTLERS),
- FEA_MAP(SMARTSHIFT),
- FEA_MAP(GTHR),
- FEA_MAP(ACDC),
- FEA_MAP(VR0HOT),
- FEA_MAP(FW_CTF),
- FEA_MAP(FAN_CONTROL),
- FEA_MAP(GFX_DCS),
- FEA_MAP(GFX_READ_MARGIN),
- FEA_MAP(LED_DISPLAY),
- FEA_MAP(GFXCLK_SPREAD_SPECTRUM),
- FEA_MAP(OUT_OF_BAND_MONITOR),
- FEA_MAP(OPTIMIZED_VMIN),
- FEA_MAP(GFX_IMU),
- FEA_MAP(BOOT_TIME_CAL),
- FEA_MAP(GFX_PCC_DFLL),
- FEA_MAP(SOC_CG),
- FEA_MAP(DF_CSTATE),
- FEA_MAP(GFX_EDC),
- FEA_MAP(BOOT_POWER_OPT),
- FEA_MAP(CLOCK_POWER_DOWN_BYPASS),
- FEA_MAP(DS_VCN),
- FEA_MAP(BACO_CG),
- FEA_MAP(MEM_TEMP_READ),
- FEA_MAP(ATHUB_MMHUB_PG),
- FEA_MAP(SOC_PCC),
- [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
- [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
- [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_table_map[SMU_TABLE_COUNT] = {
- TAB_MAP(PPTABLE),
- TAB_MAP(WATERMARKS),
- TAB_MAP(AVFS_PSM_DEBUG),
- TAB_MAP(PMSTATUSLOG),
- TAB_MAP(SMU_METRICS),
- TAB_MAP(DRIVER_SMU_CONFIG),
- TAB_MAP(ACTIVITY_MONITOR_COEFF),
- [SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
- TAB_MAP(I2C_COMMANDS),
- TAB_MAP(ECCINFO),
- TAB_MAP(OVERDRIVE),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
- PWR_MAP(AC),
- PWR_MAP(DC),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_workload_map[PP_SMC_POWER_PROFILE_COUNT] = {
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT, WORKLOAD_PPLIB_DEFAULT_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_FULLSCREEN3D, WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_POWERSAVING, WORKLOAD_PPLIB_POWER_SAVING_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VIDEO, WORKLOAD_PPLIB_VIDEO_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VR, WORKLOAD_PPLIB_VR_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_COMPUTE, WORKLOAD_PPLIB_COMPUTE_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_CUSTOM, WORKLOAD_PPLIB_CUSTOM_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_WINDOW3D, WORKLOAD_PPLIB_WINDOW_3D_BIT),
-};
-
-static const uint8_t smu_v14_0_2_throttler_map[] = {
- [THROTTLER_PPT0_BIT] = (SMU_THROTTLER_PPT0_BIT),
- [THROTTLER_PPT1_BIT] = (SMU_THROTTLER_PPT1_BIT),
- [THROTTLER_PPT2_BIT] = (SMU_THROTTLER_PPT2_BIT),
- [THROTTLER_PPT3_BIT] = (SMU_THROTTLER_PPT3_BIT),
- [THROTTLER_TDC_GFX_BIT] = (SMU_THROTTLER_TDC_GFX_BIT),
- [THROTTLER_TDC_SOC_BIT] = (SMU_THROTTLER_TDC_SOC_BIT),
- [THROTTLER_TEMP_EDGE_BIT] = (SMU_THROTTLER_TEMP_EDGE_BIT),
- [THROTTLER_TEMP_HOTSPOT_BIT] = (SMU_THROTTLER_TEMP_HOTSPOT_BIT),
- [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
- [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
- [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
- [THROTTLER_TEMP_VR_MEM0_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
- [THROTTLER_TEMP_VR_MEM1_BIT] = (SMU_THROTTLER_TEMP_VR_MEM1_BIT),
- [THROTTLER_TEMP_LIQUID0_BIT] = (SMU_THROTTLER_TEMP_LIQUID0_BIT),
- [THROTTLER_TEMP_LIQUID1_BIT] = (SMU_THROTTLER_TEMP_LIQUID1_BIT),
- [THROTTLER_GFX_APCC_PLUS_BIT] = (SMU_THROTTLER_APCC_BIT),
- [THROTTLER_FIT_BIT] = (SMU_THROTTLER_FIT_BIT),
-};
-
-static int
-smu_v14_0_2_get_allowed_feature_mask(struct smu_context *smu,
- uint32_t *feature_mask, uint32_t num)
-{
- struct amdgpu_device *adev = smu->adev;
- /*u32 smu_version;*/
-
- if (num > 2)
- return -EINVAL;
-
- memset(feature_mask, 0xff, sizeof(uint32_t) * num);
-
- if (adev->pm.pp_feature & PP_SCLK_DPM_MASK) {
- *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT);
- *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_IMU_BIT);
- }
-#if 0
- if (!(adev->pg_flags & AMD_PG_SUPPORT_ATHUB) ||
- !(adev->pg_flags & AMD_PG_SUPPORT_MMHUB))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_ATHUB_MMHUB_PG_BIT);
-
- if (!(adev->pm.pp_feature & PP_SOCCLK_DPM_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT);
-
- /* PMFW 78.58 contains a critical fix for gfxoff feature */
- smu_cmn_get_smc_version(smu, NULL, &smu_version);
- if ((smu_version < 0x004e3a00) ||
- !(adev->pm.pp_feature & PP_GFXOFF_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFXOFF_BIT);
-
- if (!(adev->pm.pp_feature & PP_MCLK_DPM_MASK)) {
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_UCLK_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VMEMP_SCALING_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VDDIO_MEM_SCALING_BIT);
- }
-
- if (!(adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_GFXCLK_BIT);
-
- if (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK)) {
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_LINK_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_LCLK_BIT);
- }
-
- if (!(adev->pm.pp_feature & PP_ULV_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFX_ULV_BIT);
-#endif
-
- return 0;
-}
-
-static int smu_v14_0_2_check_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
- struct smu_baco_context *smu_baco = &smu->smu_baco;
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
- const OverDriveLimits_t * const overdrive_lowerlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMin;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_HARDWAREDC)
- smu->dc_controlled_by_gpio = true;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_BACO) {
- smu_baco->platform_support = true;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_MACO)
- smu_baco->maco_support = true;
- }
-
- if (!overdrive_lowerlimits->FeatureCtrlMask ||
- !overdrive_upperlimits->FeatureCtrlMask)
- smu->od_enabled = false;
-
- table_context->thermal_controller_type =
- powerplay_table->thermal_controller_type;
-
- /*
- * Instead of having its own buffer space and get overdrive_table copied,
- * smu->od_settings just points to the actual overdrive_table
- */
- smu->od_settings = &powerplay_table->overdrive_table;
-
- smu->adev->pm.no_fan =
- !(pptable->PFE_Settings.FeaturesToRun[0] & (1 << FEATURE_FAN_CONTROL_BIT));
-
- return 0;
-}
-
-static int smu_v14_0_2_store_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
-
- memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
- sizeof(PPTable_t));
-
- return 0;
-}
-
-#ifndef atom_smc_dpm_info_table_14_0_0
-struct atom_smc_dpm_info_table_14_0_0 {
- struct atom_common_table_header table_header;
- BoardTable_t BoardTable;
-};
-#endif
-
-static int smu_v14_0_2_append_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *smc_pptable = table_context->driver_pptable;
- struct atom_smc_dpm_info_table_14_0_0 *smc_dpm_table;
- BoardTable_t *BoardTable = &smc_pptable->BoardTable;
- int index, ret;
-
- index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
- smc_dpm_info);
-
- ret = amdgpu_atombios_get_data_table(smu->adev, index, NULL, NULL, NULL,
- (uint8_t **)&smc_dpm_table);
- if (ret)
- return ret;
-
- memcpy(BoardTable, &smc_dpm_table->BoardTable, sizeof(BoardTable_t));
-
- return 0;
-}
-
-#if 0
-static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
- void **table,
- uint32_t *size)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- void *combo_pptable = smu_table->combo_pptable;
- int ret = 0;
-
- ret = smu_cmn_get_combo_pptable(smu);
- if (ret)
- return ret;
-
- *table = combo_pptable;
- *size = sizeof(struct smu_14_0_powerplay_table);
-
- return 0;
-}
-#endif
-
-static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
- void **table,
- uint32_t *size)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- void *combo_pptable = smu_table->combo_pptable;
- int ret = 0;
-
- ret = smu_cmn_get_combo_pptable(smu);
- if (ret)
- return ret;
-
- *table = combo_pptable;
- *size = sizeof(struct smu_14_0_2_powerplay_table);
-
- return 0;
-}
-
-static int smu_v14_0_2_setup_pptable(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct amdgpu_device *adev = smu->adev;
- int ret = 0;
-
- if (amdgpu_sriov_vf(smu->adev))
- return 0;
-
- if (!adev->scpm_enabled)
- ret = smu_v14_0_setup_pptable(smu);
- else
- ret = smu_v14_0_2_get_pptable_from_pmfw(smu,
- &smu_table->power_play_table,
- &smu_table->power_play_table_size);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_store_powerplay_table(smu);
- if (ret)
- return ret;
-
- /*
- * With SCPM enabled, the operation below will be handled
- * by PSP. Driver involvment is unnecessary and useless.
- */
- if (!adev->scpm_enabled) {
- ret = smu_v14_0_2_append_powerplay_table(smu);
- if (ret)
- return ret;
- }
-
- ret = smu_v14_0_2_check_powerplay_table(smu);
- if (ret)
- return ret;
-
- return ret;
-}
-
-static int smu_v14_0_2_tables_init(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct smu_table *tables = smu_table->tables;
-
- SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetricsExternal_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU14_TOOL_SIZE,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- sizeof(DpmActivityMonitorCoeffIntExternal_t), PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_COMBO_PPTABLE, MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_ECCINFO, sizeof(EccInfoTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
-
- smu_table->metrics_table = kzalloc(sizeof(SmuMetricsExternal_t), GFP_KERNEL);
- if (!smu_table->metrics_table)
- goto err0_out;
- smu_table->metrics_time = 0;
-
- smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
- smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
- if (!smu_table->gpu_metrics_table)
- goto err1_out;
-
- smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
- if (!smu_table->watermarks_table)
- goto err2_out;
-
- smu_table->ecc_table = kzalloc(tables[SMU_TABLE_ECCINFO].size, GFP_KERNEL);
- if (!smu_table->ecc_table)
- goto err3_out;
-
- return 0;
-
-err3_out:
- kfree(smu_table->watermarks_table);
-err2_out:
- kfree(smu_table->gpu_metrics_table);
-err1_out:
- kfree(smu_table->metrics_table);
-err0_out:
- return -ENOMEM;
-}
-
-static int smu_v14_0_2_allocate_dpm_context(struct smu_context *smu)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
-
- smu_dpm->dpm_context = kzalloc(sizeof(struct smu_14_0_dpm_context),
- GFP_KERNEL);
- if (!smu_dpm->dpm_context)
- return -ENOMEM;
-
- smu_dpm->dpm_context_size = sizeof(struct smu_14_0_dpm_context);
-
- return 0;
-}
-
-static int smu_v14_0_2_init_smc_tables(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_v14_0_2_tables_init(smu);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_allocate_dpm_context(smu);
- if (ret)
- return ret;
-
- return smu_v14_0_init_smc_tables(smu);
-}
-
-static int smu_v14_0_2_set_default_dpm_table(struct smu_context *smu)
-{
- struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- SkuTable_t *skutable = &pptable->SkuTable;
- struct smu_14_0_dpm_table *dpm_table;
- struct smu_14_0_pcie_table *pcie_table;
- uint32_t link_level;
- int ret = 0;
-
- /* socclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.soc_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_SOCCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* gfxclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.gfx_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_GFXCLK,
- dpm_table);
- if (ret)
- return ret;
-
- /*
- * Update the reported maximum shader clock to the value
- * which can be guarded to be achieved on all cards. This
- * is aligned with Window setting. And considering that value
- * might be not the peak frequency the card can achieve, it
- * is normal some real-time clock frequency can overtake this
- * labelled maximum clock frequency(for example in pp_dpm_sclk
- * sysfs output).
- */
- if (skutable->DriverReportedClocks.GameClockAc &&
- (dpm_table->dpm_levels[dpm_table->count - 1].value >
- skutable->DriverReportedClocks.GameClockAc)) {
- dpm_table->dpm_levels[dpm_table->count - 1].value =
- skutable->DriverReportedClocks.GameClockAc;
- dpm_table->max = skutable->DriverReportedClocks.GameClockAc;
- }
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* uclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.uclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_UCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* fclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.fclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_FCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* vclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.vclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_VCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_VCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* dclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.dclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_DCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* lclk dpm table setup */
- pcie_table = &dpm_context->dpm_tables.pcie_table;
- pcie_table->num_of_link_levels = 0;
- for (link_level = 0; link_level < NUM_LINK_LEVELS; link_level++) {
- if (!skutable->PcieGenSpeed[link_level] &&
- !skutable->PcieLaneCount[link_level] &&
- !skutable->LclkFreq[link_level])
- continue;
-
- pcie_table->pcie_gen[pcie_table->num_of_link_levels] =
- skutable->PcieGenSpeed[link_level];
- pcie_table->pcie_lane[pcie_table->num_of_link_levels] =
- skutable->PcieLaneCount[link_level];
- pcie_table->clk_freq[pcie_table->num_of_link_levels] =
- skutable->LclkFreq[link_level];
- pcie_table->num_of_link_levels++;
- }
-
- /* dcefclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.dcef_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCN_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_DCEFCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- return 0;
-}
-
-static bool smu_v14_0_2_is_dpm_running(struct smu_context *smu)
-{
- int ret = 0;
- uint64_t feature_enabled;
-
- ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
- if (ret)
- return false;
-
- return !!(feature_enabled & SMC_DPM_FEATURE);
-}
-
-static void smu_v14_0_2_dump_pptable(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- PFE_Settings_t *PFEsettings = &pptable->PFE_Settings;
-
- dev_info(smu->adev->dev, "Dumped PPTable:\n");
-
- dev_info(smu->adev->dev, "Version = 0x%08x\n", PFEsettings->Version);
- dev_info(smu->adev->dev, "FeaturesToRun[0] = 0x%08x\n", PFEsettings->FeaturesToRun[0]);
- dev_info(smu->adev->dev, "FeaturesToRun[1] = 0x%08x\n", PFEsettings->FeaturesToRun[1]);
-}
-
-static uint32_t smu_v14_0_2_get_throttler_status(SmuMetrics_t *metrics)
-{
- uint32_t throttler_status = 0;
- int i;
-
- for (i = 0; i < THROTTLER_COUNT; i++)
- throttler_status |=
- (metrics->ThrottlingPercentage[i] ? 1U << i : 0);
-
- return throttler_status;
-}
-
-#define SMU_14_0_2_BUSY_THRESHOLD 5
-static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu,
- MetricsMember_t member,
- uint32_t *value)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- SmuMetrics_t *metrics =
- &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- NULL,
- false);
- if (ret)
- return ret;
-
- switch (member) {
- case METRICS_CURR_GFXCLK:
- *value = metrics->CurrClock[PPCLK_GFXCLK];
- break;
- case METRICS_CURR_SOCCLK:
- *value = metrics->CurrClock[PPCLK_SOCCLK];
- break;
- case METRICS_CURR_UCLK:
- *value = metrics->CurrClock[PPCLK_UCLK];
- break;
- case METRICS_CURR_VCLK:
- *value = metrics->CurrClock[PPCLK_VCLK_0];
- break;
- case METRICS_CURR_DCLK:
- *value = metrics->CurrClock[PPCLK_DCLK_0];
- break;
- case METRICS_CURR_FCLK:
- *value = metrics->CurrClock[PPCLK_FCLK];
- break;
- case METRICS_CURR_DCEFCLK:
- *value = metrics->CurrClock[PPCLK_DCFCLK];
- break;
- case METRICS_AVERAGE_GFXCLK:
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageGfxclkFrequencyPostDs;
- else
- *value = metrics->AverageGfxclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_FCLK:
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageFclkFrequencyPostDs;
- else
- *value = metrics->AverageFclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_UCLK:
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageMemclkFrequencyPostDs;
- else
- *value = metrics->AverageMemclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_VCLK:
- *value = metrics->AverageVclk0Frequency;
- break;
- case METRICS_AVERAGE_DCLK:
- *value = metrics->AverageDclk0Frequency;
- break;
- case METRICS_AVERAGE_VCLK1:
- *value = metrics->AverageVclk1Frequency;
- break;
- case METRICS_AVERAGE_DCLK1:
- *value = metrics->AverageDclk1Frequency;
- break;
- case METRICS_AVERAGE_GFXACTIVITY:
- *value = metrics->AverageGfxActivity;
- break;
- case METRICS_AVERAGE_MEMACTIVITY:
- *value = metrics->AverageUclkActivity;
- break;
- case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
- break;
- case METRICS_TEMPERATURE_EDGE:
- *value = metrics->AvgTemperature[TEMP_EDGE] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_HOTSPOT:
- *value = metrics->AvgTemperature[TEMP_HOTSPOT] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_MEM:
- *value = metrics->AvgTemperature[TEMP_MEM] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_VRGFX:
- *value = metrics->AvgTemperature[TEMP_VR_GFX] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_VRSOC:
- *value = metrics->AvgTemperature[TEMP_VR_SOC] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_THROTTLER_STATUS:
- *value = smu_v14_0_2_get_throttler_status(metrics);
- break;
- case METRICS_CURR_FANSPEED:
- *value = metrics->AvgFanRpm;
- break;
- case METRICS_CURR_FANPWM:
- *value = metrics->AvgFanPwm;
- break;
- case METRICS_VOLTAGE_VDDGFX:
- *value = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- break;
- case METRICS_PCIE_RATE:
- *value = metrics->PcieRate;
- break;
- case METRICS_PCIE_WIDTH:
- *value = metrics->PcieWidth;
- break;
- default:
- *value = UINT_MAX;
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_get_dpm_ultimate_freq(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t *min,
- uint32_t *max)
-{
- struct smu_14_0_dpm_context *dpm_context =
- smu->smu_dpm.dpm_context;
- struct smu_14_0_dpm_table *dpm_table;
-
- switch (clk_type) {
- case SMU_MCLK:
- case SMU_UCLK:
- /* uclk dpm table */
- dpm_table = &dpm_context->dpm_tables.uclk_table;
- break;
- case SMU_GFXCLK:
- case SMU_SCLK:
- /* gfxclk dpm table */
- dpm_table = &dpm_context->dpm_tables.gfx_table;
- break;
- case SMU_SOCCLK:
- /* socclk dpm table */
- dpm_table = &dpm_context->dpm_tables.soc_table;
- break;
- case SMU_FCLK:
- /* fclk dpm table */
- dpm_table = &dpm_context->dpm_tables.fclk_table;
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- /* vclk dpm table */
- dpm_table = &dpm_context->dpm_tables.vclk_table;
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- /* dclk dpm table */
- dpm_table = &dpm_context->dpm_tables.dclk_table;
- break;
- default:
- dev_err(smu->adev->dev, "Unsupported clock type!\n");
- return -EINVAL;
- }
-
- if (min)
- *min = dpm_table->min;
- if (max)
- *max = dpm_table->max;
-
- return 0;
-}
-
-static int smu_v14_0_2_read_sensor(struct smu_context *smu,
- enum amd_pp_sensors sensor,
- void *data,
- uint32_t *size)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *smc_pptable = table_context->driver_pptable;
- int ret = 0;
-
- switch (sensor) {
- case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
- *(uint16_t *)data = smc_pptable->CustomSkuTable.FanMaximumRpm;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_MEM_LOAD:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_MEMACTIVITY,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GPU_LOAD:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_GFXACTIVITY,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_SOCKETPOWER,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_HOTSPOT,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_EDGE_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_EDGE,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_MEM_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_MEM,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GFX_MCLK:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_CURR_UCLK,
- (uint32_t *)data);
- *(uint32_t *)data *= 100;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GFX_SCLK:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_GFXCLK,
- (uint32_t *)data);
- *(uint32_t *)data *= 100;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_VDDGFX:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_VOLTAGE_VDDGFX,
- (uint32_t *)data);
- *size = 4;
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_get_current_clk_freq_by_table(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t *value)
-{
- MetricsMember_t member_type;
- int clk_id = 0;
-
- clk_id = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_CLK,
- clk_type);
- if (clk_id < 0)
- return -EINVAL;
-
- switch (clk_id) {
- case PPCLK_GFXCLK:
- member_type = METRICS_AVERAGE_GFXCLK;
- break;
- case PPCLK_UCLK:
- member_type = METRICS_CURR_UCLK;
- break;
- case PPCLK_FCLK:
- member_type = METRICS_CURR_FCLK;
- break;
- case PPCLK_SOCCLK:
- member_type = METRICS_CURR_SOCCLK;
- break;
- case PPCLK_VCLK_0:
- member_type = METRICS_AVERAGE_VCLK;
- break;
- case PPCLK_DCLK_0:
- member_type = METRICS_AVERAGE_DCLK;
- break;
- case PPCLK_DCFCLK:
- member_type = METRICS_CURR_DCEFCLK;
- break;
- default:
- return -EINVAL;
- }
-
- return smu_v14_0_2_get_smu_metrics_data(smu,
- member_type,
- value);
-}
-
-static bool smu_v14_0_2_is_od_feature_supported(struct smu_context *smu,
- int od_feature_bit)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
-
- return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
-}
-
-static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
- int od_feature_bit,
- int32_t *min,
- int32_t *max)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
- const OverDriveLimits_t * const overdrive_lowerlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMin;
- int32_t od_min_setting, od_max_setting;
-
- switch (od_feature_bit) {
- case PP_OD_FEATURE_GFXCLK_FMIN:
- od_min_setting = overdrive_lowerlimits->GfxclkFmin;
- od_max_setting = overdrive_upperlimits->GfxclkFmin;
- break;
- case PP_OD_FEATURE_GFXCLK_FMAX:
- od_min_setting = overdrive_lowerlimits->GfxclkFmax;
- od_max_setting = overdrive_upperlimits->GfxclkFmax;
- break;
- case PP_OD_FEATURE_UCLK_FMIN:
- od_min_setting = overdrive_lowerlimits->UclkFmin;
- od_max_setting = overdrive_upperlimits->UclkFmin;
- break;
- case PP_OD_FEATURE_UCLK_FMAX:
- od_min_setting = overdrive_lowerlimits->UclkFmax;
- od_max_setting = overdrive_upperlimits->UclkFmax;
- break;
- case PP_OD_FEATURE_GFX_VF_CURVE:
- od_min_setting = overdrive_lowerlimits->VoltageOffsetPerZoneBoundary[0];
- od_max_setting = overdrive_upperlimits->VoltageOffsetPerZoneBoundary[0];
- break;
- case PP_OD_FEATURE_FAN_CURVE_TEMP:
- od_min_setting = overdrive_lowerlimits->FanLinearTempPoints[0];
- od_max_setting = overdrive_upperlimits->FanLinearTempPoints[0];
- break;
- case PP_OD_FEATURE_FAN_CURVE_PWM:
- od_min_setting = overdrive_lowerlimits->FanLinearPwmPoints[0];
- od_max_setting = overdrive_upperlimits->FanLinearPwmPoints[0];
- break;
- case PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT:
- od_min_setting = overdrive_lowerlimits->AcousticLimitRpmThreshold;
- od_max_setting = overdrive_upperlimits->AcousticLimitRpmThreshold;
- break;
- case PP_OD_FEATURE_FAN_ACOUSTIC_TARGET:
- od_min_setting = overdrive_lowerlimits->AcousticTargetRpmThreshold;
- od_max_setting = overdrive_upperlimits->AcousticTargetRpmThreshold;
- break;
- case PP_OD_FEATURE_FAN_TARGET_TEMPERATURE:
- od_min_setting = overdrive_lowerlimits->FanTargetTemperature;
- od_max_setting = overdrive_upperlimits->FanTargetTemperature;
- break;
- case PP_OD_FEATURE_FAN_MINIMUM_PWM:
- od_min_setting = overdrive_lowerlimits->FanMinimumPwm;
- od_max_setting = overdrive_upperlimits->FanMinimumPwm;
- break;
- default:
- od_min_setting = od_max_setting = INT_MAX;
- break;
- }
-
- if (min)
- *min = od_min_setting;
- if (max)
- *max = od_max_setting;
-}
-
-static int smu_v14_0_2_print_clk_levels(struct smu_context *smu,
- enum smu_clk_type clk_type,
- char *buf)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
- struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
- struct smu_14_0_dpm_table *single_dpm_table;
- struct smu_14_0_pcie_table *pcie_table;
- uint32_t gen_speed, lane_width;
- int i, curr_freq, size = 0;
- int32_t min_value, max_value;
- int ret = 0;
-
- smu_cmn_get_sysfs_buf(&buf, &size);
-
- if (amdgpu_ras_intr_triggered()) {
- size += sysfs_emit_at(buf, size, "unavailable\n");
- return size;
- }
-
- switch (clk_type) {
- case SMU_SCLK:
- single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
- break;
- case SMU_MCLK:
- single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- break;
- case SMU_SOCCLK:
- single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- break;
- case SMU_FCLK:
- single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- break;
- case SMU_DCEFCLK:
- single_dpm_table = &(dpm_context->dpm_tables.dcef_table);
- break;
- default:
- break;
- }
-
- switch (clk_type) {
- case SMU_SCLK:
- case SMU_MCLK:
- case SMU_SOCCLK:
- case SMU_FCLK:
- case SMU_VCLK:
- case SMU_VCLK1:
- case SMU_DCLK:
- case SMU_DCLK1:
- case SMU_DCEFCLK:
- ret = smu_v14_0_2_get_current_clk_freq_by_table(smu, clk_type, &curr_freq);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to get current clock freq!");
- return ret;
- }
-
- if (single_dpm_table->is_fine_grained) {
- /*
- * For fine grained dpms, there are only two dpm levels:
- * - level 0 -> min clock freq
- * - level 1 -> max clock freq
- * And the current clock frequency can be any value between them.
- * So, if the current clock frequency is not at level 0 or level 1,
- * we will fake it as three dpm levels:
- * - level 0 -> min clock freq
- * - level 1 -> current actual clock freq
- * - level 2 -> max clock freq
- */
- if ((single_dpm_table->dpm_levels[0].value != curr_freq) &&
- (single_dpm_table->dpm_levels[1].value != curr_freq)) {
- size += sysfs_emit_at(buf, size, "0: %uMhz\n",
- single_dpm_table->dpm_levels[0].value);
- size += sysfs_emit_at(buf, size, "1: %uMhz *\n",
- curr_freq);
- size += sysfs_emit_at(buf, size, "2: %uMhz\n",
- single_dpm_table->dpm_levels[1].value);
- } else {
- size += sysfs_emit_at(buf, size, "0: %uMhz %s\n",
- single_dpm_table->dpm_levels[0].value,
- single_dpm_table->dpm_levels[0].value == curr_freq ? "*" : "");
- size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
- single_dpm_table->dpm_levels[1].value,
- single_dpm_table->dpm_levels[1].value == curr_freq ? "*" : "");
- }
- } else {
- for (i = 0; i < single_dpm_table->count; i++)
- size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
- i, single_dpm_table->dpm_levels[i].value,
- single_dpm_table->dpm_levels[i].value == curr_freq ? "*" : "");
- }
- break;
- case SMU_PCIE:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_PCIE_RATE,
- &gen_speed);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_PCIE_WIDTH,
- &lane_width);
- if (ret)
- return ret;
-
- pcie_table = &(dpm_context->dpm_tables.pcie_table);
- for (i = 0; i < pcie_table->num_of_link_levels; i++)
- size += sysfs_emit_at(buf, size, "%d: %s %s %dMhz %s\n", i,
- (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s," :
- (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s," :
- (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s," :
- (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," : "",
- (pcie_table->pcie_lane[i] == 1) ? "x1" :
- (pcie_table->pcie_lane[i] == 2) ? "x2" :
- (pcie_table->pcie_lane[i] == 3) ? "x4" :
- (pcie_table->pcie_lane[i] == 4) ? "x8" :
- (pcie_table->pcie_lane[i] == 5) ? "x12" :
- (pcie_table->pcie_lane[i] == 6) ? "x16" : "",
- pcie_table->clk_freq[i],
- (gen_speed == DECODE_GEN_SPEED(pcie_table->pcie_gen[i])) &&
- (lane_width == DECODE_LANE_WIDTH(pcie_table->pcie_lane[i])) ?
- "*" : "");
- break;
-
- case SMU_OD_SCLK:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_GFXCLK_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_SCLK:\n");
- size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMhz\n",
- od_table->OverDriveTable.GfxclkFmin,
- od_table->OverDriveTable.GfxclkFmax);
- break;
-
- case SMU_OD_MCLK:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_UCLK_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_MCLK:\n");
- size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMHz\n",
- od_table->OverDriveTable.UclkFmin,
- od_table->OverDriveTable.UclkFmax);
- break;
-
- case SMU_OD_VDDGFX_OFFSET:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_GFX_VF_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_VDDGFX_OFFSET:\n");
- size += sysfs_emit_at(buf, size, "%dmV\n",
- od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[0]);
- break;
-
- case SMU_OD_FAN_CURVE:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_FAN_CURVE:\n");
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++)
- size += sysfs_emit_at(buf, size, "%d: %dC %d%%\n",
- i,
- (int)od_table->OverDriveTable.FanLinearTempPoints[i],
- (int)od_table->OverDriveTable.FanLinearPwmPoints[i]);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_TEMP,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "FAN_CURVE(hotspot temp): %uC %uC\n",
- min_value, max_value);
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_PWM,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "FAN_CURVE(fan speed): %u%% %u%%\n",
- min_value, max_value);
-
- break;
-
- case SMU_OD_ACOUSTIC_LIMIT:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_LIMIT:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.AcousticLimitRpmThreshold);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "ACOUSTIC_LIMIT: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_ACOUSTIC_TARGET:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_TARGET:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.AcousticTargetRpmThreshold);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "ACOUSTIC_TARGET: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_FAN_TARGET_TEMPERATURE:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "FAN_TARGET_TEMPERATURE:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.FanTargetTemperature);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "TARGET_TEMPERATURE: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_FAN_MINIMUM_PWM:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "FAN_MINIMUM_PWM:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.FanMinimumPwm);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_MINIMUM_PWM,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "MINIMUM_PWM: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_RANGE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT) &&
- !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT) &&
- !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMIN,
- &min_value,
- NULL);
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMAX,
- NULL,
- &max_value);
- size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
- min_value, max_value);
- }
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMIN,
- &min_value,
- NULL);
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMAX,
- NULL,
- &max_value);
- size += sysfs_emit_at(buf, size, "MCLK: %7uMhz %10uMhz\n",
- min_value, max_value);
- }
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFX_VF_CURVE,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "VDDGFX_OFFSET: %7dmv %10dmv\n",
- min_value, max_value);
- }
- break;
-
- default:
- break;
- }
-
- return size;
-}
-
-static int smu_v14_0_2_force_clk_levels(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t mask)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
- struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
- struct smu_14_0_dpm_table *single_dpm_table;
- uint32_t soft_min_level, soft_max_level;
- uint32_t min_freq, max_freq;
- int ret = 0;
-
- soft_min_level = mask ? (ffs(mask) - 1) : 0;
- soft_max_level = mask ? (fls(mask) - 1) : 0;
-
- switch (clk_type) {
- case SMU_GFXCLK:
- case SMU_SCLK:
- single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
- break;
- case SMU_MCLK:
- case SMU_UCLK:
- single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- break;
- case SMU_SOCCLK:
- single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- break;
- case SMU_FCLK:
- single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- break;
- default:
- break;
- }
-
- switch (clk_type) {
- case SMU_GFXCLK:
- case SMU_SCLK:
- case SMU_MCLK:
- case SMU_UCLK:
- case SMU_SOCCLK:
- case SMU_FCLK:
- case SMU_VCLK:
- case SMU_VCLK1:
- case SMU_DCLK:
- case SMU_DCLK1:
- if (single_dpm_table->is_fine_grained) {
- /* There is only 2 levels for fine grained DPM */
- soft_max_level = (soft_max_level >= 1 ? 1 : 0);
- soft_min_level = (soft_min_level >= 1 ? 1 : 0);
- } else {
- if ((soft_max_level >= single_dpm_table->count) ||
- (soft_min_level >= single_dpm_table->count))
- return -EINVAL;
- }
-
- min_freq = single_dpm_table->dpm_levels[soft_min_level].value;
- max_freq = single_dpm_table->dpm_levels[soft_max_level].value;
-
- ret = smu_v14_0_set_soft_freq_limited_range(smu,
- clk_type,
- min_freq,
- max_freq);
- break;
- case SMU_DCEFCLK:
- case SMU_PCIE:
- default:
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_update_pcie_parameters(struct smu_context *smu,
- uint8_t pcie_gen_cap,
- uint8_t pcie_width_cap)
-{
- struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
- struct smu_14_0_pcie_table *pcie_table =
- &dpm_context->dpm_tables.pcie_table;
- uint32_t smu_pcie_arg;
- int ret, i;
-
- for (i = 0; i < pcie_table->num_of_link_levels; i++) {
- if (pcie_table->pcie_gen[i] > pcie_gen_cap)
- pcie_table->pcie_gen[i] = pcie_gen_cap;
- if (pcie_table->pcie_lane[i] > pcie_width_cap)
- pcie_table->pcie_lane[i] = pcie_width_cap;
-
- smu_pcie_arg = i << 16;
- smu_pcie_arg |= pcie_table->pcie_gen[i] << 8;
- smu_pcie_arg |= pcie_table->pcie_lane[i];
-
- ret = smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_OverridePcieParameters,
- smu_pcie_arg,
- NULL);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static const struct smu_temperature_range smu14_thermal_policy[] = {
- {-273150, 99000, 99000, -273150, 99000, 99000, -273150, 99000, 99000},
- { 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000},
-};
-
-static int smu_v14_0_2_get_thermal_temperature_range(struct smu_context *smu,
- struct smu_temperature_range *range)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
- PPTable_t *pptable = smu->smu_table.driver_pptable;
-
- if (amdgpu_sriov_vf(smu->adev))
- return 0;
-
- if (!range)
- return -EINVAL;
-
- memcpy(range, &smu14_thermal_policy[0], sizeof(struct smu_temperature_range));
-
- range->max = pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->edge_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] + CTF_OFFSET_EDGE) *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->hotspot_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->hotspot_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] + CTF_OFFSET_HOTSPOT) *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->mem_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->mem_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] + CTF_OFFSET_MEM)*
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->software_shutdown_temp = powerplay_table->software_shutdown_temp;
- range->software_shutdown_temp_offset = pptable->CustomSkuTable.FanAbnormalTempLimitOffset;
-
- return 0;
-}
-
-static int smu_v14_0_2_populate_umd_state_clk(struct smu_context *smu)
-{
- struct smu_14_0_dpm_context *dpm_context =
- smu->smu_dpm.dpm_context;
- struct smu_14_0_dpm_table *gfx_table =
- &dpm_context->dpm_tables.gfx_table;
- struct smu_14_0_dpm_table *mem_table =
- &dpm_context->dpm_tables.uclk_table;
- struct smu_14_0_dpm_table *soc_table =
- &dpm_context->dpm_tables.soc_table;
- struct smu_14_0_dpm_table *vclk_table =
- &dpm_context->dpm_tables.vclk_table;
- struct smu_14_0_dpm_table *dclk_table =
- &dpm_context->dpm_tables.dclk_table;
- struct smu_14_0_dpm_table *fclk_table =
- &dpm_context->dpm_tables.fclk_table;
- struct smu_umd_pstate_table *pstate_table =
- &smu->pstate_table;
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- DriverReportedClocks_t driver_clocks =
- pptable->SkuTable.DriverReportedClocks;
-
- pstate_table->gfxclk_pstate.min = gfx_table->min;
- if (driver_clocks.GameClockAc &&
- (driver_clocks.GameClockAc < gfx_table->max))
- pstate_table->gfxclk_pstate.peak = driver_clocks.GameClockAc;
- else
- pstate_table->gfxclk_pstate.peak = gfx_table->max;
-
- pstate_table->uclk_pstate.min = mem_table->min;
- pstate_table->uclk_pstate.peak = mem_table->max;
-
- pstate_table->socclk_pstate.min = soc_table->min;
- pstate_table->socclk_pstate.peak = soc_table->max;
-
- pstate_table->vclk_pstate.min = vclk_table->min;
- pstate_table->vclk_pstate.peak = vclk_table->max;
-
- pstate_table->dclk_pstate.min = dclk_table->min;
- pstate_table->dclk_pstate.peak = dclk_table->max;
-
- pstate_table->fclk_pstate.min = fclk_table->min;
- pstate_table->fclk_pstate.peak = fclk_table->max;
-
- if (driver_clocks.BaseClockAc &&
- driver_clocks.BaseClockAc < gfx_table->max)
- pstate_table->gfxclk_pstate.standard = driver_clocks.BaseClockAc;
- else
- pstate_table->gfxclk_pstate.standard = gfx_table->max;
- pstate_table->uclk_pstate.standard = mem_table->max;
- pstate_table->socclk_pstate.standard = soc_table->min;
- pstate_table->vclk_pstate.standard = vclk_table->min;
- pstate_table->dclk_pstate.standard = dclk_table->min;
- pstate_table->fclk_pstate.standard = fclk_table->min;
-
- return 0;
-}
-
-static void smu_v14_0_2_get_unique_id(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- SmuMetrics_t *metrics =
- &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
- struct amdgpu_device *adev = smu->adev;
- uint32_t upper32 = 0, lower32 = 0;
- int ret;
-
- ret = smu_cmn_get_metrics_table(smu, NULL, false);
- if (ret)
- goto out;
-
- upper32 = metrics->PublicSerialNumberUpper;
- lower32 = metrics->PublicSerialNumberLower;
-
-out:
- adev->unique_id = ((uint64_t)upper32 << 32) | lower32;
-}
-
-static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
- uint32_t *current_power_limit,
- uint32_t *default_power_limit,
- uint32_t *max_power_limit,
- uint32_t *min_power_limit)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- CustomSkuTable_t *skutable = &pptable->CustomSkuTable;
- uint32_t power_limit;
- uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
-
- if (smu_v14_0_get_current_power_limit(smu, &power_limit))
- power_limit = smu->adev->pm.ac_power ?
- skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
- skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
-
- if (current_power_limit)
- *current_power_limit = power_limit;
- if (default_power_limit)
- *default_power_limit = power_limit;
-
- if (max_power_limit)
- *max_power_limit = msg_limit;
-
- if (min_power_limit)
- *min_power_limit = 0;
-
- return 0;
-}
-
-static int smu_v14_0_2_get_power_profile_mode(struct smu_context *smu,
- char *buf)
-{
- DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
- DpmActivityMonitorCoeffInt_t *activity_monitor =
- &(activity_monitor_external.DpmActivityMonitorCoeffInt);
- static const char *title[] = {
- "PROFILE_INDEX(NAME)",
- "CLOCK_TYPE(NAME)",
- "FPS",
- "MinActiveFreqType",
- "MinActiveFreq",
- "BoosterFreqType",
- "BoosterFreq",
- "PD_Data_limit_c",
- "PD_Data_error_coeff",
- "PD_Data_error_rate_coeff"};
- int16_t workload_type = 0;
- uint32_t i, size = 0;
- int result = 0;
-
- if (!buf)
- return -EINVAL;
-
- size += sysfs_emit_at(buf, size, "%16s %s %s %s %s %s %s %s %s %s\n",
- title[0], title[1], title[2], title[3], title[4], title[5],
- title[6], title[7], title[8], title[9]);
-
- for (i = 0; i < PP_SMC_POWER_PROFILE_COUNT; i++) {
- /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
- workload_type = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_WORKLOAD,
- i);
- if (workload_type == -ENOTSUPP)
- continue;
- else if (workload_type < 0)
- return -EINVAL;
-
- result = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- workload_type,
- (void *)(&activity_monitor_external),
- false);
- if (result) {
- dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
- return result;
- }
-
- size += sysfs_emit_at(buf, size, "%2d %14s%s:\n",
- i, amdgpu_pp_profile_name[i], (i == smu->power_profile_mode) ? "*" : " ");
-
- size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
- " ",
- 0,
- "GFXCLK",
- activity_monitor->Gfx_FPS,
- activity_monitor->Gfx_MinActiveFreqType,
- activity_monitor->Gfx_MinActiveFreq,
- activity_monitor->Gfx_BoosterFreqType,
- activity_monitor->Gfx_BoosterFreq,
- activity_monitor->Gfx_PD_Data_limit_c,
- activity_monitor->Gfx_PD_Data_error_coeff,
- activity_monitor->Gfx_PD_Data_error_rate_coeff);
-
- size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
- " ",
- 1,
- "FCLK",
- activity_monitor->Fclk_FPS,
- activity_monitor->Fclk_MinActiveFreqType,
- activity_monitor->Fclk_MinActiveFreq,
- activity_monitor->Fclk_BoosterFreqType,
- activity_monitor->Fclk_BoosterFreq,
- activity_monitor->Fclk_PD_Data_limit_c,
- activity_monitor->Fclk_PD_Data_error_coeff,
- activity_monitor->Fclk_PD_Data_error_rate_coeff);
- }
-
- return size;
-}
-
-static int smu_v14_0_2_set_power_profile_mode(struct smu_context *smu,
- long *input,
- uint32_t size)
-{
- DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
- DpmActivityMonitorCoeffInt_t *activity_monitor =
- &(activity_monitor_external.DpmActivityMonitorCoeffInt);
- int workload_type, ret = 0;
-
- smu->power_profile_mode = input[size];
-
- if (smu->power_profile_mode >= PP_SMC_POWER_PROFILE_COUNT) {
- dev_err(smu->adev->dev, "Invalid power profile mode %d\n", smu->power_profile_mode);
- return -EINVAL;
- }
-
- if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
- if (size != 9)
- return -EINVAL;
-
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- WORKLOAD_PPLIB_CUSTOM_BIT,
- (void *)(&activity_monitor_external),
- false);
- if (ret) {
- dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
- return ret;
- }
-
- switch (input[0]) {
- case 0: /* Gfxclk */
- activity_monitor->Gfx_FPS = input[1];
- activity_monitor->Gfx_MinActiveFreqType = input[2];
- activity_monitor->Gfx_MinActiveFreq = input[3];
- activity_monitor->Gfx_BoosterFreqType = input[4];
- activity_monitor->Gfx_BoosterFreq = input[5];
- activity_monitor->Gfx_PD_Data_limit_c = input[6];
- activity_monitor->Gfx_PD_Data_error_coeff = input[7];
- activity_monitor->Gfx_PD_Data_error_rate_coeff = input[8];
- break;
- case 1: /* Fclk */
- activity_monitor->Fclk_FPS = input[1];
- activity_monitor->Fclk_MinActiveFreqType = input[2];
- activity_monitor->Fclk_MinActiveFreq = input[3];
- activity_monitor->Fclk_BoosterFreqType = input[4];
- activity_monitor->Fclk_BoosterFreq = input[5];
- activity_monitor->Fclk_PD_Data_limit_c = input[6];
- activity_monitor->Fclk_PD_Data_error_coeff = input[7];
- activity_monitor->Fclk_PD_Data_error_rate_coeff = input[8];
- break;
- default:
- return -EINVAL;
- }
-
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- WORKLOAD_PPLIB_CUSTOM_BIT,
- (void *)(&activity_monitor_external),
- true);
- if (ret) {
- dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
- return ret;
- }
- }
-
- /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
- workload_type = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_WORKLOAD,
- smu->power_profile_mode);
- if (workload_type < 0)
- return -EINVAL;
-
- return smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_SetWorkloadMask,
- 1 << workload_type,
- NULL);
-}
-
-static int smu_v14_0_2_baco_enter(struct smu_context *smu)
-{
- struct smu_baco_context *smu_baco = &smu->smu_baco;
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev))
- return smu_v14_0_baco_set_armd3_sequence(smu,
- smu_baco->maco_support ? BACO_SEQ_BAMACO : BACO_SEQ_BACO);
- else
- return smu_v14_0_baco_enter(smu);
-}
-
-static int smu_v14_0_2_baco_exit(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev)) {
- /* Wait for PMFW handling for the Dstate change */
- usleep_range(10000, 11000);
- return smu_v14_0_baco_set_armd3_sequence(smu, BACO_SEQ_ULPS);
- } else {
- return smu_v14_0_baco_exit(smu);
- }
-}
-
-static bool smu_v14_0_2_is_mode1_reset_supported(struct smu_context *smu)
-{
- // TODO
-
- return true;
-}
-
-static int smu_v14_0_2_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msg, int num_msgs)
-{
- struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(i2c_adap);
- struct amdgpu_device *adev = smu_i2c->adev;
- struct smu_context *smu = adev->powerplay.pp_handle;
- struct smu_table_context *smu_table = &smu->smu_table;
- struct smu_table *table = &smu_table->driver_table;
- SwI2cRequest_t *req, *res = (SwI2cRequest_t *)table->cpu_addr;
- int i, j, r, c;
- u16 dir;
-
- if (!adev->pm.dpm_enabled)
- return -EBUSY;
-
- req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- req->I2CcontrollerPort = smu_i2c->port;
- req->I2CSpeed = I2C_SPEED_FAST_400K;
- req->SlaveAddress = msg[0].addr << 1; /* wants an 8-bit address */
- dir = msg[0].flags & I2C_M_RD;
-
- for (c = i = 0; i < num_msgs; i++) {
- for (j = 0; j < msg[i].len; j++, c++) {
- SwI2cCmd_t *cmd = &req->SwI2cCmds[c];
-
- if (!(msg[i].flags & I2C_M_RD)) {
- /* write */
- cmd->CmdConfig |= CMDCONFIG_READWRITE_MASK;
- cmd->ReadWriteData = msg[i].buf[j];
- }
-
- if ((dir ^ msg[i].flags) & I2C_M_RD) {
- /* The direction changes.
- */
- dir = msg[i].flags & I2C_M_RD;
- cmd->CmdConfig |= CMDCONFIG_RESTART_MASK;
- }
-
- req->NumCmds++;
-
- /*
- * Insert STOP if we are at the last byte of either last
- * message for the transaction or the client explicitly
- * requires a STOP at this particular message.
- */
- if ((j == msg[i].len - 1) &&
- ((i == num_msgs - 1) || (msg[i].flags & I2C_M_STOP))) {
- cmd->CmdConfig &= ~CMDCONFIG_RESTART_MASK;
- cmd->CmdConfig |= CMDCONFIG_STOP_MASK;
- }
- }
- }
- mutex_lock(&adev->pm.mutex);
- r = smu_cmn_update_table(smu, SMU_TABLE_I2C_COMMANDS, 0, req, true);
- mutex_unlock(&adev->pm.mutex);
- if (r)
- goto fail;
-
- for (c = i = 0; i < num_msgs; i++) {
- if (!(msg[i].flags & I2C_M_RD)) {
- c += msg[i].len;
- continue;
- }
- for (j = 0; j < msg[i].len; j++, c++) {
- SwI2cCmd_t *cmd = &res->SwI2cCmds[c];
-
- msg[i].buf[j] = cmd->ReadWriteData;
- }
- }
- r = num_msgs;
-fail:
- kfree(req);
- return r;
-}
-
-static u32 smu_v14_0_2_i2c_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm smu_v14_0_2_i2c_algo = {
- .master_xfer = smu_v14_0_2_i2c_xfer,
- .functionality = smu_v14_0_2_i2c_func,
-};
-
-static const struct i2c_adapter_quirks smu_v14_0_2_i2c_control_quirks = {
- .flags = I2C_AQ_COMB | I2C_AQ_COMB_SAME_ADDR | I2C_AQ_NO_ZERO_LEN,
- .max_read_len = MAX_SW_I2C_COMMANDS,
- .max_write_len = MAX_SW_I2C_COMMANDS,
- .max_comb_1st_msg_len = 2,
- .max_comb_2nd_msg_len = MAX_SW_I2C_COMMANDS - 2,
-};
-
-static int smu_v14_0_2_i2c_control_init(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
- int res, i;
-
- for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- smu_i2c->adev = adev;
- smu_i2c->port = i;
- mutex_init(&smu_i2c->mutex);
- control->owner = THIS_MODULE;
- control->dev.parent = &adev->pdev->dev;
- control->algo = &smu_v14_0_2_i2c_algo;
- snprintf(control->name, sizeof(control->name), "AMDGPU SMU %d", i);
- control->quirks = &smu_v14_0_2_i2c_control_quirks;
- i2c_set_adapdata(control, smu_i2c);
-
- res = i2c_add_adapter(control);
- if (res) {
- DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
- goto Out_err;
- }
- }
-
- /* assign the buses used for the FRU EEPROM and RAS EEPROM */
- /* XXX ideally this would be something in a vbios data table */
- adev->pm.ras_eeprom_i2c_bus = &adev->pm.smu_i2c[1].adapter;
- adev->pm.fru_eeprom_i2c_bus = &adev->pm.smu_i2c[0].adapter;
-
- return 0;
-Out_err:
- for ( ; i >= 0; i--) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- i2c_del_adapter(control);
- }
- return res;
-}
-
-static void smu_v14_0_2_i2c_control_fini(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
- int i;
-
- for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- i2c_del_adapter(control);
- }
- adev->pm.ras_eeprom_i2c_bus = NULL;
- adev->pm.fru_eeprom_i2c_bus = NULL;
-}
-
-static int smu_v14_0_2_set_mp1_state(struct smu_context *smu,
- enum pp_mp1_state mp1_state)
-{
- int ret;
-
- switch (mp1_state) {
- case PP_MP1_STATE_UNLOAD:
- ret = smu_cmn_set_mp1_state(smu, mp1_state);
- break;
- default:
- /* Ignore others */
- ret = 0;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_set_df_cstate(struct smu_context *smu,
- enum pp_df_cstate state)
-{
- return smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_DFCstateControl,
- state,
- NULL);
-}
-
-static int smu_v14_0_2_mode1_reset(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_cmn_send_debug_smc_msg(smu, DEBUGSMC_MSG_Mode1Reset);
- if (!ret) {
- if (amdgpu_emu_mode == 1)
- msleep(50000);
- else
- msleep(1000);
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_mode2_reset(struct smu_context *smu)
-{
- int ret = 0;
-
- // TODO
-
- return ret;
-}
-
-static int smu_v14_0_2_enable_gfx_features(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(14, 0, 2))
- return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_EnableAllSmuFeatures,
- FEATURE_PWR_GFX, NULL);
- else
- return -EOPNOTSUPP;
-}
-
-static void smu_v14_0_2_set_smu_mailbox_registers(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- smu->param_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_82);
- smu->msg_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_66);
- smu->resp_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_90);
-
- smu->debug_param_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_53);
- smu->debug_msg_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_75);
- smu->debug_resp_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_54);
-}
-
-static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
- void **table)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_3 *gpu_metrics =
- (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
- SmuMetricsExternal_t metrics_ext;
- SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- &metrics_ext,
- true);
- if (ret)
- return ret;
-
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
-
- gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
- gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
- gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
- gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
- gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
- gpu_metrics->temperature_vrmem = max(metrics->AvgTemperature[TEMP_VR_MEM0],
- metrics->AvgTemperature[TEMP_VR_MEM1]);
-
- gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
- gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
- gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage,
- metrics->Vcn1ActivityPercentage);
-
- gpu_metrics->average_socket_power = metrics->AverageSocketPower;
- gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
-
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
- else
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
-
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
- else
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
-
- gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
- gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
- gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
- gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
-
- gpu_metrics->current_gfxclk = gpu_metrics->average_gfxclk_frequency;
- gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK];
- gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK];
- gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
- gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_0];
-
- gpu_metrics->throttle_status =
- smu_v14_0_2_get_throttler_status(metrics);
- gpu_metrics->indep_throttle_status =
- smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
- smu_v14_0_2_throttler_map);
-
- gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
-
- gpu_metrics->pcie_link_width = metrics->PcieWidth;
- if ((metrics->PcieRate - 1) > LINK_SPEED_MAX)
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(1);
- else
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(metrics->PcieRate);
-
- gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
-
- gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_VDD_SOC];
- gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VDDIO_MEM];
-
- *table = (void *)gpu_metrics;
-
- return sizeof(struct gpu_metrics_v1_3);
-}
-
-static void smu_v14_0_2_dump_od_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- struct amdgpu_device *adev = smu->adev;
-
- dev_dbg(adev->dev, "OD: Gfxclk: (%d, %d)\n", od_table->OverDriveTable.GfxclkFmin,
- od_table->OverDriveTable.GfxclkFmax);
- dev_dbg(adev->dev, "OD: Uclk: (%d, %d)\n", od_table->OverDriveTable.UclkFmin,
- od_table->OverDriveTable.UclkFmax);
-}
-
-static int smu_v14_0_2_upload_overdrive_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- int ret;
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_OVERDRIVE,
- 0,
- (void *)od_table,
- true);
- if (ret)
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
-
- return ret;
-}
-
-static void smu_v14_0_2_set_supported_od_feature_mask(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- adev->pm.od_feature_mask |= OD_OPS_SUPPORT_FAN_CURVE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_CURVE_SET |
- OD_OPS_SUPPORT_ACOUSTIC_LIMIT_THRESHOLD_RETRIEVE |
- OD_OPS_SUPPORT_ACOUSTIC_LIMIT_THRESHOLD_SET |
- OD_OPS_SUPPORT_ACOUSTIC_TARGET_THRESHOLD_RETRIEVE |
- OD_OPS_SUPPORT_ACOUSTIC_TARGET_THRESHOLD_SET |
- OD_OPS_SUPPORT_FAN_TARGET_TEMPERATURE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_TARGET_TEMPERATURE_SET |
- OD_OPS_SUPPORT_FAN_MINIMUM_PWM_RETRIEVE |
- OD_OPS_SUPPORT_FAN_MINIMUM_PWM_SET;
-}
-
-static int smu_v14_0_2_get_overdrive_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- int ret;
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_OVERDRIVE,
- 0,
- (void *)od_table,
- false);
- if (ret)
- dev_err(smu->adev->dev, "Failed to get overdrive table!\n");
-
- return ret;
-}
-
-static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
-{
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
- OverDriveTableExternal_t *boot_od_table =
- (OverDriveTableExternal_t *)smu->smu_table.boot_overdrive_table;
- OverDriveTableExternal_t *user_od_table =
- (OverDriveTableExternal_t *)smu->smu_table.user_overdrive_table;
- OverDriveTableExternal_t user_od_table_bak;
- int ret;
- int i;
-
- ret = smu_v14_0_2_get_overdrive_table(smu, boot_od_table);
- if (ret)
- return ret;
-
- smu_v14_0_2_dump_od_table(smu, boot_od_table);
-
- memcpy(od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
-
- /*
- * For S3/S4/Runpm resume, we need to setup those overdrive tables again,
- * but we have to preserve user defined values in "user_od_table".
- */
- if (!smu->adev->in_suspend) {
- memcpy(user_od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
- smu->user_dpm_profile.user_od = false;
- } else if (smu->user_dpm_profile.user_od) {
- memcpy(&user_od_table_bak,
- user_od_table,
- sizeof(OverDriveTableExternal_t));
- memcpy(user_od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
- user_od_table->OverDriveTable.GfxclkFmin =
- user_od_table_bak.OverDriveTable.GfxclkFmin;
- user_od_table->OverDriveTable.GfxclkFmax =
- user_od_table_bak.OverDriveTable.GfxclkFmax;
- user_od_table->OverDriveTable.UclkFmin =
- user_od_table_bak.OverDriveTable.UclkFmin;
- user_od_table->OverDriveTable.UclkFmax =
- user_od_table_bak.OverDriveTable.UclkFmax;
- for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++)
- user_od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[i] =
- user_od_table_bak.OverDriveTable.VoltageOffsetPerZoneBoundary[i];
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++) {
- user_od_table->OverDriveTable.FanLinearTempPoints[i] =
- user_od_table_bak.OverDriveTable.FanLinearTempPoints[i];
- user_od_table->OverDriveTable.FanLinearPwmPoints[i] =
- user_od_table_bak.OverDriveTable.FanLinearPwmPoints[i];
- }
- user_od_table->OverDriveTable.AcousticLimitRpmThreshold =
- user_od_table_bak.OverDriveTable.AcousticLimitRpmThreshold;
- user_od_table->OverDriveTable.AcousticTargetRpmThreshold =
- user_od_table_bak.OverDriveTable.AcousticTargetRpmThreshold;
- user_od_table->OverDriveTable.FanTargetTemperature =
- user_od_table_bak.OverDriveTable.FanTargetTemperature;
- user_od_table->OverDriveTable.FanMinimumPwm =
- user_od_table_bak.OverDriveTable.FanMinimumPwm;
- }
-
- smu_v14_0_2_set_supported_od_feature_mask(smu);
-
- return 0;
-}
-
-static int smu_v14_0_2_restore_user_od_settings(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table = table_context->overdrive_table;
- OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
- int res;
-
- user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
- BIT(PP_OD_FEATURE_UCLK_BIT) |
- BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
- BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
- memcpy(od_table, user_od_table, sizeof(OverDriveTableExternal_t));
-
- return res;
-}
-
-static int smu_v14_0_2_od_restore_table_single(struct smu_context *smu, long input)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *boot_overdrive_table =
- (OverDriveTableExternal_t *)table_context->boot_overdrive_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- struct amdgpu_device *adev = smu->adev;
- int i;
-
- switch (input) {
- case PP_OD_EDIT_FAN_CURVE:
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS; i++) {
- od_table->OverDriveTable.FanLinearTempPoints[i] =
- boot_overdrive_table->OverDriveTable.FanLinearTempPoints[i];
- od_table->OverDriveTable.FanLinearPwmPoints[i] =
- boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
- }
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_ACOUSTIC_LIMIT:
- od_table->OverDriveTable.AcousticLimitRpmThreshold =
- boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_ACOUSTIC_TARGET:
- od_table->OverDriveTable.AcousticTargetRpmThreshold =
- boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
- od_table->OverDriveTable.FanTargetTemperature =
- boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_FAN_MINIMUM_PWM:
- od_table->OverDriveTable.FanMinimumPwm =
- boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- default:
- dev_info(adev->dev, "Invalid table index: %ld\n", input);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
- enum PP_OD_DPM_TABLE_COMMAND type,
- long input[],
- uint32_t size)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- struct amdgpu_device *adev = smu->adev;
- uint32_t offset_of_voltageoffset;
- int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
- int i, ret = 0;
-
- switch (type) {
- case PP_OD_EDIT_SCLK_VDDC_TABLE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
- dev_warn(adev->dev, "GFXCLK_LIMITS setting not supported!\n");
- return -ENOTSUPP;
- }
-
- for (i = 0; i < size; i += 2) {
- if (i + 2 > size) {
- dev_info(adev->dev, "invalid number of input parameters %d\n", size);
- return -EINVAL;
- }
-
- switch (input[i]) {
- case 0:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMIN,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "GfxclkFmin (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.GfxclkFmin = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_GFXCLK_BIT;
- break;
-
- case 1:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMAX,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "GfxclkFmax (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.GfxclkFmax = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_GFXCLK_BIT;
- break;
-
- default:
- dev_info(adev->dev, "Invalid SCLK_VDDC_TABLE index: %ld\n", input[i]);
- dev_info(adev->dev, "Supported indices: [0:min,1:max]\n");
- return -EINVAL;
- }
- }
-
- if (od_table->OverDriveTable.GfxclkFmin > od_table->OverDriveTable.GfxclkFmax) {
- dev_err(adev->dev,
- "Invalid setting: GfxclkFmin(%u) is bigger than GfxclkFmax(%u)\n",
- (uint32_t)od_table->OverDriveTable.GfxclkFmin,
- (uint32_t)od_table->OverDriveTable.GfxclkFmax);
- return -EINVAL;
- }
- break;
-
- case PP_OD_EDIT_MCLK_VDDC_TABLE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
- dev_warn(adev->dev, "UCLK_LIMITS setting not supported!\n");
- return -ENOTSUPP;
- }
-
- for (i = 0; i < size; i += 2) {
- if (i + 2 > size) {
- dev_info(adev->dev, "invalid number of input parameters %d\n", size);
- return -EINVAL;
- }
-
- switch (input[i]) {
- case 0:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMIN,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "UclkFmin (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.UclkFmin = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_UCLK_BIT;
- break;
-
- case 1:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMAX,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "UclkFmax (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.UclkFmax = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_UCLK_BIT;
- break;
-
- default:
- dev_info(adev->dev, "Invalid MCLK_VDDC_TABLE index: %ld\n", input[i]);
- dev_info(adev->dev, "Supported indices: [0:min,1:max]\n");
- return -EINVAL;
- }
- }
-
- if (od_table->OverDriveTable.UclkFmin > od_table->OverDriveTable.UclkFmax) {
- dev_err(adev->dev,
- "Invalid setting: UclkFmin(%u) is bigger than UclkFmax(%u)\n",
- (uint32_t)od_table->OverDriveTable.UclkFmin,
- (uint32_t)od_table->OverDriveTable.UclkFmax);
- return -EINVAL;
- }
- break;
-
- case PP_OD_EDIT_VDDGFX_OFFSET:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
- dev_warn(adev->dev, "Gfx offset setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFX_VF_CURVE,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "Voltage offset (%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++)
- od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[i] = input[0];
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_CURVE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- if (input[0] >= NUM_OD_FAN_MAX_POINTS - 1 ||
- input[0] < 0)
- return -EINVAL;
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_TEMP,
- &minimum,
- &maximum);
- if (input[1] < minimum ||
- input[1] > maximum) {
- dev_info(adev->dev, "Fan curve temp setting(%ld) must be within [%d, %d]!\n",
- input[1], minimum, maximum);
- return -EINVAL;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_PWM,
- &minimum,
- &maximum);
- if (input[2] < minimum ||
- input[2] > maximum) {
- dev_info(adev->dev, "Fan curve pwm setting(%ld) must be within [%d, %d]!\n",
- input[2], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanLinearTempPoints[input[0]] = input[1];
- od_table->OverDriveTable.FanLinearPwmPoints[input[0]] = input[2];
- od_table->OverDriveTable.FanMode = FAN_MODE_MANUAL_LINEAR;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_ACOUSTIC_LIMIT:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "acoustic limit threshold setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.AcousticLimitRpmThreshold = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_ACOUSTIC_TARGET:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "acoustic target threshold setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.AcousticTargetRpmThreshold = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "fan target temperature setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanTargetTemperature = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_MINIMUM_PWM:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_MINIMUM_PWM,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "fan minimum pwm setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanMinimumPwm = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_RESTORE_DEFAULT_TABLE:
- if (size == 1) {
- ret = smu_v14_0_2_od_restore_table_single(smu, input[0]);
- if (ret)
- return ret;
- } else {
- feature_ctrlmask = od_table->OverDriveTable.FeatureCtrlMask;
- memcpy(od_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t));
- od_table->OverDriveTable.FeatureCtrlMask = feature_ctrlmask;
- }
- fallthrough;
- case PP_OD_COMMIT_DPM_TABLE:
- /*
- * The member below instructs PMFW the settings focused in
- * this single operation.
- * `uint32_t FeatureCtrlMask;`
- * It does not contain actual informations about user's custom
- * settings. Thus we do not cache it.
- */
- offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset)) {
- smu_v14_0_2_dump_od_table(smu, od_table);
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
-
- od_table->OverDriveTable.FeatureCtrlMask = 0;
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
- (u8 *)od_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
-
- if (!memcmp(table_context->user_overdrive_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t)))
- smu->user_dpm_profile.user_od = false;
- else
- smu->user_dpm_profile.user_od = true;
- }
- break;
-
- default:
- return -ENOSYS;
- }
-
- return ret;
-}
-
-<<<<<<<
-static int smu_v14_0_2_set_power_limit(struct smu_context *smu,
- enum smu_ppt_limit_type limit_type,
- uint32_t limit)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- int ret = 0;
-
- if (limit_type != SMU_DEFAULT_PPT_LIMIT)
- return -EINVAL;
-
- if (limit <= msg_limit) {
- if (smu->current_power_limit > msg_limit) {
- od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
- }
- return smu_v14_0_set_power_limit(smu, limit_type, limit);
- } else if (smu->od_enabled) {
- ret = smu_v14_0_set_power_limit(smu, limit_type, msg_limit);
- if (ret)
- return ret;
-
- od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
-
- smu->current_power_limit = limit;
- } else {
- return -EINVAL;
- }
-
- return 0;
-=======
-static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
- void **table)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_3 *gpu_metrics =
- (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
- SmuMetricsExternal_t metrics_ext;
- SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- &metrics_ext,
- true);
- if (ret)
- return ret;
-
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
-
- gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
- gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
- gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
- gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
- gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
- gpu_metrics->temperature_vrmem = max(metrics->AvgTemperature[TEMP_VR_MEM0],
- metrics->AvgTemperature[TEMP_VR_MEM1]);
-
- gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
- gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
- gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage,
- metrics->Vcn1ActivityPercentage);
-
- gpu_metrics->average_socket_power = metrics->AverageSocketPower;
- gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
-
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
- else
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
-
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
- else
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
-
- gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
- gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
- gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
- gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
-
- gpu_metrics->current_gfxclk = gpu_metrics->average_gfxclk_frequency;
- gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK];
- gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK];
- gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
- gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_0];
-
- gpu_metrics->throttle_status =
- smu_v14_0_2_get_throttler_status(metrics);
- gpu_metrics->indep_throttle_status =
- smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
- smu_v14_0_2_throttler_map);
-
- gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
-
- gpu_metrics->pcie_link_width = metrics->PcieWidth;
- if ((metrics->PcieRate - 1) > LINK_SPEED_MAX)
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(1);
- else
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(metrics->PcieRate);
-
- gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
-
- gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_VDD_SOC];
- gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VDDIO_MEM];
-
- *table = (void *)gpu_metrics;
-
- return sizeof(struct gpu_metrics_v1_3);
->>>>>>>
-}
-
-static const struct pptable_funcs smu_v14_0_2_ppt_funcs = {
- .get_allowed_feature_mask = smu_v14_0_2_get_allowed_feature_mask,
- .set_default_dpm_table = smu_v14_0_2_set_default_dpm_table,
- .i2c_init = smu_v14_0_2_i2c_control_init,
- .i2c_fini = smu_v14_0_2_i2c_control_fini,
- .is_dpm_running = smu_v14_0_2_is_dpm_running,
- .dump_pptable = smu_v14_0_2_dump_pptable,
- .init_microcode = smu_v14_0_init_microcode,
- .load_microcode = smu_v14_0_load_microcode,
- .fini_microcode = smu_v14_0_fini_microcode,
- .init_smc_tables = smu_v14_0_2_init_smc_tables,
- .fini_smc_tables = smu_v14_0_fini_smc_tables,
- .init_power = smu_v14_0_init_power,
- .fini_power = smu_v14_0_fini_power,
- .check_fw_status = smu_v14_0_check_fw_status,
- .setup_pptable = smu_v14_0_2_setup_pptable,
- .check_fw_version = smu_v14_0_check_fw_version,
- .write_pptable = smu_cmn_write_pptable,
- .set_driver_table_location = smu_v14_0_set_driver_table_location,
- .system_features_control = smu_v14_0_system_features_control,
- .set_allowed_mask = smu_v14_0_set_allowed_mask,
- .get_enabled_mask = smu_cmn_get_enabled_mask,
- .dpm_set_vcn_enable = smu_v14_0_set_vcn_enable,
- .dpm_set_jpeg_enable = smu_v14_0_set_jpeg_enable,
- .get_dpm_ultimate_freq = smu_v14_0_2_get_dpm_ultimate_freq,
- .get_vbios_bootup_values = smu_v14_0_get_vbios_bootup_values,
- .read_sensor = smu_v14_0_2_read_sensor,
- .feature_is_enabled = smu_cmn_feature_is_enabled,
- .print_clk_levels = smu_v14_0_2_print_clk_levels,
- .force_clk_levels = smu_v14_0_2_force_clk_levels,
- .update_pcie_parameters = smu_v14_0_2_update_pcie_parameters,
- .get_thermal_temperature_range = smu_v14_0_2_get_thermal_temperature_range,
- .register_irq_handler = smu_v14_0_register_irq_handler,
- .enable_thermal_alert = smu_v14_0_enable_thermal_alert,
- .disable_thermal_alert = smu_v14_0_disable_thermal_alert,
- .notify_memory_pool_location = smu_v14_0_notify_memory_pool_location,
- .get_gpu_metrics = smu_v14_0_2_get_gpu_metrics,
- .set_soft_freq_limited_range = smu_v14_0_set_soft_freq_limited_range,
- .set_default_od_settings = smu_v14_0_2_set_default_od_settings,
- .restore_user_od_settings = smu_v14_0_2_restore_user_od_settings,
- .od_edit_dpm_table = smu_v14_0_2_od_edit_dpm_table,
- .init_pptable_microcode = smu_v14_0_init_pptable_microcode,
- .populate_umd_state_clk = smu_v14_0_2_populate_umd_state_clk,
- .set_performance_level = smu_v14_0_set_performance_level,
- .gfx_off_control = smu_v14_0_gfx_off_control,
- .get_unique_id = smu_v14_0_2_get_unique_id,
- .get_power_limit = smu_v14_0_2_get_power_limit,
- .set_power_limit = smu_v14_0_2_set_power_limit,
- .set_power_source = smu_v14_0_set_power_source,
- .get_power_profile_mode = smu_v14_0_2_get_power_profile_mode,
- .set_power_profile_mode = smu_v14_0_2_set_power_profile_mode,
- .run_btc = smu_v14_0_run_btc,
- .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
- .set_pp_feature_mask = smu_cmn_set_pp_feature_mask,
- .set_tool_table_location = smu_v14_0_set_tool_table_location,
- .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,
- .mode1_reset = smu_v14_0_2_mode1_reset,
- .mode2_reset = smu_v14_0_2_mode2_reset,
- .enable_gfx_features = smu_v14_0_2_enable_gfx_features,
- .set_mp1_state = smu_v14_0_2_set_mp1_state,
- .set_df_cstate = smu_v14_0_2_set_df_cstate,
-#if 0
- .gpo_control = smu_v14_0_gpo_control,
-#endif
-};
-
-void smu_v14_0_2_set_ppt_funcs(struct smu_context *smu)
-{
- smu->ppt_funcs = &smu_v14_0_2_ppt_funcs;
- smu->message_map = smu_v14_0_2_message_map;
- smu->clock_map = smu_v14_0_2_clk_map;
- smu->feature_map = smu_v14_0_2_feature_mask_map;
- smu->table_map = smu_v14_0_2_table_map;
- smu->pwr_src_map = smu_v14_0_2_pwr_src_map;
- smu->workload_map = smu_v14_0_2_workload_map;
- smu_v14_0_2_set_smu_mailbox_registers(smu);
-}
diff --git a/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage.1 b/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage.1
deleted file mode 100644
index 6878263c313c..000000000000
--- a/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage.1
+++ /dev/null
@@ -1,2956 +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.
- *
- */
-
-#define SWSMU_CODE_LAYER_L2
-
-#include <linux/firmware.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include "amdgpu.h"
-#include "amdgpu_smu.h"
-#include "atomfirmware.h"
-#include "amdgpu_atomfirmware.h"
-#include "amdgpu_atombios.h"
-#include "smu_v14_0.h"
-#include "smu14_driver_if_v14_0.h"
-#include "soc15_common.h"
-#include "atom.h"
-#include "smu_v14_0_2_ppt.h"
-#include "smu_v14_0_2_pptable.h"
-#include "smu_v14_0_2_ppsmc.h"
-#include "mp/mp_14_0_2_offset.h"
-#include "mp/mp_14_0_2_sh_mask.h"
-
-#include "smu_cmn.h"
-#include "amdgpu_ras.h"
-
-/*
- * DO NOT use these for err/warn/info/debug messages.
- * Use dev_err, dev_warn, dev_info and dev_dbg instead.
- * They are more MGPU friendly.
- */
-#undef pr_err
-#undef pr_warn
-#undef pr_info
-#undef pr_debug
-
-#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
-
-#define FEATURE_MASK(feature) (1ULL << feature)
-#define SMC_DPM_FEATURE ( \
- FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_UCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_LINK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_FCLK_BIT))
-
-#define MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE 0x4000
-#define DEBUGSMC_MSG_Mode1Reset 2
-#define LINK_SPEED_MAX 3
-<<<<<<<
-=======
-
-#define PP_OD_FEATURE_GFXCLK_FMIN 0
-#define PP_OD_FEATURE_GFXCLK_FMAX 1
-#define PP_OD_FEATURE_UCLK_FMIN 2
-#define PP_OD_FEATURE_UCLK_FMAX 3
-#define PP_OD_FEATURE_GFX_VF_CURVE 4
-#define PP_OD_FEATURE_FAN_CURVE_TEMP 5
-#define PP_OD_FEATURE_FAN_CURVE_PWM 6
-#define PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT 7
-#define PP_OD_FEATURE_FAN_ACOUSTIC_TARGET 8
-#define PP_OD_FEATURE_FAN_TARGET_TEMPERATURE 9
-#define PP_OD_FEATURE_FAN_MINIMUM_PWM 10
->>>>>>>
-
-static struct cmn2asic_msg_mapping smu_v14_0_2_message_map[SMU_MSG_MAX_COUNT] = {
- MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
- MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
- MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
- MSG_MAP(SetAllowedFeaturesMaskLow, PPSMC_MSG_SetAllowedFeaturesMaskLow, 0),
- MSG_MAP(SetAllowedFeaturesMaskHigh, PPSMC_MSG_SetAllowedFeaturesMaskHigh, 0),
- MSG_MAP(EnableAllSmuFeatures, PPSMC_MSG_EnableAllSmuFeatures, 0),
- MSG_MAP(DisableAllSmuFeatures, PPSMC_MSG_DisableAllSmuFeatures, 0),
- MSG_MAP(EnableSmuFeaturesLow, PPSMC_MSG_EnableSmuFeaturesLow, 1),
- MSG_MAP(EnableSmuFeaturesHigh, PPSMC_MSG_EnableSmuFeaturesHigh, 1),
- MSG_MAP(DisableSmuFeaturesLow, PPSMC_MSG_DisableSmuFeaturesLow, 1),
- MSG_MAP(DisableSmuFeaturesHigh, PPSMC_MSG_DisableSmuFeaturesHigh, 1),
- MSG_MAP(GetEnabledSmuFeaturesLow, PPSMC_MSG_GetRunningSmuFeaturesLow, 1),
- MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetRunningSmuFeaturesHigh, 1),
- MSG_MAP(SetWorkloadMask, PPSMC_MSG_SetWorkloadMask, 1),
- MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0),
- MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
- MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
- MSG_MAP(SetToolsDramAddrHigh, PPSMC_MSG_SetToolsDramAddrHigh, 0),
- MSG_MAP(SetToolsDramAddrLow, PPSMC_MSG_SetToolsDramAddrLow, 0),
- MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
- MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 0),
- MSG_MAP(UseDefaultPPTable, PPSMC_MSG_UseDefaultPPTable, 0),
- MSG_MAP(RunDcBtc, PPSMC_MSG_RunDcBtc, 0),
- MSG_MAP(EnterBaco, PPSMC_MSG_EnterBaco, 0),
- MSG_MAP(ExitBaco, PPSMC_MSG_ExitBaco, 0),
- MSG_MAP(SetSoftMinByFreq, PPSMC_MSG_SetSoftMinByFreq, 1),
- MSG_MAP(SetSoftMaxByFreq, PPSMC_MSG_SetSoftMaxByFreq, 1),
- MSG_MAP(SetHardMinByFreq, PPSMC_MSG_SetHardMinByFreq, 1),
- MSG_MAP(SetHardMaxByFreq, PPSMC_MSG_SetHardMaxByFreq, 0),
- MSG_MAP(GetMinDpmFreq, PPSMC_MSG_GetMinDpmFreq, 1),
- MSG_MAP(GetMaxDpmFreq, PPSMC_MSG_GetMaxDpmFreq, 1),
- MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1),
- MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 0),
- MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 0),
- MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 0),
- MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 0),
- MSG_MAP(GetDcModeMaxDpmFreq, PPSMC_MSG_GetDcModeMaxDpmFreq, 1),
- MSG_MAP(OverridePcieParameters, PPSMC_MSG_OverridePcieParameters, 0),
- MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0),
- MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0),
- MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0),
- MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 0),
- MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 0),
- MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm, 0),
- MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0),
- MSG_MAP(NotifyPowerSource, PPSMC_MSG_NotifyPowerSource, 0),
- MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 0),
- MSG_MAP(DFCstateControl, PPSMC_MSG_SetExternalClientDfCstateAllow, 0),
- MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0),
- MSG_MAP(SetNumBadMemoryPagesRetired, PPSMC_MSG_SetNumBadMemoryPagesRetired, 0),
- MSG_MAP(SetBadMemoryPagesRetiredFlagsPerChannel,
- PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel, 0),
- MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0),
- MSG_MAP(ReenableAcDcInterrupt, PPSMC_MSG_ReenableAcDcInterrupt, 0),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_clk_map[SMU_CLK_COUNT] = {
- CLK_MAP(GFXCLK, PPCLK_GFXCLK),
- CLK_MAP(SCLK, PPCLK_GFXCLK),
- CLK_MAP(SOCCLK, PPCLK_SOCCLK),
- CLK_MAP(FCLK, PPCLK_FCLK),
- CLK_MAP(UCLK, PPCLK_UCLK),
- CLK_MAP(MCLK, PPCLK_UCLK),
- CLK_MAP(VCLK, PPCLK_VCLK_0),
- CLK_MAP(DCLK, PPCLK_DCLK_0),
- CLK_MAP(DCEFCLK, PPCLK_DCFCLK),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_feature_mask_map[SMU_FEATURE_COUNT] = {
- FEA_MAP(FW_DATA_READ),
- FEA_MAP(DPM_GFXCLK),
- FEA_MAP(DPM_GFX_POWER_OPTIMIZER),
- FEA_MAP(DPM_UCLK),
- FEA_MAP(DPM_FCLK),
- FEA_MAP(DPM_SOCCLK),
- FEA_MAP(DPM_LINK),
- FEA_MAP(DPM_DCN),
- FEA_MAP(VMEMP_SCALING),
- FEA_MAP(VDDIO_MEM_SCALING),
- FEA_MAP(DS_GFXCLK),
- FEA_MAP(DS_SOCCLK),
- FEA_MAP(DS_FCLK),
- FEA_MAP(DS_LCLK),
- FEA_MAP(DS_DCFCLK),
- FEA_MAP(DS_UCLK),
- FEA_MAP(GFX_ULV),
- FEA_MAP(FW_DSTATE),
- FEA_MAP(GFXOFF),
- FEA_MAP(BACO),
- FEA_MAP(MM_DPM),
- FEA_MAP(SOC_MPCLK_DS),
- FEA_MAP(BACO_MPCLK_DS),
- FEA_MAP(THROTTLERS),
- FEA_MAP(SMARTSHIFT),
- FEA_MAP(GTHR),
- FEA_MAP(ACDC),
- FEA_MAP(VR0HOT),
- FEA_MAP(FW_CTF),
- FEA_MAP(FAN_CONTROL),
- FEA_MAP(GFX_DCS),
- FEA_MAP(GFX_READ_MARGIN),
- FEA_MAP(LED_DISPLAY),
- FEA_MAP(GFXCLK_SPREAD_SPECTRUM),
- FEA_MAP(OUT_OF_BAND_MONITOR),
- FEA_MAP(OPTIMIZED_VMIN),
- FEA_MAP(GFX_IMU),
- FEA_MAP(BOOT_TIME_CAL),
- FEA_MAP(GFX_PCC_DFLL),
- FEA_MAP(SOC_CG),
- FEA_MAP(DF_CSTATE),
- FEA_MAP(GFX_EDC),
- FEA_MAP(BOOT_POWER_OPT),
- FEA_MAP(CLOCK_POWER_DOWN_BYPASS),
- FEA_MAP(DS_VCN),
- FEA_MAP(BACO_CG),
- FEA_MAP(MEM_TEMP_READ),
- FEA_MAP(ATHUB_MMHUB_PG),
- FEA_MAP(SOC_PCC),
- [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
- [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
- [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_table_map[SMU_TABLE_COUNT] = {
- TAB_MAP(PPTABLE),
- TAB_MAP(WATERMARKS),
- TAB_MAP(AVFS_PSM_DEBUG),
- TAB_MAP(PMSTATUSLOG),
- TAB_MAP(SMU_METRICS),
- TAB_MAP(DRIVER_SMU_CONFIG),
- TAB_MAP(ACTIVITY_MONITOR_COEFF),
- [SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
- TAB_MAP(I2C_COMMANDS),
- TAB_MAP(ECCINFO),
- TAB_MAP(OVERDRIVE),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
- PWR_MAP(AC),
- PWR_MAP(DC),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_workload_map[PP_SMC_POWER_PROFILE_COUNT] = {
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT, WORKLOAD_PPLIB_DEFAULT_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_FULLSCREEN3D, WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_POWERSAVING, WORKLOAD_PPLIB_POWER_SAVING_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VIDEO, WORKLOAD_PPLIB_VIDEO_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VR, WORKLOAD_PPLIB_VR_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_COMPUTE, WORKLOAD_PPLIB_COMPUTE_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_CUSTOM, WORKLOAD_PPLIB_CUSTOM_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_WINDOW3D, WORKLOAD_PPLIB_WINDOW_3D_BIT),
-};
-
-static const uint8_t smu_v14_0_2_throttler_map[] = {
- [THROTTLER_PPT0_BIT] = (SMU_THROTTLER_PPT0_BIT),
- [THROTTLER_PPT1_BIT] = (SMU_THROTTLER_PPT1_BIT),
- [THROTTLER_PPT2_BIT] = (SMU_THROTTLER_PPT2_BIT),
- [THROTTLER_PPT3_BIT] = (SMU_THROTTLER_PPT3_BIT),
- [THROTTLER_TDC_GFX_BIT] = (SMU_THROTTLER_TDC_GFX_BIT),
- [THROTTLER_TDC_SOC_BIT] = (SMU_THROTTLER_TDC_SOC_BIT),
- [THROTTLER_TEMP_EDGE_BIT] = (SMU_THROTTLER_TEMP_EDGE_BIT),
- [THROTTLER_TEMP_HOTSPOT_BIT] = (SMU_THROTTLER_TEMP_HOTSPOT_BIT),
- [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
- [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
- [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
- [THROTTLER_TEMP_VR_MEM0_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
- [THROTTLER_TEMP_VR_MEM1_BIT] = (SMU_THROTTLER_TEMP_VR_MEM1_BIT),
- [THROTTLER_TEMP_LIQUID0_BIT] = (SMU_THROTTLER_TEMP_LIQUID0_BIT),
- [THROTTLER_TEMP_LIQUID1_BIT] = (SMU_THROTTLER_TEMP_LIQUID1_BIT),
- [THROTTLER_GFX_APCC_PLUS_BIT] = (SMU_THROTTLER_APCC_BIT),
- [THROTTLER_FIT_BIT] = (SMU_THROTTLER_FIT_BIT),
-};
-
-static int
-smu_v14_0_2_get_allowed_feature_mask(struct smu_context *smu,
- uint32_t *feature_mask, uint32_t num)
-{
- struct amdgpu_device *adev = smu->adev;
- /*u32 smu_version;*/
-
- if (num > 2)
- return -EINVAL;
-
- memset(feature_mask, 0xff, sizeof(uint32_t) * num);
-
- if (adev->pm.pp_feature & PP_SCLK_DPM_MASK) {
- *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT);
- *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_IMU_BIT);
- }
-#if 0
- if (!(adev->pg_flags & AMD_PG_SUPPORT_ATHUB) ||
- !(adev->pg_flags & AMD_PG_SUPPORT_MMHUB))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_ATHUB_MMHUB_PG_BIT);
-
- if (!(adev->pm.pp_feature & PP_SOCCLK_DPM_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT);
-
- /* PMFW 78.58 contains a critical fix for gfxoff feature */
- smu_cmn_get_smc_version(smu, NULL, &smu_version);
- if ((smu_version < 0x004e3a00) ||
- !(adev->pm.pp_feature & PP_GFXOFF_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFXOFF_BIT);
-
- if (!(adev->pm.pp_feature & PP_MCLK_DPM_MASK)) {
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_UCLK_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VMEMP_SCALING_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VDDIO_MEM_SCALING_BIT);
- }
-
- if (!(adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_GFXCLK_BIT);
-
- if (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK)) {
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_LINK_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_LCLK_BIT);
- }
-
- if (!(adev->pm.pp_feature & PP_ULV_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFX_ULV_BIT);
-#endif
-
- return 0;
-}
-
-static int smu_v14_0_2_check_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
- struct smu_baco_context *smu_baco = &smu->smu_baco;
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
- const OverDriveLimits_t * const overdrive_lowerlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMin;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_HARDWAREDC)
- smu->dc_controlled_by_gpio = true;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_BACO) {
- smu_baco->platform_support = true;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_MACO)
- smu_baco->maco_support = true;
- }
-
- if (!overdrive_lowerlimits->FeatureCtrlMask ||
- !overdrive_upperlimits->FeatureCtrlMask)
- smu->od_enabled = false;
-
- table_context->thermal_controller_type =
- powerplay_table->thermal_controller_type;
-
- /*
- * Instead of having its own buffer space and get overdrive_table copied,
- * smu->od_settings just points to the actual overdrive_table
- */
- smu->od_settings = &powerplay_table->overdrive_table;
-
- smu->adev->pm.no_fan =
- !(pptable->PFE_Settings.FeaturesToRun[0] & (1 << FEATURE_FAN_CONTROL_BIT));
-
- return 0;
-}
-
-static int smu_v14_0_2_store_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
-
- memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
- sizeof(PPTable_t));
-
- return 0;
-}
-
-#ifndef atom_smc_dpm_info_table_14_0_0
-struct atom_smc_dpm_info_table_14_0_0 {
- struct atom_common_table_header table_header;
- BoardTable_t BoardTable;
-};
-#endif
-
-static int smu_v14_0_2_append_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *smc_pptable = table_context->driver_pptable;
- struct atom_smc_dpm_info_table_14_0_0 *smc_dpm_table;
- BoardTable_t *BoardTable = &smc_pptable->BoardTable;
- int index, ret;
-
- index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
- smc_dpm_info);
-
- ret = amdgpu_atombios_get_data_table(smu->adev, index, NULL, NULL, NULL,
- (uint8_t **)&smc_dpm_table);
- if (ret)
- return ret;
-
- memcpy(BoardTable, &smc_dpm_table->BoardTable, sizeof(BoardTable_t));
-
- return 0;
-}
-
-#if 0
-static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
- void **table,
- uint32_t *size)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- void *combo_pptable = smu_table->combo_pptable;
- int ret = 0;
-
- ret = smu_cmn_get_combo_pptable(smu);
- if (ret)
- return ret;
-
- *table = combo_pptable;
- *size = sizeof(struct smu_14_0_powerplay_table);
-
- return 0;
-}
-#endif
-
-static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
- void **table,
- uint32_t *size)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- void *combo_pptable = smu_table->combo_pptable;
- int ret = 0;
-
- ret = smu_cmn_get_combo_pptable(smu);
- if (ret)
- return ret;
-
- *table = combo_pptable;
- *size = sizeof(struct smu_14_0_2_powerplay_table);
-
- return 0;
-}
-
-static int smu_v14_0_2_setup_pptable(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct amdgpu_device *adev = smu->adev;
- int ret = 0;
-
- if (amdgpu_sriov_vf(smu->adev))
- return 0;
-
- if (!adev->scpm_enabled)
- ret = smu_v14_0_setup_pptable(smu);
- else
- ret = smu_v14_0_2_get_pptable_from_pmfw(smu,
- &smu_table->power_play_table,
- &smu_table->power_play_table_size);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_store_powerplay_table(smu);
- if (ret)
- return ret;
-
- /*
- * With SCPM enabled, the operation below will be handled
- * by PSP. Driver involvment is unnecessary and useless.
- */
- if (!adev->scpm_enabled) {
- ret = smu_v14_0_2_append_powerplay_table(smu);
- if (ret)
- return ret;
- }
-
- ret = smu_v14_0_2_check_powerplay_table(smu);
- if (ret)
- return ret;
-
- return ret;
-}
-
-static int smu_v14_0_2_tables_init(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct smu_table *tables = smu_table->tables;
-
- SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetricsExternal_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU14_TOOL_SIZE,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- sizeof(DpmActivityMonitorCoeffIntExternal_t), PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_COMBO_PPTABLE, MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_ECCINFO, sizeof(EccInfoTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
-
- smu_table->metrics_table = kzalloc(sizeof(SmuMetricsExternal_t), GFP_KERNEL);
- if (!smu_table->metrics_table)
- goto err0_out;
- smu_table->metrics_time = 0;
-
- smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
- smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
- if (!smu_table->gpu_metrics_table)
- goto err1_out;
-
- smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
- if (!smu_table->watermarks_table)
- goto err2_out;
-
- smu_table->ecc_table = kzalloc(tables[SMU_TABLE_ECCINFO].size, GFP_KERNEL);
- if (!smu_table->ecc_table)
- goto err3_out;
-
- return 0;
-
-err3_out:
- kfree(smu_table->watermarks_table);
-err2_out:
- kfree(smu_table->gpu_metrics_table);
-err1_out:
- kfree(smu_table->metrics_table);
-err0_out:
- return -ENOMEM;
-}
-
-static int smu_v14_0_2_allocate_dpm_context(struct smu_context *smu)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
-
- smu_dpm->dpm_context = kzalloc(sizeof(struct smu_14_0_dpm_context),
- GFP_KERNEL);
- if (!smu_dpm->dpm_context)
- return -ENOMEM;
-
- smu_dpm->dpm_context_size = sizeof(struct smu_14_0_dpm_context);
-
- return 0;
-}
-
-static int smu_v14_0_2_init_smc_tables(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_v14_0_2_tables_init(smu);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_allocate_dpm_context(smu);
- if (ret)
- return ret;
-
- return smu_v14_0_init_smc_tables(smu);
-}
-
-static int smu_v14_0_2_set_default_dpm_table(struct smu_context *smu)
-{
- struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- SkuTable_t *skutable = &pptable->SkuTable;
- struct smu_14_0_dpm_table *dpm_table;
- struct smu_14_0_pcie_table *pcie_table;
- uint32_t link_level;
- int ret = 0;
-
- /* socclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.soc_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_SOCCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* gfxclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.gfx_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_GFXCLK,
- dpm_table);
- if (ret)
- return ret;
-
- /*
- * Update the reported maximum shader clock to the value
- * which can be guarded to be achieved on all cards. This
- * is aligned with Window setting. And considering that value
- * might be not the peak frequency the card can achieve, it
- * is normal some real-time clock frequency can overtake this
- * labelled maximum clock frequency(for example in pp_dpm_sclk
- * sysfs output).
- */
- if (skutable->DriverReportedClocks.GameClockAc &&
- (dpm_table->dpm_levels[dpm_table->count - 1].value >
- skutable->DriverReportedClocks.GameClockAc)) {
- dpm_table->dpm_levels[dpm_table->count - 1].value =
- skutable->DriverReportedClocks.GameClockAc;
- dpm_table->max = skutable->DriverReportedClocks.GameClockAc;
- }
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* uclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.uclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_UCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* fclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.fclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_FCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* vclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.vclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_VCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_VCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* dclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.dclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_DCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* lclk dpm table setup */
- pcie_table = &dpm_context->dpm_tables.pcie_table;
- pcie_table->num_of_link_levels = 0;
- for (link_level = 0; link_level < NUM_LINK_LEVELS; link_level++) {
- if (!skutable->PcieGenSpeed[link_level] &&
- !skutable->PcieLaneCount[link_level] &&
- !skutable->LclkFreq[link_level])
- continue;
-
- pcie_table->pcie_gen[pcie_table->num_of_link_levels] =
- skutable->PcieGenSpeed[link_level];
- pcie_table->pcie_lane[pcie_table->num_of_link_levels] =
- skutable->PcieLaneCount[link_level];
- pcie_table->clk_freq[pcie_table->num_of_link_levels] =
- skutable->LclkFreq[link_level];
- pcie_table->num_of_link_levels++;
- }
-
- /* dcefclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.dcef_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCN_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_DCEFCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- return 0;
-}
-
-static bool smu_v14_0_2_is_dpm_running(struct smu_context *smu)
-{
- int ret = 0;
- uint64_t feature_enabled;
-
- ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
- if (ret)
- return false;
-
- return !!(feature_enabled & SMC_DPM_FEATURE);
-}
-
-static void smu_v14_0_2_dump_pptable(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- PFE_Settings_t *PFEsettings = &pptable->PFE_Settings;
-
- dev_info(smu->adev->dev, "Dumped PPTable:\n");
-
- dev_info(smu->adev->dev, "Version = 0x%08x\n", PFEsettings->Version);
- dev_info(smu->adev->dev, "FeaturesToRun[0] = 0x%08x\n", PFEsettings->FeaturesToRun[0]);
- dev_info(smu->adev->dev, "FeaturesToRun[1] = 0x%08x\n", PFEsettings->FeaturesToRun[1]);
-}
-
-static uint32_t smu_v14_0_2_get_throttler_status(SmuMetrics_t *metrics)
-{
- uint32_t throttler_status = 0;
- int i;
-
- for (i = 0; i < THROTTLER_COUNT; i++)
- throttler_status |=
- (metrics->ThrottlingPercentage[i] ? 1U << i : 0);
-
- return throttler_status;
-}
-
-#define SMU_14_0_2_BUSY_THRESHOLD 5
-static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu,
- MetricsMember_t member,
- uint32_t *value)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- SmuMetrics_t *metrics =
- &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- NULL,
- false);
- if (ret)
- return ret;
-
- switch (member) {
- case METRICS_CURR_GFXCLK:
- *value = metrics->CurrClock[PPCLK_GFXCLK];
- break;
- case METRICS_CURR_SOCCLK:
- *value = metrics->CurrClock[PPCLK_SOCCLK];
- break;
- case METRICS_CURR_UCLK:
- *value = metrics->CurrClock[PPCLK_UCLK];
- break;
- case METRICS_CURR_VCLK:
- *value = metrics->CurrClock[PPCLK_VCLK_0];
- break;
- case METRICS_CURR_DCLK:
- *value = metrics->CurrClock[PPCLK_DCLK_0];
- break;
- case METRICS_CURR_FCLK:
- *value = metrics->CurrClock[PPCLK_FCLK];
- break;
- case METRICS_CURR_DCEFCLK:
- *value = metrics->CurrClock[PPCLK_DCFCLK];
- break;
- case METRICS_AVERAGE_GFXCLK:
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageGfxclkFrequencyPostDs;
- else
- *value = metrics->AverageGfxclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_FCLK:
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageFclkFrequencyPostDs;
- else
- *value = metrics->AverageFclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_UCLK:
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageMemclkFrequencyPostDs;
- else
- *value = metrics->AverageMemclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_VCLK:
- *value = metrics->AverageVclk0Frequency;
- break;
- case METRICS_AVERAGE_DCLK:
- *value = metrics->AverageDclk0Frequency;
- break;
- case METRICS_AVERAGE_VCLK1:
- *value = metrics->AverageVclk1Frequency;
- break;
- case METRICS_AVERAGE_DCLK1:
- *value = metrics->AverageDclk1Frequency;
- break;
- case METRICS_AVERAGE_GFXACTIVITY:
- *value = metrics->AverageGfxActivity;
- break;
- case METRICS_AVERAGE_MEMACTIVITY:
- *value = metrics->AverageUclkActivity;
- break;
- case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
- break;
- case METRICS_TEMPERATURE_EDGE:
- *value = metrics->AvgTemperature[TEMP_EDGE] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_HOTSPOT:
- *value = metrics->AvgTemperature[TEMP_HOTSPOT] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_MEM:
- *value = metrics->AvgTemperature[TEMP_MEM] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_VRGFX:
- *value = metrics->AvgTemperature[TEMP_VR_GFX] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_VRSOC:
- *value = metrics->AvgTemperature[TEMP_VR_SOC] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_THROTTLER_STATUS:
- *value = smu_v14_0_2_get_throttler_status(metrics);
- break;
- case METRICS_CURR_FANSPEED:
- *value = metrics->AvgFanRpm;
- break;
- case METRICS_CURR_FANPWM:
- *value = metrics->AvgFanPwm;
- break;
- case METRICS_VOLTAGE_VDDGFX:
- *value = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- break;
- case METRICS_PCIE_RATE:
- *value = metrics->PcieRate;
- break;
- case METRICS_PCIE_WIDTH:
- *value = metrics->PcieWidth;
- break;
- default:
- *value = UINT_MAX;
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_get_dpm_ultimate_freq(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t *min,
- uint32_t *max)
-{
- struct smu_14_0_dpm_context *dpm_context =
- smu->smu_dpm.dpm_context;
- struct smu_14_0_dpm_table *dpm_table;
-
- switch (clk_type) {
- case SMU_MCLK:
- case SMU_UCLK:
- /* uclk dpm table */
- dpm_table = &dpm_context->dpm_tables.uclk_table;
- break;
- case SMU_GFXCLK:
- case SMU_SCLK:
- /* gfxclk dpm table */
- dpm_table = &dpm_context->dpm_tables.gfx_table;
- break;
- case SMU_SOCCLK:
- /* socclk dpm table */
- dpm_table = &dpm_context->dpm_tables.soc_table;
- break;
- case SMU_FCLK:
- /* fclk dpm table */
- dpm_table = &dpm_context->dpm_tables.fclk_table;
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- /* vclk dpm table */
- dpm_table = &dpm_context->dpm_tables.vclk_table;
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- /* dclk dpm table */
- dpm_table = &dpm_context->dpm_tables.dclk_table;
- break;
- default:
- dev_err(smu->adev->dev, "Unsupported clock type!\n");
- return -EINVAL;
- }
-
- if (min)
- *min = dpm_table->min;
- if (max)
- *max = dpm_table->max;
-
- return 0;
-}
-
-static int smu_v14_0_2_read_sensor(struct smu_context *smu,
- enum amd_pp_sensors sensor,
- void *data,
- uint32_t *size)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *smc_pptable = table_context->driver_pptable;
- int ret = 0;
-
- switch (sensor) {
- case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
- *(uint16_t *)data = smc_pptable->CustomSkuTable.FanMaximumRpm;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_MEM_LOAD:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_MEMACTIVITY,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GPU_LOAD:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_GFXACTIVITY,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_SOCKETPOWER,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_HOTSPOT,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_EDGE_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_EDGE,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_MEM_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_MEM,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GFX_MCLK:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_CURR_UCLK,
- (uint32_t *)data);
- *(uint32_t *)data *= 100;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GFX_SCLK:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_GFXCLK,
- (uint32_t *)data);
- *(uint32_t *)data *= 100;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_VDDGFX:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_VOLTAGE_VDDGFX,
- (uint32_t *)data);
- *size = 4;
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_get_current_clk_freq_by_table(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t *value)
-{
- MetricsMember_t member_type;
- int clk_id = 0;
-
- clk_id = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_CLK,
- clk_type);
- if (clk_id < 0)
- return -EINVAL;
-
- switch (clk_id) {
- case PPCLK_GFXCLK:
- member_type = METRICS_AVERAGE_GFXCLK;
- break;
- case PPCLK_UCLK:
- member_type = METRICS_CURR_UCLK;
- break;
- case PPCLK_FCLK:
- member_type = METRICS_CURR_FCLK;
- break;
- case PPCLK_SOCCLK:
- member_type = METRICS_CURR_SOCCLK;
- break;
- case PPCLK_VCLK_0:
- member_type = METRICS_AVERAGE_VCLK;
- break;
- case PPCLK_DCLK_0:
- member_type = METRICS_AVERAGE_DCLK;
- break;
- case PPCLK_DCFCLK:
- member_type = METRICS_CURR_DCEFCLK;
- break;
- default:
- return -EINVAL;
- }
-
- return smu_v14_0_2_get_smu_metrics_data(smu,
- member_type,
- value);
-}
-
-static bool smu_v14_0_2_is_od_feature_supported(struct smu_context *smu,
- int od_feature_bit)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
-
- return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
-}
-
-static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
- int od_feature_bit,
- int32_t *min,
- int32_t *max)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
- const OverDriveLimits_t * const overdrive_lowerlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMin;
- int32_t od_min_setting, od_max_setting;
-
- switch (od_feature_bit) {
- case PP_OD_FEATURE_GFXCLK_FMIN:
- od_min_setting = overdrive_lowerlimits->GfxclkFmin;
- od_max_setting = overdrive_upperlimits->GfxclkFmin;
- break;
- case PP_OD_FEATURE_GFXCLK_FMAX:
- od_min_setting = overdrive_lowerlimits->GfxclkFmax;
- od_max_setting = overdrive_upperlimits->GfxclkFmax;
- break;
- case PP_OD_FEATURE_UCLK_FMIN:
- od_min_setting = overdrive_lowerlimits->UclkFmin;
- od_max_setting = overdrive_upperlimits->UclkFmin;
- break;
- case PP_OD_FEATURE_UCLK_FMAX:
- od_min_setting = overdrive_lowerlimits->UclkFmax;
- od_max_setting = overdrive_upperlimits->UclkFmax;
- break;
- case PP_OD_FEATURE_GFX_VF_CURVE:
- od_min_setting = overdrive_lowerlimits->VoltageOffsetPerZoneBoundary[0];
- od_max_setting = overdrive_upperlimits->VoltageOffsetPerZoneBoundary[0];
- break;
- case PP_OD_FEATURE_FAN_CURVE_TEMP:
- od_min_setting = overdrive_lowerlimits->FanLinearTempPoints[0];
- od_max_setting = overdrive_upperlimits->FanLinearTempPoints[0];
- break;
- case PP_OD_FEATURE_FAN_CURVE_PWM:
- od_min_setting = overdrive_lowerlimits->FanLinearPwmPoints[0];
- od_max_setting = overdrive_upperlimits->FanLinearPwmPoints[0];
- break;
- case PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT:
- od_min_setting = overdrive_lowerlimits->AcousticLimitRpmThreshold;
- od_max_setting = overdrive_upperlimits->AcousticLimitRpmThreshold;
- break;
- case PP_OD_FEATURE_FAN_ACOUSTIC_TARGET:
- od_min_setting = overdrive_lowerlimits->AcousticTargetRpmThreshold;
- od_max_setting = overdrive_upperlimits->AcousticTargetRpmThreshold;
- break;
- case PP_OD_FEATURE_FAN_TARGET_TEMPERATURE:
- od_min_setting = overdrive_lowerlimits->FanTargetTemperature;
- od_max_setting = overdrive_upperlimits->FanTargetTemperature;
- break;
- case PP_OD_FEATURE_FAN_MINIMUM_PWM:
- od_min_setting = overdrive_lowerlimits->FanMinimumPwm;
- od_max_setting = overdrive_upperlimits->FanMinimumPwm;
- break;
- default:
- od_min_setting = od_max_setting = INT_MAX;
- break;
- }
-
- if (min)
- *min = od_min_setting;
- if (max)
- *max = od_max_setting;
-}
-
-static int smu_v14_0_2_print_clk_levels(struct smu_context *smu,
- enum smu_clk_type clk_type,
- char *buf)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
- struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
- struct smu_14_0_dpm_table *single_dpm_table;
- struct smu_14_0_pcie_table *pcie_table;
- uint32_t gen_speed, lane_width;
- int i, curr_freq, size = 0;
- int32_t min_value, max_value;
- int ret = 0;
-
- smu_cmn_get_sysfs_buf(&buf, &size);
-
- if (amdgpu_ras_intr_triggered()) {
- size += sysfs_emit_at(buf, size, "unavailable\n");
- return size;
- }
-
- switch (clk_type) {
- case SMU_SCLK:
- single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
- break;
- case SMU_MCLK:
- single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- break;
- case SMU_SOCCLK:
- single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- break;
- case SMU_FCLK:
- single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- break;
- case SMU_DCEFCLK:
- single_dpm_table = &(dpm_context->dpm_tables.dcef_table);
- break;
- default:
- break;
- }
-
- switch (clk_type) {
- case SMU_SCLK:
- case SMU_MCLK:
- case SMU_SOCCLK:
- case SMU_FCLK:
- case SMU_VCLK:
- case SMU_VCLK1:
- case SMU_DCLK:
- case SMU_DCLK1:
- case SMU_DCEFCLK:
- ret = smu_v14_0_2_get_current_clk_freq_by_table(smu, clk_type, &curr_freq);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to get current clock freq!");
- return ret;
- }
-
- if (single_dpm_table->is_fine_grained) {
- /*
- * For fine grained dpms, there are only two dpm levels:
- * - level 0 -> min clock freq
- * - level 1 -> max clock freq
- * And the current clock frequency can be any value between them.
- * So, if the current clock frequency is not at level 0 or level 1,
- * we will fake it as three dpm levels:
- * - level 0 -> min clock freq
- * - level 1 -> current actual clock freq
- * - level 2 -> max clock freq
- */
- if ((single_dpm_table->dpm_levels[0].value != curr_freq) &&
- (single_dpm_table->dpm_levels[1].value != curr_freq)) {
- size += sysfs_emit_at(buf, size, "0: %uMhz\n",
- single_dpm_table->dpm_levels[0].value);
- size += sysfs_emit_at(buf, size, "1: %uMhz *\n",
- curr_freq);
- size += sysfs_emit_at(buf, size, "2: %uMhz\n",
- single_dpm_table->dpm_levels[1].value);
- } else {
- size += sysfs_emit_at(buf, size, "0: %uMhz %s\n",
- single_dpm_table->dpm_levels[0].value,
- single_dpm_table->dpm_levels[0].value == curr_freq ? "*" : "");
- size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
- single_dpm_table->dpm_levels[1].value,
- single_dpm_table->dpm_levels[1].value == curr_freq ? "*" : "");
- }
- } else {
- for (i = 0; i < single_dpm_table->count; i++)
- size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
- i, single_dpm_table->dpm_levels[i].value,
- single_dpm_table->dpm_levels[i].value == curr_freq ? "*" : "");
- }
- break;
- case SMU_PCIE:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_PCIE_RATE,
- &gen_speed);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_PCIE_WIDTH,
- &lane_width);
- if (ret)
- return ret;
-
- pcie_table = &(dpm_context->dpm_tables.pcie_table);
- for (i = 0; i < pcie_table->num_of_link_levels; i++)
- size += sysfs_emit_at(buf, size, "%d: %s %s %dMhz %s\n", i,
- (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s," :
- (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s," :
- (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s," :
- (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," : "",
- (pcie_table->pcie_lane[i] == 1) ? "x1" :
- (pcie_table->pcie_lane[i] == 2) ? "x2" :
- (pcie_table->pcie_lane[i] == 3) ? "x4" :
- (pcie_table->pcie_lane[i] == 4) ? "x8" :
- (pcie_table->pcie_lane[i] == 5) ? "x12" :
- (pcie_table->pcie_lane[i] == 6) ? "x16" : "",
- pcie_table->clk_freq[i],
- (gen_speed == DECODE_GEN_SPEED(pcie_table->pcie_gen[i])) &&
- (lane_width == DECODE_LANE_WIDTH(pcie_table->pcie_lane[i])) ?
- "*" : "");
- break;
-
- case SMU_OD_SCLK:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_GFXCLK_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_SCLK:\n");
- size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMhz\n",
- od_table->OverDriveTable.GfxclkFmin,
- od_table->OverDriveTable.GfxclkFmax);
- break;
-
- case SMU_OD_MCLK:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_UCLK_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_MCLK:\n");
- size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMHz\n",
- od_table->OverDriveTable.UclkFmin,
- od_table->OverDriveTable.UclkFmax);
- break;
-
- case SMU_OD_VDDGFX_OFFSET:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_GFX_VF_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_VDDGFX_OFFSET:\n");
- size += sysfs_emit_at(buf, size, "%dmV\n",
- od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[0]);
- break;
-
- case SMU_OD_FAN_CURVE:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_FAN_CURVE:\n");
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++)
- size += sysfs_emit_at(buf, size, "%d: %dC %d%%\n",
- i,
- (int)od_table->OverDriveTable.FanLinearTempPoints[i],
- (int)od_table->OverDriveTable.FanLinearPwmPoints[i]);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_TEMP,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "FAN_CURVE(hotspot temp): %uC %uC\n",
- min_value, max_value);
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_PWM,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "FAN_CURVE(fan speed): %u%% %u%%\n",
- min_value, max_value);
-
- break;
-
- case SMU_OD_ACOUSTIC_LIMIT:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_LIMIT:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.AcousticLimitRpmThreshold);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "ACOUSTIC_LIMIT: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_ACOUSTIC_TARGET:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_TARGET:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.AcousticTargetRpmThreshold);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "ACOUSTIC_TARGET: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_FAN_TARGET_TEMPERATURE:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "FAN_TARGET_TEMPERATURE:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.FanTargetTemperature);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "TARGET_TEMPERATURE: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_FAN_MINIMUM_PWM:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "FAN_MINIMUM_PWM:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.FanMinimumPwm);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_MINIMUM_PWM,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "MINIMUM_PWM: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_RANGE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT) &&
- !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT) &&
- !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMIN,
- &min_value,
- NULL);
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMAX,
- NULL,
- &max_value);
- size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
- min_value, max_value);
- }
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMIN,
- &min_value,
- NULL);
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMAX,
- NULL,
- &max_value);
- size += sysfs_emit_at(buf, size, "MCLK: %7uMhz %10uMhz\n",
- min_value, max_value);
- }
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFX_VF_CURVE,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "VDDGFX_OFFSET: %7dmv %10dmv\n",
- min_value, max_value);
- }
- break;
-
- default:
- break;
- }
-
- return size;
-}
-
-static int smu_v14_0_2_force_clk_levels(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t mask)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
- struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
- struct smu_14_0_dpm_table *single_dpm_table;
- uint32_t soft_min_level, soft_max_level;
- uint32_t min_freq, max_freq;
- int ret = 0;
-
- soft_min_level = mask ? (ffs(mask) - 1) : 0;
- soft_max_level = mask ? (fls(mask) - 1) : 0;
-
- switch (clk_type) {
- case SMU_GFXCLK:
- case SMU_SCLK:
- single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
- break;
- case SMU_MCLK:
- case SMU_UCLK:
- single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- break;
- case SMU_SOCCLK:
- single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- break;
- case SMU_FCLK:
- single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- break;
- default:
- break;
- }
-
- switch (clk_type) {
- case SMU_GFXCLK:
- case SMU_SCLK:
- case SMU_MCLK:
- case SMU_UCLK:
- case SMU_SOCCLK:
- case SMU_FCLK:
- case SMU_VCLK:
- case SMU_VCLK1:
- case SMU_DCLK:
- case SMU_DCLK1:
- if (single_dpm_table->is_fine_grained) {
- /* There is only 2 levels for fine grained DPM */
- soft_max_level = (soft_max_level >= 1 ? 1 : 0);
- soft_min_level = (soft_min_level >= 1 ? 1 : 0);
- } else {
- if ((soft_max_level >= single_dpm_table->count) ||
- (soft_min_level >= single_dpm_table->count))
- return -EINVAL;
- }
-
- min_freq = single_dpm_table->dpm_levels[soft_min_level].value;
- max_freq = single_dpm_table->dpm_levels[soft_max_level].value;
-
- ret = smu_v14_0_set_soft_freq_limited_range(smu,
- clk_type,
- min_freq,
- max_freq);
- break;
- case SMU_DCEFCLK:
- case SMU_PCIE:
- default:
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_update_pcie_parameters(struct smu_context *smu,
- uint8_t pcie_gen_cap,
- uint8_t pcie_width_cap)
-{
- struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
- struct smu_14_0_pcie_table *pcie_table =
- &dpm_context->dpm_tables.pcie_table;
- uint32_t smu_pcie_arg;
- int ret, i;
-
- for (i = 0; i < pcie_table->num_of_link_levels; i++) {
- if (pcie_table->pcie_gen[i] > pcie_gen_cap)
- pcie_table->pcie_gen[i] = pcie_gen_cap;
- if (pcie_table->pcie_lane[i] > pcie_width_cap)
- pcie_table->pcie_lane[i] = pcie_width_cap;
-
- smu_pcie_arg = i << 16;
- smu_pcie_arg |= pcie_table->pcie_gen[i] << 8;
- smu_pcie_arg |= pcie_table->pcie_lane[i];
-
- ret = smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_OverridePcieParameters,
- smu_pcie_arg,
- NULL);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static const struct smu_temperature_range smu14_thermal_policy[] = {
- {-273150, 99000, 99000, -273150, 99000, 99000, -273150, 99000, 99000},
- { 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000},
-};
-
-static int smu_v14_0_2_get_thermal_temperature_range(struct smu_context *smu,
- struct smu_temperature_range *range)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
- PPTable_t *pptable = smu->smu_table.driver_pptable;
-
- if (amdgpu_sriov_vf(smu->adev))
- return 0;
-
- if (!range)
- return -EINVAL;
-
- memcpy(range, &smu14_thermal_policy[0], sizeof(struct smu_temperature_range));
-
- range->max = pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->edge_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] + CTF_OFFSET_EDGE) *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->hotspot_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->hotspot_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] + CTF_OFFSET_HOTSPOT) *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->mem_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->mem_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] + CTF_OFFSET_MEM)*
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->software_shutdown_temp = powerplay_table->software_shutdown_temp;
- range->software_shutdown_temp_offset = pptable->CustomSkuTable.FanAbnormalTempLimitOffset;
-
- return 0;
-}
-
-static int smu_v14_0_2_populate_umd_state_clk(struct smu_context *smu)
-{
- struct smu_14_0_dpm_context *dpm_context =
- smu->smu_dpm.dpm_context;
- struct smu_14_0_dpm_table *gfx_table =
- &dpm_context->dpm_tables.gfx_table;
- struct smu_14_0_dpm_table *mem_table =
- &dpm_context->dpm_tables.uclk_table;
- struct smu_14_0_dpm_table *soc_table =
- &dpm_context->dpm_tables.soc_table;
- struct smu_14_0_dpm_table *vclk_table =
- &dpm_context->dpm_tables.vclk_table;
- struct smu_14_0_dpm_table *dclk_table =
- &dpm_context->dpm_tables.dclk_table;
- struct smu_14_0_dpm_table *fclk_table =
- &dpm_context->dpm_tables.fclk_table;
- struct smu_umd_pstate_table *pstate_table =
- &smu->pstate_table;
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- DriverReportedClocks_t driver_clocks =
- pptable->SkuTable.DriverReportedClocks;
-
- pstate_table->gfxclk_pstate.min = gfx_table->min;
- if (driver_clocks.GameClockAc &&
- (driver_clocks.GameClockAc < gfx_table->max))
- pstate_table->gfxclk_pstate.peak = driver_clocks.GameClockAc;
- else
- pstate_table->gfxclk_pstate.peak = gfx_table->max;
-
- pstate_table->uclk_pstate.min = mem_table->min;
- pstate_table->uclk_pstate.peak = mem_table->max;
-
- pstate_table->socclk_pstate.min = soc_table->min;
- pstate_table->socclk_pstate.peak = soc_table->max;
-
- pstate_table->vclk_pstate.min = vclk_table->min;
- pstate_table->vclk_pstate.peak = vclk_table->max;
-
- pstate_table->dclk_pstate.min = dclk_table->min;
- pstate_table->dclk_pstate.peak = dclk_table->max;
-
- pstate_table->fclk_pstate.min = fclk_table->min;
- pstate_table->fclk_pstate.peak = fclk_table->max;
-
- if (driver_clocks.BaseClockAc &&
- driver_clocks.BaseClockAc < gfx_table->max)
- pstate_table->gfxclk_pstate.standard = driver_clocks.BaseClockAc;
- else
- pstate_table->gfxclk_pstate.standard = gfx_table->max;
- pstate_table->uclk_pstate.standard = mem_table->max;
- pstate_table->socclk_pstate.standard = soc_table->min;
- pstate_table->vclk_pstate.standard = vclk_table->min;
- pstate_table->dclk_pstate.standard = dclk_table->min;
- pstate_table->fclk_pstate.standard = fclk_table->min;
-
- return 0;
-}
-
-static void smu_v14_0_2_get_unique_id(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- SmuMetrics_t *metrics =
- &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
- struct amdgpu_device *adev = smu->adev;
- uint32_t upper32 = 0, lower32 = 0;
- int ret;
-
- ret = smu_cmn_get_metrics_table(smu, NULL, false);
- if (ret)
- goto out;
-
- upper32 = metrics->PublicSerialNumberUpper;
- lower32 = metrics->PublicSerialNumberLower;
-
-out:
- adev->unique_id = ((uint64_t)upper32 << 32) | lower32;
-}
-
-static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
- uint32_t *current_power_limit,
- uint32_t *default_power_limit,
- uint32_t *max_power_limit,
- uint32_t *min_power_limit)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- CustomSkuTable_t *skutable = &pptable->CustomSkuTable;
- uint32_t power_limit;
- uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
-
- if (smu_v14_0_get_current_power_limit(smu, &power_limit))
- power_limit = smu->adev->pm.ac_power ?
- skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
- skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
-
- if (current_power_limit)
- *current_power_limit = power_limit;
- if (default_power_limit)
- *default_power_limit = power_limit;
-
- if (max_power_limit)
- *max_power_limit = msg_limit;
-
- if (min_power_limit)
- *min_power_limit = 0;
-
- return 0;
-}
-
-static int smu_v14_0_2_get_power_profile_mode(struct smu_context *smu,
- char *buf)
-{
- DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
- DpmActivityMonitorCoeffInt_t *activity_monitor =
- &(activity_monitor_external.DpmActivityMonitorCoeffInt);
- static const char *title[] = {
- "PROFILE_INDEX(NAME)",
- "CLOCK_TYPE(NAME)",
- "FPS",
- "MinActiveFreqType",
- "MinActiveFreq",
- "BoosterFreqType",
- "BoosterFreq",
- "PD_Data_limit_c",
- "PD_Data_error_coeff",
- "PD_Data_error_rate_coeff"};
- int16_t workload_type = 0;
- uint32_t i, size = 0;
- int result = 0;
-
- if (!buf)
- return -EINVAL;
-
- size += sysfs_emit_at(buf, size, "%16s %s %s %s %s %s %s %s %s %s\n",
- title[0], title[1], title[2], title[3], title[4], title[5],
- title[6], title[7], title[8], title[9]);
-
- for (i = 0; i < PP_SMC_POWER_PROFILE_COUNT; i++) {
- /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
- workload_type = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_WORKLOAD,
- i);
- if (workload_type == -ENOTSUPP)
- continue;
- else if (workload_type < 0)
- return -EINVAL;
-
- result = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- workload_type,
- (void *)(&activity_monitor_external),
- false);
- if (result) {
- dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
- return result;
- }
-
- size += sysfs_emit_at(buf, size, "%2d %14s%s:\n",
- i, amdgpu_pp_profile_name[i], (i == smu->power_profile_mode) ? "*" : " ");
-
- size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
- " ",
- 0,
- "GFXCLK",
- activity_monitor->Gfx_FPS,
- activity_monitor->Gfx_MinActiveFreqType,
- activity_monitor->Gfx_MinActiveFreq,
- activity_monitor->Gfx_BoosterFreqType,
- activity_monitor->Gfx_BoosterFreq,
- activity_monitor->Gfx_PD_Data_limit_c,
- activity_monitor->Gfx_PD_Data_error_coeff,
- activity_monitor->Gfx_PD_Data_error_rate_coeff);
-
- size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
- " ",
- 1,
- "FCLK",
- activity_monitor->Fclk_FPS,
- activity_monitor->Fclk_MinActiveFreqType,
- activity_monitor->Fclk_MinActiveFreq,
- activity_monitor->Fclk_BoosterFreqType,
- activity_monitor->Fclk_BoosterFreq,
- activity_monitor->Fclk_PD_Data_limit_c,
- activity_monitor->Fclk_PD_Data_error_coeff,
- activity_monitor->Fclk_PD_Data_error_rate_coeff);
- }
-
- return size;
-}
-
-static int smu_v14_0_2_set_power_profile_mode(struct smu_context *smu,
- long *input,
- uint32_t size)
-{
- DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
- DpmActivityMonitorCoeffInt_t *activity_monitor =
- &(activity_monitor_external.DpmActivityMonitorCoeffInt);
- int workload_type, ret = 0;
-
- smu->power_profile_mode = input[size];
-
- if (smu->power_profile_mode >= PP_SMC_POWER_PROFILE_COUNT) {
- dev_err(smu->adev->dev, "Invalid power profile mode %d\n", smu->power_profile_mode);
- return -EINVAL;
- }
-
- if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
- if (size != 9)
- return -EINVAL;
-
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- WORKLOAD_PPLIB_CUSTOM_BIT,
- (void *)(&activity_monitor_external),
- false);
- if (ret) {
- dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
- return ret;
- }
-
- switch (input[0]) {
- case 0: /* Gfxclk */
- activity_monitor->Gfx_FPS = input[1];
- activity_monitor->Gfx_MinActiveFreqType = input[2];
- activity_monitor->Gfx_MinActiveFreq = input[3];
- activity_monitor->Gfx_BoosterFreqType = input[4];
- activity_monitor->Gfx_BoosterFreq = input[5];
- activity_monitor->Gfx_PD_Data_limit_c = input[6];
- activity_monitor->Gfx_PD_Data_error_coeff = input[7];
- activity_monitor->Gfx_PD_Data_error_rate_coeff = input[8];
- break;
- case 1: /* Fclk */
- activity_monitor->Fclk_FPS = input[1];
- activity_monitor->Fclk_MinActiveFreqType = input[2];
- activity_monitor->Fclk_MinActiveFreq = input[3];
- activity_monitor->Fclk_BoosterFreqType = input[4];
- activity_monitor->Fclk_BoosterFreq = input[5];
- activity_monitor->Fclk_PD_Data_limit_c = input[6];
- activity_monitor->Fclk_PD_Data_error_coeff = input[7];
- activity_monitor->Fclk_PD_Data_error_rate_coeff = input[8];
- break;
- default:
- return -EINVAL;
- }
-
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- WORKLOAD_PPLIB_CUSTOM_BIT,
- (void *)(&activity_monitor_external),
- true);
- if (ret) {
- dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
- return ret;
- }
- }
-
- /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
- workload_type = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_WORKLOAD,
- smu->power_profile_mode);
- if (workload_type < 0)
- return -EINVAL;
-
- return smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_SetWorkloadMask,
- 1 << workload_type,
- NULL);
-}
-
-static int smu_v14_0_2_baco_enter(struct smu_context *smu)
-{
- struct smu_baco_context *smu_baco = &smu->smu_baco;
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev))
- return smu_v14_0_baco_set_armd3_sequence(smu,
- smu_baco->maco_support ? BACO_SEQ_BAMACO : BACO_SEQ_BACO);
- else
- return smu_v14_0_baco_enter(smu);
-}
-
-static int smu_v14_0_2_baco_exit(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev)) {
- /* Wait for PMFW handling for the Dstate change */
- usleep_range(10000, 11000);
- return smu_v14_0_baco_set_armd3_sequence(smu, BACO_SEQ_ULPS);
- } else {
- return smu_v14_0_baco_exit(smu);
- }
-}
-
-static bool smu_v14_0_2_is_mode1_reset_supported(struct smu_context *smu)
-{
- // TODO
-
- return true;
-}
-
-static int smu_v14_0_2_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msg, int num_msgs)
-{
- struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(i2c_adap);
- struct amdgpu_device *adev = smu_i2c->adev;
- struct smu_context *smu = adev->powerplay.pp_handle;
- struct smu_table_context *smu_table = &smu->smu_table;
- struct smu_table *table = &smu_table->driver_table;
- SwI2cRequest_t *req, *res = (SwI2cRequest_t *)table->cpu_addr;
- int i, j, r, c;
- u16 dir;
-
- if (!adev->pm.dpm_enabled)
- return -EBUSY;
-
- req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- req->I2CcontrollerPort = smu_i2c->port;
- req->I2CSpeed = I2C_SPEED_FAST_400K;
- req->SlaveAddress = msg[0].addr << 1; /* wants an 8-bit address */
- dir = msg[0].flags & I2C_M_RD;
-
- for (c = i = 0; i < num_msgs; i++) {
- for (j = 0; j < msg[i].len; j++, c++) {
- SwI2cCmd_t *cmd = &req->SwI2cCmds[c];
-
- if (!(msg[i].flags & I2C_M_RD)) {
- /* write */
- cmd->CmdConfig |= CMDCONFIG_READWRITE_MASK;
- cmd->ReadWriteData = msg[i].buf[j];
- }
-
- if ((dir ^ msg[i].flags) & I2C_M_RD) {
- /* The direction changes.
- */
- dir = msg[i].flags & I2C_M_RD;
- cmd->CmdConfig |= CMDCONFIG_RESTART_MASK;
- }
-
- req->NumCmds++;
-
- /*
- * Insert STOP if we are at the last byte of either last
- * message for the transaction or the client explicitly
- * requires a STOP at this particular message.
- */
- if ((j == msg[i].len - 1) &&
- ((i == num_msgs - 1) || (msg[i].flags & I2C_M_STOP))) {
- cmd->CmdConfig &= ~CMDCONFIG_RESTART_MASK;
- cmd->CmdConfig |= CMDCONFIG_STOP_MASK;
- }
- }
- }
- mutex_lock(&adev->pm.mutex);
- r = smu_cmn_update_table(smu, SMU_TABLE_I2C_COMMANDS, 0, req, true);
- mutex_unlock(&adev->pm.mutex);
- if (r)
- goto fail;
-
- for (c = i = 0; i < num_msgs; i++) {
- if (!(msg[i].flags & I2C_M_RD)) {
- c += msg[i].len;
- continue;
- }
- for (j = 0; j < msg[i].len; j++, c++) {
- SwI2cCmd_t *cmd = &res->SwI2cCmds[c];
-
- msg[i].buf[j] = cmd->ReadWriteData;
- }
- }
- r = num_msgs;
-fail:
- kfree(req);
- return r;
-}
-
-static u32 smu_v14_0_2_i2c_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm smu_v14_0_2_i2c_algo = {
- .master_xfer = smu_v14_0_2_i2c_xfer,
- .functionality = smu_v14_0_2_i2c_func,
-};
-
-static const struct i2c_adapter_quirks smu_v14_0_2_i2c_control_quirks = {
- .flags = I2C_AQ_COMB | I2C_AQ_COMB_SAME_ADDR | I2C_AQ_NO_ZERO_LEN,
- .max_read_len = MAX_SW_I2C_COMMANDS,
- .max_write_len = MAX_SW_I2C_COMMANDS,
- .max_comb_1st_msg_len = 2,
- .max_comb_2nd_msg_len = MAX_SW_I2C_COMMANDS - 2,
-};
-
-static int smu_v14_0_2_i2c_control_init(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
- int res, i;
-
- for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- smu_i2c->adev = adev;
- smu_i2c->port = i;
- mutex_init(&smu_i2c->mutex);
- control->owner = THIS_MODULE;
- control->dev.parent = &adev->pdev->dev;
- control->algo = &smu_v14_0_2_i2c_algo;
- snprintf(control->name, sizeof(control->name), "AMDGPU SMU %d", i);
- control->quirks = &smu_v14_0_2_i2c_control_quirks;
- i2c_set_adapdata(control, smu_i2c);
-
- res = i2c_add_adapter(control);
- if (res) {
- DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
- goto Out_err;
- }
- }
-
- /* assign the buses used for the FRU EEPROM and RAS EEPROM */
- /* XXX ideally this would be something in a vbios data table */
- adev->pm.ras_eeprom_i2c_bus = &adev->pm.smu_i2c[1].adapter;
- adev->pm.fru_eeprom_i2c_bus = &adev->pm.smu_i2c[0].adapter;
-
- return 0;
-Out_err:
- for ( ; i >= 0; i--) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- i2c_del_adapter(control);
- }
- return res;
-}
-
-static void smu_v14_0_2_i2c_control_fini(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
- int i;
-
- for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- i2c_del_adapter(control);
- }
- adev->pm.ras_eeprom_i2c_bus = NULL;
- adev->pm.fru_eeprom_i2c_bus = NULL;
-}
-
-static int smu_v14_0_2_set_mp1_state(struct smu_context *smu,
- enum pp_mp1_state mp1_state)
-{
- int ret;
-
- switch (mp1_state) {
- case PP_MP1_STATE_UNLOAD:
- ret = smu_cmn_set_mp1_state(smu, mp1_state);
- break;
- default:
- /* Ignore others */
- ret = 0;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_set_df_cstate(struct smu_context *smu,
- enum pp_df_cstate state)
-{
- return smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_DFCstateControl,
- state,
- NULL);
-}
-
-static int smu_v14_0_2_mode1_reset(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_cmn_send_debug_smc_msg(smu, DEBUGSMC_MSG_Mode1Reset);
- if (!ret) {
- if (amdgpu_emu_mode == 1)
- msleep(50000);
- else
- msleep(1000);
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_mode2_reset(struct smu_context *smu)
-{
- int ret = 0;
-
- // TODO
-
- return ret;
-}
-
-static int smu_v14_0_2_enable_gfx_features(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(14, 0, 2))
- return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_EnableAllSmuFeatures,
- FEATURE_PWR_GFX, NULL);
- else
- return -EOPNOTSUPP;
-}
-
-static void smu_v14_0_2_set_smu_mailbox_registers(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- smu->param_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_82);
- smu->msg_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_66);
- smu->resp_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_90);
-
- smu->debug_param_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_53);
- smu->debug_msg_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_75);
- smu->debug_resp_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_54);
-}
-
-static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
- void **table)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_3 *gpu_metrics =
- (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
- SmuMetricsExternal_t metrics_ext;
- SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- &metrics_ext,
- true);
- if (ret)
- return ret;
-
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
-
- gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
- gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
- gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
- gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
- gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
- gpu_metrics->temperature_vrmem = max(metrics->AvgTemperature[TEMP_VR_MEM0],
- metrics->AvgTemperature[TEMP_VR_MEM1]);
-
- gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
- gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
- gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage,
- metrics->Vcn1ActivityPercentage);
-
- gpu_metrics->average_socket_power = metrics->AverageSocketPower;
- gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
-
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
- else
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
-
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
- else
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
-
- gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
- gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
- gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
- gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
-
- gpu_metrics->current_gfxclk = gpu_metrics->average_gfxclk_frequency;
- gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK];
- gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK];
- gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
- gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_0];
-
- gpu_metrics->throttle_status =
- smu_v14_0_2_get_throttler_status(metrics);
- gpu_metrics->indep_throttle_status =
- smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
- smu_v14_0_2_throttler_map);
-
- gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
-
- gpu_metrics->pcie_link_width = metrics->PcieWidth;
- if ((metrics->PcieRate - 1) > LINK_SPEED_MAX)
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(1);
- else
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(metrics->PcieRate);
-
- gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
-
- gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_VDD_SOC];
- gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VDDIO_MEM];
-
- *table = (void *)gpu_metrics;
-
- return sizeof(struct gpu_metrics_v1_3);
-}
-
-static void smu_v14_0_2_dump_od_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- struct amdgpu_device *adev = smu->adev;
-
- dev_dbg(adev->dev, "OD: Gfxclk: (%d, %d)\n", od_table->OverDriveTable.GfxclkFmin,
- od_table->OverDriveTable.GfxclkFmax);
- dev_dbg(adev->dev, "OD: Uclk: (%d, %d)\n", od_table->OverDriveTable.UclkFmin,
- od_table->OverDriveTable.UclkFmax);
-}
-
-static int smu_v14_0_2_upload_overdrive_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- int ret;
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_OVERDRIVE,
- 0,
- (void *)od_table,
- true);
- if (ret)
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
-
- return ret;
-}
-
-static void smu_v14_0_2_set_supported_od_feature_mask(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- adev->pm.od_feature_mask |= OD_OPS_SUPPORT_FAN_CURVE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_CURVE_SET |
- OD_OPS_SUPPORT_ACOUSTIC_LIMIT_THRESHOLD_RETRIEVE |
- OD_OPS_SUPPORT_ACOUSTIC_LIMIT_THRESHOLD_SET |
- OD_OPS_SUPPORT_ACOUSTIC_TARGET_THRESHOLD_RETRIEVE |
- OD_OPS_SUPPORT_ACOUSTIC_TARGET_THRESHOLD_SET |
- OD_OPS_SUPPORT_FAN_TARGET_TEMPERATURE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_TARGET_TEMPERATURE_SET |
- OD_OPS_SUPPORT_FAN_MINIMUM_PWM_RETRIEVE |
- OD_OPS_SUPPORT_FAN_MINIMUM_PWM_SET;
-}
-
-static int smu_v14_0_2_get_overdrive_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- int ret;
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_OVERDRIVE,
- 0,
- (void *)od_table,
- false);
- if (ret)
- dev_err(smu->adev->dev, "Failed to get overdrive table!\n");
-
- return ret;
-}
-
-static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
-{
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
- OverDriveTableExternal_t *boot_od_table =
- (OverDriveTableExternal_t *)smu->smu_table.boot_overdrive_table;
- OverDriveTableExternal_t *user_od_table =
- (OverDriveTableExternal_t *)smu->smu_table.user_overdrive_table;
- OverDriveTableExternal_t user_od_table_bak;
- int ret;
- int i;
-
- ret = smu_v14_0_2_get_overdrive_table(smu, boot_od_table);
- if (ret)
- return ret;
-
- smu_v14_0_2_dump_od_table(smu, boot_od_table);
-
- memcpy(od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
-
- /*
- * For S3/S4/Runpm resume, we need to setup those overdrive tables again,
- * but we have to preserve user defined values in "user_od_table".
- */
- if (!smu->adev->in_suspend) {
- memcpy(user_od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
- smu->user_dpm_profile.user_od = false;
- } else if (smu->user_dpm_profile.user_od) {
- memcpy(&user_od_table_bak,
- user_od_table,
- sizeof(OverDriveTableExternal_t));
- memcpy(user_od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
- user_od_table->OverDriveTable.GfxclkFmin =
- user_od_table_bak.OverDriveTable.GfxclkFmin;
- user_od_table->OverDriveTable.GfxclkFmax =
- user_od_table_bak.OverDriveTable.GfxclkFmax;
- user_od_table->OverDriveTable.UclkFmin =
- user_od_table_bak.OverDriveTable.UclkFmin;
- user_od_table->OverDriveTable.UclkFmax =
- user_od_table_bak.OverDriveTable.UclkFmax;
- for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++)
- user_od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[i] =
- user_od_table_bak.OverDriveTable.VoltageOffsetPerZoneBoundary[i];
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++) {
- user_od_table->OverDriveTable.FanLinearTempPoints[i] =
- user_od_table_bak.OverDriveTable.FanLinearTempPoints[i];
- user_od_table->OverDriveTable.FanLinearPwmPoints[i] =
- user_od_table_bak.OverDriveTable.FanLinearPwmPoints[i];
- }
- user_od_table->OverDriveTable.AcousticLimitRpmThreshold =
- user_od_table_bak.OverDriveTable.AcousticLimitRpmThreshold;
- user_od_table->OverDriveTable.AcousticTargetRpmThreshold =
- user_od_table_bak.OverDriveTable.AcousticTargetRpmThreshold;
- user_od_table->OverDriveTable.FanTargetTemperature =
- user_od_table_bak.OverDriveTable.FanTargetTemperature;
- user_od_table->OverDriveTable.FanMinimumPwm =
- user_od_table_bak.OverDriveTable.FanMinimumPwm;
- }
-
- smu_v14_0_2_set_supported_od_feature_mask(smu);
-
- return 0;
-}
-
-static int smu_v14_0_2_restore_user_od_settings(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table = table_context->overdrive_table;
- OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
- int res;
-
- user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
- BIT(PP_OD_FEATURE_UCLK_BIT) |
- BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
- BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
- memcpy(od_table, user_od_table, sizeof(OverDriveTableExternal_t));
-
- return res;
-}
-
-static int smu_v14_0_2_od_restore_table_single(struct smu_context *smu, long input)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *boot_overdrive_table =
- (OverDriveTableExternal_t *)table_context->boot_overdrive_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- struct amdgpu_device *adev = smu->adev;
- int i;
-
- switch (input) {
- case PP_OD_EDIT_FAN_CURVE:
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS; i++) {
- od_table->OverDriveTable.FanLinearTempPoints[i] =
- boot_overdrive_table->OverDriveTable.FanLinearTempPoints[i];
- od_table->OverDriveTable.FanLinearPwmPoints[i] =
- boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
- }
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_ACOUSTIC_LIMIT:
- od_table->OverDriveTable.AcousticLimitRpmThreshold =
- boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_ACOUSTIC_TARGET:
- od_table->OverDriveTable.AcousticTargetRpmThreshold =
- boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
- od_table->OverDriveTable.FanTargetTemperature =
- boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_FAN_MINIMUM_PWM:
- od_table->OverDriveTable.FanMinimumPwm =
- boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- default:
- dev_info(adev->dev, "Invalid table index: %ld\n", input);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
- enum PP_OD_DPM_TABLE_COMMAND type,
- long input[],
- uint32_t size)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- struct amdgpu_device *adev = smu->adev;
- uint32_t offset_of_voltageoffset;
- int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
- int i, ret = 0;
-
- switch (type) {
- case PP_OD_EDIT_SCLK_VDDC_TABLE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
- dev_warn(adev->dev, "GFXCLK_LIMITS setting not supported!\n");
- return -ENOTSUPP;
- }
-
- for (i = 0; i < size; i += 2) {
- if (i + 2 > size) {
- dev_info(adev->dev, "invalid number of input parameters %d\n", size);
- return -EINVAL;
- }
-
- switch (input[i]) {
- case 0:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMIN,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "GfxclkFmin (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.GfxclkFmin = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_GFXCLK_BIT;
- break;
-
- case 1:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMAX,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "GfxclkFmax (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.GfxclkFmax = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_GFXCLK_BIT;
- break;
-
- default:
- dev_info(adev->dev, "Invalid SCLK_VDDC_TABLE index: %ld\n", input[i]);
- dev_info(adev->dev, "Supported indices: [0:min,1:max]\n");
- return -EINVAL;
- }
- }
-
- if (od_table->OverDriveTable.GfxclkFmin > od_table->OverDriveTable.GfxclkFmax) {
- dev_err(adev->dev,
- "Invalid setting: GfxclkFmin(%u) is bigger than GfxclkFmax(%u)\n",
- (uint32_t)od_table->OverDriveTable.GfxclkFmin,
- (uint32_t)od_table->OverDriveTable.GfxclkFmax);
- return -EINVAL;
- }
- break;
-
- case PP_OD_EDIT_MCLK_VDDC_TABLE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
- dev_warn(adev->dev, "UCLK_LIMITS setting not supported!\n");
- return -ENOTSUPP;
- }
-
- for (i = 0; i < size; i += 2) {
- if (i + 2 > size) {
- dev_info(adev->dev, "invalid number of input parameters %d\n", size);
- return -EINVAL;
- }
-
- switch (input[i]) {
- case 0:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMIN,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "UclkFmin (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.UclkFmin = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_UCLK_BIT;
- break;
-
- case 1:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMAX,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "UclkFmax (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.UclkFmax = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_UCLK_BIT;
- break;
-
- default:
- dev_info(adev->dev, "Invalid MCLK_VDDC_TABLE index: %ld\n", input[i]);
- dev_info(adev->dev, "Supported indices: [0:min,1:max]\n");
- return -EINVAL;
- }
- }
-
- if (od_table->OverDriveTable.UclkFmin > od_table->OverDriveTable.UclkFmax) {
- dev_err(adev->dev,
- "Invalid setting: UclkFmin(%u) is bigger than UclkFmax(%u)\n",
- (uint32_t)od_table->OverDriveTable.UclkFmin,
- (uint32_t)od_table->OverDriveTable.UclkFmax);
- return -EINVAL;
- }
- break;
-
- case PP_OD_EDIT_VDDGFX_OFFSET:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
- dev_warn(adev->dev, "Gfx offset setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFX_VF_CURVE,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "Voltage offset (%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++)
- od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[i] = input[0];
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_CURVE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- if (input[0] >= NUM_OD_FAN_MAX_POINTS - 1 ||
- input[0] < 0)
- return -EINVAL;
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_TEMP,
- &minimum,
- &maximum);
- if (input[1] < minimum ||
- input[1] > maximum) {
- dev_info(adev->dev, "Fan curve temp setting(%ld) must be within [%d, %d]!\n",
- input[1], minimum, maximum);
- return -EINVAL;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_PWM,
- &minimum,
- &maximum);
- if (input[2] < minimum ||
- input[2] > maximum) {
- dev_info(adev->dev, "Fan curve pwm setting(%ld) must be within [%d, %d]!\n",
- input[2], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanLinearTempPoints[input[0]] = input[1];
- od_table->OverDriveTable.FanLinearPwmPoints[input[0]] = input[2];
- od_table->OverDriveTable.FanMode = FAN_MODE_MANUAL_LINEAR;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_ACOUSTIC_LIMIT:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "acoustic limit threshold setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.AcousticLimitRpmThreshold = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_ACOUSTIC_TARGET:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "acoustic target threshold setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.AcousticTargetRpmThreshold = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "fan target temperature setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanTargetTemperature = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_MINIMUM_PWM:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_MINIMUM_PWM,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "fan minimum pwm setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanMinimumPwm = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_RESTORE_DEFAULT_TABLE:
- if (size == 1) {
- ret = smu_v14_0_2_od_restore_table_single(smu, input[0]);
- if (ret)
- return ret;
- } else {
- feature_ctrlmask = od_table->OverDriveTable.FeatureCtrlMask;
- memcpy(od_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t));
- od_table->OverDriveTable.FeatureCtrlMask = feature_ctrlmask;
- }
- fallthrough;
- case PP_OD_COMMIT_DPM_TABLE:
- /*
- * The member below instructs PMFW the settings focused in
- * this single operation.
- * `uint32_t FeatureCtrlMask;`
- * It does not contain actual informations about user's custom
- * settings. Thus we do not cache it.
- */
- offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset)) {
- smu_v14_0_2_dump_od_table(smu, od_table);
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
-
- od_table->OverDriveTable.FeatureCtrlMask = 0;
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
- (u8 *)od_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
-
- if (!memcmp(table_context->user_overdrive_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t)))
- smu->user_dpm_profile.user_od = false;
- else
- smu->user_dpm_profile.user_od = true;
- }
- break;
-
- default:
- return -ENOSYS;
- }
-
- return ret;
-}
-
-<<<<<<<
-static int smu_v14_0_2_set_power_limit(struct smu_context *smu,
- enum smu_ppt_limit_type limit_type,
- uint32_t limit)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- int ret = 0;
-
- if (limit_type != SMU_DEFAULT_PPT_LIMIT)
- return -EINVAL;
-
- if (limit <= msg_limit) {
- if (smu->current_power_limit > msg_limit) {
- od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
- }
- return smu_v14_0_set_power_limit(smu, limit_type, limit);
- } else if (smu->od_enabled) {
- ret = smu_v14_0_set_power_limit(smu, limit_type, msg_limit);
- if (ret)
- return ret;
-
- od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
-
- smu->current_power_limit = limit;
- } else {
- return -EINVAL;
- }
-
- return 0;
-=======
-static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
- void **table)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_3 *gpu_metrics =
- (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
- SmuMetricsExternal_t metrics_ext;
- SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- &metrics_ext,
- true);
- if (ret)
- return ret;
-
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
-
- gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
- gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
- gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
- gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
- gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
- gpu_metrics->temperature_vrmem = max(metrics->AvgTemperature[TEMP_VR_MEM0],
- metrics->AvgTemperature[TEMP_VR_MEM1]);
-
- gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
- gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
- gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage,
- metrics->Vcn1ActivityPercentage);
-
- gpu_metrics->average_socket_power = metrics->AverageSocketPower;
- gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
-
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
- else
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
-
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
- else
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
-
- gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
- gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
- gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
- gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
-
- gpu_metrics->current_gfxclk = gpu_metrics->average_gfxclk_frequency;
- gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK];
- gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK];
- gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
- gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_0];
-
- gpu_metrics->throttle_status =
- smu_v14_0_2_get_throttler_status(metrics);
- gpu_metrics->indep_throttle_status =
- smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
- smu_v14_0_2_throttler_map);
-
- gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
-
- gpu_metrics->pcie_link_width = metrics->PcieWidth;
- if ((metrics->PcieRate - 1) > LINK_SPEED_MAX)
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(1);
- else
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(metrics->PcieRate);
-
- gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
-
- gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_VDD_SOC];
- gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VDDIO_MEM];
-
- *table = (void *)gpu_metrics;
-
- return sizeof(struct gpu_metrics_v1_3);
->>>>>>>
-}
-
-static const struct pptable_funcs smu_v14_0_2_ppt_funcs = {
- .get_allowed_feature_mask = smu_v14_0_2_get_allowed_feature_mask,
- .set_default_dpm_table = smu_v14_0_2_set_default_dpm_table,
- .i2c_init = smu_v14_0_2_i2c_control_init,
- .i2c_fini = smu_v14_0_2_i2c_control_fini,
- .is_dpm_running = smu_v14_0_2_is_dpm_running,
- .dump_pptable = smu_v14_0_2_dump_pptable,
- .init_microcode = smu_v14_0_init_microcode,
- .load_microcode = smu_v14_0_load_microcode,
- .fini_microcode = smu_v14_0_fini_microcode,
- .init_smc_tables = smu_v14_0_2_init_smc_tables,
- .fini_smc_tables = smu_v14_0_fini_smc_tables,
- .init_power = smu_v14_0_init_power,
- .fini_power = smu_v14_0_fini_power,
- .check_fw_status = smu_v14_0_check_fw_status,
- .setup_pptable = smu_v14_0_2_setup_pptable,
- .check_fw_version = smu_v14_0_check_fw_version,
- .write_pptable = smu_cmn_write_pptable,
- .set_driver_table_location = smu_v14_0_set_driver_table_location,
- .system_features_control = smu_v14_0_system_features_control,
- .set_allowed_mask = smu_v14_0_set_allowed_mask,
- .get_enabled_mask = smu_cmn_get_enabled_mask,
- .dpm_set_vcn_enable = smu_v14_0_set_vcn_enable,
- .dpm_set_jpeg_enable = smu_v14_0_set_jpeg_enable,
- .get_dpm_ultimate_freq = smu_v14_0_2_get_dpm_ultimate_freq,
- .get_vbios_bootup_values = smu_v14_0_get_vbios_bootup_values,
- .read_sensor = smu_v14_0_2_read_sensor,
- .feature_is_enabled = smu_cmn_feature_is_enabled,
- .print_clk_levels = smu_v14_0_2_print_clk_levels,
- .force_clk_levels = smu_v14_0_2_force_clk_levels,
- .update_pcie_parameters = smu_v14_0_2_update_pcie_parameters,
- .get_thermal_temperature_range = smu_v14_0_2_get_thermal_temperature_range,
- .register_irq_handler = smu_v14_0_register_irq_handler,
- .enable_thermal_alert = smu_v14_0_enable_thermal_alert,
- .disable_thermal_alert = smu_v14_0_disable_thermal_alert,
- .notify_memory_pool_location = smu_v14_0_notify_memory_pool_location,
- .get_gpu_metrics = smu_v14_0_2_get_gpu_metrics,
- .set_soft_freq_limited_range = smu_v14_0_set_soft_freq_limited_range,
- .set_default_od_settings = smu_v14_0_2_set_default_od_settings,
- .restore_user_od_settings = smu_v14_0_2_restore_user_od_settings,
- .od_edit_dpm_table = smu_v14_0_2_od_edit_dpm_table,
- .init_pptable_microcode = smu_v14_0_init_pptable_microcode,
- .populate_umd_state_clk = smu_v14_0_2_populate_umd_state_clk,
- .set_performance_level = smu_v14_0_set_performance_level,
- .gfx_off_control = smu_v14_0_gfx_off_control,
- .get_unique_id = smu_v14_0_2_get_unique_id,
- .get_power_limit = smu_v14_0_2_get_power_limit,
- .set_power_limit = smu_v14_0_2_set_power_limit,
- .set_power_source = smu_v14_0_set_power_source,
- .get_power_profile_mode = smu_v14_0_2_get_power_profile_mode,
- .set_power_profile_mode = smu_v14_0_2_set_power_profile_mode,
- .run_btc = smu_v14_0_run_btc,
- .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
- .set_pp_feature_mask = smu_cmn_set_pp_feature_mask,
- .set_tool_table_location = smu_v14_0_set_tool_table_location,
- .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,
- .mode1_reset = smu_v14_0_2_mode1_reset,
- .mode2_reset = smu_v14_0_2_mode2_reset,
- .enable_gfx_features = smu_v14_0_2_enable_gfx_features,
- .set_mp1_state = smu_v14_0_2_set_mp1_state,
- .set_df_cstate = smu_v14_0_2_set_df_cstate,
-#if 0
- .gpo_control = smu_v14_0_gpo_control,
-#endif
-};
-
-void smu_v14_0_2_set_ppt_funcs(struct smu_context *smu)
-{
- smu->ppt_funcs = &smu_v14_0_2_ppt_funcs;
- smu->message_map = smu_v14_0_2_message_map;
- smu->clock_map = smu_v14_0_2_clk_map;
- smu->feature_map = smu_v14_0_2_feature_mask_map;
- smu->table_map = smu_v14_0_2_table_map;
- smu->pwr_src_map = smu_v14_0_2_pwr_src_map;
- smu->workload_map = smu_v14_0_2_workload_map;
- smu_v14_0_2_set_smu_mailbox_registers(smu);
-}
diff --git a/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage.2 b/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage.2
deleted file mode 100644
index 6878263c313c..000000000000
--- a/rr-cache/02c9ed6a88e6cfc33649cb70da4aaaf2dc874129/preimage.2
+++ /dev/null
@@ -1,2956 +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.
- *
- */
-
-#define SWSMU_CODE_LAYER_L2
-
-#include <linux/firmware.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include "amdgpu.h"
-#include "amdgpu_smu.h"
-#include "atomfirmware.h"
-#include "amdgpu_atomfirmware.h"
-#include "amdgpu_atombios.h"
-#include "smu_v14_0.h"
-#include "smu14_driver_if_v14_0.h"
-#include "soc15_common.h"
-#include "atom.h"
-#include "smu_v14_0_2_ppt.h"
-#include "smu_v14_0_2_pptable.h"
-#include "smu_v14_0_2_ppsmc.h"
-#include "mp/mp_14_0_2_offset.h"
-#include "mp/mp_14_0_2_sh_mask.h"
-
-#include "smu_cmn.h"
-#include "amdgpu_ras.h"
-
-/*
- * DO NOT use these for err/warn/info/debug messages.
- * Use dev_err, dev_warn, dev_info and dev_dbg instead.
- * They are more MGPU friendly.
- */
-#undef pr_err
-#undef pr_warn
-#undef pr_info
-#undef pr_debug
-
-#define to_amdgpu_device(x) (container_of(x, struct amdgpu_device, pm.smu_i2c))
-
-#define FEATURE_MASK(feature) (1ULL << feature)
-#define SMC_DPM_FEATURE ( \
- FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_UCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_LINK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT) | \
- FEATURE_MASK(FEATURE_DPM_FCLK_BIT))
-
-#define MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE 0x4000
-#define DEBUGSMC_MSG_Mode1Reset 2
-#define LINK_SPEED_MAX 3
-<<<<<<<
-=======
-
-#define PP_OD_FEATURE_GFXCLK_FMIN 0
-#define PP_OD_FEATURE_GFXCLK_FMAX 1
-#define PP_OD_FEATURE_UCLK_FMIN 2
-#define PP_OD_FEATURE_UCLK_FMAX 3
-#define PP_OD_FEATURE_GFX_VF_CURVE 4
-#define PP_OD_FEATURE_FAN_CURVE_TEMP 5
-#define PP_OD_FEATURE_FAN_CURVE_PWM 6
-#define PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT 7
-#define PP_OD_FEATURE_FAN_ACOUSTIC_TARGET 8
-#define PP_OD_FEATURE_FAN_TARGET_TEMPERATURE 9
-#define PP_OD_FEATURE_FAN_MINIMUM_PWM 10
->>>>>>>
-
-static struct cmn2asic_msg_mapping smu_v14_0_2_message_map[SMU_MSG_MAX_COUNT] = {
- MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
- MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
- MSG_MAP(GetDriverIfVersion, PPSMC_MSG_GetDriverIfVersion, 1),
- MSG_MAP(SetAllowedFeaturesMaskLow, PPSMC_MSG_SetAllowedFeaturesMaskLow, 0),
- MSG_MAP(SetAllowedFeaturesMaskHigh, PPSMC_MSG_SetAllowedFeaturesMaskHigh, 0),
- MSG_MAP(EnableAllSmuFeatures, PPSMC_MSG_EnableAllSmuFeatures, 0),
- MSG_MAP(DisableAllSmuFeatures, PPSMC_MSG_DisableAllSmuFeatures, 0),
- MSG_MAP(EnableSmuFeaturesLow, PPSMC_MSG_EnableSmuFeaturesLow, 1),
- MSG_MAP(EnableSmuFeaturesHigh, PPSMC_MSG_EnableSmuFeaturesHigh, 1),
- MSG_MAP(DisableSmuFeaturesLow, PPSMC_MSG_DisableSmuFeaturesLow, 1),
- MSG_MAP(DisableSmuFeaturesHigh, PPSMC_MSG_DisableSmuFeaturesHigh, 1),
- MSG_MAP(GetEnabledSmuFeaturesLow, PPSMC_MSG_GetRunningSmuFeaturesLow, 1),
- MSG_MAP(GetEnabledSmuFeaturesHigh, PPSMC_MSG_GetRunningSmuFeaturesHigh, 1),
- MSG_MAP(SetWorkloadMask, PPSMC_MSG_SetWorkloadMask, 1),
- MSG_MAP(SetPptLimit, PPSMC_MSG_SetPptLimit, 0),
- MSG_MAP(SetDriverDramAddrHigh, PPSMC_MSG_SetDriverDramAddrHigh, 1),
- MSG_MAP(SetDriverDramAddrLow, PPSMC_MSG_SetDriverDramAddrLow, 1),
- MSG_MAP(SetToolsDramAddrHigh, PPSMC_MSG_SetToolsDramAddrHigh, 0),
- MSG_MAP(SetToolsDramAddrLow, PPSMC_MSG_SetToolsDramAddrLow, 0),
- MSG_MAP(TransferTableSmu2Dram, PPSMC_MSG_TransferTableSmu2Dram, 1),
- MSG_MAP(TransferTableDram2Smu, PPSMC_MSG_TransferTableDram2Smu, 0),
- MSG_MAP(UseDefaultPPTable, PPSMC_MSG_UseDefaultPPTable, 0),
- MSG_MAP(RunDcBtc, PPSMC_MSG_RunDcBtc, 0),
- MSG_MAP(EnterBaco, PPSMC_MSG_EnterBaco, 0),
- MSG_MAP(ExitBaco, PPSMC_MSG_ExitBaco, 0),
- MSG_MAP(SetSoftMinByFreq, PPSMC_MSG_SetSoftMinByFreq, 1),
- MSG_MAP(SetSoftMaxByFreq, PPSMC_MSG_SetSoftMaxByFreq, 1),
- MSG_MAP(SetHardMinByFreq, PPSMC_MSG_SetHardMinByFreq, 1),
- MSG_MAP(SetHardMaxByFreq, PPSMC_MSG_SetHardMaxByFreq, 0),
- MSG_MAP(GetMinDpmFreq, PPSMC_MSG_GetMinDpmFreq, 1),
- MSG_MAP(GetMaxDpmFreq, PPSMC_MSG_GetMaxDpmFreq, 1),
- MSG_MAP(GetDpmFreqByIndex, PPSMC_MSG_GetDpmFreqByIndex, 1),
- MSG_MAP(PowerUpVcn, PPSMC_MSG_PowerUpVcn, 0),
- MSG_MAP(PowerDownVcn, PPSMC_MSG_PowerDownVcn, 0),
- MSG_MAP(PowerUpJpeg, PPSMC_MSG_PowerUpJpeg, 0),
- MSG_MAP(PowerDownJpeg, PPSMC_MSG_PowerDownJpeg, 0),
- MSG_MAP(GetDcModeMaxDpmFreq, PPSMC_MSG_GetDcModeMaxDpmFreq, 1),
- MSG_MAP(OverridePcieParameters, PPSMC_MSG_OverridePcieParameters, 0),
- MSG_MAP(DramLogSetDramAddrHigh, PPSMC_MSG_DramLogSetDramAddrHigh, 0),
- MSG_MAP(DramLogSetDramAddrLow, PPSMC_MSG_DramLogSetDramAddrLow, 0),
- MSG_MAP(DramLogSetDramSize, PPSMC_MSG_DramLogSetDramSize, 0),
- MSG_MAP(AllowGfxOff, PPSMC_MSG_AllowGfxOff, 0),
- MSG_MAP(DisallowGfxOff, PPSMC_MSG_DisallowGfxOff, 0),
- MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm, 0),
- MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0),
- MSG_MAP(NotifyPowerSource, PPSMC_MSG_NotifyPowerSource, 0),
- MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareMp1ForUnload, 0),
- MSG_MAP(DFCstateControl, PPSMC_MSG_SetExternalClientDfCstateAllow, 0),
- MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0),
- MSG_MAP(SetNumBadMemoryPagesRetired, PPSMC_MSG_SetNumBadMemoryPagesRetired, 0),
- MSG_MAP(SetBadMemoryPagesRetiredFlagsPerChannel,
- PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel, 0),
- MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0),
- MSG_MAP(ReenableAcDcInterrupt, PPSMC_MSG_ReenableAcDcInterrupt, 0),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_clk_map[SMU_CLK_COUNT] = {
- CLK_MAP(GFXCLK, PPCLK_GFXCLK),
- CLK_MAP(SCLK, PPCLK_GFXCLK),
- CLK_MAP(SOCCLK, PPCLK_SOCCLK),
- CLK_MAP(FCLK, PPCLK_FCLK),
- CLK_MAP(UCLK, PPCLK_UCLK),
- CLK_MAP(MCLK, PPCLK_UCLK),
- CLK_MAP(VCLK, PPCLK_VCLK_0),
- CLK_MAP(DCLK, PPCLK_DCLK_0),
- CLK_MAP(DCEFCLK, PPCLK_DCFCLK),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_feature_mask_map[SMU_FEATURE_COUNT] = {
- FEA_MAP(FW_DATA_READ),
- FEA_MAP(DPM_GFXCLK),
- FEA_MAP(DPM_GFX_POWER_OPTIMIZER),
- FEA_MAP(DPM_UCLK),
- FEA_MAP(DPM_FCLK),
- FEA_MAP(DPM_SOCCLK),
- FEA_MAP(DPM_LINK),
- FEA_MAP(DPM_DCN),
- FEA_MAP(VMEMP_SCALING),
- FEA_MAP(VDDIO_MEM_SCALING),
- FEA_MAP(DS_GFXCLK),
- FEA_MAP(DS_SOCCLK),
- FEA_MAP(DS_FCLK),
- FEA_MAP(DS_LCLK),
- FEA_MAP(DS_DCFCLK),
- FEA_MAP(DS_UCLK),
- FEA_MAP(GFX_ULV),
- FEA_MAP(FW_DSTATE),
- FEA_MAP(GFXOFF),
- FEA_MAP(BACO),
- FEA_MAP(MM_DPM),
- FEA_MAP(SOC_MPCLK_DS),
- FEA_MAP(BACO_MPCLK_DS),
- FEA_MAP(THROTTLERS),
- FEA_MAP(SMARTSHIFT),
- FEA_MAP(GTHR),
- FEA_MAP(ACDC),
- FEA_MAP(VR0HOT),
- FEA_MAP(FW_CTF),
- FEA_MAP(FAN_CONTROL),
- FEA_MAP(GFX_DCS),
- FEA_MAP(GFX_READ_MARGIN),
- FEA_MAP(LED_DISPLAY),
- FEA_MAP(GFXCLK_SPREAD_SPECTRUM),
- FEA_MAP(OUT_OF_BAND_MONITOR),
- FEA_MAP(OPTIMIZED_VMIN),
- FEA_MAP(GFX_IMU),
- FEA_MAP(BOOT_TIME_CAL),
- FEA_MAP(GFX_PCC_DFLL),
- FEA_MAP(SOC_CG),
- FEA_MAP(DF_CSTATE),
- FEA_MAP(GFX_EDC),
- FEA_MAP(BOOT_POWER_OPT),
- FEA_MAP(CLOCK_POWER_DOWN_BYPASS),
- FEA_MAP(DS_VCN),
- FEA_MAP(BACO_CG),
- FEA_MAP(MEM_TEMP_READ),
- FEA_MAP(ATHUB_MMHUB_PG),
- FEA_MAP(SOC_PCC),
- [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
- [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
- [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_table_map[SMU_TABLE_COUNT] = {
- TAB_MAP(PPTABLE),
- TAB_MAP(WATERMARKS),
- TAB_MAP(AVFS_PSM_DEBUG),
- TAB_MAP(PMSTATUSLOG),
- TAB_MAP(SMU_METRICS),
- TAB_MAP(DRIVER_SMU_CONFIG),
- TAB_MAP(ACTIVITY_MONITOR_COEFF),
- [SMU_TABLE_COMBO_PPTABLE] = {1, TABLE_COMBO_PPTABLE},
- TAB_MAP(I2C_COMMANDS),
- TAB_MAP(ECCINFO),
- TAB_MAP(OVERDRIVE),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_pwr_src_map[SMU_POWER_SOURCE_COUNT] = {
- PWR_MAP(AC),
- PWR_MAP(DC),
-};
-
-static struct cmn2asic_mapping smu_v14_0_2_workload_map[PP_SMC_POWER_PROFILE_COUNT] = {
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_BOOTUP_DEFAULT, WORKLOAD_PPLIB_DEFAULT_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_FULLSCREEN3D, WORKLOAD_PPLIB_FULL_SCREEN_3D_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_POWERSAVING, WORKLOAD_PPLIB_POWER_SAVING_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VIDEO, WORKLOAD_PPLIB_VIDEO_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_VR, WORKLOAD_PPLIB_VR_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_COMPUTE, WORKLOAD_PPLIB_COMPUTE_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_CUSTOM, WORKLOAD_PPLIB_CUSTOM_BIT),
- WORKLOAD_MAP(PP_SMC_POWER_PROFILE_WINDOW3D, WORKLOAD_PPLIB_WINDOW_3D_BIT),
-};
-
-static const uint8_t smu_v14_0_2_throttler_map[] = {
- [THROTTLER_PPT0_BIT] = (SMU_THROTTLER_PPT0_BIT),
- [THROTTLER_PPT1_BIT] = (SMU_THROTTLER_PPT1_BIT),
- [THROTTLER_PPT2_BIT] = (SMU_THROTTLER_PPT2_BIT),
- [THROTTLER_PPT3_BIT] = (SMU_THROTTLER_PPT3_BIT),
- [THROTTLER_TDC_GFX_BIT] = (SMU_THROTTLER_TDC_GFX_BIT),
- [THROTTLER_TDC_SOC_BIT] = (SMU_THROTTLER_TDC_SOC_BIT),
- [THROTTLER_TEMP_EDGE_BIT] = (SMU_THROTTLER_TEMP_EDGE_BIT),
- [THROTTLER_TEMP_HOTSPOT_BIT] = (SMU_THROTTLER_TEMP_HOTSPOT_BIT),
- [THROTTLER_TEMP_MEM_BIT] = (SMU_THROTTLER_TEMP_MEM_BIT),
- [THROTTLER_TEMP_VR_GFX_BIT] = (SMU_THROTTLER_TEMP_VR_GFX_BIT),
- [THROTTLER_TEMP_VR_SOC_BIT] = (SMU_THROTTLER_TEMP_VR_SOC_BIT),
- [THROTTLER_TEMP_VR_MEM0_BIT] = (SMU_THROTTLER_TEMP_VR_MEM0_BIT),
- [THROTTLER_TEMP_VR_MEM1_BIT] = (SMU_THROTTLER_TEMP_VR_MEM1_BIT),
- [THROTTLER_TEMP_LIQUID0_BIT] = (SMU_THROTTLER_TEMP_LIQUID0_BIT),
- [THROTTLER_TEMP_LIQUID1_BIT] = (SMU_THROTTLER_TEMP_LIQUID1_BIT),
- [THROTTLER_GFX_APCC_PLUS_BIT] = (SMU_THROTTLER_APCC_BIT),
- [THROTTLER_FIT_BIT] = (SMU_THROTTLER_FIT_BIT),
-};
-
-static int
-smu_v14_0_2_get_allowed_feature_mask(struct smu_context *smu,
- uint32_t *feature_mask, uint32_t num)
-{
- struct amdgpu_device *adev = smu->adev;
- /*u32 smu_version;*/
-
- if (num > 2)
- return -EINVAL;
-
- memset(feature_mask, 0xff, sizeof(uint32_t) * num);
-
- if (adev->pm.pp_feature & PP_SCLK_DPM_MASK) {
- *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_DPM_GFXCLK_BIT);
- *(uint64_t *)feature_mask |= FEATURE_MASK(FEATURE_GFX_IMU_BIT);
- }
-#if 0
- if (!(adev->pg_flags & AMD_PG_SUPPORT_ATHUB) ||
- !(adev->pg_flags & AMD_PG_SUPPORT_MMHUB))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_ATHUB_MMHUB_PG_BIT);
-
- if (!(adev->pm.pp_feature & PP_SOCCLK_DPM_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_SOCCLK_BIT);
-
- /* PMFW 78.58 contains a critical fix for gfxoff feature */
- smu_cmn_get_smc_version(smu, NULL, &smu_version);
- if ((smu_version < 0x004e3a00) ||
- !(adev->pm.pp_feature & PP_GFXOFF_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFXOFF_BIT);
-
- if (!(adev->pm.pp_feature & PP_MCLK_DPM_MASK)) {
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_UCLK_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VMEMP_SCALING_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_VDDIO_MEM_SCALING_BIT);
- }
-
- if (!(adev->pm.pp_feature & PP_SCLK_DEEP_SLEEP_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_GFXCLK_BIT);
-
- if (!(adev->pm.pp_feature & PP_PCIE_DPM_MASK)) {
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DPM_LINK_BIT);
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_DS_LCLK_BIT);
- }
-
- if (!(adev->pm.pp_feature & PP_ULV_MASK))
- *(uint64_t *)feature_mask &= ~FEATURE_MASK(FEATURE_GFX_ULV_BIT);
-#endif
-
- return 0;
-}
-
-static int smu_v14_0_2_check_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
- struct smu_baco_context *smu_baco = &smu->smu_baco;
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
- const OverDriveLimits_t * const overdrive_lowerlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMin;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_HARDWAREDC)
- smu->dc_controlled_by_gpio = true;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_BACO) {
- smu_baco->platform_support = true;
-
- if (powerplay_table->platform_caps & SMU_14_0_2_PP_PLATFORM_CAP_MACO)
- smu_baco->maco_support = true;
- }
-
- if (!overdrive_lowerlimits->FeatureCtrlMask ||
- !overdrive_upperlimits->FeatureCtrlMask)
- smu->od_enabled = false;
-
- table_context->thermal_controller_type =
- powerplay_table->thermal_controller_type;
-
- /*
- * Instead of having its own buffer space and get overdrive_table copied,
- * smu->od_settings just points to the actual overdrive_table
- */
- smu->od_settings = &powerplay_table->overdrive_table;
-
- smu->adev->pm.no_fan =
- !(pptable->PFE_Settings.FeaturesToRun[0] & (1 << FEATURE_FAN_CONTROL_BIT));
-
- return 0;
-}
-
-static int smu_v14_0_2_store_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
-
- memcpy(table_context->driver_pptable, &powerplay_table->smc_pptable,
- sizeof(PPTable_t));
-
- return 0;
-}
-
-#ifndef atom_smc_dpm_info_table_14_0_0
-struct atom_smc_dpm_info_table_14_0_0 {
- struct atom_common_table_header table_header;
- BoardTable_t BoardTable;
-};
-#endif
-
-static int smu_v14_0_2_append_powerplay_table(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *smc_pptable = table_context->driver_pptable;
- struct atom_smc_dpm_info_table_14_0_0 *smc_dpm_table;
- BoardTable_t *BoardTable = &smc_pptable->BoardTable;
- int index, ret;
-
- index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1,
- smc_dpm_info);
-
- ret = amdgpu_atombios_get_data_table(smu->adev, index, NULL, NULL, NULL,
- (uint8_t **)&smc_dpm_table);
- if (ret)
- return ret;
-
- memcpy(BoardTable, &smc_dpm_table->BoardTable, sizeof(BoardTable_t));
-
- return 0;
-}
-
-#if 0
-static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
- void **table,
- uint32_t *size)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- void *combo_pptable = smu_table->combo_pptable;
- int ret = 0;
-
- ret = smu_cmn_get_combo_pptable(smu);
- if (ret)
- return ret;
-
- *table = combo_pptable;
- *size = sizeof(struct smu_14_0_powerplay_table);
-
- return 0;
-}
-#endif
-
-static int smu_v14_0_2_get_pptable_from_pmfw(struct smu_context *smu,
- void **table,
- uint32_t *size)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- void *combo_pptable = smu_table->combo_pptable;
- int ret = 0;
-
- ret = smu_cmn_get_combo_pptable(smu);
- if (ret)
- return ret;
-
- *table = combo_pptable;
- *size = sizeof(struct smu_14_0_2_powerplay_table);
-
- return 0;
-}
-
-static int smu_v14_0_2_setup_pptable(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct amdgpu_device *adev = smu->adev;
- int ret = 0;
-
- if (amdgpu_sriov_vf(smu->adev))
- return 0;
-
- if (!adev->scpm_enabled)
- ret = smu_v14_0_setup_pptable(smu);
- else
- ret = smu_v14_0_2_get_pptable_from_pmfw(smu,
- &smu_table->power_play_table,
- &smu_table->power_play_table_size);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_store_powerplay_table(smu);
- if (ret)
- return ret;
-
- /*
- * With SCPM enabled, the operation below will be handled
- * by PSP. Driver involvment is unnecessary and useless.
- */
- if (!adev->scpm_enabled) {
- ret = smu_v14_0_2_append_powerplay_table(smu);
- if (ret)
- return ret;
- }
-
- ret = smu_v14_0_2_check_powerplay_table(smu);
- if (ret)
- return ret;
-
- return ret;
-}
-
-static int smu_v14_0_2_tables_init(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct smu_table *tables = smu_table->tables;
-
- SMU_TABLE_INIT(tables, SMU_TABLE_PPTABLE, sizeof(PPTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetricsExternal_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_I2C_COMMANDS, sizeof(SwI2cRequest_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_OVERDRIVE, sizeof(OverDriveTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_PMSTATUSLOG, SMU14_TOOL_SIZE,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- sizeof(DpmActivityMonitorCoeffIntExternal_t), PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_COMBO_PPTABLE, MP0_MP1_DATA_REGION_SIZE_COMBOPPTABLE,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_ECCINFO, sizeof(EccInfoTable_t),
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
-
- smu_table->metrics_table = kzalloc(sizeof(SmuMetricsExternal_t), GFP_KERNEL);
- if (!smu_table->metrics_table)
- goto err0_out;
- smu_table->metrics_time = 0;
-
- smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_3);
- smu_table->gpu_metrics_table = kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
- if (!smu_table->gpu_metrics_table)
- goto err1_out;
-
- smu_table->watermarks_table = kzalloc(sizeof(Watermarks_t), GFP_KERNEL);
- if (!smu_table->watermarks_table)
- goto err2_out;
-
- smu_table->ecc_table = kzalloc(tables[SMU_TABLE_ECCINFO].size, GFP_KERNEL);
- if (!smu_table->ecc_table)
- goto err3_out;
-
- return 0;
-
-err3_out:
- kfree(smu_table->watermarks_table);
-err2_out:
- kfree(smu_table->gpu_metrics_table);
-err1_out:
- kfree(smu_table->metrics_table);
-err0_out:
- return -ENOMEM;
-}
-
-static int smu_v14_0_2_allocate_dpm_context(struct smu_context *smu)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
-
- smu_dpm->dpm_context = kzalloc(sizeof(struct smu_14_0_dpm_context),
- GFP_KERNEL);
- if (!smu_dpm->dpm_context)
- return -ENOMEM;
-
- smu_dpm->dpm_context_size = sizeof(struct smu_14_0_dpm_context);
-
- return 0;
-}
-
-static int smu_v14_0_2_init_smc_tables(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_v14_0_2_tables_init(smu);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_allocate_dpm_context(smu);
- if (ret)
- return ret;
-
- return smu_v14_0_init_smc_tables(smu);
-}
-
-static int smu_v14_0_2_set_default_dpm_table(struct smu_context *smu)
-{
- struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- SkuTable_t *skutable = &pptable->SkuTable;
- struct smu_14_0_dpm_table *dpm_table;
- struct smu_14_0_pcie_table *pcie_table;
- uint32_t link_level;
- int ret = 0;
-
- /* socclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.soc_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_SOCCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_SOCCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.socclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* gfxclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.gfx_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_GFXCLK,
- dpm_table);
- if (ret)
- return ret;
-
- /*
- * Update the reported maximum shader clock to the value
- * which can be guarded to be achieved on all cards. This
- * is aligned with Window setting. And considering that value
- * might be not the peak frequency the card can achieve, it
- * is normal some real-time clock frequency can overtake this
- * labelled maximum clock frequency(for example in pp_dpm_sclk
- * sysfs output).
- */
- if (skutable->DriverReportedClocks.GameClockAc &&
- (dpm_table->dpm_levels[dpm_table->count - 1].value >
- skutable->DriverReportedClocks.GameClockAc)) {
- dpm_table->dpm_levels[dpm_table->count - 1].value =
- skutable->DriverReportedClocks.GameClockAc;
- dpm_table->max = skutable->DriverReportedClocks.GameClockAc;
- }
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* uclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.uclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_UCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.uclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* fclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.fclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_FCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.fclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* vclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.vclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_VCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_VCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.vclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* dclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.dclk_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCLK_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_DCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- /* lclk dpm table setup */
- pcie_table = &dpm_context->dpm_tables.pcie_table;
- pcie_table->num_of_link_levels = 0;
- for (link_level = 0; link_level < NUM_LINK_LEVELS; link_level++) {
- if (!skutable->PcieGenSpeed[link_level] &&
- !skutable->PcieLaneCount[link_level] &&
- !skutable->LclkFreq[link_level])
- continue;
-
- pcie_table->pcie_gen[pcie_table->num_of_link_levels] =
- skutable->PcieGenSpeed[link_level];
- pcie_table->pcie_lane[pcie_table->num_of_link_levels] =
- skutable->PcieLaneCount[link_level];
- pcie_table->clk_freq[pcie_table->num_of_link_levels] =
- skutable->LclkFreq[link_level];
- pcie_table->num_of_link_levels++;
- }
-
- /* dcefclk dpm table setup */
- dpm_table = &dpm_context->dpm_tables.dcef_table;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_DCN_BIT)) {
- ret = smu_v14_0_set_single_dpm_table(smu,
- SMU_DCEFCLK,
- dpm_table);
- if (ret)
- return ret;
- } else {
- dpm_table->count = 1;
- dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.dcefclk / 100;
- dpm_table->dpm_levels[0].enabled = true;
- dpm_table->min = dpm_table->dpm_levels[0].value;
- dpm_table->max = dpm_table->dpm_levels[0].value;
- }
-
- return 0;
-}
-
-static bool smu_v14_0_2_is_dpm_running(struct smu_context *smu)
-{
- int ret = 0;
- uint64_t feature_enabled;
-
- ret = smu_cmn_get_enabled_mask(smu, &feature_enabled);
- if (ret)
- return false;
-
- return !!(feature_enabled & SMC_DPM_FEATURE);
-}
-
-static void smu_v14_0_2_dump_pptable(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- PFE_Settings_t *PFEsettings = &pptable->PFE_Settings;
-
- dev_info(smu->adev->dev, "Dumped PPTable:\n");
-
- dev_info(smu->adev->dev, "Version = 0x%08x\n", PFEsettings->Version);
- dev_info(smu->adev->dev, "FeaturesToRun[0] = 0x%08x\n", PFEsettings->FeaturesToRun[0]);
- dev_info(smu->adev->dev, "FeaturesToRun[1] = 0x%08x\n", PFEsettings->FeaturesToRun[1]);
-}
-
-static uint32_t smu_v14_0_2_get_throttler_status(SmuMetrics_t *metrics)
-{
- uint32_t throttler_status = 0;
- int i;
-
- for (i = 0; i < THROTTLER_COUNT; i++)
- throttler_status |=
- (metrics->ThrottlingPercentage[i] ? 1U << i : 0);
-
- return throttler_status;
-}
-
-#define SMU_14_0_2_BUSY_THRESHOLD 5
-static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu,
- MetricsMember_t member,
- uint32_t *value)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- SmuMetrics_t *metrics =
- &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- NULL,
- false);
- if (ret)
- return ret;
-
- switch (member) {
- case METRICS_CURR_GFXCLK:
- *value = metrics->CurrClock[PPCLK_GFXCLK];
- break;
- case METRICS_CURR_SOCCLK:
- *value = metrics->CurrClock[PPCLK_SOCCLK];
- break;
- case METRICS_CURR_UCLK:
- *value = metrics->CurrClock[PPCLK_UCLK];
- break;
- case METRICS_CURR_VCLK:
- *value = metrics->CurrClock[PPCLK_VCLK_0];
- break;
- case METRICS_CURR_DCLK:
- *value = metrics->CurrClock[PPCLK_DCLK_0];
- break;
- case METRICS_CURR_FCLK:
- *value = metrics->CurrClock[PPCLK_FCLK];
- break;
- case METRICS_CURR_DCEFCLK:
- *value = metrics->CurrClock[PPCLK_DCFCLK];
- break;
- case METRICS_AVERAGE_GFXCLK:
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageGfxclkFrequencyPostDs;
- else
- *value = metrics->AverageGfxclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_FCLK:
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageFclkFrequencyPostDs;
- else
- *value = metrics->AverageFclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_UCLK:
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- *value = metrics->AverageMemclkFrequencyPostDs;
- else
- *value = metrics->AverageMemclkFrequencyPreDs;
- break;
- case METRICS_AVERAGE_VCLK:
- *value = metrics->AverageVclk0Frequency;
- break;
- case METRICS_AVERAGE_DCLK:
- *value = metrics->AverageDclk0Frequency;
- break;
- case METRICS_AVERAGE_VCLK1:
- *value = metrics->AverageVclk1Frequency;
- break;
- case METRICS_AVERAGE_DCLK1:
- *value = metrics->AverageDclk1Frequency;
- break;
- case METRICS_AVERAGE_GFXACTIVITY:
- *value = metrics->AverageGfxActivity;
- break;
- case METRICS_AVERAGE_MEMACTIVITY:
- *value = metrics->AverageUclkActivity;
- break;
- case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
- break;
- case METRICS_TEMPERATURE_EDGE:
- *value = metrics->AvgTemperature[TEMP_EDGE] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_HOTSPOT:
- *value = metrics->AvgTemperature[TEMP_HOTSPOT] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_MEM:
- *value = metrics->AvgTemperature[TEMP_MEM] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_VRGFX:
- *value = metrics->AvgTemperature[TEMP_VR_GFX] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_TEMPERATURE_VRSOC:
- *value = metrics->AvgTemperature[TEMP_VR_SOC] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- break;
- case METRICS_THROTTLER_STATUS:
- *value = smu_v14_0_2_get_throttler_status(metrics);
- break;
- case METRICS_CURR_FANSPEED:
- *value = metrics->AvgFanRpm;
- break;
- case METRICS_CURR_FANPWM:
- *value = metrics->AvgFanPwm;
- break;
- case METRICS_VOLTAGE_VDDGFX:
- *value = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- break;
- case METRICS_PCIE_RATE:
- *value = metrics->PcieRate;
- break;
- case METRICS_PCIE_WIDTH:
- *value = metrics->PcieWidth;
- break;
- default:
- *value = UINT_MAX;
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_get_dpm_ultimate_freq(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t *min,
- uint32_t *max)
-{
- struct smu_14_0_dpm_context *dpm_context =
- smu->smu_dpm.dpm_context;
- struct smu_14_0_dpm_table *dpm_table;
-
- switch (clk_type) {
- case SMU_MCLK:
- case SMU_UCLK:
- /* uclk dpm table */
- dpm_table = &dpm_context->dpm_tables.uclk_table;
- break;
- case SMU_GFXCLK:
- case SMU_SCLK:
- /* gfxclk dpm table */
- dpm_table = &dpm_context->dpm_tables.gfx_table;
- break;
- case SMU_SOCCLK:
- /* socclk dpm table */
- dpm_table = &dpm_context->dpm_tables.soc_table;
- break;
- case SMU_FCLK:
- /* fclk dpm table */
- dpm_table = &dpm_context->dpm_tables.fclk_table;
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- /* vclk dpm table */
- dpm_table = &dpm_context->dpm_tables.vclk_table;
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- /* dclk dpm table */
- dpm_table = &dpm_context->dpm_tables.dclk_table;
- break;
- default:
- dev_err(smu->adev->dev, "Unsupported clock type!\n");
- return -EINVAL;
- }
-
- if (min)
- *min = dpm_table->min;
- if (max)
- *max = dpm_table->max;
-
- return 0;
-}
-
-static int smu_v14_0_2_read_sensor(struct smu_context *smu,
- enum amd_pp_sensors sensor,
- void *data,
- uint32_t *size)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *smc_pptable = table_context->driver_pptable;
- int ret = 0;
-
- switch (sensor) {
- case AMDGPU_PP_SENSOR_MAX_FAN_RPM:
- *(uint16_t *)data = smc_pptable->CustomSkuTable.FanMaximumRpm;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_MEM_LOAD:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_MEMACTIVITY,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GPU_LOAD:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_GFXACTIVITY,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_SOCKETPOWER,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_HOTSPOT_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_HOTSPOT,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_EDGE_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_EDGE,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_MEM_TEMP:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_TEMPERATURE_MEM,
- (uint32_t *)data);
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GFX_MCLK:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_CURR_UCLK,
- (uint32_t *)data);
- *(uint32_t *)data *= 100;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_GFX_SCLK:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_AVERAGE_GFXCLK,
- (uint32_t *)data);
- *(uint32_t *)data *= 100;
- *size = 4;
- break;
- case AMDGPU_PP_SENSOR_VDDGFX:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_VOLTAGE_VDDGFX,
- (uint32_t *)data);
- *size = 4;
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_get_current_clk_freq_by_table(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t *value)
-{
- MetricsMember_t member_type;
- int clk_id = 0;
-
- clk_id = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_CLK,
- clk_type);
- if (clk_id < 0)
- return -EINVAL;
-
- switch (clk_id) {
- case PPCLK_GFXCLK:
- member_type = METRICS_AVERAGE_GFXCLK;
- break;
- case PPCLK_UCLK:
- member_type = METRICS_CURR_UCLK;
- break;
- case PPCLK_FCLK:
- member_type = METRICS_CURR_FCLK;
- break;
- case PPCLK_SOCCLK:
- member_type = METRICS_CURR_SOCCLK;
- break;
- case PPCLK_VCLK_0:
- member_type = METRICS_AVERAGE_VCLK;
- break;
- case PPCLK_DCLK_0:
- member_type = METRICS_AVERAGE_DCLK;
- break;
- case PPCLK_DCFCLK:
- member_type = METRICS_CURR_DCEFCLK;
- break;
- default:
- return -EINVAL;
- }
-
- return smu_v14_0_2_get_smu_metrics_data(smu,
- member_type,
- value);
-}
-
-static bool smu_v14_0_2_is_od_feature_supported(struct smu_context *smu,
- int od_feature_bit)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
-
- return overdrive_upperlimits->FeatureCtrlMask & (1U << od_feature_bit);
-}
-
-static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
- int od_feature_bit,
- int32_t *min,
- int32_t *max)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- const OverDriveLimits_t * const overdrive_upperlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMax;
- const OverDriveLimits_t * const overdrive_lowerlimits =
- &pptable->SkuTable.OverDriveLimitsBasicMin;
- int32_t od_min_setting, od_max_setting;
-
- switch (od_feature_bit) {
- case PP_OD_FEATURE_GFXCLK_FMIN:
- od_min_setting = overdrive_lowerlimits->GfxclkFmin;
- od_max_setting = overdrive_upperlimits->GfxclkFmin;
- break;
- case PP_OD_FEATURE_GFXCLK_FMAX:
- od_min_setting = overdrive_lowerlimits->GfxclkFmax;
- od_max_setting = overdrive_upperlimits->GfxclkFmax;
- break;
- case PP_OD_FEATURE_UCLK_FMIN:
- od_min_setting = overdrive_lowerlimits->UclkFmin;
- od_max_setting = overdrive_upperlimits->UclkFmin;
- break;
- case PP_OD_FEATURE_UCLK_FMAX:
- od_min_setting = overdrive_lowerlimits->UclkFmax;
- od_max_setting = overdrive_upperlimits->UclkFmax;
- break;
- case PP_OD_FEATURE_GFX_VF_CURVE:
- od_min_setting = overdrive_lowerlimits->VoltageOffsetPerZoneBoundary[0];
- od_max_setting = overdrive_upperlimits->VoltageOffsetPerZoneBoundary[0];
- break;
- case PP_OD_FEATURE_FAN_CURVE_TEMP:
- od_min_setting = overdrive_lowerlimits->FanLinearTempPoints[0];
- od_max_setting = overdrive_upperlimits->FanLinearTempPoints[0];
- break;
- case PP_OD_FEATURE_FAN_CURVE_PWM:
- od_min_setting = overdrive_lowerlimits->FanLinearPwmPoints[0];
- od_max_setting = overdrive_upperlimits->FanLinearPwmPoints[0];
- break;
- case PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT:
- od_min_setting = overdrive_lowerlimits->AcousticLimitRpmThreshold;
- od_max_setting = overdrive_upperlimits->AcousticLimitRpmThreshold;
- break;
- case PP_OD_FEATURE_FAN_ACOUSTIC_TARGET:
- od_min_setting = overdrive_lowerlimits->AcousticTargetRpmThreshold;
- od_max_setting = overdrive_upperlimits->AcousticTargetRpmThreshold;
- break;
- case PP_OD_FEATURE_FAN_TARGET_TEMPERATURE:
- od_min_setting = overdrive_lowerlimits->FanTargetTemperature;
- od_max_setting = overdrive_upperlimits->FanTargetTemperature;
- break;
- case PP_OD_FEATURE_FAN_MINIMUM_PWM:
- od_min_setting = overdrive_lowerlimits->FanMinimumPwm;
- od_max_setting = overdrive_upperlimits->FanMinimumPwm;
- break;
- default:
- od_min_setting = od_max_setting = INT_MAX;
- break;
- }
-
- if (min)
- *min = od_min_setting;
- if (max)
- *max = od_max_setting;
-}
-
-static int smu_v14_0_2_print_clk_levels(struct smu_context *smu,
- enum smu_clk_type clk_type,
- char *buf)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
- struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
- struct smu_14_0_dpm_table *single_dpm_table;
- struct smu_14_0_pcie_table *pcie_table;
- uint32_t gen_speed, lane_width;
- int i, curr_freq, size = 0;
- int32_t min_value, max_value;
- int ret = 0;
-
- smu_cmn_get_sysfs_buf(&buf, &size);
-
- if (amdgpu_ras_intr_triggered()) {
- size += sysfs_emit_at(buf, size, "unavailable\n");
- return size;
- }
-
- switch (clk_type) {
- case SMU_SCLK:
- single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
- break;
- case SMU_MCLK:
- single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- break;
- case SMU_SOCCLK:
- single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- break;
- case SMU_FCLK:
- single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- break;
- case SMU_DCEFCLK:
- single_dpm_table = &(dpm_context->dpm_tables.dcef_table);
- break;
- default:
- break;
- }
-
- switch (clk_type) {
- case SMU_SCLK:
- case SMU_MCLK:
- case SMU_SOCCLK:
- case SMU_FCLK:
- case SMU_VCLK:
- case SMU_VCLK1:
- case SMU_DCLK:
- case SMU_DCLK1:
- case SMU_DCEFCLK:
- ret = smu_v14_0_2_get_current_clk_freq_by_table(smu, clk_type, &curr_freq);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to get current clock freq!");
- return ret;
- }
-
- if (single_dpm_table->is_fine_grained) {
- /*
- * For fine grained dpms, there are only two dpm levels:
- * - level 0 -> min clock freq
- * - level 1 -> max clock freq
- * And the current clock frequency can be any value between them.
- * So, if the current clock frequency is not at level 0 or level 1,
- * we will fake it as three dpm levels:
- * - level 0 -> min clock freq
- * - level 1 -> current actual clock freq
- * - level 2 -> max clock freq
- */
- if ((single_dpm_table->dpm_levels[0].value != curr_freq) &&
- (single_dpm_table->dpm_levels[1].value != curr_freq)) {
- size += sysfs_emit_at(buf, size, "0: %uMhz\n",
- single_dpm_table->dpm_levels[0].value);
- size += sysfs_emit_at(buf, size, "1: %uMhz *\n",
- curr_freq);
- size += sysfs_emit_at(buf, size, "2: %uMhz\n",
- single_dpm_table->dpm_levels[1].value);
- } else {
- size += sysfs_emit_at(buf, size, "0: %uMhz %s\n",
- single_dpm_table->dpm_levels[0].value,
- single_dpm_table->dpm_levels[0].value == curr_freq ? "*" : "");
- size += sysfs_emit_at(buf, size, "1: %uMhz %s\n",
- single_dpm_table->dpm_levels[1].value,
- single_dpm_table->dpm_levels[1].value == curr_freq ? "*" : "");
- }
- } else {
- for (i = 0; i < single_dpm_table->count; i++)
- size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n",
- i, single_dpm_table->dpm_levels[i].value,
- single_dpm_table->dpm_levels[i].value == curr_freq ? "*" : "");
- }
- break;
- case SMU_PCIE:
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_PCIE_RATE,
- &gen_speed);
- if (ret)
- return ret;
-
- ret = smu_v14_0_2_get_smu_metrics_data(smu,
- METRICS_PCIE_WIDTH,
- &lane_width);
- if (ret)
- return ret;
-
- pcie_table = &(dpm_context->dpm_tables.pcie_table);
- for (i = 0; i < pcie_table->num_of_link_levels; i++)
- size += sysfs_emit_at(buf, size, "%d: %s %s %dMhz %s\n", i,
- (pcie_table->pcie_gen[i] == 0) ? "2.5GT/s," :
- (pcie_table->pcie_gen[i] == 1) ? "5.0GT/s," :
- (pcie_table->pcie_gen[i] == 2) ? "8.0GT/s," :
- (pcie_table->pcie_gen[i] == 3) ? "16.0GT/s," : "",
- (pcie_table->pcie_lane[i] == 1) ? "x1" :
- (pcie_table->pcie_lane[i] == 2) ? "x2" :
- (pcie_table->pcie_lane[i] == 3) ? "x4" :
- (pcie_table->pcie_lane[i] == 4) ? "x8" :
- (pcie_table->pcie_lane[i] == 5) ? "x12" :
- (pcie_table->pcie_lane[i] == 6) ? "x16" : "",
- pcie_table->clk_freq[i],
- (gen_speed == DECODE_GEN_SPEED(pcie_table->pcie_gen[i])) &&
- (lane_width == DECODE_LANE_WIDTH(pcie_table->pcie_lane[i])) ?
- "*" : "");
- break;
-
- case SMU_OD_SCLK:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_GFXCLK_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_SCLK:\n");
- size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMhz\n",
- od_table->OverDriveTable.GfxclkFmin,
- od_table->OverDriveTable.GfxclkFmax);
- break;
-
- case SMU_OD_MCLK:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_UCLK_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_MCLK:\n");
- size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMHz\n",
- od_table->OverDriveTable.UclkFmin,
- od_table->OverDriveTable.UclkFmax);
- break;
-
- case SMU_OD_VDDGFX_OFFSET:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_GFX_VF_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_VDDGFX_OFFSET:\n");
- size += sysfs_emit_at(buf, size, "%dmV\n",
- od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[0]);
- break;
-
- case SMU_OD_FAN_CURVE:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_FAN_CURVE:\n");
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++)
- size += sysfs_emit_at(buf, size, "%d: %dC %d%%\n",
- i,
- (int)od_table->OverDriveTable.FanLinearTempPoints[i],
- (int)od_table->OverDriveTable.FanLinearPwmPoints[i]);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_TEMP,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "FAN_CURVE(hotspot temp): %uC %uC\n",
- min_value, max_value);
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_PWM,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "FAN_CURVE(fan speed): %u%% %u%%\n",
- min_value, max_value);
-
- break;
-
- case SMU_OD_ACOUSTIC_LIMIT:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_LIMIT:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.AcousticLimitRpmThreshold);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "ACOUSTIC_LIMIT: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_ACOUSTIC_TARGET:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "OD_ACOUSTIC_TARGET:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.AcousticTargetRpmThreshold);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "ACOUSTIC_TARGET: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_FAN_TARGET_TEMPERATURE:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "FAN_TARGET_TEMPERATURE:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.FanTargetTemperature);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "TARGET_TEMPERATURE: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_FAN_MINIMUM_PWM:
- if (!smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "FAN_MINIMUM_PWM:\n");
- size += sysfs_emit_at(buf, size, "%d\n",
- (int)od_table->OverDriveTable.FanMinimumPwm);
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_MINIMUM_PWM,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "MINIMUM_PWM: %u %u\n",
- min_value, max_value);
- break;
-
- case SMU_OD_RANGE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT) &&
- !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT) &&
- !smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT))
- break;
-
- size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMIN,
- &min_value,
- NULL);
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMAX,
- NULL,
- &max_value);
- size += sysfs_emit_at(buf, size, "SCLK: %7uMhz %10uMhz\n",
- min_value, max_value);
- }
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMIN,
- &min_value,
- NULL);
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMAX,
- NULL,
- &max_value);
- size += sysfs_emit_at(buf, size, "MCLK: %7uMhz %10uMhz\n",
- min_value, max_value);
- }
-
- if (smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFX_VF_CURVE,
- &min_value,
- &max_value);
- size += sysfs_emit_at(buf, size, "VDDGFX_OFFSET: %7dmv %10dmv\n",
- min_value, max_value);
- }
- break;
-
- default:
- break;
- }
-
- return size;
-}
-
-static int smu_v14_0_2_force_clk_levels(struct smu_context *smu,
- enum smu_clk_type clk_type,
- uint32_t mask)
-{
- struct smu_dpm_context *smu_dpm = &smu->smu_dpm;
- struct smu_14_0_dpm_context *dpm_context = smu_dpm->dpm_context;
- struct smu_14_0_dpm_table *single_dpm_table;
- uint32_t soft_min_level, soft_max_level;
- uint32_t min_freq, max_freq;
- int ret = 0;
-
- soft_min_level = mask ? (ffs(mask) - 1) : 0;
- soft_max_level = mask ? (fls(mask) - 1) : 0;
-
- switch (clk_type) {
- case SMU_GFXCLK:
- case SMU_SCLK:
- single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
- break;
- case SMU_MCLK:
- case SMU_UCLK:
- single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- break;
- case SMU_SOCCLK:
- single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- break;
- case SMU_FCLK:
- single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- break;
- case SMU_VCLK:
- case SMU_VCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- break;
- case SMU_DCLK:
- case SMU_DCLK1:
- single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- break;
- default:
- break;
- }
-
- switch (clk_type) {
- case SMU_GFXCLK:
- case SMU_SCLK:
- case SMU_MCLK:
- case SMU_UCLK:
- case SMU_SOCCLK:
- case SMU_FCLK:
- case SMU_VCLK:
- case SMU_VCLK1:
- case SMU_DCLK:
- case SMU_DCLK1:
- if (single_dpm_table->is_fine_grained) {
- /* There is only 2 levels for fine grained DPM */
- soft_max_level = (soft_max_level >= 1 ? 1 : 0);
- soft_min_level = (soft_min_level >= 1 ? 1 : 0);
- } else {
- if ((soft_max_level >= single_dpm_table->count) ||
- (soft_min_level >= single_dpm_table->count))
- return -EINVAL;
- }
-
- min_freq = single_dpm_table->dpm_levels[soft_min_level].value;
- max_freq = single_dpm_table->dpm_levels[soft_max_level].value;
-
- ret = smu_v14_0_set_soft_freq_limited_range(smu,
- clk_type,
- min_freq,
- max_freq);
- break;
- case SMU_DCEFCLK:
- case SMU_PCIE:
- default:
- break;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_update_pcie_parameters(struct smu_context *smu,
- uint8_t pcie_gen_cap,
- uint8_t pcie_width_cap)
-{
- struct smu_14_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
- struct smu_14_0_pcie_table *pcie_table =
- &dpm_context->dpm_tables.pcie_table;
- uint32_t smu_pcie_arg;
- int ret, i;
-
- for (i = 0; i < pcie_table->num_of_link_levels; i++) {
- if (pcie_table->pcie_gen[i] > pcie_gen_cap)
- pcie_table->pcie_gen[i] = pcie_gen_cap;
- if (pcie_table->pcie_lane[i] > pcie_width_cap)
- pcie_table->pcie_lane[i] = pcie_width_cap;
-
- smu_pcie_arg = i << 16;
- smu_pcie_arg |= pcie_table->pcie_gen[i] << 8;
- smu_pcie_arg |= pcie_table->pcie_lane[i];
-
- ret = smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_OverridePcieParameters,
- smu_pcie_arg,
- NULL);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static const struct smu_temperature_range smu14_thermal_policy[] = {
- {-273150, 99000, 99000, -273150, 99000, 99000, -273150, 99000, 99000},
- { 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000, 120000},
-};
-
-static int smu_v14_0_2_get_thermal_temperature_range(struct smu_context *smu,
- struct smu_temperature_range *range)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- struct smu_14_0_2_powerplay_table *powerplay_table =
- table_context->power_play_table;
- PPTable_t *pptable = smu->smu_table.driver_pptable;
-
- if (amdgpu_sriov_vf(smu->adev))
- return 0;
-
- if (!range)
- return -EINVAL;
-
- memcpy(range, &smu14_thermal_policy[0], sizeof(struct smu_temperature_range));
-
- range->max = pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->edge_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_EDGE] + CTF_OFFSET_EDGE) *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->hotspot_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->hotspot_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_HOTSPOT] + CTF_OFFSET_HOTSPOT) *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->mem_crit_max = pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] *
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->mem_emergency_max = (pptable->CustomSkuTable.TemperatureLimit[TEMP_MEM] + CTF_OFFSET_MEM)*
- SMU_TEMPERATURE_UNITS_PER_CENTIGRADES;
- range->software_shutdown_temp = powerplay_table->software_shutdown_temp;
- range->software_shutdown_temp_offset = pptable->CustomSkuTable.FanAbnormalTempLimitOffset;
-
- return 0;
-}
-
-static int smu_v14_0_2_populate_umd_state_clk(struct smu_context *smu)
-{
- struct smu_14_0_dpm_context *dpm_context =
- smu->smu_dpm.dpm_context;
- struct smu_14_0_dpm_table *gfx_table =
- &dpm_context->dpm_tables.gfx_table;
- struct smu_14_0_dpm_table *mem_table =
- &dpm_context->dpm_tables.uclk_table;
- struct smu_14_0_dpm_table *soc_table =
- &dpm_context->dpm_tables.soc_table;
- struct smu_14_0_dpm_table *vclk_table =
- &dpm_context->dpm_tables.vclk_table;
- struct smu_14_0_dpm_table *dclk_table =
- &dpm_context->dpm_tables.dclk_table;
- struct smu_14_0_dpm_table *fclk_table =
- &dpm_context->dpm_tables.fclk_table;
- struct smu_umd_pstate_table *pstate_table =
- &smu->pstate_table;
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- DriverReportedClocks_t driver_clocks =
- pptable->SkuTable.DriverReportedClocks;
-
- pstate_table->gfxclk_pstate.min = gfx_table->min;
- if (driver_clocks.GameClockAc &&
- (driver_clocks.GameClockAc < gfx_table->max))
- pstate_table->gfxclk_pstate.peak = driver_clocks.GameClockAc;
- else
- pstate_table->gfxclk_pstate.peak = gfx_table->max;
-
- pstate_table->uclk_pstate.min = mem_table->min;
- pstate_table->uclk_pstate.peak = mem_table->max;
-
- pstate_table->socclk_pstate.min = soc_table->min;
- pstate_table->socclk_pstate.peak = soc_table->max;
-
- pstate_table->vclk_pstate.min = vclk_table->min;
- pstate_table->vclk_pstate.peak = vclk_table->max;
-
- pstate_table->dclk_pstate.min = dclk_table->min;
- pstate_table->dclk_pstate.peak = dclk_table->max;
-
- pstate_table->fclk_pstate.min = fclk_table->min;
- pstate_table->fclk_pstate.peak = fclk_table->max;
-
- if (driver_clocks.BaseClockAc &&
- driver_clocks.BaseClockAc < gfx_table->max)
- pstate_table->gfxclk_pstate.standard = driver_clocks.BaseClockAc;
- else
- pstate_table->gfxclk_pstate.standard = gfx_table->max;
- pstate_table->uclk_pstate.standard = mem_table->max;
- pstate_table->socclk_pstate.standard = soc_table->min;
- pstate_table->vclk_pstate.standard = vclk_table->min;
- pstate_table->dclk_pstate.standard = dclk_table->min;
- pstate_table->fclk_pstate.standard = fclk_table->min;
-
- return 0;
-}
-
-static void smu_v14_0_2_get_unique_id(struct smu_context *smu)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- SmuMetrics_t *metrics =
- &(((SmuMetricsExternal_t *)(smu_table->metrics_table))->SmuMetrics);
- struct amdgpu_device *adev = smu->adev;
- uint32_t upper32 = 0, lower32 = 0;
- int ret;
-
- ret = smu_cmn_get_metrics_table(smu, NULL, false);
- if (ret)
- goto out;
-
- upper32 = metrics->PublicSerialNumberUpper;
- lower32 = metrics->PublicSerialNumberLower;
-
-out:
- adev->unique_id = ((uint64_t)upper32 << 32) | lower32;
-}
-
-static int smu_v14_0_2_get_power_limit(struct smu_context *smu,
- uint32_t *current_power_limit,
- uint32_t *default_power_limit,
- uint32_t *max_power_limit,
- uint32_t *min_power_limit)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- PPTable_t *pptable = table_context->driver_pptable;
- CustomSkuTable_t *skutable = &pptable->CustomSkuTable;
- uint32_t power_limit;
- uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
-
- if (smu_v14_0_get_current_power_limit(smu, &power_limit))
- power_limit = smu->adev->pm.ac_power ?
- skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] :
- skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0];
-
- if (current_power_limit)
- *current_power_limit = power_limit;
- if (default_power_limit)
- *default_power_limit = power_limit;
-
- if (max_power_limit)
- *max_power_limit = msg_limit;
-
- if (min_power_limit)
- *min_power_limit = 0;
-
- return 0;
-}
-
-static int smu_v14_0_2_get_power_profile_mode(struct smu_context *smu,
- char *buf)
-{
- DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
- DpmActivityMonitorCoeffInt_t *activity_monitor =
- &(activity_monitor_external.DpmActivityMonitorCoeffInt);
- static const char *title[] = {
- "PROFILE_INDEX(NAME)",
- "CLOCK_TYPE(NAME)",
- "FPS",
- "MinActiveFreqType",
- "MinActiveFreq",
- "BoosterFreqType",
- "BoosterFreq",
- "PD_Data_limit_c",
- "PD_Data_error_coeff",
- "PD_Data_error_rate_coeff"};
- int16_t workload_type = 0;
- uint32_t i, size = 0;
- int result = 0;
-
- if (!buf)
- return -EINVAL;
-
- size += sysfs_emit_at(buf, size, "%16s %s %s %s %s %s %s %s %s %s\n",
- title[0], title[1], title[2], title[3], title[4], title[5],
- title[6], title[7], title[8], title[9]);
-
- for (i = 0; i < PP_SMC_POWER_PROFILE_COUNT; i++) {
- /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
- workload_type = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_WORKLOAD,
- i);
- if (workload_type == -ENOTSUPP)
- continue;
- else if (workload_type < 0)
- return -EINVAL;
-
- result = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- workload_type,
- (void *)(&activity_monitor_external),
- false);
- if (result) {
- dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
- return result;
- }
-
- size += sysfs_emit_at(buf, size, "%2d %14s%s:\n",
- i, amdgpu_pp_profile_name[i], (i == smu->power_profile_mode) ? "*" : " ");
-
- size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
- " ",
- 0,
- "GFXCLK",
- activity_monitor->Gfx_FPS,
- activity_monitor->Gfx_MinActiveFreqType,
- activity_monitor->Gfx_MinActiveFreq,
- activity_monitor->Gfx_BoosterFreqType,
- activity_monitor->Gfx_BoosterFreq,
- activity_monitor->Gfx_PD_Data_limit_c,
- activity_monitor->Gfx_PD_Data_error_coeff,
- activity_monitor->Gfx_PD_Data_error_rate_coeff);
-
- size += sysfs_emit_at(buf, size, "%19s %d(%13s) %7d %7d %7d %7d %7d %7d %7d %7d\n",
- " ",
- 1,
- "FCLK",
- activity_monitor->Fclk_FPS,
- activity_monitor->Fclk_MinActiveFreqType,
- activity_monitor->Fclk_MinActiveFreq,
- activity_monitor->Fclk_BoosterFreqType,
- activity_monitor->Fclk_BoosterFreq,
- activity_monitor->Fclk_PD_Data_limit_c,
- activity_monitor->Fclk_PD_Data_error_coeff,
- activity_monitor->Fclk_PD_Data_error_rate_coeff);
- }
-
- return size;
-}
-
-static int smu_v14_0_2_set_power_profile_mode(struct smu_context *smu,
- long *input,
- uint32_t size)
-{
- DpmActivityMonitorCoeffIntExternal_t activity_monitor_external;
- DpmActivityMonitorCoeffInt_t *activity_monitor =
- &(activity_monitor_external.DpmActivityMonitorCoeffInt);
- int workload_type, ret = 0;
-
- smu->power_profile_mode = input[size];
-
- if (smu->power_profile_mode >= PP_SMC_POWER_PROFILE_COUNT) {
- dev_err(smu->adev->dev, "Invalid power profile mode %d\n", smu->power_profile_mode);
- return -EINVAL;
- }
-
- if (smu->power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
- if (size != 9)
- return -EINVAL;
-
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- WORKLOAD_PPLIB_CUSTOM_BIT,
- (void *)(&activity_monitor_external),
- false);
- if (ret) {
- dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__);
- return ret;
- }
-
- switch (input[0]) {
- case 0: /* Gfxclk */
- activity_monitor->Gfx_FPS = input[1];
- activity_monitor->Gfx_MinActiveFreqType = input[2];
- activity_monitor->Gfx_MinActiveFreq = input[3];
- activity_monitor->Gfx_BoosterFreqType = input[4];
- activity_monitor->Gfx_BoosterFreq = input[5];
- activity_monitor->Gfx_PD_Data_limit_c = input[6];
- activity_monitor->Gfx_PD_Data_error_coeff = input[7];
- activity_monitor->Gfx_PD_Data_error_rate_coeff = input[8];
- break;
- case 1: /* Fclk */
- activity_monitor->Fclk_FPS = input[1];
- activity_monitor->Fclk_MinActiveFreqType = input[2];
- activity_monitor->Fclk_MinActiveFreq = input[3];
- activity_monitor->Fclk_BoosterFreqType = input[4];
- activity_monitor->Fclk_BoosterFreq = input[5];
- activity_monitor->Fclk_PD_Data_limit_c = input[6];
- activity_monitor->Fclk_PD_Data_error_coeff = input[7];
- activity_monitor->Fclk_PD_Data_error_rate_coeff = input[8];
- break;
- default:
- return -EINVAL;
- }
-
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_ACTIVITY_MONITOR_COEFF,
- WORKLOAD_PPLIB_CUSTOM_BIT,
- (void *)(&activity_monitor_external),
- true);
- if (ret) {
- dev_err(smu->adev->dev, "[%s] Failed to set activity monitor!", __func__);
- return ret;
- }
- }
-
- /* conv PP_SMC_POWER_PROFILE* to WORKLOAD_PPLIB_*_BIT */
- workload_type = smu_cmn_to_asic_specific_index(smu,
- CMN2ASIC_MAPPING_WORKLOAD,
- smu->power_profile_mode);
- if (workload_type < 0)
- return -EINVAL;
-
- return smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_SetWorkloadMask,
- 1 << workload_type,
- NULL);
-}
-
-static int smu_v14_0_2_baco_enter(struct smu_context *smu)
-{
- struct smu_baco_context *smu_baco = &smu->smu_baco;
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev))
- return smu_v14_0_baco_set_armd3_sequence(smu,
- smu_baco->maco_support ? BACO_SEQ_BAMACO : BACO_SEQ_BACO);
- else
- return smu_v14_0_baco_enter(smu);
-}
-
-static int smu_v14_0_2_baco_exit(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->in_runpm && smu_cmn_is_audio_func_enabled(adev)) {
- /* Wait for PMFW handling for the Dstate change */
- usleep_range(10000, 11000);
- return smu_v14_0_baco_set_armd3_sequence(smu, BACO_SEQ_ULPS);
- } else {
- return smu_v14_0_baco_exit(smu);
- }
-}
-
-static bool smu_v14_0_2_is_mode1_reset_supported(struct smu_context *smu)
-{
- // TODO
-
- return true;
-}
-
-static int smu_v14_0_2_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msg, int num_msgs)
-{
- struct amdgpu_smu_i2c_bus *smu_i2c = i2c_get_adapdata(i2c_adap);
- struct amdgpu_device *adev = smu_i2c->adev;
- struct smu_context *smu = adev->powerplay.pp_handle;
- struct smu_table_context *smu_table = &smu->smu_table;
- struct smu_table *table = &smu_table->driver_table;
- SwI2cRequest_t *req, *res = (SwI2cRequest_t *)table->cpu_addr;
- int i, j, r, c;
- u16 dir;
-
- if (!adev->pm.dpm_enabled)
- return -EBUSY;
-
- req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (!req)
- return -ENOMEM;
-
- req->I2CcontrollerPort = smu_i2c->port;
- req->I2CSpeed = I2C_SPEED_FAST_400K;
- req->SlaveAddress = msg[0].addr << 1; /* wants an 8-bit address */
- dir = msg[0].flags & I2C_M_RD;
-
- for (c = i = 0; i < num_msgs; i++) {
- for (j = 0; j < msg[i].len; j++, c++) {
- SwI2cCmd_t *cmd = &req->SwI2cCmds[c];
-
- if (!(msg[i].flags & I2C_M_RD)) {
- /* write */
- cmd->CmdConfig |= CMDCONFIG_READWRITE_MASK;
- cmd->ReadWriteData = msg[i].buf[j];
- }
-
- if ((dir ^ msg[i].flags) & I2C_M_RD) {
- /* The direction changes.
- */
- dir = msg[i].flags & I2C_M_RD;
- cmd->CmdConfig |= CMDCONFIG_RESTART_MASK;
- }
-
- req->NumCmds++;
-
- /*
- * Insert STOP if we are at the last byte of either last
- * message for the transaction or the client explicitly
- * requires a STOP at this particular message.
- */
- if ((j == msg[i].len - 1) &&
- ((i == num_msgs - 1) || (msg[i].flags & I2C_M_STOP))) {
- cmd->CmdConfig &= ~CMDCONFIG_RESTART_MASK;
- cmd->CmdConfig |= CMDCONFIG_STOP_MASK;
- }
- }
- }
- mutex_lock(&adev->pm.mutex);
- r = smu_cmn_update_table(smu, SMU_TABLE_I2C_COMMANDS, 0, req, true);
- mutex_unlock(&adev->pm.mutex);
- if (r)
- goto fail;
-
- for (c = i = 0; i < num_msgs; i++) {
- if (!(msg[i].flags & I2C_M_RD)) {
- c += msg[i].len;
- continue;
- }
- for (j = 0; j < msg[i].len; j++, c++) {
- SwI2cCmd_t *cmd = &res->SwI2cCmds[c];
-
- msg[i].buf[j] = cmd->ReadWriteData;
- }
- }
- r = num_msgs;
-fail:
- kfree(req);
- return r;
-}
-
-static u32 smu_v14_0_2_i2c_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm smu_v14_0_2_i2c_algo = {
- .master_xfer = smu_v14_0_2_i2c_xfer,
- .functionality = smu_v14_0_2_i2c_func,
-};
-
-static const struct i2c_adapter_quirks smu_v14_0_2_i2c_control_quirks = {
- .flags = I2C_AQ_COMB | I2C_AQ_COMB_SAME_ADDR | I2C_AQ_NO_ZERO_LEN,
- .max_read_len = MAX_SW_I2C_COMMANDS,
- .max_write_len = MAX_SW_I2C_COMMANDS,
- .max_comb_1st_msg_len = 2,
- .max_comb_2nd_msg_len = MAX_SW_I2C_COMMANDS - 2,
-};
-
-static int smu_v14_0_2_i2c_control_init(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
- int res, i;
-
- for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- smu_i2c->adev = adev;
- smu_i2c->port = i;
- mutex_init(&smu_i2c->mutex);
- control->owner = THIS_MODULE;
- control->dev.parent = &adev->pdev->dev;
- control->algo = &smu_v14_0_2_i2c_algo;
- snprintf(control->name, sizeof(control->name), "AMDGPU SMU %d", i);
- control->quirks = &smu_v14_0_2_i2c_control_quirks;
- i2c_set_adapdata(control, smu_i2c);
-
- res = i2c_add_adapter(control);
- if (res) {
- DRM_ERROR("Failed to register hw i2c, err: %d\n", res);
- goto Out_err;
- }
- }
-
- /* assign the buses used for the FRU EEPROM and RAS EEPROM */
- /* XXX ideally this would be something in a vbios data table */
- adev->pm.ras_eeprom_i2c_bus = &adev->pm.smu_i2c[1].adapter;
- adev->pm.fru_eeprom_i2c_bus = &adev->pm.smu_i2c[0].adapter;
-
- return 0;
-Out_err:
- for ( ; i >= 0; i--) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- i2c_del_adapter(control);
- }
- return res;
-}
-
-static void smu_v14_0_2_i2c_control_fini(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
- int i;
-
- for (i = 0; i < MAX_SMU_I2C_BUSES; i++) {
- struct amdgpu_smu_i2c_bus *smu_i2c = &adev->pm.smu_i2c[i];
- struct i2c_adapter *control = &smu_i2c->adapter;
-
- i2c_del_adapter(control);
- }
- adev->pm.ras_eeprom_i2c_bus = NULL;
- adev->pm.fru_eeprom_i2c_bus = NULL;
-}
-
-static int smu_v14_0_2_set_mp1_state(struct smu_context *smu,
- enum pp_mp1_state mp1_state)
-{
- int ret;
-
- switch (mp1_state) {
- case PP_MP1_STATE_UNLOAD:
- ret = smu_cmn_set_mp1_state(smu, mp1_state);
- break;
- default:
- /* Ignore others */
- ret = 0;
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_set_df_cstate(struct smu_context *smu,
- enum pp_df_cstate state)
-{
- return smu_cmn_send_smc_msg_with_param(smu,
- SMU_MSG_DFCstateControl,
- state,
- NULL);
-}
-
-static int smu_v14_0_2_mode1_reset(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_cmn_send_debug_smc_msg(smu, DEBUGSMC_MSG_Mode1Reset);
- if (!ret) {
- if (amdgpu_emu_mode == 1)
- msleep(50000);
- else
- msleep(1000);
- }
-
- return ret;
-}
-
-static int smu_v14_0_2_mode2_reset(struct smu_context *smu)
-{
- int ret = 0;
-
- // TODO
-
- return ret;
-}
-
-static int smu_v14_0_2_enable_gfx_features(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (adev->ip_versions[MP1_HWIP][0] == IP_VERSION(14, 0, 2))
- return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_EnableAllSmuFeatures,
- FEATURE_PWR_GFX, NULL);
- else
- return -EOPNOTSUPP;
-}
-
-static void smu_v14_0_2_set_smu_mailbox_registers(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- smu->param_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_82);
- smu->msg_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_66);
- smu->resp_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_90);
-
- smu->debug_param_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_53);
- smu->debug_msg_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_75);
- smu->debug_resp_reg = SOC15_REG_OFFSET(MP1, 0, regMP1_SMN_C2PMSG_54);
-}
-
-static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
- void **table)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_3 *gpu_metrics =
- (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
- SmuMetricsExternal_t metrics_ext;
- SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- &metrics_ext,
- true);
- if (ret)
- return ret;
-
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
-
- gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
- gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
- gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
- gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
- gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
- gpu_metrics->temperature_vrmem = max(metrics->AvgTemperature[TEMP_VR_MEM0],
- metrics->AvgTemperature[TEMP_VR_MEM1]);
-
- gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
- gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
- gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage,
- metrics->Vcn1ActivityPercentage);
-
- gpu_metrics->average_socket_power = metrics->AverageSocketPower;
- gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
-
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
- else
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
-
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
- else
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
-
- gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
- gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
- gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
- gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
-
- gpu_metrics->current_gfxclk = gpu_metrics->average_gfxclk_frequency;
- gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK];
- gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK];
- gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
- gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_0];
-
- gpu_metrics->throttle_status =
- smu_v14_0_2_get_throttler_status(metrics);
- gpu_metrics->indep_throttle_status =
- smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
- smu_v14_0_2_throttler_map);
-
- gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
-
- gpu_metrics->pcie_link_width = metrics->PcieWidth;
- if ((metrics->PcieRate - 1) > LINK_SPEED_MAX)
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(1);
- else
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(metrics->PcieRate);
-
- gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
-
- gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_VDD_SOC];
- gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VDDIO_MEM];
-
- *table = (void *)gpu_metrics;
-
- return sizeof(struct gpu_metrics_v1_3);
-}
-
-static void smu_v14_0_2_dump_od_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- struct amdgpu_device *adev = smu->adev;
-
- dev_dbg(adev->dev, "OD: Gfxclk: (%d, %d)\n", od_table->OverDriveTable.GfxclkFmin,
- od_table->OverDriveTable.GfxclkFmax);
- dev_dbg(adev->dev, "OD: Uclk: (%d, %d)\n", od_table->OverDriveTable.UclkFmin,
- od_table->OverDriveTable.UclkFmax);
-}
-
-static int smu_v14_0_2_upload_overdrive_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- int ret;
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_OVERDRIVE,
- 0,
- (void *)od_table,
- true);
- if (ret)
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
-
- return ret;
-}
-
-static void smu_v14_0_2_set_supported_od_feature_mask(struct smu_context *smu)
-{
- struct amdgpu_device *adev = smu->adev;
-
- if (smu_v14_0_2_is_od_feature_supported(smu,
- PP_OD_FEATURE_FAN_CURVE_BIT))
- adev->pm.od_feature_mask |= OD_OPS_SUPPORT_FAN_CURVE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_CURVE_SET |
- OD_OPS_SUPPORT_ACOUSTIC_LIMIT_THRESHOLD_RETRIEVE |
- OD_OPS_SUPPORT_ACOUSTIC_LIMIT_THRESHOLD_SET |
- OD_OPS_SUPPORT_ACOUSTIC_TARGET_THRESHOLD_RETRIEVE |
- OD_OPS_SUPPORT_ACOUSTIC_TARGET_THRESHOLD_SET |
- OD_OPS_SUPPORT_FAN_TARGET_TEMPERATURE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_TARGET_TEMPERATURE_SET |
- OD_OPS_SUPPORT_FAN_MINIMUM_PWM_RETRIEVE |
- OD_OPS_SUPPORT_FAN_MINIMUM_PWM_SET;
-}
-
-static int smu_v14_0_2_get_overdrive_table(struct smu_context *smu,
- OverDriveTableExternal_t *od_table)
-{
- int ret;
- ret = smu_cmn_update_table(smu,
- SMU_TABLE_OVERDRIVE,
- 0,
- (void *)od_table,
- false);
- if (ret)
- dev_err(smu->adev->dev, "Failed to get overdrive table!\n");
-
- return ret;
-}
-
-static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
-{
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)smu->smu_table.overdrive_table;
- OverDriveTableExternal_t *boot_od_table =
- (OverDriveTableExternal_t *)smu->smu_table.boot_overdrive_table;
- OverDriveTableExternal_t *user_od_table =
- (OverDriveTableExternal_t *)smu->smu_table.user_overdrive_table;
- OverDriveTableExternal_t user_od_table_bak;
- int ret;
- int i;
-
- ret = smu_v14_0_2_get_overdrive_table(smu, boot_od_table);
- if (ret)
- return ret;
-
- smu_v14_0_2_dump_od_table(smu, boot_od_table);
-
- memcpy(od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
-
- /*
- * For S3/S4/Runpm resume, we need to setup those overdrive tables again,
- * but we have to preserve user defined values in "user_od_table".
- */
- if (!smu->adev->in_suspend) {
- memcpy(user_od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
- smu->user_dpm_profile.user_od = false;
- } else if (smu->user_dpm_profile.user_od) {
- memcpy(&user_od_table_bak,
- user_od_table,
- sizeof(OverDriveTableExternal_t));
- memcpy(user_od_table,
- boot_od_table,
- sizeof(OverDriveTableExternal_t));
- user_od_table->OverDriveTable.GfxclkFmin =
- user_od_table_bak.OverDriveTable.GfxclkFmin;
- user_od_table->OverDriveTable.GfxclkFmax =
- user_od_table_bak.OverDriveTable.GfxclkFmax;
- user_od_table->OverDriveTable.UclkFmin =
- user_od_table_bak.OverDriveTable.UclkFmin;
- user_od_table->OverDriveTable.UclkFmax =
- user_od_table_bak.OverDriveTable.UclkFmax;
- for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++)
- user_od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[i] =
- user_od_table_bak.OverDriveTable.VoltageOffsetPerZoneBoundary[i];
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS - 1; i++) {
- user_od_table->OverDriveTable.FanLinearTempPoints[i] =
- user_od_table_bak.OverDriveTable.FanLinearTempPoints[i];
- user_od_table->OverDriveTable.FanLinearPwmPoints[i] =
- user_od_table_bak.OverDriveTable.FanLinearPwmPoints[i];
- }
- user_od_table->OverDriveTable.AcousticLimitRpmThreshold =
- user_od_table_bak.OverDriveTable.AcousticLimitRpmThreshold;
- user_od_table->OverDriveTable.AcousticTargetRpmThreshold =
- user_od_table_bak.OverDriveTable.AcousticTargetRpmThreshold;
- user_od_table->OverDriveTable.FanTargetTemperature =
- user_od_table_bak.OverDriveTable.FanTargetTemperature;
- user_od_table->OverDriveTable.FanMinimumPwm =
- user_od_table_bak.OverDriveTable.FanMinimumPwm;
- }
-
- smu_v14_0_2_set_supported_od_feature_mask(smu);
-
- return 0;
-}
-
-static int smu_v14_0_2_restore_user_od_settings(struct smu_context *smu)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table = table_context->overdrive_table;
- OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
- int res;
-
- user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
- BIT(PP_OD_FEATURE_UCLK_BIT) |
- BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
- BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
- memcpy(od_table, user_od_table, sizeof(OverDriveTableExternal_t));
-
- return res;
-}
-
-static int smu_v14_0_2_od_restore_table_single(struct smu_context *smu, long input)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *boot_overdrive_table =
- (OverDriveTableExternal_t *)table_context->boot_overdrive_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- struct amdgpu_device *adev = smu->adev;
- int i;
-
- switch (input) {
- case PP_OD_EDIT_FAN_CURVE:
- for (i = 0; i < NUM_OD_FAN_MAX_POINTS; i++) {
- od_table->OverDriveTable.FanLinearTempPoints[i] =
- boot_overdrive_table->OverDriveTable.FanLinearTempPoints[i];
- od_table->OverDriveTable.FanLinearPwmPoints[i] =
- boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
- }
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_ACOUSTIC_LIMIT:
- od_table->OverDriveTable.AcousticLimitRpmThreshold =
- boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_ACOUSTIC_TARGET:
- od_table->OverDriveTable.AcousticTargetRpmThreshold =
- boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
- od_table->OverDriveTable.FanTargetTemperature =
- boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- case PP_OD_EDIT_FAN_MINIMUM_PWM:
- od_table->OverDriveTable.FanMinimumPwm =
- boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
- default:
- dev_info(adev->dev, "Invalid table index: %ld\n", input);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
- enum PP_OD_DPM_TABLE_COMMAND type,
- long input[],
- uint32_t size)
-{
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- struct amdgpu_device *adev = smu->adev;
- uint32_t offset_of_voltageoffset;
- int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
- int i, ret = 0;
-
- switch (type) {
- case PP_OD_EDIT_SCLK_VDDC_TABLE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT)) {
- dev_warn(adev->dev, "GFXCLK_LIMITS setting not supported!\n");
- return -ENOTSUPP;
- }
-
- for (i = 0; i < size; i += 2) {
- if (i + 2 > size) {
- dev_info(adev->dev, "invalid number of input parameters %d\n", size);
- return -EINVAL;
- }
-
- switch (input[i]) {
- case 0:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMIN,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "GfxclkFmin (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.GfxclkFmin = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_GFXCLK_BIT;
- break;
-
- case 1:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFXCLK_FMAX,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "GfxclkFmax (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.GfxclkFmax = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_GFXCLK_BIT;
- break;
-
- default:
- dev_info(adev->dev, "Invalid SCLK_VDDC_TABLE index: %ld\n", input[i]);
- dev_info(adev->dev, "Supported indices: [0:min,1:max]\n");
- return -EINVAL;
- }
- }
-
- if (od_table->OverDriveTable.GfxclkFmin > od_table->OverDriveTable.GfxclkFmax) {
- dev_err(adev->dev,
- "Invalid setting: GfxclkFmin(%u) is bigger than GfxclkFmax(%u)\n",
- (uint32_t)od_table->OverDriveTable.GfxclkFmin,
- (uint32_t)od_table->OverDriveTable.GfxclkFmax);
- return -EINVAL;
- }
- break;
-
- case PP_OD_EDIT_MCLK_VDDC_TABLE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT)) {
- dev_warn(adev->dev, "UCLK_LIMITS setting not supported!\n");
- return -ENOTSUPP;
- }
-
- for (i = 0; i < size; i += 2) {
- if (i + 2 > size) {
- dev_info(adev->dev, "invalid number of input parameters %d\n", size);
- return -EINVAL;
- }
-
- switch (input[i]) {
- case 0:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMIN,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "UclkFmin (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.UclkFmin = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_UCLK_BIT;
- break;
-
- case 1:
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_UCLK_FMAX,
- &minimum,
- &maximum);
- if (input[i + 1] < minimum ||
- input[i + 1] > maximum) {
- dev_info(adev->dev, "UclkFmax (%ld) must be within [%u, %u]!\n",
- input[i + 1], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.UclkFmax = input[i + 1];
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_UCLK_BIT;
- break;
-
- default:
- dev_info(adev->dev, "Invalid MCLK_VDDC_TABLE index: %ld\n", input[i]);
- dev_info(adev->dev, "Supported indices: [0:min,1:max]\n");
- return -EINVAL;
- }
- }
-
- if (od_table->OverDriveTable.UclkFmin > od_table->OverDriveTable.UclkFmax) {
- dev_err(adev->dev,
- "Invalid setting: UclkFmin(%u) is bigger than UclkFmax(%u)\n",
- (uint32_t)od_table->OverDriveTable.UclkFmin,
- (uint32_t)od_table->OverDriveTable.UclkFmax);
- return -EINVAL;
- }
- break;
-
- case PP_OD_EDIT_VDDGFX_OFFSET:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFX_VF_CURVE_BIT)) {
- dev_warn(adev->dev, "Gfx offset setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_GFX_VF_CURVE,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "Voltage offset (%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- for (i = 0; i < PP_NUM_OD_VF_CURVE_POINTS; i++)
- od_table->OverDriveTable.VoltageOffsetPerZoneBoundary[i] = input[0];
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_CURVE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- if (input[0] >= NUM_OD_FAN_MAX_POINTS - 1 ||
- input[0] < 0)
- return -EINVAL;
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_TEMP,
- &minimum,
- &maximum);
- if (input[1] < minimum ||
- input[1] > maximum) {
- dev_info(adev->dev, "Fan curve temp setting(%ld) must be within [%d, %d]!\n",
- input[1], minimum, maximum);
- return -EINVAL;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_CURVE_PWM,
- &minimum,
- &maximum);
- if (input[2] < minimum ||
- input[2] > maximum) {
- dev_info(adev->dev, "Fan curve pwm setting(%ld) must be within [%d, %d]!\n",
- input[2], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanLinearTempPoints[input[0]] = input[1];
- od_table->OverDriveTable.FanLinearPwmPoints[input[0]] = input[2];
- od_table->OverDriveTable.FanMode = FAN_MODE_MANUAL_LINEAR;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_ACOUSTIC_LIMIT:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_LIMIT,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "acoustic limit threshold setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.AcousticLimitRpmThreshold = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_ACOUSTIC_TARGET:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_ACOUSTIC_TARGET,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "acoustic target threshold setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.AcousticTargetRpmThreshold = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_TARGET_TEMPERATURE,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "fan target temperature setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanTargetTemperature = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_EDIT_FAN_MINIMUM_PWM:
- if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_FAN_CURVE_BIT)) {
- dev_warn(adev->dev, "Fan curve setting not supported!\n");
- return -ENOTSUPP;
- }
-
- smu_v14_0_2_get_od_setting_limits(smu,
- PP_OD_FEATURE_FAN_MINIMUM_PWM,
- &minimum,
- &maximum);
- if (input[0] < minimum ||
- input[0] > maximum) {
- dev_info(adev->dev, "fan minimum pwm setting(%ld) must be within [%d, %d]!\n",
- input[0], minimum, maximum);
- return -EINVAL;
- }
-
- od_table->OverDriveTable.FanMinimumPwm = input[0];
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- break;
-
- case PP_OD_RESTORE_DEFAULT_TABLE:
- if (size == 1) {
- ret = smu_v14_0_2_od_restore_table_single(smu, input[0]);
- if (ret)
- return ret;
- } else {
- feature_ctrlmask = od_table->OverDriveTable.FeatureCtrlMask;
- memcpy(od_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t));
- od_table->OverDriveTable.FeatureCtrlMask = feature_ctrlmask;
- }
- fallthrough;
- case PP_OD_COMMIT_DPM_TABLE:
- /*
- * The member below instructs PMFW the settings focused in
- * this single operation.
- * `uint32_t FeatureCtrlMask;`
- * It does not contain actual informations about user's custom
- * settings. Thus we do not cache it.
- */
- offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset)) {
- smu_v14_0_2_dump_od_table(smu, od_table);
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
-
- od_table->OverDriveTable.FeatureCtrlMask = 0;
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
- (u8 *)od_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
-
- if (!memcmp(table_context->user_overdrive_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t)))
- smu->user_dpm_profile.user_od = false;
- else
- smu->user_dpm_profile.user_od = true;
- }
- break;
-
- default:
- return -ENOSYS;
- }
-
- return ret;
-}
-
-<<<<<<<
-static int smu_v14_0_2_set_power_limit(struct smu_context *smu,
- enum smu_ppt_limit_type limit_type,
- uint32_t limit)
-{
- PPTable_t *pptable = smu->smu_table.driver_pptable;
- uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
- int ret = 0;
-
- if (limit_type != SMU_DEFAULT_PPT_LIMIT)
- return -EINVAL;
-
- if (limit <= msg_limit) {
- if (smu->current_power_limit > msg_limit) {
- od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
- }
- return smu_v14_0_set_power_limit(smu, limit_type, limit);
- } else if (smu->od_enabled) {
- ret = smu_v14_0_set_power_limit(smu, limit_type, msg_limit);
- if (ret)
- return ret;
-
- od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
- if (ret) {
- dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
- return ret;
- }
-
- smu->current_power_limit = limit;
- } else {
- return -EINVAL;
- }
-
- return 0;
-=======
-static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
- void **table)
-{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_3 *gpu_metrics =
- (struct gpu_metrics_v1_3 *)smu_table->gpu_metrics_table;
- SmuMetricsExternal_t metrics_ext;
- SmuMetrics_t *metrics = &metrics_ext.SmuMetrics;
- int ret = 0;
-
- ret = smu_cmn_get_metrics_table(smu,
- &metrics_ext,
- true);
- if (ret)
- return ret;
-
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 3);
-
- gpu_metrics->temperature_edge = metrics->AvgTemperature[TEMP_EDGE];
- gpu_metrics->temperature_hotspot = metrics->AvgTemperature[TEMP_HOTSPOT];
- gpu_metrics->temperature_mem = metrics->AvgTemperature[TEMP_MEM];
- gpu_metrics->temperature_vrgfx = metrics->AvgTemperature[TEMP_VR_GFX];
- gpu_metrics->temperature_vrsoc = metrics->AvgTemperature[TEMP_VR_SOC];
- gpu_metrics->temperature_vrmem = max(metrics->AvgTemperature[TEMP_VR_MEM0],
- metrics->AvgTemperature[TEMP_VR_MEM1]);
-
- gpu_metrics->average_gfx_activity = metrics->AverageGfxActivity;
- gpu_metrics->average_umc_activity = metrics->AverageUclkActivity;
- gpu_metrics->average_mm_activity = max(metrics->Vcn0ActivityPercentage,
- metrics->Vcn1ActivityPercentage);
-
- gpu_metrics->average_socket_power = metrics->AverageSocketPower;
- gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
-
- if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;
- else
- gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPreDs;
-
- if (metrics->AverageUclkActivity <= SMU_14_0_2_BUSY_THRESHOLD)
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPostDs;
- else
- gpu_metrics->average_uclk_frequency = metrics->AverageMemclkFrequencyPreDs;
-
- gpu_metrics->average_vclk0_frequency = metrics->AverageVclk0Frequency;
- gpu_metrics->average_dclk0_frequency = metrics->AverageDclk0Frequency;
- gpu_metrics->average_vclk1_frequency = metrics->AverageVclk1Frequency;
- gpu_metrics->average_dclk1_frequency = metrics->AverageDclk1Frequency;
-
- gpu_metrics->current_gfxclk = gpu_metrics->average_gfxclk_frequency;
- gpu_metrics->current_socclk = metrics->CurrClock[PPCLK_SOCCLK];
- gpu_metrics->current_uclk = metrics->CurrClock[PPCLK_UCLK];
- gpu_metrics->current_vclk0 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk0 = metrics->CurrClock[PPCLK_DCLK_0];
- gpu_metrics->current_vclk1 = metrics->CurrClock[PPCLK_VCLK_0];
- gpu_metrics->current_dclk1 = metrics->CurrClock[PPCLK_DCLK_0];
-
- gpu_metrics->throttle_status =
- smu_v14_0_2_get_throttler_status(metrics);
- gpu_metrics->indep_throttle_status =
- smu_cmn_get_indep_throttler_status(gpu_metrics->throttle_status,
- smu_v14_0_2_throttler_map);
-
- gpu_metrics->current_fan_speed = metrics->AvgFanRpm;
-
- gpu_metrics->pcie_link_width = metrics->PcieWidth;
- if ((metrics->PcieRate - 1) > LINK_SPEED_MAX)
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(1);
- else
- gpu_metrics->pcie_link_speed = pcie_gen_to_speed(metrics->PcieRate);
-
- gpu_metrics->system_clock_counter = ktime_get_boottime_ns();
-
- gpu_metrics->voltage_gfx = metrics->AvgVoltage[SVI_PLANE_VDD_GFX];
- gpu_metrics->voltage_soc = metrics->AvgVoltage[SVI_PLANE_VDD_SOC];
- gpu_metrics->voltage_mem = metrics->AvgVoltage[SVI_PLANE_VDDIO_MEM];
-
- *table = (void *)gpu_metrics;
-
- return sizeof(struct gpu_metrics_v1_3);
->>>>>>>
-}
-
-static const struct pptable_funcs smu_v14_0_2_ppt_funcs = {
- .get_allowed_feature_mask = smu_v14_0_2_get_allowed_feature_mask,
- .set_default_dpm_table = smu_v14_0_2_set_default_dpm_table,
- .i2c_init = smu_v14_0_2_i2c_control_init,
- .i2c_fini = smu_v14_0_2_i2c_control_fini,
- .is_dpm_running = smu_v14_0_2_is_dpm_running,
- .dump_pptable = smu_v14_0_2_dump_pptable,
- .init_microcode = smu_v14_0_init_microcode,
- .load_microcode = smu_v14_0_load_microcode,
- .fini_microcode = smu_v14_0_fini_microcode,
- .init_smc_tables = smu_v14_0_2_init_smc_tables,
- .fini_smc_tables = smu_v14_0_fini_smc_tables,
- .init_power = smu_v14_0_init_power,
- .fini_power = smu_v14_0_fini_power,
- .check_fw_status = smu_v14_0_check_fw_status,
- .setup_pptable = smu_v14_0_2_setup_pptable,
- .check_fw_version = smu_v14_0_check_fw_version,
- .write_pptable = smu_cmn_write_pptable,
- .set_driver_table_location = smu_v14_0_set_driver_table_location,
- .system_features_control = smu_v14_0_system_features_control,
- .set_allowed_mask = smu_v14_0_set_allowed_mask,
- .get_enabled_mask = smu_cmn_get_enabled_mask,
- .dpm_set_vcn_enable = smu_v14_0_set_vcn_enable,
- .dpm_set_jpeg_enable = smu_v14_0_set_jpeg_enable,
- .get_dpm_ultimate_freq = smu_v14_0_2_get_dpm_ultimate_freq,
- .get_vbios_bootup_values = smu_v14_0_get_vbios_bootup_values,
- .read_sensor = smu_v14_0_2_read_sensor,
- .feature_is_enabled = smu_cmn_feature_is_enabled,
- .print_clk_levels = smu_v14_0_2_print_clk_levels,
- .force_clk_levels = smu_v14_0_2_force_clk_levels,
- .update_pcie_parameters = smu_v14_0_2_update_pcie_parameters,
- .get_thermal_temperature_range = smu_v14_0_2_get_thermal_temperature_range,
- .register_irq_handler = smu_v14_0_register_irq_handler,
- .enable_thermal_alert = smu_v14_0_enable_thermal_alert,
- .disable_thermal_alert = smu_v14_0_disable_thermal_alert,
- .notify_memory_pool_location = smu_v14_0_notify_memory_pool_location,
- .get_gpu_metrics = smu_v14_0_2_get_gpu_metrics,
- .set_soft_freq_limited_range = smu_v14_0_set_soft_freq_limited_range,
- .set_default_od_settings = smu_v14_0_2_set_default_od_settings,
- .restore_user_od_settings = smu_v14_0_2_restore_user_od_settings,
- .od_edit_dpm_table = smu_v14_0_2_od_edit_dpm_table,
- .init_pptable_microcode = smu_v14_0_init_pptable_microcode,
- .populate_umd_state_clk = smu_v14_0_2_populate_umd_state_clk,
- .set_performance_level = smu_v14_0_set_performance_level,
- .gfx_off_control = smu_v14_0_gfx_off_control,
- .get_unique_id = smu_v14_0_2_get_unique_id,
- .get_power_limit = smu_v14_0_2_get_power_limit,
- .set_power_limit = smu_v14_0_2_set_power_limit,
- .set_power_source = smu_v14_0_set_power_source,
- .get_power_profile_mode = smu_v14_0_2_get_power_profile_mode,
- .set_power_profile_mode = smu_v14_0_2_set_power_profile_mode,
- .run_btc = smu_v14_0_run_btc,
- .get_pp_feature_mask = smu_cmn_get_pp_feature_mask,
- .set_pp_feature_mask = smu_cmn_set_pp_feature_mask,
- .set_tool_table_location = smu_v14_0_set_tool_table_location,
- .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,
- .mode1_reset = smu_v14_0_2_mode1_reset,
- .mode2_reset = smu_v14_0_2_mode2_reset,
- .enable_gfx_features = smu_v14_0_2_enable_gfx_features,
- .set_mp1_state = smu_v14_0_2_set_mp1_state,
- .set_df_cstate = smu_v14_0_2_set_df_cstate,
-#if 0
- .gpo_control = smu_v14_0_gpo_control,
-#endif
-};
-
-void smu_v14_0_2_set_ppt_funcs(struct smu_context *smu)
-{
- smu->ppt_funcs = &smu_v14_0_2_ppt_funcs;
- smu->message_map = smu_v14_0_2_message_map;
- smu->clock_map = smu_v14_0_2_clk_map;
- smu->feature_map = smu_v14_0_2_feature_mask_map;
- smu->table_map = smu_v14_0_2_table_map;
- smu->pwr_src_map = smu_v14_0_2_pwr_src_map;
- smu->workload_map = smu_v14_0_2_workload_map;
- smu_v14_0_2_set_smu_mailbox_registers(smu);
-}
diff --git a/rr-cache/0c1c391f6e92e878a32995cabf64a0f916149ff2/postimage b/rr-cache/0c1c391f6e92e878a32995cabf64a0f916149ff2/postimage
deleted file mode 100644
index 15541932b809..000000000000
--- a/rr-cache/0c1c391f6e92e878a32995cabf64a0f916149ff2/postimage
+++ /dev/null
@@ -1,2104 +0,0 @@
-/*
- * Copyright © 2008 Intel Corporation
- * 2014 Red Hat 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 (including the next
- * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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 <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_fixed.h>
-#include <drm/drm_probe_helper.h>
-
-#include "i915_drv.h"
-#include "i915_reg.h"
-#include "intel_atomic.h"
-#include "intel_audio.h"
-#include "intel_connector.h"
-#include "intel_crtc.h"
-#include "intel_ddi.h"
-#include "intel_de.h"
-#include "intel_display_driver.h"
-#include "intel_display_types.h"
-#include "intel_dp.h"
-#include "intel_dp_hdcp.h"
-#include "intel_dp_mst.h"
-#include "intel_dp_tunnel.h"
-#include "intel_dp_link_training.h"
-#include "intel_dpio_phy.h"
-#include "intel_hdcp.h"
-#include "intel_hotplug.h"
-#include "intel_link_bw.h"
-#include "intel_psr.h"
-#include "intel_vdsc.h"
-#include "skl_scaler.h"
-
-static int intel_dp_mst_max_dpt_bpp(const struct intel_crtc_state *crtc_state,
- bool dsc)
-{
- struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
-
- if (!intel_dp_is_uhbr(crtc_state) || DISPLAY_VER(i915) >= 20 || !dsc)
- return INT_MAX;
-
- /*
- * DSC->DPT interface width:
- * ICL-MTL: 72 bits (each branch has 72 bits, only left branch is used)
- * LNL+: 144 bits (not a bottleneck in any config)
- *
- * Bspec/49259 suggests that the FEC overhead needs to be
- * applied here, though HW people claim that neither this FEC
- * or any other overhead is applicable here (that is the actual
- * available_bw is just symbol_clock * 72). However based on
- * testing on MTL-P the
- * - DELL U3224KBA display
- * - Unigraf UCD-500 CTS test sink
- * devices the
- * - 5120x2880/995.59Mhz
- * - 6016x3384/1357.23Mhz
- * - 6144x3456/1413.39Mhz
- * modes (all the ones having a DPT limit on the above devices),
- * both the channel coding efficiency and an additional 3%
- * overhead needs to be accounted for.
- */
- return div64_u64(mul_u32_u32(intel_dp_link_symbol_clock(crtc_state->port_clock) * 72,
- drm_dp_bw_channel_coding_efficiency(true)),
- mul_u32_u32(adjusted_mode->crtc_clock, 1030000));
-}
-
-static int intel_dp_mst_bw_overhead(const struct intel_crtc_state *crtc_state,
- const struct intel_connector *connector,
- bool ssc, bool dsc, int bpp_x16)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- unsigned long flags = DRM_DP_BW_OVERHEAD_MST;
- int dsc_slice_count = 0;
- int overhead;
-
- flags |= intel_dp_is_uhbr(crtc_state) ? DRM_DP_BW_OVERHEAD_UHBR : 0;
- flags |= ssc ? DRM_DP_BW_OVERHEAD_SSC_REF_CLK : 0;
- flags |= crtc_state->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0;
-
- if (dsc) {
- flags |= DRM_DP_BW_OVERHEAD_DSC;
- dsc_slice_count = intel_dp_dsc_get_slice_count(connector,
- adjusted_mode->clock,
- adjusted_mode->hdisplay,
- crtc_state->joiner_pipes);
- }
-
- overhead = drm_dp_bw_overhead(crtc_state->lane_count,
- adjusted_mode->hdisplay,
- dsc_slice_count,
- bpp_x16,
- flags);
-
- /*
- * TODO: clarify whether a minimum required by the fixed FEC overhead
- * in the bspec audio programming sequence is required here.
- */
- return max(overhead, intel_dp_bw_fec_overhead(crtc_state->fec_enable));
-}
-
-static void intel_dp_mst_compute_m_n(const struct intel_crtc_state *crtc_state,
- const struct intel_connector *connector,
- int overhead,
- int bpp_x16,
- struct intel_link_m_n *m_n)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
-
- /* TODO: Check WA 14013163432 to set data M/N for full BW utilization. */
- intel_link_compute_m_n(bpp_x16, crtc_state->lane_count,
- adjusted_mode->crtc_clock,
- crtc_state->port_clock,
- overhead,
- m_n);
-
- m_n->tu = DIV_ROUND_UP_ULL(mul_u32_u32(m_n->data_m, 64), m_n->data_n);
-}
-
-static int intel_dp_mst_calc_pbn(int pixel_clock, int bpp_x16, int bw_overhead)
-{
- int effective_data_rate =
- intel_dp_effective_data_rate(pixel_clock, bpp_x16, bw_overhead);
-
- /*
- * TODO: Use drm_dp_calc_pbn_mode() instead, once it's converted
- * to calculate PBN with the BW overhead passed to it.
- */
- return DIV_ROUND_UP(effective_data_rate * 64, 54 * 1000);
-}
-
-static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- int max_bpp,
- int min_bpp,
- struct link_config_limits *limits,
- struct drm_connector_state *conn_state,
- int step,
- bool dsc)
-{
- struct drm_atomic_state *state = crtc_state->uapi.state;
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct drm_dp_mst_topology_state *mst_state;
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- int bpp, slots = -EINVAL;
- int max_dpt_bpp;
- int ret = 0;
-
- mst_state = drm_atomic_get_mst_topology_state(state, &intel_dp->mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- crtc_state->lane_count = limits->max_lane_count;
- crtc_state->port_clock = limits->max_rate;
-
- if (dsc) {
- if (!intel_dp_supports_fec(intel_dp, connector, crtc_state))
- return -EINVAL;
-
- crtc_state->fec_enable = !intel_dp_is_uhbr(crtc_state);
- }
-
- mst_state->pbn_div = drm_dp_get_vc_payload_bw(&intel_dp->mst_mgr,
- crtc_state->port_clock,
- crtc_state->lane_count);
-
- max_dpt_bpp = intel_dp_mst_max_dpt_bpp(crtc_state, dsc);
- if (max_bpp > max_dpt_bpp) {
- drm_dbg_kms(&i915->drm, "Limiting bpp to max DPT bpp (%d -> %d)\n",
- max_bpp, max_dpt_bpp);
- max_bpp = max_dpt_bpp;
- }
-
- drm_dbg_kms(&i915->drm, "Looking for slots in range min bpp %d max bpp %d\n",
- min_bpp, max_bpp);
-
- for (bpp = max_bpp; bpp >= min_bpp; bpp -= step) {
- int local_bw_overhead;
- int remote_bw_overhead;
- int link_bpp_x16;
- int remote_tu;
- fixed20_12 pbn;
-
- drm_dbg_kms(&i915->drm, "Trying bpp %d\n", bpp);
-
- link_bpp_x16 = fxp_q4_from_int(dsc ? bpp :
- intel_dp_output_bpp(crtc_state->output_format, bpp));
-
- local_bw_overhead = intel_dp_mst_bw_overhead(crtc_state, connector,
- false, dsc, link_bpp_x16);
- remote_bw_overhead = intel_dp_mst_bw_overhead(crtc_state, connector,
- true, dsc, link_bpp_x16);
-
- intel_dp_mst_compute_m_n(crtc_state, connector,
- local_bw_overhead,
- link_bpp_x16,
- &crtc_state->dp_m_n);
-
- /*
- * The TU size programmed to the HW determines which slots in
- * an MTP frame are used for this stream, which needs to match
- * the payload size programmed to the first downstream branch
- * device's payload table.
- *
- * Note that atm the payload's PBN value DRM core sends via
- * the ALLOCATE_PAYLOAD side-band message matches the payload
- * size (which it calculates from the PBN value) it programs
- * to the first branch device's payload table. The allocation
- * in the payload table could be reduced though (to
- * crtc_state->dp_m_n.tu), provided that the driver doesn't
- * enable SSC on the corresponding link.
- */
- pbn.full = dfixed_const(intel_dp_mst_calc_pbn(adjusted_mode->crtc_clock,
- link_bpp_x16,
- remote_bw_overhead));
- remote_tu = DIV_ROUND_UP(pbn.full, mst_state->pbn_div.full);
-
- /*
- * Aligning the TUs ensures that symbols consisting of multiple
- * (4) symbol cycles don't get split between two consecutive
- * MTPs, as required by Bspec.
- * TODO: remove the alignment restriction for 128b/132b links
- * on some platforms, where Bspec allows this.
- */
- remote_tu = ALIGN(remote_tu, 4 / crtc_state->lane_count);
-
- /*
- * Also align PBNs accordingly, since MST core will derive its
- * own copy of TU from the PBN in drm_dp_atomic_find_time_slots().
- * The above comment about the difference between the PBN
- * allocated for the whole path and the TUs allocated for the
- * first branch device's link also applies here.
- */
- pbn.full = remote_tu * mst_state->pbn_div.full;
- crtc_state->pbn = dfixed_trunc(pbn);
-
- drm_WARN_ON(&i915->drm, remote_tu < crtc_state->dp_m_n.tu);
- crtc_state->dp_m_n.tu = remote_tu;
-
- slots = drm_dp_atomic_find_time_slots(state, &intel_dp->mst_mgr,
- connector->port,
- crtc_state->pbn);
- if (slots == -EDEADLK)
- return slots;
-
- if (slots >= 0) {
- drm_WARN_ON(&i915->drm, slots != crtc_state->dp_m_n.tu);
-
- break;
- }
- }
-
- /* We failed to find a proper bpp/timeslots, return error */
- if (ret)
- slots = ret;
-
- if (slots < 0) {
- drm_dbg_kms(&i915->drm, "failed finding vcpi slots:%d\n",
- slots);
- } else {
- if (!dsc)
- crtc_state->pipe_bpp = bpp;
- else
- crtc_state->dsc.compressed_bpp_x16 = fxp_q4_from_int(bpp);
- drm_dbg_kms(&i915->drm, "Got %d slots for pipe bpp %d dsc %d\n", slots, bpp, dsc);
- }
-
- return slots;
-}
-
-static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state,
- struct link_config_limits *limits)
-{
- int slots = -EINVAL;
-
- /*
- * FIXME: allocate the BW according to link_bpp, which in the case of
- * YUV420 is only half of the pipe bpp value.
- */
- slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state,
- fxp_q4_to_int(limits->link.max_bpp_x16),
- fxp_q4_to_int(limits->link.min_bpp_x16),
- limits,
- conn_state, 2 * 3, false);
-
- if (slots < 0)
- return slots;
-
- return 0;
-}
-
-static int intel_dp_dsc_mst_compute_link_config(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state,
- struct link_config_limits *limits)
-{
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- int slots = -EINVAL;
- int i, num_bpc;
- u8 dsc_bpc[3] = {};
- int min_bpp, max_bpp, sink_min_bpp, sink_max_bpp;
- u8 dsc_max_bpc;
- int min_compressed_bpp, max_compressed_bpp;
-
- /* Max DSC Input BPC for ICL is 10 and for TGL+ is 12 */
- if (DISPLAY_VER(i915) >= 12)
- dsc_max_bpc = min_t(u8, 12, conn_state->max_requested_bpc);
- else
- dsc_max_bpc = min_t(u8, 10, conn_state->max_requested_bpc);
-
- max_bpp = min_t(u8, dsc_max_bpc * 3, limits->pipe.max_bpp);
- min_bpp = limits->pipe.min_bpp;
-
- num_bpc = drm_dp_dsc_sink_supported_input_bpcs(connector->dp.dsc_dpcd,
- dsc_bpc);
-
- drm_dbg_kms(&i915->drm, "DSC Source supported min bpp %d max bpp %d\n",
- min_bpp, max_bpp);
-
- sink_max_bpp = dsc_bpc[0] * 3;
- sink_min_bpp = sink_max_bpp;
-
- for (i = 1; i < num_bpc; i++) {
- if (sink_min_bpp > dsc_bpc[i] * 3)
- sink_min_bpp = dsc_bpc[i] * 3;
- if (sink_max_bpp < dsc_bpc[i] * 3)
- sink_max_bpp = dsc_bpc[i] * 3;
- }
-
- drm_dbg_kms(&i915->drm, "DSC Sink supported min bpp %d max bpp %d\n",
- sink_min_bpp, sink_max_bpp);
-
- if (min_bpp < sink_min_bpp)
- min_bpp = sink_min_bpp;
-
- if (max_bpp > sink_max_bpp)
- max_bpp = sink_max_bpp;
-
- crtc_state->pipe_bpp = max_bpp;
-
- max_compressed_bpp = intel_dp_dsc_sink_max_compressed_bpp(connector,
- crtc_state,
- max_bpp / 3);
- max_compressed_bpp = min(max_compressed_bpp,
- fxp_q4_to_int(limits->link.max_bpp_x16));
-
- min_compressed_bpp = intel_dp_dsc_sink_min_compressed_bpp(crtc_state);
- min_compressed_bpp = max(min_compressed_bpp,
- fxp_q4_to_int_roundup(limits->link.min_bpp_x16));
-
- drm_dbg_kms(&i915->drm, "DSC Sink supported compressed min bpp %d compressed max bpp %d\n",
- min_compressed_bpp, max_compressed_bpp);
-
- /* Align compressed bpps according to our own constraints */
- max_compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, max_compressed_bpp,
- crtc_state->pipe_bpp);
- min_compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, min_compressed_bpp,
- crtc_state->pipe_bpp);
-
- slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state, max_compressed_bpp,
- min_compressed_bpp, limits,
- conn_state, 1, true);
-
- if (slots < 0)
- return slots;
-
- return 0;
-}
-static int intel_dp_mst_update_slots(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct drm_dp_mst_topology_mgr *mgr = &intel_dp->mst_mgr;
- struct drm_dp_mst_topology_state *topology_state;
- u8 link_coding_cap = intel_dp_is_uhbr(crtc_state) ?
- DP_CAP_ANSI_128B132B : DP_CAP_ANSI_8B10B;
-
- topology_state = drm_atomic_get_mst_topology_state(conn_state->state, mgr);
- if (IS_ERR(topology_state)) {
- drm_dbg_kms(&i915->drm, "slot update failed\n");
- return PTR_ERR(topology_state);
- }
-
- drm_dp_mst_update_slots(topology_state, link_coding_cap);
-
- return 0;
-}
-
-static int mode_hblank_period_ns(const struct drm_display_mode *mode)
-{
- return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(mode->htotal - mode->hdisplay,
- NSEC_PER_SEC / 1000),
- mode->crtc_clock);
-}
-
-static bool
-hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector,
- const struct intel_crtc_state *crtc_state,
- const struct link_config_limits *limits)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- bool is_uhbr_sink = connector->mst_port &&
- drm_dp_128b132b_supported(connector->mst_port->dpcd);
- int hblank_limit = is_uhbr_sink ? 500 : 300;
-
- if (!connector->dp.dsc_hblank_expansion_quirk)
- return false;
-
- if (is_uhbr_sink && !drm_dp_is_uhbr_rate(limits->max_rate))
- return false;
-
- if (mode_hblank_period_ns(adjusted_mode) > hblank_limit)
- return false;
-
- return true;
-}
-
-static bool
-adjust_limits_for_dsc_hblank_expansion_quirk(const struct intel_connector *connector,
- const struct intel_crtc_state *crtc_state,
- struct link_config_limits *limits,
- bool dsc)
-{
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- int min_bpp_x16 = limits->link.min_bpp_x16;
-
- if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state, limits))
- return true;
-
- if (!dsc) {
- if (intel_dp_supports_dsc(connector, crtc_state)) {
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] DSC needed by hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name);
- return false;
- }
-
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] Increasing link min bpp to 24 due to hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name);
-
- if (limits->link.max_bpp_x16 < fxp_q4_from_int(24))
- return false;
-
- limits->link.min_bpp_x16 = fxp_q4_from_int(24);
-
- return true;
- }
-
- drm_WARN_ON(&i915->drm, limits->min_rate != limits->max_rate);
-
- if (limits->max_rate < 540000)
- min_bpp_x16 = fxp_q4_from_int(13);
- else if (limits->max_rate < 810000)
- min_bpp_x16 = fxp_q4_from_int(10);
-
- if (limits->link.min_bpp_x16 >= min_bpp_x16)
- return true;
-
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] Increasing link min bpp to " FXP_Q4_FMT " in DSC mode due to hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name,
- FXP_Q4_ARGS(min_bpp_x16));
-
- if (limits->link.max_bpp_x16 < min_bpp_x16)
- return false;
-
- limits->link.min_bpp_x16 = min_bpp_x16;
-
- return true;
-}
-
-static bool
-intel_dp_mst_compute_config_limits(struct intel_dp *intel_dp,
- const struct intel_connector *connector,
- struct intel_crtc_state *crtc_state,
- bool dsc,
- struct link_config_limits *limits)
-{
- /*
- * for MST we always configure max link bw - the spec doesn't
- * seem to suggest we should do otherwise.
- */
- limits->min_rate = limits->max_rate =
- intel_dp_max_link_rate(intel_dp);
-
- limits->min_lane_count = limits->max_lane_count =
- intel_dp_max_lane_count(intel_dp);
-
- limits->pipe.min_bpp = intel_dp_min_bpp(crtc_state->output_format);
- /*
- * FIXME: If all the streams can't fit into the link with
- * their current pipe_bpp we should reduce pipe_bpp across
- * the board until things start to fit. Until then we
- * limit to <= 8bpc since that's what was hardcoded for all
- * MST streams previously. This hack should be removed once
- * we have the proper retry logic in place.
- */
- limits->pipe.max_bpp = min(crtc_state->pipe_bpp, 24);
-
- intel_dp_adjust_compliance_config(intel_dp, crtc_state, limits);
-
- if (!intel_dp_compute_config_link_bpp_limits(intel_dp,
- crtc_state,
- dsc,
- limits))
- return false;
-
- return adjust_limits_for_dsc_hblank_expansion_quirk(connector,
- crtc_state,
- limits,
- dsc);
-}
-
-static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
- struct intel_crtc_state *pipe_config,
- struct drm_connector_state *conn_state)
-{
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- 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_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- const struct drm_display_mode *adjusted_mode =
- &pipe_config->hw.adjusted_mode;
- struct link_config_limits limits;
- bool dsc_needed, joiner_needs_dsc;
- int ret = 0;
-
- if (pipe_config->fec_enable &&
- !intel_dp_supports_fec(intel_dp, connector, pipe_config))
- return -EINVAL;
-
- if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
- return -EINVAL;
-
- if (intel_dp_need_joiner(intel_dp, connector,
- adjusted_mode->crtc_hdisplay,
- adjusted_mode->crtc_clock))
- pipe_config->joiner_pipes = GENMASK(crtc->pipe + 1, crtc->pipe);
-
- pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
- pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
- pipe_config->has_pch_encoder = false;
-
- joiner_needs_dsc = intel_dp_joiner_needs_dsc(dev_priv, pipe_config->joiner_pipes);
-
- dsc_needed = joiner_needs_dsc || intel_dp->force_dsc_en ||
- !intel_dp_mst_compute_config_limits(intel_dp,
- connector,
- pipe_config,
- false,
- &limits);
-
- if (!dsc_needed) {
- ret = intel_dp_mst_compute_link_config(encoder, pipe_config,
- conn_state, &limits);
-
- if (ret == -EDEADLK)
- return ret;
-
- if (ret)
- dsc_needed = true;
- }
-
- /* enable compression if the mode doesn't fit available BW */
- if (dsc_needed) {
- drm_dbg_kms(&dev_priv->drm, "Try DSC (fallback=%s, joiner=%s, force=%s)\n",
- str_yes_no(ret), str_yes_no(joiner_needs_dsc),
- str_yes_no(intel_dp->force_dsc_en));
-
- if (!intel_dp_supports_dsc(connector, pipe_config))
- return -EINVAL;
-
- if (!intel_dp_mst_compute_config_limits(intel_dp,
- connector,
- pipe_config,
- true,
- &limits))
- return -EINVAL;
-
- /*
- * FIXME: As bpc is hardcoded to 8, as mentioned above,
- * WARN and ignore the debug flag force_dsc_bpc for now.
- */
- drm_WARN(&dev_priv->drm, intel_dp->force_dsc_bpc, "Cannot Force BPC for MST\n");
- /*
- * Try to get at least some timeslots and then see, if
- * we can fit there with DSC.
- */
- drm_dbg_kms(&dev_priv->drm, "Trying to find VCPI slots in DSC mode\n");
-
- ret = intel_dp_dsc_mst_compute_link_config(encoder, pipe_config,
- conn_state, &limits);
- if (ret < 0)
- return ret;
-
- ret = intel_dp_dsc_compute_config(intel_dp, pipe_config,
- conn_state, &limits,
- pipe_config->dp_m_n.tu, false);
- }
-
- if (ret)
- return ret;
-
- ret = intel_dp_mst_update_slots(encoder, pipe_config, conn_state);
- if (ret)
- return ret;
-
- pipe_config->limited_color_range =
- intel_dp_limited_color_range(pipe_config, conn_state);
-
- if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv))
- pipe_config->lane_lat_optim_mask =
- bxt_dpio_phy_calc_lane_lat_optim_mask(pipe_config->lane_count);
-
- intel_dp_audio_compute_config(encoder, pipe_config, conn_state);
-
- intel_ddi_compute_min_voltage_level(pipe_config);
-
- intel_psr_compute_config(intel_dp, pipe_config, conn_state);
-
- return intel_dp_tunnel_atomic_compute_stream_bw(state, intel_dp, connector,
- pipe_config);
-}
-
-/*
- * Iterate over all connectors and return a mask of
- * all CPU transcoders streaming over the same DP link.
- */
-static unsigned int
-intel_dp_mst_transcoder_mask(struct intel_atomic_state *state,
- struct intel_dp *mst_port)
-{
- struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- const struct intel_digital_connector_state *conn_state;
- struct intel_connector *connector;
- u8 transcoders = 0;
- int i;
-
- if (DISPLAY_VER(dev_priv) < 12)
- return 0;
-
- for_each_new_intel_connector_in_state(state, connector, conn_state, i) {
- const struct intel_crtc_state *crtc_state;
- struct intel_crtc *crtc;
-
- if (connector->mst_port != mst_port || !conn_state->base.crtc)
- continue;
-
- crtc = to_intel_crtc(conn_state->base.crtc);
- crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
-
- if (!crtc_state->hw.active)
- continue;
-
- transcoders |= BIT(crtc_state->cpu_transcoder);
- }
-
- return transcoders;
-}
-
-static u8 get_pipes_downstream_of_mst_port(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct drm_dp_mst_port *parent_port)
-{
- const struct intel_digital_connector_state *conn_state;
- struct intel_connector *connector;
- u8 mask = 0;
- int i;
-
- for_each_new_intel_connector_in_state(state, connector, conn_state, i) {
- if (!conn_state->base.crtc)
- continue;
-
- if (&connector->mst_port->mst_mgr != mst_mgr)
- continue;
-
- if (connector->port != parent_port &&
- !drm_dp_mst_port_downstream_of_parent(mst_mgr,
- connector->port,
- parent_port))
- continue;
-
- mask |= BIT(to_intel_crtc(conn_state->base.crtc)->pipe);
- }
-
- return mask;
-}
-
-static int intel_dp_mst_check_fec_change(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct intel_link_bw_limits *limits)
-{
- struct drm_i915_private *i915 = to_i915(state->base.dev);
- struct intel_crtc *crtc;
- u8 mst_pipe_mask;
- u8 fec_pipe_mask = 0;
- int ret;
-
- mst_pipe_mask = get_pipes_downstream_of_mst_port(state, mst_mgr, NULL);
-
- for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, mst_pipe_mask) {
- struct intel_crtc_state *crtc_state =
- intel_atomic_get_new_crtc_state(state, crtc);
-
- /* Atomic connector check should've added all the MST CRTCs. */
- if (drm_WARN_ON(&i915->drm, !crtc_state))
- return -EINVAL;
-
- if (crtc_state->fec_enable)
- fec_pipe_mask |= BIT(crtc->pipe);
- }
-
- if (!fec_pipe_mask || mst_pipe_mask == fec_pipe_mask)
- return 0;
-
- limits->force_fec_pipes |= mst_pipe_mask;
-
- ret = intel_modeset_pipes_in_mask_early(state, "MST FEC",
- mst_pipe_mask);
-
- return ret ? : -EAGAIN;
-}
-
-static int intel_dp_mst_check_bw(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct drm_dp_mst_topology_state *mst_state,
- struct intel_link_bw_limits *limits)
-{
- struct drm_dp_mst_port *mst_port;
- u8 mst_port_pipes;
- int ret;
-
- ret = drm_dp_mst_atomic_check_mgr(&state->base, mst_mgr, mst_state, &mst_port);
- if (ret != -ENOSPC)
- return ret;
-
- mst_port_pipes = get_pipes_downstream_of_mst_port(state, mst_mgr, mst_port);
-
- ret = intel_link_bw_reduce_bpp(state, limits,
- mst_port_pipes, "MST link BW");
-
- return ret ? : -EAGAIN;
-}
-
-/**
- * intel_dp_mst_atomic_check_link - check all modeset MST link configuration
- * @state: intel atomic state
- * @limits: link BW limits
- *
- * Check the link configuration for all modeset MST outputs. If the
- * configuration is invalid @limits will be updated if possible to
- * reduce the total BW, after which the configuration for all CRTCs in
- * @state must be recomputed with the updated @limits.
- *
- * Returns:
- * - 0 if the confugration is valid
- * - %-EAGAIN, if the configuration is invalid and @limits got updated
- * with fallback values with which the configuration of all CRTCs in
- * @state must be recomputed
- * - Other negative error, if the configuration is invalid without a
- * fallback possibility, or the check failed for another reason
- */
-int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
- struct intel_link_bw_limits *limits)
-{
- struct drm_dp_mst_topology_mgr *mgr;
- struct drm_dp_mst_topology_state *mst_state;
- int ret;
- int i;
-
- for_each_new_mst_mgr_in_state(&state->base, mgr, mst_state, i) {
- ret = intel_dp_mst_check_fec_change(state, mgr, limits);
- if (ret)
- return ret;
-
- ret = intel_dp_mst_check_bw(state, mgr, mst_state,
- limits);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int intel_dp_mst_compute_config_late(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_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
-
- /* lowest numbered transcoder will be designated master */
- crtc_state->mst_master_transcoder =
- ffs(intel_dp_mst_transcoder_mask(state, intel_dp)) - 1;
-
- return 0;
-}
-
-/*
- * If one of the connectors in a MST stream needs a modeset, mark all CRTCs
- * that shares the same MST stream as mode changed,
- * intel_modeset_pipe_config()+intel_crtc_check_fastset() will take care to do
- * a fastset when possible.
- *
- * On TGL+ this is required since each stream go through a master transcoder,
- * so if the master transcoder needs modeset, all other streams in the
- * topology need a modeset. All platforms need to add the atomic state
- * for all streams in the topology, since a modeset on one may require
- * changing the MST link BW usage of the others, which in turn needs a
- * recomputation of the corresponding CRTC states.
- */
-static int
-intel_dp_mst_atomic_topology_check(struct intel_connector *connector,
- struct intel_atomic_state *state)
-{
- struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- struct drm_connector_list_iter connector_list_iter;
- struct intel_connector *connector_iter;
- int ret = 0;
-
- if (!intel_connector_needs_modeset(state, &connector->base))
- return 0;
-
- drm_connector_list_iter_begin(&dev_priv->drm, &connector_list_iter);
- for_each_intel_connector_iter(connector_iter, &connector_list_iter) {
- struct intel_digital_connector_state *conn_iter_state;
- struct intel_crtc_state *crtc_state;
- struct intel_crtc *crtc;
-
- if (connector_iter->mst_port != connector->mst_port ||
- connector_iter == connector)
- continue;
-
- conn_iter_state = intel_atomic_get_digital_connector_state(state,
- connector_iter);
- if (IS_ERR(conn_iter_state)) {
- ret = PTR_ERR(conn_iter_state);
- break;
- }
-
- if (!conn_iter_state->base.crtc)
- continue;
-
- crtc = to_intel_crtc(conn_iter_state->base.crtc);
- crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- break;
- }
-
- ret = drm_atomic_add_affected_planes(&state->base, &crtc->base);
- if (ret)
- break;
- crtc_state->uapi.mode_changed = true;
- }
- drm_connector_list_iter_end(&connector_list_iter);
-
- return ret;
-}
-
-static int
-intel_dp_mst_atomic_check(struct drm_connector *connector,
- struct drm_atomic_state *_state)
-{
- struct intel_atomic_state *state = to_intel_atomic_state(_state);
- struct intel_connector *intel_connector =
- to_intel_connector(connector);
- int ret;
-
- ret = intel_digital_connector_atomic_check(connector, &state->base);
- if (ret)
- return ret;
-
- ret = intel_dp_mst_atomic_topology_check(intel_connector, state);
- if (ret)
- return ret;
-
- if (intel_connector_needs_modeset(state, connector)) {
- ret = intel_dp_tunnel_atomic_check_state(state,
- intel_connector->mst_port,
- intel_connector);
- if (ret)
- return ret;
- }
-
- return drm_dp_atomic_release_time_slots(&state->base,
- &intel_connector->mst_port->mst_mgr,
- intel_connector->port);
-}
-
-static void clear_act_sent(struct intel_encoder *encoder,
- const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
-
- intel_de_write(i915, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_ACT_SENT);
-}
-
-static void wait_for_act_sent(struct intel_encoder *encoder,
- const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
-
- if (intel_de_wait_for_set(i915, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_ACT_SENT, 1))
- drm_err(&i915->drm, "Timed out waiting for ACT sent\n");
-
- drm_dp_check_act_status(&intel_dp->mst_mgr);
-}
-
-static void intel_mst_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector =
- to_intel_connector(old_conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
-
- drm_dbg_kms(&i915->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- if (intel_dp->active_mst_links == 1)
- intel_dp->link_trained = false;
-
- intel_hdcp_disable(intel_mst->connector);
-
- intel_dp_sink_disable_decompression(state, connector, old_crtc_state);
-}
-
-static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector =
- to_intel_connector(old_conn_state->connector);
- struct drm_dp_mst_topology_state *old_mst_state =
- drm_atomic_get_old_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- struct drm_dp_mst_topology_state *new_mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- const struct drm_dp_mst_atomic_payload *old_payload =
- drm_atomic_get_mst_payload_state(old_mst_state, connector->port);
- struct drm_dp_mst_atomic_payload *new_payload =
- drm_atomic_get_mst_payload_state(new_mst_state, connector->port);
- struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct intel_crtc *pipe_crtc;
- bool last_mst_stream;
-
- intel_dp->active_mst_links--;
- last_mst_stream = intel_dp->active_mst_links == 0;
- drm_WARN_ON(&dev_priv->drm,
- DISPLAY_VER(dev_priv) >= 12 && last_mst_stream &&
- !intel_dp_mst_is_master_trans(old_crtc_state));
-
- for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(old_crtc_state)) {
- const struct intel_crtc_state *old_pipe_crtc_state =
- intel_atomic_get_old_crtc_state(state, pipe_crtc);
-
- intel_crtc_vblank_off(old_pipe_crtc_state);
- }
-
- intel_disable_transcoder(old_crtc_state);
-
- drm_dp_remove_payload_part1(&intel_dp->mst_mgr, new_mst_state, new_payload);
-
- clear_act_sent(encoder, old_crtc_state);
-
- intel_de_rmw(dev_priv,
- TRANS_DDI_FUNC_CTL(dev_priv, old_crtc_state->cpu_transcoder),
- TRANS_DDI_DP_VC_PAYLOAD_ALLOC, 0);
-
- wait_for_act_sent(encoder, old_crtc_state);
-
- drm_dp_remove_payload_part2(&intel_dp->mst_mgr, new_mst_state,
- old_payload, new_payload);
-
- intel_ddi_disable_transcoder_func(old_crtc_state);
-
- for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(old_crtc_state)) {
- const struct intel_crtc_state *old_pipe_crtc_state =
- intel_atomic_get_old_crtc_state(state, pipe_crtc);
-
- intel_dsc_disable(old_pipe_crtc_state);
-
- if (DISPLAY_VER(dev_priv) >= 9)
- skl_scaler_disable(old_pipe_crtc_state);
- else
- ilk_pfit_disable(old_pipe_crtc_state);
- }
-
- /*
- * Power down mst path before disabling the port, otherwise we end
- * up getting interrupts from the sink upon detecting link loss.
- */
- drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port,
- false);
-
- /*
- * BSpec 4287: disable DIP after the transcoder is disabled and before
- * the transcoder clock select is set to none.
- */
- intel_dp_set_infoframes(&dig_port->base, false,
- old_crtc_state, NULL);
- /*
- * From TGL spec: "If multi-stream slave transcoder: Configure
- * Transcoder Clock Select to direct no clock to the transcoder"
- *
- * From older GENs spec: "Configure Transcoder Clock Select to direct
- * no clock to the transcoder"
- */
- if (DISPLAY_VER(dev_priv) < 12 || !last_mst_stream)
- intel_ddi_disable_transcoder_clock(old_crtc_state);
-
-
- intel_mst->connector = NULL;
- if (last_mst_stream)
- dig_port->base.post_disable(state, &dig_port->base,
- old_crtc_state, NULL);
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-}
-
-static void intel_mst_post_pll_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (intel_dp->active_mst_links == 0 &&
- dig_port->base.post_pll_disable)
- dig_port->base.post_pll_disable(state, encoder, old_crtc_state, old_conn_state);
-}
-
-static void intel_mst_pre_pll_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (intel_dp->active_mst_links == 0)
- dig_port->base.pre_pll_enable(state, &dig_port->base,
- pipe_config, NULL);
- else
- /*
- * The port PLL state needs to get updated for secondary
- * streams as for the primary stream.
- */
- intel_ddi_update_active_dpll(state, &dig_port->base,
- to_intel_crtc(pipe_config->uapi.crtc));
-}
-
-static bool intel_mst_probed_link_params_valid(struct intel_dp *intel_dp,
- int link_rate, int lane_count)
-{
- return intel_dp->link.mst_probed_rate == link_rate &&
- intel_dp->link.mst_probed_lane_count == lane_count;
-}
-
-static void intel_mst_set_probed_link_params(struct intel_dp *intel_dp,
- int link_rate, int lane_count)
-{
- intel_dp->link.mst_probed_rate = link_rate;
- intel_dp->link.mst_probed_lane_count = lane_count;
-}
-
-static void intel_mst_reprobe_topology(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state)
-{
- if (intel_mst_probed_link_params_valid(intel_dp,
- crtc_state->port_clock, crtc_state->lane_count))
- return;
-
- drm_dp_mst_topology_queue_probe(&intel_dp->mst_mgr);
-
- intel_mst_set_probed_link_params(intel_dp,
- crtc_state->port_clock, crtc_state->lane_count);
-}
-
-static void intel_mst_pre_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_dp_mst_topology_state *mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- int ret;
- bool first_mst_stream;
-
- /* MST encoders are bound to a crtc, not to a connector,
- * force the mapping here for get_hw_state.
- */
- connector->encoder = encoder;
- intel_mst->connector = connector;
- first_mst_stream = intel_dp->active_mst_links == 0;
- drm_WARN_ON(&dev_priv->drm,
- DISPLAY_VER(dev_priv) >= 12 && first_mst_stream &&
- !intel_dp_mst_is_master_trans(pipe_config));
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- if (first_mst_stream)
- intel_dp_set_power(intel_dp, DP_SET_POWER_D0);
-
- drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true);
-
- intel_dp_sink_enable_decompression(state, connector, pipe_config);
-
- if (first_mst_stream) {
- dig_port->base.pre_enable(state, &dig_port->base,
- pipe_config, NULL);
-
- intel_mst_reprobe_topology(intel_dp, pipe_config);
- }
-
- intel_dp->active_mst_links++;
-
- ret = drm_dp_add_payload_part1(&intel_dp->mst_mgr, mst_state,
- drm_atomic_get_mst_payload_state(mst_state, connector->port));
- if (ret < 0)
- intel_dp_queue_modeset_retry_for_link(state, &dig_port->base, pipe_config);
-
- /*
- * Before Gen 12 this is not done as part of
- * dig_port->base.pre_enable() and should be done here. For
- * Gen 12+ the step in which this should be done is different for the
- * first MST stream, so it's done on the DDI for the first stream and
- * here for the following ones.
- */
- if (DISPLAY_VER(dev_priv) < 12 || !first_mst_stream)
- intel_ddi_enable_transcoder_clock(encoder, pipe_config);
-
- intel_dsc_dp_pps_write(&dig_port->base, pipe_config);
- intel_ddi_set_dp_msa(pipe_config, conn_state);
-}
-
-static void enable_bs_jitter_was(const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
- u32 clear = 0;
- u32 set = 0;
-
- if (!IS_ALDERLAKE_P(i915))
- return;
-
- if (!IS_DISPLAY_STEP(i915, STEP_D0, STEP_FOREVER))
- return;
-
- /* Wa_14013163432:adlp */
- if (crtc_state->fec_enable || intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_FEC_BS_JITTER_WA(crtc_state->cpu_transcoder);
-
- /* Wa_14014143976:adlp */
- if (IS_DISPLAY_STEP(i915, STEP_E0, STEP_FOREVER)) {
- if (intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_SHORT_HBLANK_WA(crtc_state->cpu_transcoder);
- else if (crtc_state->fec_enable)
- clear |= DP_MST_SHORT_HBLANK_WA(crtc_state->cpu_transcoder);
-
- if (crtc_state->fec_enable || intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_DPT_DPTP_ALIGN_WA(crtc_state->cpu_transcoder);
- }
-
- if (!clear && !set)
- return;
-
- intel_de_rmw(i915, CHICKEN_MISC_3, clear, set);
-}
-
-static void intel_mst_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector = to_intel_connector(conn_state->connector);
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct drm_dp_mst_topology_state *mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- enum transcoder trans = pipe_config->cpu_transcoder;
- bool first_mst_stream = intel_dp->active_mst_links == 1;
- struct intel_crtc *pipe_crtc;
- int ret;
-
- drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder);
-
- if (intel_dp_is_uhbr(pipe_config)) {
- const struct drm_display_mode *adjusted_mode =
- &pipe_config->hw.adjusted_mode;
- u64 crtc_clock_hz = KHz(adjusted_mode->crtc_clock);
-
- intel_de_write(dev_priv, TRANS_DP2_VFREQHIGH(pipe_config->cpu_transcoder),
- TRANS_DP2_VFREQ_PIXEL_CLOCK(crtc_clock_hz >> 24));
- intel_de_write(dev_priv, TRANS_DP2_VFREQLOW(pipe_config->cpu_transcoder),
- TRANS_DP2_VFREQ_PIXEL_CLOCK(crtc_clock_hz & 0xffffff));
- }
-
- enable_bs_jitter_was(pipe_config);
-
- intel_ddi_enable_transcoder_func(encoder, pipe_config);
-
- clear_act_sent(encoder, pipe_config);
-
- intel_de_rmw(dev_priv, TRANS_DDI_FUNC_CTL(dev_priv, trans), 0,
- TRANS_DDI_DP_VC_PAYLOAD_ALLOC);
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- wait_for_act_sent(encoder, pipe_config);
-
- if (first_mst_stream)
- intel_ddi_wait_for_fec_status(encoder, pipe_config, true);
-
- ret = drm_dp_add_payload_part2(&intel_dp->mst_mgr,
- drm_atomic_get_mst_payload_state(mst_state,
- connector->port));
- if (ret < 0)
- intel_dp_queue_modeset_retry_for_link(state, &dig_port->base, pipe_config);
-
- if (DISPLAY_VER(dev_priv) >= 12)
- intel_de_rmw(dev_priv, hsw_chicken_trans_reg(dev_priv, trans),
- FECSTALL_DIS_DPTSTREAM_DPTTG,
- pipe_config->fec_enable ? FECSTALL_DIS_DPTSTREAM_DPTTG : 0);
-
- intel_audio_sdp_split_update(pipe_config);
-
- intel_enable_transcoder(pipe_config);
-
- for_each_intel_crtc_in_pipe_mask_reverse(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(pipe_config)) {
- const struct intel_crtc_state *pipe_crtc_state =
- intel_atomic_get_new_crtc_state(state, pipe_crtc);
-
- intel_crtc_vblank_on(pipe_crtc_state);
- }
-
- intel_hdcp_enable(state, encoder, pipe_config, conn_state);
-}
-
-static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
- enum pipe *pipe)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- *pipe = intel_mst->pipe;
- if (intel_mst->connector)
- return true;
- return false;
-}
-
-static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
- struct intel_crtc_state *pipe_config)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
-
- dig_port->base.get_config(&dig_port->base, pipe_config);
-}
-
-static bool intel_dp_mst_initial_fastset_check(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
-
- return intel_dp_initial_fastset_check(&dig_port->base, crtc_state);
-}
-
-static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct drm_i915_private *i915 = to_i915(intel_connector->base.dev);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- const struct drm_edid *drm_edid;
- int ret;
-
- if (drm_connector_is_unregistered(connector))
- return intel_connector_update_modes(connector, NULL);
-
- if (!intel_display_driver_check_access(i915))
- return drm_edid_connector_add_modes(connector);
-
- drm_edid = drm_dp_mst_edid_read(connector, &intel_dp->mst_mgr, intel_connector->port);
-
- ret = intel_connector_update_modes(connector, drm_edid);
-
- drm_edid_free(drm_edid);
-
- return ret;
-}
-
-static int
-intel_dp_mst_connector_late_register(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
- int ret;
-
- ret = drm_dp_mst_connector_late_register(connector,
- intel_connector->port);
- if (ret < 0)
- return ret;
-
- ret = intel_connector_register(connector);
- if (ret < 0)
- drm_dp_mst_connector_early_unregister(connector,
- intel_connector->port);
-
- return ret;
-}
-
-static void
-intel_dp_mst_connector_early_unregister(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
-
- intel_connector_unregister(connector);
- drm_dp_mst_connector_early_unregister(connector,
- intel_connector->port);
-}
-
-static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
- .fill_modes = drm_helper_probe_single_connector_modes,
- .atomic_get_property = intel_digital_connector_atomic_get_property,
- .atomic_set_property = intel_digital_connector_atomic_set_property,
- .late_register = intel_dp_mst_connector_late_register,
- .early_unregister = intel_dp_mst_connector_early_unregister,
- .destroy = intel_connector_destroy,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = intel_digital_connector_duplicate_state,
-};
-
-static int intel_dp_mst_get_modes(struct drm_connector *connector)
-{
- return intel_dp_mst_get_ddc_modes(connector);
-}
-
-static int
-intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
- struct drm_display_mode *mode,
- struct drm_modeset_acquire_ctx *ctx,
- enum drm_mode_status *status)
-{
- struct drm_i915_private *dev_priv = to_i915(connector->dev);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- struct drm_dp_mst_topology_mgr *mgr = &intel_dp->mst_mgr;
- struct drm_dp_mst_port *port = intel_connector->port;
- const int min_bpp = 18;
- int max_dotclk = to_i915(connector->dev)->display.cdclk.max_dotclk_freq;
- int max_rate, mode_rate, max_lanes, max_link_clock;
- int ret;
- bool dsc = false, joiner = false;
- u16 dsc_max_compressed_bpp = 0;
- u8 dsc_slice_count = 0;
- int target_clock = mode->clock;
-
- if (drm_connector_is_unregistered(connector)) {
- *status = MODE_ERROR;
- return 0;
- }
-
- *status = intel_cpu_transcoder_mode_valid(dev_priv, mode);
- if (*status != MODE_OK)
- return 0;
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
- *status = MODE_H_ILLEGAL;
- return 0;
- }
-
- if (mode->clock < 10000) {
- *status = MODE_CLOCK_LOW;
- return 0;
- }
-
- max_link_clock = intel_dp_max_link_rate(intel_dp);
- max_lanes = intel_dp_max_lane_count(intel_dp);
-
- max_rate = intel_dp_max_link_data_rate(intel_dp,
- max_link_clock, max_lanes);
- mode_rate = intel_dp_link_required(mode->clock, min_bpp);
-
- /*
- * TODO:
- * - Also check if compression would allow for the mode
- * - Calculate the overhead using drm_dp_bw_overhead() /
- * drm_dp_bw_channel_coding_efficiency(), similarly to the
- * compute config code, as drm_dp_calc_pbn_mode() doesn't
- * account with all the overheads.
- * - Check here and during compute config the BW reported by
- * DFP_Link_Available_Payload_Bandwidth_Number (or the
- * corresponding link capabilities of the sink) in case the
- * stream is uncompressed for it by the last branch device.
- */
- if (intel_dp_need_joiner(intel_dp, intel_connector,
- mode->hdisplay, target_clock)) {
- joiner = true;
- max_dotclk *= 2;
- }
-
- ret = drm_modeset_lock(&mgr->base.lock, ctx);
- if (ret)
- return ret;
-
- if (mode_rate > max_rate || mode->clock > max_dotclk ||
- drm_dp_calc_pbn_mode(mode->clock, min_bpp << 4) > port->full_pbn) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- if (intel_dp_has_dsc(intel_connector)) {
- /*
- * TBD pass the connector BPC,
- * for now U8_MAX so that max BPC on that platform would be picked
- */
- int pipe_bpp = intel_dp_dsc_compute_max_bpp(intel_connector, U8_MAX);
-
- if (drm_dp_sink_supports_fec(intel_connector->dp.fec_capability)) {
- dsc_max_compressed_bpp =
- intel_dp_dsc_get_max_compressed_bpp(dev_priv,
- max_link_clock,
- max_lanes,
- target_clock,
- mode->hdisplay,
- joiner,
- INTEL_OUTPUT_FORMAT_RGB,
- pipe_bpp, 64);
- dsc_slice_count =
- intel_dp_dsc_get_slice_count(intel_connector,
- target_clock,
- mode->hdisplay,
- joiner);
- }
-
- dsc = dsc_max_compressed_bpp && dsc_slice_count;
- }
-
- if (intel_dp_joiner_needs_dsc(dev_priv, joiner) && !dsc) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- if (mode_rate > max_rate && !dsc) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- *status = intel_mode_valid_max_plane_size(dev_priv, mode, joiner);
- return 0;
-}
-
-static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector,
- struct drm_atomic_state *state)
-{
- struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
- connector);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- struct intel_crtc *crtc = to_intel_crtc(connector_state->crtc);
-
- return &intel_dp->mst_encoders[crtc->pipe]->base.base;
-}
-
-static int
-intel_dp_mst_detect(struct drm_connector *connector,
- struct drm_modeset_acquire_ctx *ctx, bool force)
-{
- struct drm_i915_private *i915 = to_i915(connector->dev);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
-
- if (!intel_display_device_enabled(i915))
- return connector_status_disconnected;
-
- if (drm_connector_is_unregistered(connector))
- return connector_status_disconnected;
-
- if (!intel_display_driver_check_access(i915))
- return connector->status;
-
- return drm_dp_mst_detect_port(connector, ctx, &intel_dp->mst_mgr,
- intel_connector->port);
-}
-
-static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
- .get_modes = intel_dp_mst_get_modes,
- .mode_valid_ctx = intel_dp_mst_mode_valid_ctx,
- .atomic_best_encoder = intel_mst_atomic_best_encoder,
- .atomic_check = intel_dp_mst_atomic_check,
- .detect_ctx = intel_dp_mst_detect,
-};
-
-static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(to_intel_encoder(encoder));
-
- drm_encoder_cleanup(encoder);
- kfree(intel_mst);
-}
-
-static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
- .destroy = intel_dp_mst_encoder_destroy,
-};
-
-static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
-{
- if (intel_attached_encoder(connector) && connector->base.state->crtc) {
- enum pipe pipe;
- if (!intel_attached_encoder(connector)->get_hw_state(intel_attached_encoder(connector), &pipe))
- return false;
- return true;
- }
- return false;
-}
-
-static int intel_dp_mst_add_properties(struct intel_dp *intel_dp,
- struct drm_connector *connector,
- const char *pathprop)
-{
- struct drm_i915_private *i915 = to_i915(connector->dev);
-
- drm_object_attach_property(&connector->base,
- i915->drm.mode_config.path_property, 0);
- drm_object_attach_property(&connector->base,
- i915->drm.mode_config.tile_property, 0);
-
- intel_attach_force_audio_property(connector);
- intel_attach_broadcast_rgb_property(connector);
-
- /*
- * Reuse the prop from the SST connector because we're
- * not allowed to create new props after device registration.
- */
- connector->max_bpc_property =
- intel_dp->attached_connector->base.max_bpc_property;
- if (connector->max_bpc_property)
- drm_connector_attach_max_bpc_property(connector, 6, 12);
-
- return drm_connector_set_path_property(connector, pathprop);
-}
-
-static void
-intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp,
- struct intel_connector *connector)
-{
- u8 dpcd_caps[DP_RECEIVER_CAP_SIZE];
-
- if (!connector->dp.dsc_decompression_aux)
- return;
-
- if (drm_dp_read_dpcd_caps(connector->dp.dsc_decompression_aux, dpcd_caps) < 0)
- return;
-
- intel_dp_get_dsc_sink_cap(dpcd_caps[DP_DPCD_REV], connector);
-}
-
-static bool detect_dsc_hblank_expansion_quirk(const struct intel_connector *connector)
-{
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- struct drm_dp_aux *aux = connector->dp.dsc_decompression_aux;
- struct drm_dp_desc desc;
- u8 dpcd[DP_RECEIVER_CAP_SIZE];
-
- if (!aux)
- return false;
-
- /*
- * A logical port's OUI (at least for affected sinks) is all 0, so
- * instead of that the parent port's OUI is used for identification.
- */
- if (drm_dp_mst_port_is_logical(connector->port)) {
- aux = drm_dp_mst_aux_for_parent(connector->port);
- if (!aux)
- aux = &connector->mst_port->aux;
- }
-
- if (drm_dp_read_dpcd_caps(aux, dpcd) < 0)
- return false;
-
- if (drm_dp_read_desc(aux, &desc, drm_dp_is_branch(dpcd)) < 0)
- return false;
-
- if (!drm_dp_has_quirk(&desc,
- DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC))
- return false;
-
- /*
- * UHBR (MST sink) devices requiring this quirk don't advertise the
- * HBLANK expansion support. Presuming that they perform HBLANK
- * expansion internally, or are affected by this issue on modes with a
- * short HBLANK for other reasons.
- */
- if (!drm_dp_128b132b_supported(dpcd) &&
- !(dpcd[DP_RECEIVE_PORT_0_CAP_0] & DP_HBLANK_EXPANSION_CAPABLE))
- return false;
-
- drm_dbg_kms(&i915->drm,
- "[CONNECTOR:%d:%s] DSC HBLANK expansion quirk detected\n",
- connector->base.base.id, connector->base.name);
-
- return true;
-}
-
-static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port,
- const char *pathprop)
-{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct drm_device *dev = dig_port->base.base.dev;
- struct drm_i915_private *dev_priv = to_i915(dev);
- struct intel_connector *intel_connector;
- struct drm_connector *connector;
- enum pipe pipe;
- int ret;
-
- intel_connector = intel_connector_alloc();
- if (!intel_connector)
- return NULL;
-
- intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
- intel_connector->sync_state = intel_dp_connector_sync_state;
- intel_connector->mst_port = intel_dp;
- intel_connector->port = port;
- drm_dp_mst_get_port_malloc(port);
-
- intel_dp_init_modeset_retry_work(intel_connector);
-
- intel_connector->dp.dsc_decompression_aux = drm_dp_mst_dsc_aux_for_port(port);
- intel_dp_mst_read_decompression_port_dsc_caps(intel_dp, intel_connector);
- intel_connector->dp.dsc_hblank_expansion_quirk =
- detect_dsc_hblank_expansion_quirk(intel_connector);
-
- connector = &intel_connector->base;
- ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs,
- DRM_MODE_CONNECTOR_DisplayPort);
- if (ret) {
- drm_dp_mst_put_port_malloc(port);
- intel_connector_free(intel_connector);
- return NULL;
- }
-
- drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
-
- for_each_pipe(dev_priv, pipe) {
- struct drm_encoder *enc =
- &intel_dp->mst_encoders[pipe]->base.base;
-
- ret = drm_connector_attach_encoder(&intel_connector->base, enc);
- if (ret)
- goto err;
- }
-
- ret = intel_dp_mst_add_properties(intel_dp, connector, pathprop);
- if (ret)
- goto err;
-
- ret = intel_dp_hdcp_init(dig_port, intel_connector);
- if (ret)
- drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP MST init failed, skipping.\n",
- connector->name, connector->base.id);
-
- return connector;
-
-err:
- drm_connector_cleanup(connector);
- return NULL;
-}
-
-static void
-intel_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr)
-{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
-
- intel_hpd_trigger_irq(dp_to_dig_port(intel_dp));
-}
-
-static const struct drm_dp_mst_topology_cbs mst_cbs = {
- .add_connector = intel_dp_add_mst_connector,
- .poll_hpd_irq = intel_dp_mst_poll_hpd_irq,
-};
-
-static struct intel_dp_mst_encoder *
-intel_dp_create_fake_mst_encoder(struct intel_digital_port *dig_port, enum pipe pipe)
-{
- struct intel_dp_mst_encoder *intel_mst;
- struct intel_encoder *intel_encoder;
- struct drm_device *dev = dig_port->base.base.dev;
-
- intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
-
- if (!intel_mst)
- return NULL;
-
- intel_mst->pipe = pipe;
- intel_encoder = &intel_mst->base;
- intel_mst->primary = dig_port;
-
- drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
- DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe));
-
- intel_encoder->type = INTEL_OUTPUT_DP_MST;
- intel_encoder->power_domain = dig_port->base.power_domain;
- intel_encoder->port = dig_port->base.port;
- intel_encoder->cloneable = 0;
- /*
- * This is wrong, but broken userspace uses the intersection
- * of possible_crtcs of all the encoders of a given connector
- * to figure out which crtcs can drive said connector. What
- * should be used instead is the union of possible_crtcs.
- * To keep such userspace functioning we must misconfigure
- * this to make sure the intersection is not empty :(
- */
- intel_encoder->pipe_mask = ~0;
-
- intel_encoder->compute_config = intel_dp_mst_compute_config;
- intel_encoder->compute_config_late = intel_dp_mst_compute_config_late;
- intel_encoder->disable = intel_mst_disable_dp;
- intel_encoder->post_disable = intel_mst_post_disable_dp;
- intel_encoder->post_pll_disable = intel_mst_post_pll_disable_dp;
- intel_encoder->update_pipe = intel_ddi_update_pipe;
- intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp;
- intel_encoder->pre_enable = intel_mst_pre_enable_dp;
- intel_encoder->enable = intel_mst_enable_dp;
- intel_encoder->audio_enable = intel_audio_codec_enable;
- intel_encoder->audio_disable = intel_audio_codec_disable;
- intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
- intel_encoder->get_config = intel_dp_mst_enc_get_config;
- intel_encoder->initial_fastset_check = intel_dp_mst_initial_fastset_check;
-
- return intel_mst;
-
-}
-
-static bool
-intel_dp_create_fake_mst_encoders(struct intel_digital_port *dig_port)
-{
- struct intel_dp *intel_dp = &dig_port->dp;
- struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
- enum pipe pipe;
-
- for_each_pipe(dev_priv, pipe)
- intel_dp->mst_encoders[pipe] = intel_dp_create_fake_mst_encoder(dig_port, pipe);
- return true;
-}
-
-int
-intel_dp_mst_encoder_active_links(struct intel_digital_port *dig_port)
-{
- return dig_port->dp.active_mst_links;
-}
-
-int
-intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_base_id)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- struct intel_dp *intel_dp = &dig_port->dp;
- enum port port = dig_port->base.port;
- int ret;
-
- if (!HAS_DP_MST(i915) || intel_dp_is_edp(intel_dp))
- return 0;
-
- if (DISPLAY_VER(i915) < 12 && port == PORT_A)
- return 0;
-
- if (DISPLAY_VER(i915) < 11 && port == PORT_E)
- return 0;
-
- intel_dp->mst_mgr.cbs = &mst_cbs;
-
- /* create encoders */
- intel_dp_create_fake_mst_encoders(dig_port);
- ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, &i915->drm,
- &intel_dp->aux, 16, 3, conn_base_id);
- if (ret) {
- intel_dp->mst_mgr.cbs = NULL;
- return ret;
- }
-
- return 0;
-}
-
-bool intel_dp_mst_source_support(struct intel_dp *intel_dp)
-{
- return intel_dp->mst_mgr.cbs;
-}
-
-void
-intel_dp_mst_encoder_cleanup(struct intel_digital_port *dig_port)
-{
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (!intel_dp_mst_source_support(intel_dp))
- return;
-
- drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
- /* encoders will get killed by normal cleanup */
-
- intel_dp->mst_mgr.cbs = NULL;
-}
-
-bool intel_dp_mst_is_master_trans(const struct intel_crtc_state *crtc_state)
-{
- return crtc_state->mst_master_transcoder == crtc_state->cpu_transcoder;
-}
-
-bool intel_dp_mst_is_slave_trans(const struct intel_crtc_state *crtc_state)
-{
- return crtc_state->mst_master_transcoder != INVALID_TRANSCODER &&
- crtc_state->mst_master_transcoder != crtc_state->cpu_transcoder;
-}
-
-/**
- * intel_dp_mst_add_topology_state_for_connector - add MST topology state for a connector
- * @state: atomic state
- * @connector: connector to add the state for
- * @crtc: the CRTC @connector is attached to
- *
- * Add the MST topology state for @connector to @state.
- *
- * Returns 0 on success, negative error code on failure.
- */
-static int
-intel_dp_mst_add_topology_state_for_connector(struct intel_atomic_state *state,
- struct intel_connector *connector,
- struct intel_crtc *crtc)
-{
- struct drm_dp_mst_topology_state *mst_state;
-
- if (!connector->mst_port)
- return 0;
-
- mst_state = drm_atomic_get_mst_topology_state(&state->base,
- &connector->mst_port->mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- mst_state->pending_crtc_mask |= drm_crtc_mask(&crtc->base);
-
- return 0;
-}
-
-/**
- * intel_dp_mst_add_topology_state_for_crtc - add MST topology state for a CRTC
- * @state: atomic state
- * @crtc: CRTC to add the state for
- *
- * Add the MST topology state for @crtc to @state.
- *
- * Returns 0 on success, negative error code on failure.
- */
-int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state *state,
- struct intel_crtc *crtc)
-{
- struct drm_connector *_connector;
- struct drm_connector_state *conn_state;
- int i;
-
- for_each_new_connector_in_state(&state->base, _connector, conn_state, i) {
- struct intel_connector *connector = to_intel_connector(_connector);
- int ret;
-
- if (conn_state->crtc != &crtc->base)
- continue;
-
- ret = intel_dp_mst_add_topology_state_for_connector(state, connector, crtc);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static struct intel_connector *
-get_connector_in_state_for_crtc(struct intel_atomic_state *state,
- const struct intel_crtc *crtc)
-{
- struct drm_connector_state *old_conn_state;
- struct drm_connector_state *new_conn_state;
- struct drm_connector *_connector;
- int i;
-
- for_each_oldnew_connector_in_state(&state->base, _connector,
- old_conn_state, new_conn_state, i) {
- struct intel_connector *connector =
- to_intel_connector(_connector);
-
- if (old_conn_state->crtc == &crtc->base ||
- new_conn_state->crtc == &crtc->base)
- return connector;
- }
-
- return NULL;
-}
-
-/**
- * intel_dp_mst_crtc_needs_modeset - check if changes in topology need to modeset the given CRTC
- * @state: atomic state
- * @crtc: CRTC for which to check the modeset requirement
- *
- * Check if any change in a MST topology requires a forced modeset on @crtc in
- * this topology. One such change is enabling/disabling the DSC decompression
- * state in the first branch device's UFP DPCD as required by one CRTC, while
- * the other @crtc in the same topology is still active, requiring a full modeset
- * on @crtc.
- */
-bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
- struct intel_crtc *crtc)
-{
- const struct intel_connector *crtc_connector;
- const struct drm_connector_state *conn_state;
- const struct drm_connector *_connector;
- int i;
-
- if (!intel_crtc_has_type(intel_atomic_get_new_crtc_state(state, crtc),
- INTEL_OUTPUT_DP_MST))
- return false;
-
- crtc_connector = get_connector_in_state_for_crtc(state, crtc);
-
- if (!crtc_connector)
- /* None of the connectors in the topology needs modeset */
- return false;
-
- for_each_new_connector_in_state(&state->base, _connector, conn_state, i) {
- const struct intel_connector *connector =
- to_intel_connector(_connector);
- const struct intel_crtc_state *new_crtc_state;
- const struct intel_crtc_state *old_crtc_state;
- struct intel_crtc *crtc_iter;
-
- if (connector->mst_port != crtc_connector->mst_port ||
- !conn_state->crtc)
- continue;
-
- crtc_iter = to_intel_crtc(conn_state->crtc);
-
- new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc_iter);
- old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc_iter);
-
- if (!intel_crtc_needs_modeset(new_crtc_state))
- continue;
-
- if (old_crtc_state->dsc.compression_enable ==
- new_crtc_state->dsc.compression_enable)
- continue;
- /*
- * Toggling the decompression flag because of this stream in
- * the first downstream branch device's UFP DPCD may reset the
- * whole branch device. To avoid the reset while other streams
- * are also active modeset the whole MST topology in this
- * case.
- */
- if (connector->dp.dsc_decompression_aux ==
- &connector->mst_port->aux)
- return true;
- }
-
- return false;
-}
-
-/**
- * intel_dp_mst_prepare_probe - Prepare an MST link for topology probing
- * @intel_dp: DP port object
- *
- * Prepare an MST link for topology probing, programming the target
- * link parameters to DPCD. This step is a requirement of the enumaration
- * of path resources during probing.
- */
-void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp)
-{
- int link_rate = intel_dp_max_link_rate(intel_dp);
- int lane_count = intel_dp_max_lane_count(intel_dp);
- u8 rate_select;
- u8 link_bw;
-
- if (intel_dp->link_trained)
- return;
-
- if (intel_mst_probed_link_params_valid(intel_dp, link_rate, lane_count))
- return;
-
- intel_dp_compute_rate(intel_dp, link_rate, &link_bw, &rate_select);
-
- intel_dp_link_training_set_mode(intel_dp, link_rate, false);
- intel_dp_link_training_set_bw(intel_dp, link_bw, rate_select, lane_count,
- drm_dp_enhanced_frame_cap(intel_dp->dpcd));
-
- intel_mst_set_probed_link_params(intel_dp, link_rate, lane_count);
-}
-
-/*
- * intel_dp_mst_verify_dpcd_state - verify the MST SW enabled state wrt. the DPCD
- * @intel_dp: DP port object
- *
- * Verify if @intel_dp's MST enabled SW state matches the corresponding DPCD
- * state. A long HPD pulse - not long enough to be detected as a disconnected
- * state - could've reset the DPCD state, which requires tearing
- * down/recreating the MST topology.
- *
- * Returns %true if the SW MST enabled and DPCD states match, %false
- * otherwise.
- */
-bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp)
-{
- struct intel_display *display = to_intel_display(intel_dp);
- struct intel_connector *connector = intel_dp->attached_connector;
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct intel_encoder *encoder = &dig_port->base;
- int ret;
- u8 val;
-
- if (!intel_dp->is_mst)
- return true;
-
- ret = drm_dp_dpcd_readb(intel_dp->mst_mgr.aux, DP_MSTM_CTRL, &val);
-
- /* Adjust the expected register value for SST + SideBand. */
- if (ret < 0 || val != (DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC)) {
- drm_dbg_kms(display->drm,
- "[CONNECTOR:%d:%s][ENCODER:%d:%s] MST mode got reset, removing topology (ret=%d, ctrl=0x%02x)\n",
- connector->base.base.id, connector->base.name,
- encoder->base.base.id, encoder->base.name,
- ret, val);
-
- return false;
- }
-
- return true;
-}
diff --git a/rr-cache/0c1c391f6e92e878a32995cabf64a0f916149ff2/preimage b/rr-cache/0c1c391f6e92e878a32995cabf64a0f916149ff2/preimage
deleted file mode 100644
index 7583b460ecc8..000000000000
--- a/rr-cache/0c1c391f6e92e878a32995cabf64a0f916149ff2/preimage
+++ /dev/null
@@ -1,2105 +0,0 @@
-/*
- * Copyright © 2008 Intel Corporation
- * 2014 Red Hat 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 (including the next
- * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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 <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_fixed.h>
-#include <drm/drm_probe_helper.h>
-
-#include "i915_drv.h"
-#include "i915_reg.h"
-#include "intel_atomic.h"
-#include "intel_audio.h"
-#include "intel_connector.h"
-#include "intel_crtc.h"
-#include "intel_ddi.h"
-#include "intel_de.h"
-#include "intel_display_driver.h"
-#include "intel_display_types.h"
-#include "intel_dp.h"
-#include "intel_dp_hdcp.h"
-#include "intel_dp_mst.h"
-#include "intel_dp_tunnel.h"
-#include "intel_dp_link_training.h"
-#include "intel_dpio_phy.h"
-#include "intel_hdcp.h"
-#include "intel_hotplug.h"
-#include "intel_link_bw.h"
-#include "intel_psr.h"
-#include "intel_vdsc.h"
-#include "skl_scaler.h"
-
-static int intel_dp_mst_max_dpt_bpp(const struct intel_crtc_state *crtc_state,
- bool dsc)
-{
- struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
-
- if (!intel_dp_is_uhbr(crtc_state) || DISPLAY_VER(i915) >= 20 || !dsc)
- return INT_MAX;
-
- /*
- * DSC->DPT interface width:
- * ICL-MTL: 72 bits (each branch has 72 bits, only left branch is used)
- * LNL+: 144 bits (not a bottleneck in any config)
- *
- * Bspec/49259 suggests that the FEC overhead needs to be
- * applied here, though HW people claim that neither this FEC
- * or any other overhead is applicable here (that is the actual
- * available_bw is just symbol_clock * 72). However based on
- * testing on MTL-P the
- * - DELL U3224KBA display
- * - Unigraf UCD-500 CTS test sink
- * devices the
- * - 5120x2880/995.59Mhz
- * - 6016x3384/1357.23Mhz
- * - 6144x3456/1413.39Mhz
- * modes (all the ones having a DPT limit on the above devices),
- * both the channel coding efficiency and an additional 3%
- * overhead needs to be accounted for.
- */
- return div64_u64(mul_u32_u32(intel_dp_link_symbol_clock(crtc_state->port_clock) * 72,
- drm_dp_bw_channel_coding_efficiency(true)),
- mul_u32_u32(adjusted_mode->crtc_clock, 1030000));
-}
-
-static int intel_dp_mst_bw_overhead(const struct intel_crtc_state *crtc_state,
- const struct intel_connector *connector,
- bool ssc, bool dsc, int bpp_x16)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- unsigned long flags = DRM_DP_BW_OVERHEAD_MST;
- int dsc_slice_count = 0;
- int overhead;
-
- flags |= intel_dp_is_uhbr(crtc_state) ? DRM_DP_BW_OVERHEAD_UHBR : 0;
- flags |= ssc ? DRM_DP_BW_OVERHEAD_SSC_REF_CLK : 0;
- flags |= crtc_state->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0;
-
- if (dsc) {
- flags |= DRM_DP_BW_OVERHEAD_DSC;
- dsc_slice_count = intel_dp_dsc_get_slice_count(connector,
- adjusted_mode->clock,
- adjusted_mode->hdisplay,
- crtc_state->joiner_pipes);
- }
-
- overhead = drm_dp_bw_overhead(crtc_state->lane_count,
- adjusted_mode->hdisplay,
- dsc_slice_count,
- bpp_x16,
- flags);
-
- /*
- * TODO: clarify whether a minimum required by the fixed FEC overhead
- * in the bspec audio programming sequence is required here.
- */
- return max(overhead, intel_dp_bw_fec_overhead(crtc_state->fec_enable));
-}
-
-static void intel_dp_mst_compute_m_n(const struct intel_crtc_state *crtc_state,
- const struct intel_connector *connector,
- int overhead,
- int bpp_x16,
- struct intel_link_m_n *m_n)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
-
- /* TODO: Check WA 14013163432 to set data M/N for full BW utilization. */
- intel_link_compute_m_n(bpp_x16, crtc_state->lane_count,
- adjusted_mode->crtc_clock,
- crtc_state->port_clock,
- overhead,
- m_n);
-
- m_n->tu = DIV_ROUND_UP_ULL(mul_u32_u32(m_n->data_m, 64), m_n->data_n);
-}
-
-static int intel_dp_mst_calc_pbn(int pixel_clock, int bpp_x16, int bw_overhead)
-{
- int effective_data_rate =
- intel_dp_effective_data_rate(pixel_clock, bpp_x16, bw_overhead);
-
- /*
- * TODO: Use drm_dp_calc_pbn_mode() instead, once it's converted
- * to calculate PBN with the BW overhead passed to it.
- */
- return DIV_ROUND_UP(effective_data_rate * 64, 54 * 1000);
-}
-
-static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- int max_bpp,
- int min_bpp,
- struct link_config_limits *limits,
- struct drm_connector_state *conn_state,
- int step,
- bool dsc)
-{
- struct drm_atomic_state *state = crtc_state->uapi.state;
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct drm_dp_mst_topology_state *mst_state;
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- int bpp, slots = -EINVAL;
- int max_dpt_bpp;
- int ret = 0;
-
- mst_state = drm_atomic_get_mst_topology_state(state, &intel_dp->mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- crtc_state->lane_count = limits->max_lane_count;
- crtc_state->port_clock = limits->max_rate;
-
- if (dsc) {
- if (!intel_dp_supports_fec(intel_dp, connector, crtc_state))
- return -EINVAL;
-
- crtc_state->fec_enable = !intel_dp_is_uhbr(crtc_state);
- }
-
- mst_state->pbn_div = drm_dp_get_vc_payload_bw(&intel_dp->mst_mgr,
- crtc_state->port_clock,
- crtc_state->lane_count);
-
- max_dpt_bpp = intel_dp_mst_max_dpt_bpp(crtc_state, dsc);
- if (max_bpp > max_dpt_bpp) {
- drm_dbg_kms(&i915->drm, "Limiting bpp to max DPT bpp (%d -> %d)\n",
- max_bpp, max_dpt_bpp);
- max_bpp = max_dpt_bpp;
- }
-
- drm_dbg_kms(&i915->drm, "Looking for slots in range min bpp %d max bpp %d\n",
- min_bpp, max_bpp);
-
- for (bpp = max_bpp; bpp >= min_bpp; bpp -= step) {
- int local_bw_overhead;
- int remote_bw_overhead;
- int link_bpp_x16;
- int remote_tu;
- fixed20_12 pbn;
-
- drm_dbg_kms(&i915->drm, "Trying bpp %d\n", bpp);
-
- link_bpp_x16 = fxp_q4_from_int(dsc ? bpp :
- intel_dp_output_bpp(crtc_state->output_format, bpp));
-
- local_bw_overhead = intel_dp_mst_bw_overhead(crtc_state, connector,
- false, dsc, link_bpp_x16);
- remote_bw_overhead = intel_dp_mst_bw_overhead(crtc_state, connector,
- true, dsc, link_bpp_x16);
-
- intel_dp_mst_compute_m_n(crtc_state, connector,
- local_bw_overhead,
- link_bpp_x16,
- &crtc_state->dp_m_n);
-
- /*
- * The TU size programmed to the HW determines which slots in
- * an MTP frame are used for this stream, which needs to match
- * the payload size programmed to the first downstream branch
- * device's payload table.
- *
- * Note that atm the payload's PBN value DRM core sends via
- * the ALLOCATE_PAYLOAD side-band message matches the payload
- * size (which it calculates from the PBN value) it programs
- * to the first branch device's payload table. The allocation
- * in the payload table could be reduced though (to
- * crtc_state->dp_m_n.tu), provided that the driver doesn't
- * enable SSC on the corresponding link.
- */
- pbn.full = dfixed_const(intel_dp_mst_calc_pbn(adjusted_mode->crtc_clock,
- link_bpp_x16,
- remote_bw_overhead));
- remote_tu = DIV_ROUND_UP(pbn.full, mst_state->pbn_div.full);
-
- /*
- * Aligning the TUs ensures that symbols consisting of multiple
- * (4) symbol cycles don't get split between two consecutive
- * MTPs, as required by Bspec.
- * TODO: remove the alignment restriction for 128b/132b links
- * on some platforms, where Bspec allows this.
- */
- remote_tu = ALIGN(remote_tu, 4 / crtc_state->lane_count);
-
- /*
- * Also align PBNs accordingly, since MST core will derive its
- * own copy of TU from the PBN in drm_dp_atomic_find_time_slots().
- * The above comment about the difference between the PBN
- * allocated for the whole path and the TUs allocated for the
- * first branch device's link also applies here.
- */
- pbn.full = remote_tu * mst_state->pbn_div.full;
- crtc_state->pbn = dfixed_trunc(pbn);
-
- drm_WARN_ON(&i915->drm, remote_tu < crtc_state->dp_m_n.tu);
- crtc_state->dp_m_n.tu = remote_tu;
-
- slots = drm_dp_atomic_find_time_slots(state, &intel_dp->mst_mgr,
- connector->port,
- crtc_state->pbn);
- if (slots == -EDEADLK)
- return slots;
-
- if (slots >= 0) {
- drm_WARN_ON(&i915->drm, slots != crtc_state->dp_m_n.tu);
-
- break;
- }
- }
-
- /* We failed to find a proper bpp/timeslots, return error */
- if (ret)
- slots = ret;
-
- if (slots < 0) {
- drm_dbg_kms(&i915->drm, "failed finding vcpi slots:%d\n",
- slots);
- } else {
- if (!dsc)
- crtc_state->pipe_bpp = bpp;
- else
- crtc_state->dsc.compressed_bpp_x16 = fxp_q4_from_int(bpp);
- drm_dbg_kms(&i915->drm, "Got %d slots for pipe bpp %d dsc %d\n", slots, bpp, dsc);
- }
-
- return slots;
-}
-
-static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state,
- struct link_config_limits *limits)
-{
- int slots = -EINVAL;
-
- /*
- * FIXME: allocate the BW according to link_bpp, which in the case of
- * YUV420 is only half of the pipe bpp value.
- */
- slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state,
- fxp_q4_to_int(limits->link.max_bpp_x16),
- fxp_q4_to_int(limits->link.min_bpp_x16),
- limits,
- conn_state, 2 * 3, false);
-
- if (slots < 0)
- return slots;
-
- return 0;
-}
-
-static int intel_dp_dsc_mst_compute_link_config(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state,
- struct link_config_limits *limits)
-{
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- int slots = -EINVAL;
- int i, num_bpc;
- u8 dsc_bpc[3] = {};
- int min_bpp, max_bpp, sink_min_bpp, sink_max_bpp;
- u8 dsc_max_bpc;
- int min_compressed_bpp, max_compressed_bpp;
-
- /* Max DSC Input BPC for ICL is 10 and for TGL+ is 12 */
- if (DISPLAY_VER(i915) >= 12)
- dsc_max_bpc = min_t(u8, 12, conn_state->max_requested_bpc);
- else
- dsc_max_bpc = min_t(u8, 10, conn_state->max_requested_bpc);
-
- max_bpp = min_t(u8, dsc_max_bpc * 3, limits->pipe.max_bpp);
- min_bpp = limits->pipe.min_bpp;
-
- num_bpc = drm_dp_dsc_sink_supported_input_bpcs(connector->dp.dsc_dpcd,
- dsc_bpc);
-
- drm_dbg_kms(&i915->drm, "DSC Source supported min bpp %d max bpp %d\n",
- min_bpp, max_bpp);
-
- sink_max_bpp = dsc_bpc[0] * 3;
- sink_min_bpp = sink_max_bpp;
-
- for (i = 1; i < num_bpc; i++) {
- if (sink_min_bpp > dsc_bpc[i] * 3)
- sink_min_bpp = dsc_bpc[i] * 3;
- if (sink_max_bpp < dsc_bpc[i] * 3)
- sink_max_bpp = dsc_bpc[i] * 3;
- }
-
- drm_dbg_kms(&i915->drm, "DSC Sink supported min bpp %d max bpp %d\n",
- sink_min_bpp, sink_max_bpp);
-
- if (min_bpp < sink_min_bpp)
- min_bpp = sink_min_bpp;
-
- if (max_bpp > sink_max_bpp)
- max_bpp = sink_max_bpp;
-
- crtc_state->pipe_bpp = max_bpp;
-
- max_compressed_bpp = intel_dp_dsc_sink_max_compressed_bpp(connector,
- crtc_state,
- max_bpp / 3);
- max_compressed_bpp = min(max_compressed_bpp,
- fxp_q4_to_int(limits->link.max_bpp_x16));
-
- min_compressed_bpp = intel_dp_dsc_sink_min_compressed_bpp(crtc_state);
- min_compressed_bpp = max(min_compressed_bpp,
- fxp_q4_to_int_roundup(limits->link.min_bpp_x16));
-
- drm_dbg_kms(&i915->drm, "DSC Sink supported compressed min bpp %d compressed max bpp %d\n",
- min_compressed_bpp, max_compressed_bpp);
-
- /* Align compressed bpps according to our own constraints */
- max_compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, max_compressed_bpp,
- crtc_state->pipe_bpp);
- min_compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, min_compressed_bpp,
- crtc_state->pipe_bpp);
-
- slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state, max_compressed_bpp,
- min_compressed_bpp, limits,
- conn_state, 1, true);
-
- if (slots < 0)
- return slots;
-
- return 0;
-}
-static int intel_dp_mst_update_slots(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct drm_dp_mst_topology_mgr *mgr = &intel_dp->mst_mgr;
- struct drm_dp_mst_topology_state *topology_state;
- u8 link_coding_cap = intel_dp_is_uhbr(crtc_state) ?
- DP_CAP_ANSI_128B132B : DP_CAP_ANSI_8B10B;
-
- topology_state = drm_atomic_get_mst_topology_state(conn_state->state, mgr);
- if (IS_ERR(topology_state)) {
- drm_dbg_kms(&i915->drm, "slot update failed\n");
- return PTR_ERR(topology_state);
- }
-
- drm_dp_mst_update_slots(topology_state, link_coding_cap);
-
- return 0;
-}
-
-static int mode_hblank_period_ns(const struct drm_display_mode *mode)
-{
- return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(mode->htotal - mode->hdisplay,
- NSEC_PER_SEC / 1000),
- mode->crtc_clock);
-}
-
-static bool
-hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector,
- const struct intel_crtc_state *crtc_state,
- const struct link_config_limits *limits)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- bool is_uhbr_sink = connector->mst_port &&
- drm_dp_128b132b_supported(connector->mst_port->dpcd);
- int hblank_limit = is_uhbr_sink ? 500 : 300;
-
- if (!connector->dp.dsc_hblank_expansion_quirk)
- return false;
-
- if (is_uhbr_sink && !drm_dp_is_uhbr_rate(limits->max_rate))
- return false;
-
- if (mode_hblank_period_ns(adjusted_mode) > hblank_limit)
- return false;
-
- return true;
-}
-
-static bool
-adjust_limits_for_dsc_hblank_expansion_quirk(const struct intel_connector *connector,
- const struct intel_crtc_state *crtc_state,
- struct link_config_limits *limits,
- bool dsc)
-{
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- int min_bpp_x16 = limits->link.min_bpp_x16;
-
- if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state, limits))
- return true;
-
- if (!dsc) {
- if (intel_dp_supports_dsc(connector, crtc_state)) {
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] DSC needed by hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name);
- return false;
- }
-
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] Increasing link min bpp to 24 due to hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name);
-
- if (limits->link.max_bpp_x16 < fxp_q4_from_int(24))
- return false;
-
- limits->link.min_bpp_x16 = fxp_q4_from_int(24);
-
- return true;
- }
-
- drm_WARN_ON(&i915->drm, limits->min_rate != limits->max_rate);
-
- if (limits->max_rate < 540000)
- min_bpp_x16 = fxp_q4_from_int(13);
- else if (limits->max_rate < 810000)
- min_bpp_x16 = fxp_q4_from_int(10);
-
- if (limits->link.min_bpp_x16 >= min_bpp_x16)
- return true;
-
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] Increasing link min bpp to " FXP_Q4_FMT " in DSC mode due to hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name,
- FXP_Q4_ARGS(min_bpp_x16));
-
- if (limits->link.max_bpp_x16 < min_bpp_x16)
- return false;
-
- limits->link.min_bpp_x16 = min_bpp_x16;
-
- return true;
-}
-
-static bool
-intel_dp_mst_compute_config_limits(struct intel_dp *intel_dp,
- const struct intel_connector *connector,
- struct intel_crtc_state *crtc_state,
- bool dsc,
- struct link_config_limits *limits)
-{
- /*
- * for MST we always configure max link bw - the spec doesn't
- * seem to suggest we should do otherwise.
- */
- limits->min_rate = limits->max_rate =
- intel_dp_max_link_rate(intel_dp);
-
- limits->min_lane_count = limits->max_lane_count =
- intel_dp_max_lane_count(intel_dp);
-
- limits->pipe.min_bpp = intel_dp_min_bpp(crtc_state->output_format);
- /*
- * FIXME: If all the streams can't fit into the link with
- * their current pipe_bpp we should reduce pipe_bpp across
- * the board until things start to fit. Until then we
- * limit to <= 8bpc since that's what was hardcoded for all
- * MST streams previously. This hack should be removed once
- * we have the proper retry logic in place.
- */
- limits->pipe.max_bpp = min(crtc_state->pipe_bpp, 24);
-
- intel_dp_adjust_compliance_config(intel_dp, crtc_state, limits);
-
- if (!intel_dp_compute_config_link_bpp_limits(intel_dp,
- crtc_state,
- dsc,
- limits))
- return false;
-
- return adjust_limits_for_dsc_hblank_expansion_quirk(connector,
- crtc_state,
- limits,
- dsc);
-}
-
-static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
- struct intel_crtc_state *pipe_config,
- struct drm_connector_state *conn_state)
-{
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- 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_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- const struct drm_display_mode *adjusted_mode =
- &pipe_config->hw.adjusted_mode;
- struct link_config_limits limits;
- bool dsc_needed, joiner_needs_dsc;
- int ret = 0;
-
- if (pipe_config->fec_enable &&
- !intel_dp_supports_fec(intel_dp, connector, pipe_config))
- return -EINVAL;
-
- if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
- return -EINVAL;
-
- if (intel_dp_need_joiner(intel_dp, connector,
- adjusted_mode->crtc_hdisplay,
- adjusted_mode->crtc_clock))
- pipe_config->joiner_pipes = GENMASK(crtc->pipe + 1, crtc->pipe);
-
- pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
- pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
- pipe_config->has_pch_encoder = false;
-
- joiner_needs_dsc = intel_dp_joiner_needs_dsc(dev_priv, pipe_config->joiner_pipes);
-
- dsc_needed = joiner_needs_dsc || intel_dp->force_dsc_en ||
- !intel_dp_mst_compute_config_limits(intel_dp,
- connector,
- pipe_config,
- false,
- &limits);
-
- if (!dsc_needed) {
- ret = intel_dp_mst_compute_link_config(encoder, pipe_config,
- conn_state, &limits);
-
- if (ret == -EDEADLK)
- return ret;
-
- if (ret)
- dsc_needed = true;
- }
-
- /* enable compression if the mode doesn't fit available BW */
- if (dsc_needed) {
- drm_dbg_kms(&dev_priv->drm, "Try DSC (fallback=%s, joiner=%s, force=%s)\n",
- str_yes_no(ret), str_yes_no(joiner_needs_dsc),
- str_yes_no(intel_dp->force_dsc_en));
-
- if (!intel_dp_supports_dsc(connector, pipe_config))
- return -EINVAL;
-
- if (!intel_dp_mst_compute_config_limits(intel_dp,
- connector,
- pipe_config,
- true,
- &limits))
- return -EINVAL;
-
- /*
- * FIXME: As bpc is hardcoded to 8, as mentioned above,
- * WARN and ignore the debug flag force_dsc_bpc for now.
- */
- drm_WARN(&dev_priv->drm, intel_dp->force_dsc_bpc, "Cannot Force BPC for MST\n");
- /*
- * Try to get at least some timeslots and then see, if
- * we can fit there with DSC.
- */
- drm_dbg_kms(&dev_priv->drm, "Trying to find VCPI slots in DSC mode\n");
-
- ret = intel_dp_dsc_mst_compute_link_config(encoder, pipe_config,
- conn_state, &limits);
- if (ret < 0)
- return ret;
-
- ret = intel_dp_dsc_compute_config(intel_dp, pipe_config,
- conn_state, &limits,
- pipe_config->dp_m_n.tu, false);
- }
-
- if (ret)
- return ret;
-
- ret = intel_dp_mst_update_slots(encoder, pipe_config, conn_state);
- if (ret)
- return ret;
-
- pipe_config->limited_color_range =
- intel_dp_limited_color_range(pipe_config, conn_state);
-
- if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv))
- pipe_config->lane_lat_optim_mask =
- bxt_dpio_phy_calc_lane_lat_optim_mask(pipe_config->lane_count);
-
- intel_dp_audio_compute_config(encoder, pipe_config, conn_state);
-
- intel_ddi_compute_min_voltage_level(pipe_config);
-
- intel_psr_compute_config(intel_dp, pipe_config, conn_state);
-
- return intel_dp_tunnel_atomic_compute_stream_bw(state, intel_dp, connector,
- pipe_config);
-}
-
-/*
- * Iterate over all connectors and return a mask of
- * all CPU transcoders streaming over the same DP link.
- */
-static unsigned int
-intel_dp_mst_transcoder_mask(struct intel_atomic_state *state,
- struct intel_dp *mst_port)
-{
- struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- const struct intel_digital_connector_state *conn_state;
- struct intel_connector *connector;
- u8 transcoders = 0;
- int i;
-
- if (DISPLAY_VER(dev_priv) < 12)
- return 0;
-
- for_each_new_intel_connector_in_state(state, connector, conn_state, i) {
- const struct intel_crtc_state *crtc_state;
- struct intel_crtc *crtc;
-
- if (connector->mst_port != mst_port || !conn_state->base.crtc)
- continue;
-
- crtc = to_intel_crtc(conn_state->base.crtc);
- crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
-
- if (!crtc_state->hw.active)
- continue;
-
- transcoders |= BIT(crtc_state->cpu_transcoder);
- }
-
- return transcoders;
-}
-
-static u8 get_pipes_downstream_of_mst_port(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct drm_dp_mst_port *parent_port)
-{
- const struct intel_digital_connector_state *conn_state;
- struct intel_connector *connector;
- u8 mask = 0;
- int i;
-
- for_each_new_intel_connector_in_state(state, connector, conn_state, i) {
- if (!conn_state->base.crtc)
- continue;
-
- if (&connector->mst_port->mst_mgr != mst_mgr)
- continue;
-
- if (connector->port != parent_port &&
- !drm_dp_mst_port_downstream_of_parent(mst_mgr,
- connector->port,
- parent_port))
- continue;
-
- mask |= BIT(to_intel_crtc(conn_state->base.crtc)->pipe);
- }
-
- return mask;
-}
-
-static int intel_dp_mst_check_fec_change(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct intel_link_bw_limits *limits)
-{
- struct drm_i915_private *i915 = to_i915(state->base.dev);
- struct intel_crtc *crtc;
- u8 mst_pipe_mask;
- u8 fec_pipe_mask = 0;
- int ret;
-
- mst_pipe_mask = get_pipes_downstream_of_mst_port(state, mst_mgr, NULL);
-
- for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, mst_pipe_mask) {
- struct intel_crtc_state *crtc_state =
- intel_atomic_get_new_crtc_state(state, crtc);
-
- /* Atomic connector check should've added all the MST CRTCs. */
- if (drm_WARN_ON(&i915->drm, !crtc_state))
- return -EINVAL;
-
- if (crtc_state->fec_enable)
- fec_pipe_mask |= BIT(crtc->pipe);
- }
-
- if (!fec_pipe_mask || mst_pipe_mask == fec_pipe_mask)
- return 0;
-
- limits->force_fec_pipes |= mst_pipe_mask;
-
- ret = intel_modeset_pipes_in_mask_early(state, "MST FEC",
- mst_pipe_mask);
-
- return ret ? : -EAGAIN;
-}
-
-static int intel_dp_mst_check_bw(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct drm_dp_mst_topology_state *mst_state,
- struct intel_link_bw_limits *limits)
-{
- struct drm_dp_mst_port *mst_port;
- u8 mst_port_pipes;
- int ret;
-
- ret = drm_dp_mst_atomic_check_mgr(&state->base, mst_mgr, mst_state, &mst_port);
- if (ret != -ENOSPC)
- return ret;
-
- mst_port_pipes = get_pipes_downstream_of_mst_port(state, mst_mgr, mst_port);
-
- ret = intel_link_bw_reduce_bpp(state, limits,
- mst_port_pipes, "MST link BW");
-
- return ret ? : -EAGAIN;
-}
-
-/**
- * intel_dp_mst_atomic_check_link - check all modeset MST link configuration
- * @state: intel atomic state
- * @limits: link BW limits
- *
- * Check the link configuration for all modeset MST outputs. If the
- * configuration is invalid @limits will be updated if possible to
- * reduce the total BW, after which the configuration for all CRTCs in
- * @state must be recomputed with the updated @limits.
- *
- * Returns:
- * - 0 if the confugration is valid
- * - %-EAGAIN, if the configuration is invalid and @limits got updated
- * with fallback values with which the configuration of all CRTCs in
- * @state must be recomputed
- * - Other negative error, if the configuration is invalid without a
- * fallback possibility, or the check failed for another reason
- */
-int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
- struct intel_link_bw_limits *limits)
-{
- struct drm_dp_mst_topology_mgr *mgr;
- struct drm_dp_mst_topology_state *mst_state;
- int ret;
- int i;
-
- for_each_new_mst_mgr_in_state(&state->base, mgr, mst_state, i) {
- ret = intel_dp_mst_check_fec_change(state, mgr, limits);
- if (ret)
- return ret;
-
- ret = intel_dp_mst_check_bw(state, mgr, mst_state,
- limits);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int intel_dp_mst_compute_config_late(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_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
-
- /* lowest numbered transcoder will be designated master */
- crtc_state->mst_master_transcoder =
- ffs(intel_dp_mst_transcoder_mask(state, intel_dp)) - 1;
-
- return 0;
-}
-
-/*
- * If one of the connectors in a MST stream needs a modeset, mark all CRTCs
- * that shares the same MST stream as mode changed,
- * intel_modeset_pipe_config()+intel_crtc_check_fastset() will take care to do
- * a fastset when possible.
- *
- * On TGL+ this is required since each stream go through a master transcoder,
- * so if the master transcoder needs modeset, all other streams in the
- * topology need a modeset. All platforms need to add the atomic state
- * for all streams in the topology, since a modeset on one may require
- * changing the MST link BW usage of the others, which in turn needs a
- * recomputation of the corresponding CRTC states.
- */
-static int
-intel_dp_mst_atomic_topology_check(struct intel_connector *connector,
- struct intel_atomic_state *state)
-{
- struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- struct drm_connector_list_iter connector_list_iter;
- struct intel_connector *connector_iter;
- int ret = 0;
-
- if (!intel_connector_needs_modeset(state, &connector->base))
- return 0;
-
- drm_connector_list_iter_begin(&dev_priv->drm, &connector_list_iter);
- for_each_intel_connector_iter(connector_iter, &connector_list_iter) {
- struct intel_digital_connector_state *conn_iter_state;
- struct intel_crtc_state *crtc_state;
- struct intel_crtc *crtc;
-
- if (connector_iter->mst_port != connector->mst_port ||
- connector_iter == connector)
- continue;
-
- conn_iter_state = intel_atomic_get_digital_connector_state(state,
- connector_iter);
- if (IS_ERR(conn_iter_state)) {
- ret = PTR_ERR(conn_iter_state);
- break;
- }
-
- if (!conn_iter_state->base.crtc)
- continue;
-
- crtc = to_intel_crtc(conn_iter_state->base.crtc);
- crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- break;
- }
-
- ret = drm_atomic_add_affected_planes(&state->base, &crtc->base);
- if (ret)
- break;
- crtc_state->uapi.mode_changed = true;
- }
- drm_connector_list_iter_end(&connector_list_iter);
-
- return ret;
-}
-
-static int
-intel_dp_mst_atomic_check(struct drm_connector *connector,
- struct drm_atomic_state *_state)
-{
- struct intel_atomic_state *state = to_intel_atomic_state(_state);
- struct intel_connector *intel_connector =
- to_intel_connector(connector);
- int ret;
-
- ret = intel_digital_connector_atomic_check(connector, &state->base);
- if (ret)
- return ret;
-
- ret = intel_dp_mst_atomic_topology_check(intel_connector, state);
- if (ret)
- return ret;
-
- if (intel_connector_needs_modeset(state, connector)) {
- ret = intel_dp_tunnel_atomic_check_state(state,
- intel_connector->mst_port,
- intel_connector);
- if (ret)
- return ret;
- }
-
- return drm_dp_atomic_release_time_slots(&state->base,
- &intel_connector->mst_port->mst_mgr,
- intel_connector->port);
-}
-
-static void clear_act_sent(struct intel_encoder *encoder,
- const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
-
- intel_de_write(i915, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_ACT_SENT);
-}
-
-static void wait_for_act_sent(struct intel_encoder *encoder,
- const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
-
- if (intel_de_wait_for_set(i915, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_ACT_SENT, 1))
- drm_err(&i915->drm, "Timed out waiting for ACT sent\n");
-
- drm_dp_check_act_status(&intel_dp->mst_mgr);
-}
-
-static void intel_mst_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector =
- to_intel_connector(old_conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
-
- drm_dbg_kms(&i915->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- if (intel_dp->active_mst_links == 1)
- intel_dp->link_trained = false;
-
- intel_hdcp_disable(intel_mst->connector);
-
- intel_dp_sink_disable_decompression(state, connector, old_crtc_state);
-}
-
-static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector =
- to_intel_connector(old_conn_state->connector);
- struct drm_dp_mst_topology_state *old_mst_state =
- drm_atomic_get_old_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- struct drm_dp_mst_topology_state *new_mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- const struct drm_dp_mst_atomic_payload *old_payload =
- drm_atomic_get_mst_payload_state(old_mst_state, connector->port);
- struct drm_dp_mst_atomic_payload *new_payload =
- drm_atomic_get_mst_payload_state(new_mst_state, connector->port);
- struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct intel_crtc *pipe_crtc;
- bool last_mst_stream;
-
- intel_dp->active_mst_links--;
- last_mst_stream = intel_dp->active_mst_links == 0;
- drm_WARN_ON(&dev_priv->drm,
- DISPLAY_VER(dev_priv) >= 12 && last_mst_stream &&
- !intel_dp_mst_is_master_trans(old_crtc_state));
-
- for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(old_crtc_state)) {
- const struct intel_crtc_state *old_pipe_crtc_state =
- intel_atomic_get_old_crtc_state(state, pipe_crtc);
-
- intel_crtc_vblank_off(old_pipe_crtc_state);
- }
-
- intel_disable_transcoder(old_crtc_state);
-
- drm_dp_remove_payload_part1(&intel_dp->mst_mgr, new_mst_state, new_payload);
-
- clear_act_sent(encoder, old_crtc_state);
-
- intel_de_rmw(dev_priv,
- TRANS_DDI_FUNC_CTL(dev_priv, old_crtc_state->cpu_transcoder),
- TRANS_DDI_DP_VC_PAYLOAD_ALLOC, 0);
-
- wait_for_act_sent(encoder, old_crtc_state);
-
- drm_dp_remove_payload_part2(&intel_dp->mst_mgr, new_mst_state,
- old_payload, new_payload);
-
- intel_ddi_disable_transcoder_func(old_crtc_state);
-
- for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(old_crtc_state)) {
- const struct intel_crtc_state *old_pipe_crtc_state =
- intel_atomic_get_old_crtc_state(state, pipe_crtc);
-
- intel_dsc_disable(old_pipe_crtc_state);
-
- if (DISPLAY_VER(dev_priv) >= 9)
- skl_scaler_disable(old_pipe_crtc_state);
- else
- ilk_pfit_disable(old_pipe_crtc_state);
- }
-
- /*
- * Power down mst path before disabling the port, otherwise we end
- * up getting interrupts from the sink upon detecting link loss.
- */
- drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port,
- false);
-
- /*
- * BSpec 4287: disable DIP after the transcoder is disabled and before
- * the transcoder clock select is set to none.
- */
- intel_dp_set_infoframes(&dig_port->base, false,
- old_crtc_state, NULL);
- /*
- * From TGL spec: "If multi-stream slave transcoder: Configure
- * Transcoder Clock Select to direct no clock to the transcoder"
- *
- * From older GENs spec: "Configure Transcoder Clock Select to direct
- * no clock to the transcoder"
- */
- if (DISPLAY_VER(dev_priv) < 12 || !last_mst_stream)
- intel_ddi_disable_transcoder_clock(old_crtc_state);
-
-
- intel_mst->connector = NULL;
- if (last_mst_stream)
- dig_port->base.post_disable(state, &dig_port->base,
- old_crtc_state, NULL);
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-}
-
-static void intel_mst_post_pll_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (intel_dp->active_mst_links == 0 &&
- dig_port->base.post_pll_disable)
- dig_port->base.post_pll_disable(state, encoder, old_crtc_state, old_conn_state);
-}
-
-static void intel_mst_pre_pll_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (intel_dp->active_mst_links == 0)
- dig_port->base.pre_pll_enable(state, &dig_port->base,
- pipe_config, NULL);
- else
- /*
- * The port PLL state needs to get updated for secondary
- * streams as for the primary stream.
- */
- intel_ddi_update_active_dpll(state, &dig_port->base,
- to_intel_crtc(pipe_config->uapi.crtc));
-}
-
-static bool intel_mst_probed_link_params_valid(struct intel_dp *intel_dp,
- int link_rate, int lane_count)
-{
- return intel_dp->link.mst_probed_rate == link_rate &&
- intel_dp->link.mst_probed_lane_count == lane_count;
-}
-
-static void intel_mst_set_probed_link_params(struct intel_dp *intel_dp,
- int link_rate, int lane_count)
-{
- intel_dp->link.mst_probed_rate = link_rate;
- intel_dp->link.mst_probed_lane_count = lane_count;
-}
-
-static void intel_mst_reprobe_topology(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state)
-{
- if (intel_mst_probed_link_params_valid(intel_dp,
- crtc_state->port_clock, crtc_state->lane_count))
- return;
-
- drm_dp_mst_topology_queue_probe(&intel_dp->mst_mgr);
-
- intel_mst_set_probed_link_params(intel_dp,
- crtc_state->port_clock, crtc_state->lane_count);
-}
-
-static void intel_mst_pre_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_dp_mst_topology_state *mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- int ret;
- bool first_mst_stream;
-
- /* MST encoders are bound to a crtc, not to a connector,
- * force the mapping here for get_hw_state.
- */
- connector->encoder = encoder;
- intel_mst->connector = connector;
- first_mst_stream = intel_dp->active_mst_links == 0;
- drm_WARN_ON(&dev_priv->drm,
- DISPLAY_VER(dev_priv) >= 12 && first_mst_stream &&
- !intel_dp_mst_is_master_trans(pipe_config));
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- if (first_mst_stream)
- intel_dp_set_power(intel_dp, DP_SET_POWER_D0);
-
- drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true);
-
- intel_dp_sink_enable_decompression(state, connector, pipe_config);
-
- if (first_mst_stream) {
- dig_port->base.pre_enable(state, &dig_port->base,
- pipe_config, NULL);
-
- intel_mst_reprobe_topology(intel_dp, pipe_config);
- }
-
- intel_dp->active_mst_links++;
-
- ret = drm_dp_add_payload_part1(&intel_dp->mst_mgr, mst_state,
- drm_atomic_get_mst_payload_state(mst_state, connector->port));
- if (ret < 0)
- intel_dp_queue_modeset_retry_for_link(state, &dig_port->base, pipe_config);
-
- /*
- * Before Gen 12 this is not done as part of
- * dig_port->base.pre_enable() and should be done here. For
- * Gen 12+ the step in which this should be done is different for the
- * first MST stream, so it's done on the DDI for the first stream and
- * here for the following ones.
- */
- if (DISPLAY_VER(dev_priv) < 12 || !first_mst_stream)
- intel_ddi_enable_transcoder_clock(encoder, pipe_config);
-
- intel_dsc_dp_pps_write(&dig_port->base, pipe_config);
- intel_ddi_set_dp_msa(pipe_config, conn_state);
-}
-
-static void enable_bs_jitter_was(const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
- u32 clear = 0;
- u32 set = 0;
-
- if (!IS_ALDERLAKE_P(i915))
- return;
-
- if (!IS_DISPLAY_STEP(i915, STEP_D0, STEP_FOREVER))
- return;
-
- /* Wa_14013163432:adlp */
- if (crtc_state->fec_enable || intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_FEC_BS_JITTER_WA(crtc_state->cpu_transcoder);
-
- /* Wa_14014143976:adlp */
- if (IS_DISPLAY_STEP(i915, STEP_E0, STEP_FOREVER)) {
- if (intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_SHORT_HBLANK_WA(crtc_state->cpu_transcoder);
- else if (crtc_state->fec_enable)
- clear |= DP_MST_SHORT_HBLANK_WA(crtc_state->cpu_transcoder);
-
- if (crtc_state->fec_enable || intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_DPT_DPTP_ALIGN_WA(crtc_state->cpu_transcoder);
- }
-
- if (!clear && !set)
- return;
-
- intel_de_rmw(i915, CHICKEN_MISC_3, clear, set);
-}
-
-static void intel_mst_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector = to_intel_connector(conn_state->connector);
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct drm_dp_mst_topology_state *mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- enum transcoder trans = pipe_config->cpu_transcoder;
- bool first_mst_stream = intel_dp->active_mst_links == 1;
- struct intel_crtc *pipe_crtc;
- int ret;
-
- drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder);
-
- if (intel_dp_is_uhbr(pipe_config)) {
- const struct drm_display_mode *adjusted_mode =
- &pipe_config->hw.adjusted_mode;
- u64 crtc_clock_hz = KHz(adjusted_mode->crtc_clock);
-
- intel_de_write(dev_priv, TRANS_DP2_VFREQHIGH(pipe_config->cpu_transcoder),
- TRANS_DP2_VFREQ_PIXEL_CLOCK(crtc_clock_hz >> 24));
- intel_de_write(dev_priv, TRANS_DP2_VFREQLOW(pipe_config->cpu_transcoder),
- TRANS_DP2_VFREQ_PIXEL_CLOCK(crtc_clock_hz & 0xffffff));
- }
-
- enable_bs_jitter_was(pipe_config);
-
- intel_ddi_enable_transcoder_func(encoder, pipe_config);
-
- clear_act_sent(encoder, pipe_config);
-
- intel_de_rmw(dev_priv, TRANS_DDI_FUNC_CTL(dev_priv, trans), 0,
- TRANS_DDI_DP_VC_PAYLOAD_ALLOC);
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- wait_for_act_sent(encoder, pipe_config);
-
- if (first_mst_stream)
- intel_ddi_wait_for_fec_status(encoder, pipe_config, true);
-
- ret = drm_dp_add_payload_part2(&intel_dp->mst_mgr,
- drm_atomic_get_mst_payload_state(mst_state,
- connector->port));
- if (ret < 0)
- intel_dp_queue_modeset_retry_for_link(state, &dig_port->base, pipe_config);
-
- if (DISPLAY_VER(dev_priv) >= 12)
- intel_de_rmw(dev_priv, hsw_chicken_trans_reg(dev_priv, trans),
- FECSTALL_DIS_DPTSTREAM_DPTTG,
- pipe_config->fec_enable ? FECSTALL_DIS_DPTSTREAM_DPTTG : 0);
-
- intel_audio_sdp_split_update(pipe_config);
-
- intel_enable_transcoder(pipe_config);
-
- for_each_intel_crtc_in_pipe_mask_reverse(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(pipe_config)) {
- const struct intel_crtc_state *pipe_crtc_state =
- intel_atomic_get_new_crtc_state(state, pipe_crtc);
-
- intel_crtc_vblank_on(pipe_crtc_state);
- }
-
- intel_hdcp_enable(state, encoder, pipe_config, conn_state);
-}
-
-static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
- enum pipe *pipe)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- *pipe = intel_mst->pipe;
- if (intel_mst->connector)
- return true;
- return false;
-}
-
-static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
- struct intel_crtc_state *pipe_config)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
-
- dig_port->base.get_config(&dig_port->base, pipe_config);
-}
-
-static bool intel_dp_mst_initial_fastset_check(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
-
- return intel_dp_initial_fastset_check(&dig_port->base, crtc_state);
-}
-
-static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct drm_i915_private *i915 = to_i915(intel_connector->base.dev);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- const struct drm_edid *drm_edid;
- int ret;
-
- if (drm_connector_is_unregistered(connector))
- return intel_connector_update_modes(connector, NULL);
-
- if (!intel_display_driver_check_access(i915))
- return drm_edid_connector_add_modes(connector);
-
- drm_edid = drm_dp_mst_edid_read(connector, &intel_dp->mst_mgr, intel_connector->port);
-
- ret = intel_connector_update_modes(connector, drm_edid);
-
- drm_edid_free(drm_edid);
-
- return ret;
-}
-
-static int
-intel_dp_mst_connector_late_register(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
- int ret;
-
- ret = drm_dp_mst_connector_late_register(connector,
- intel_connector->port);
- if (ret < 0)
- return ret;
-
- ret = intel_connector_register(connector);
- if (ret < 0)
- drm_dp_mst_connector_early_unregister(connector,
- intel_connector->port);
-
- return ret;
-}
-
-static void
-intel_dp_mst_connector_early_unregister(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
-
- intel_connector_unregister(connector);
- drm_dp_mst_connector_early_unregister(connector,
- intel_connector->port);
-}
-
-static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
- .fill_modes = drm_helper_probe_single_connector_modes,
- .atomic_get_property = intel_digital_connector_atomic_get_property,
- .atomic_set_property = intel_digital_connector_atomic_set_property,
- .late_register = intel_dp_mst_connector_late_register,
- .early_unregister = intel_dp_mst_connector_early_unregister,
- .destroy = intel_connector_destroy,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = intel_digital_connector_duplicate_state,
-};
-
-static int intel_dp_mst_get_modes(struct drm_connector *connector)
-{
- return intel_dp_mst_get_ddc_modes(connector);
-}
-
-static int
-intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
- struct drm_display_mode *mode,
- struct drm_modeset_acquire_ctx *ctx,
- enum drm_mode_status *status)
-{
- struct drm_i915_private *dev_priv = to_i915(connector->dev);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- struct drm_dp_mst_topology_mgr *mgr = &intel_dp->mst_mgr;
- struct drm_dp_mst_port *port = intel_connector->port;
- const int min_bpp = 18;
- int max_dotclk = to_i915(connector->dev)->display.cdclk.max_dotclk_freq;
- int max_rate, mode_rate, max_lanes, max_link_clock;
- int ret;
- bool dsc = false, joiner = false;
- u16 dsc_max_compressed_bpp = 0;
- u8 dsc_slice_count = 0;
- int target_clock = mode->clock;
-
- if (drm_connector_is_unregistered(connector)) {
- *status = MODE_ERROR;
- return 0;
- }
-
- *status = intel_cpu_transcoder_mode_valid(dev_priv, mode);
- if (*status != MODE_OK)
- return 0;
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
- *status = MODE_H_ILLEGAL;
- return 0;
- }
-
- if (mode->clock < 10000) {
- *status = MODE_CLOCK_LOW;
- return 0;
- }
-
- max_link_clock = intel_dp_max_link_rate(intel_dp);
- max_lanes = intel_dp_max_lane_count(intel_dp);
-
- max_rate = intel_dp_max_link_data_rate(intel_dp,
- max_link_clock, max_lanes);
- mode_rate = intel_dp_link_required(mode->clock, min_bpp);
-
- /*
- * TODO:
- * - Also check if compression would allow for the mode
- * - Calculate the overhead using drm_dp_bw_overhead() /
- * drm_dp_bw_channel_coding_efficiency(), similarly to the
- * compute config code, as drm_dp_calc_pbn_mode() doesn't
- * account with all the overheads.
- * - Check here and during compute config the BW reported by
- * DFP_Link_Available_Payload_Bandwidth_Number (or the
- * corresponding link capabilities of the sink) in case the
- * stream is uncompressed for it by the last branch device.
- */
- if (intel_dp_need_joiner(intel_dp, intel_connector,
- mode->hdisplay, target_clock)) {
- joiner = true;
- max_dotclk *= 2;
- }
-
- ret = drm_modeset_lock(&mgr->base.lock, ctx);
- if (ret)
- return ret;
-
- if (mode_rate > max_rate || mode->clock > max_dotclk ||
- drm_dp_calc_pbn_mode(mode->clock, min_bpp << 4) > port->full_pbn) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- if (intel_dp_has_dsc(intel_connector)) {
- /*
- * TBD pass the connector BPC,
- * for now U8_MAX so that max BPC on that platform would be picked
- */
- int pipe_bpp = intel_dp_dsc_compute_max_bpp(intel_connector, U8_MAX);
-
- if (drm_dp_sink_supports_fec(intel_connector->dp.fec_capability)) {
- dsc_max_compressed_bpp =
- intel_dp_dsc_get_max_compressed_bpp(dev_priv,
- max_link_clock,
- max_lanes,
- target_clock,
- mode->hdisplay,
- joiner,
- INTEL_OUTPUT_FORMAT_RGB,
- pipe_bpp, 64);
- dsc_slice_count =
- intel_dp_dsc_get_slice_count(intel_connector,
- target_clock,
- mode->hdisplay,
- joiner);
- }
-
- dsc = dsc_max_compressed_bpp && dsc_slice_count;
- }
-
- if (intel_dp_joiner_needs_dsc(dev_priv, joiner) && !dsc) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- if (mode_rate > max_rate && !dsc) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- *status = intel_mode_valid_max_plane_size(dev_priv, mode, joiner);
- return 0;
-}
-
-static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector,
- struct drm_atomic_state *state)
-{
- struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
- connector);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- struct intel_crtc *crtc = to_intel_crtc(connector_state->crtc);
-
- return &intel_dp->mst_encoders[crtc->pipe]->base.base;
-}
-
-static int
-intel_dp_mst_detect(struct drm_connector *connector,
- struct drm_modeset_acquire_ctx *ctx, bool force)
-{
- struct drm_i915_private *i915 = to_i915(connector->dev);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
-
- if (!intel_display_device_enabled(i915))
- return connector_status_disconnected;
-
- if (drm_connector_is_unregistered(connector))
- return connector_status_disconnected;
-
- if (!intel_display_driver_check_access(i915))
- return connector->status;
-
- return drm_dp_mst_detect_port(connector, ctx, &intel_dp->mst_mgr,
- intel_connector->port);
-}
-
-static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
- .get_modes = intel_dp_mst_get_modes,
- .mode_valid_ctx = intel_dp_mst_mode_valid_ctx,
- .atomic_best_encoder = intel_mst_atomic_best_encoder,
- .atomic_check = intel_dp_mst_atomic_check,
- .detect_ctx = intel_dp_mst_detect,
-};
-
-static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(to_intel_encoder(encoder));
-
- drm_encoder_cleanup(encoder);
- kfree(intel_mst);
-}
-
-static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
- .destroy = intel_dp_mst_encoder_destroy,
-};
-
-static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
-{
- if (intel_attached_encoder(connector) && connector->base.state->crtc) {
- enum pipe pipe;
- if (!intel_attached_encoder(connector)->get_hw_state(intel_attached_encoder(connector), &pipe))
- return false;
- return true;
- }
- return false;
-}
-
-static int intel_dp_mst_add_properties(struct intel_dp *intel_dp,
- struct drm_connector *connector,
- const char *pathprop)
-{
- struct drm_i915_private *i915 = to_i915(connector->dev);
-
- drm_object_attach_property(&connector->base,
- i915->drm.mode_config.path_property, 0);
- drm_object_attach_property(&connector->base,
- i915->drm.mode_config.tile_property, 0);
-
- intel_attach_force_audio_property(connector);
- intel_attach_broadcast_rgb_property(connector);
-
- /*
- * Reuse the prop from the SST connector because we're
- * not allowed to create new props after device registration.
- */
- connector->max_bpc_property =
- intel_dp->attached_connector->base.max_bpc_property;
- if (connector->max_bpc_property)
- drm_connector_attach_max_bpc_property(connector, 6, 12);
-
- return drm_connector_set_path_property(connector, pathprop);
-}
-
-static void
-intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp,
- struct intel_connector *connector)
-{
- u8 dpcd_caps[DP_RECEIVER_CAP_SIZE];
-
- if (!connector->dp.dsc_decompression_aux)
- return;
-
- if (drm_dp_read_dpcd_caps(connector->dp.dsc_decompression_aux, dpcd_caps) < 0)
- return;
-
- intel_dp_get_dsc_sink_cap(dpcd_caps[DP_DPCD_REV], connector);
-}
-
-static bool detect_dsc_hblank_expansion_quirk(const struct intel_connector *connector)
-{
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- struct drm_dp_aux *aux = connector->dp.dsc_decompression_aux;
- struct drm_dp_desc desc;
- u8 dpcd[DP_RECEIVER_CAP_SIZE];
-
- if (!aux)
- return false;
-
- /*
- * A logical port's OUI (at least for affected sinks) is all 0, so
- * instead of that the parent port's OUI is used for identification.
- */
- if (drm_dp_mst_port_is_logical(connector->port)) {
- aux = drm_dp_mst_aux_for_parent(connector->port);
- if (!aux)
- aux = &connector->mst_port->aux;
- }
-
- if (drm_dp_read_dpcd_caps(aux, dpcd) < 0)
- return false;
-
- if (drm_dp_read_desc(aux, &desc, drm_dp_is_branch(dpcd)) < 0)
- return false;
-
- if (!drm_dp_has_quirk(&desc,
- DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC))
- return false;
-
- /*
- * UHBR (MST sink) devices requiring this quirk don't advertise the
- * HBLANK expansion support. Presuming that they perform HBLANK
- * expansion internally, or are affected by this issue on modes with a
- * short HBLANK for other reasons.
- */
- if (!drm_dp_128b132b_supported(dpcd) &&
- !(dpcd[DP_RECEIVE_PORT_0_CAP_0] & DP_HBLANK_EXPANSION_CAPABLE))
- return false;
-
- drm_dbg_kms(&i915->drm,
- "[CONNECTOR:%d:%s] DSC HBLANK expansion quirk detected\n",
- connector->base.base.id, connector->base.name);
-
- return true;
-}
-
-static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port,
- const char *pathprop)
-{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct drm_device *dev = dig_port->base.base.dev;
- struct drm_i915_private *dev_priv = to_i915(dev);
- struct intel_connector *intel_connector;
- struct drm_connector *connector;
- enum pipe pipe;
- int ret;
-
- intel_connector = intel_connector_alloc();
- if (!intel_connector)
- return NULL;
-
- intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
- intel_connector->sync_state = intel_dp_connector_sync_state;
- intel_connector->mst_port = intel_dp;
- intel_connector->port = port;
- drm_dp_mst_get_port_malloc(port);
-
- intel_dp_init_modeset_retry_work(intel_connector);
-
- intel_connector->dp.dsc_decompression_aux = drm_dp_mst_dsc_aux_for_port(port);
- intel_dp_mst_read_decompression_port_dsc_caps(intel_dp, intel_connector);
- intel_connector->dp.dsc_hblank_expansion_quirk =
- detect_dsc_hblank_expansion_quirk(intel_connector);
-
- connector = &intel_connector->base;
- ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs,
- DRM_MODE_CONNECTOR_DisplayPort);
- if (ret) {
- drm_dp_mst_put_port_malloc(port);
- intel_connector_free(intel_connector);
- return NULL;
- }
-
- drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
-
- for_each_pipe(dev_priv, pipe) {
- struct drm_encoder *enc =
- &intel_dp->mst_encoders[pipe]->base.base;
-
- ret = drm_connector_attach_encoder(&intel_connector->base, enc);
- if (ret)
- goto err;
- }
-
- ret = intel_dp_mst_add_properties(intel_dp, connector, pathprop);
- if (ret)
- goto err;
-
- ret = intel_dp_hdcp_init(dig_port, intel_connector);
- if (ret)
- drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP MST init failed, skipping.\n",
- connector->name, connector->base.id);
-
- return connector;
-
-err:
- drm_connector_cleanup(connector);
- return NULL;
-}
-
-static void
-intel_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr)
-{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
-
- intel_hpd_trigger_irq(dp_to_dig_port(intel_dp));
-}
-
-static const struct drm_dp_mst_topology_cbs mst_cbs = {
- .add_connector = intel_dp_add_mst_connector,
- .poll_hpd_irq = intel_dp_mst_poll_hpd_irq,
-};
-
-static struct intel_dp_mst_encoder *
-intel_dp_create_fake_mst_encoder(struct intel_digital_port *dig_port, enum pipe pipe)
-{
- struct intel_dp_mst_encoder *intel_mst;
- struct intel_encoder *intel_encoder;
- struct drm_device *dev = dig_port->base.base.dev;
-
- intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
-
- if (!intel_mst)
- return NULL;
-
- intel_mst->pipe = pipe;
- intel_encoder = &intel_mst->base;
- intel_mst->primary = dig_port;
-
- drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
- DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe));
-
- intel_encoder->type = INTEL_OUTPUT_DP_MST;
- intel_encoder->power_domain = dig_port->base.power_domain;
- intel_encoder->port = dig_port->base.port;
- intel_encoder->cloneable = 0;
- /*
- * This is wrong, but broken userspace uses the intersection
- * of possible_crtcs of all the encoders of a given connector
- * to figure out which crtcs can drive said connector. What
- * should be used instead is the union of possible_crtcs.
- * To keep such userspace functioning we must misconfigure
- * this to make sure the intersection is not empty :(
- */
- intel_encoder->pipe_mask = ~0;
-
- intel_encoder->compute_config = intel_dp_mst_compute_config;
- intel_encoder->compute_config_late = intel_dp_mst_compute_config_late;
- intel_encoder->disable = intel_mst_disable_dp;
- intel_encoder->post_disable = intel_mst_post_disable_dp;
- intel_encoder->post_pll_disable = intel_mst_post_pll_disable_dp;
- intel_encoder->update_pipe = intel_ddi_update_pipe;
- intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp;
- intel_encoder->pre_enable = intel_mst_pre_enable_dp;
- intel_encoder->enable = intel_mst_enable_dp;
- intel_encoder->audio_enable = intel_audio_codec_enable;
- intel_encoder->audio_disable = intel_audio_codec_disable;
- intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
- intel_encoder->get_config = intel_dp_mst_enc_get_config;
- intel_encoder->initial_fastset_check = intel_dp_mst_initial_fastset_check;
-
- return intel_mst;
-
-}
-
-static bool
-intel_dp_create_fake_mst_encoders(struct intel_digital_port *dig_port)
-{
- struct intel_dp *intel_dp = &dig_port->dp;
- struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
- enum pipe pipe;
-
- for_each_pipe(dev_priv, pipe)
- intel_dp->mst_encoders[pipe] = intel_dp_create_fake_mst_encoder(dig_port, pipe);
- return true;
-}
-
-int
-intel_dp_mst_encoder_active_links(struct intel_digital_port *dig_port)
-{
- return dig_port->dp.active_mst_links;
-}
-
-int
-intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_base_id)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- struct intel_dp *intel_dp = &dig_port->dp;
- enum port port = dig_port->base.port;
- int ret;
-
- if (!HAS_DP_MST(i915) || intel_dp_is_edp(intel_dp))
- return 0;
-
- if (DISPLAY_VER(i915) < 12 && port == PORT_A)
- return 0;
-
- if (DISPLAY_VER(i915) < 11 && port == PORT_E)
- return 0;
-
- intel_dp->mst_mgr.cbs = &mst_cbs;
-
- /* create encoders */
- intel_dp_create_fake_mst_encoders(dig_port);
- ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, &i915->drm,
- &intel_dp->aux, 16, 3, conn_base_id);
- if (ret) {
- intel_dp->mst_mgr.cbs = NULL;
- return ret;
- }
-
- return 0;
-}
-
-bool intel_dp_mst_source_support(struct intel_dp *intel_dp)
-{
- return intel_dp->mst_mgr.cbs;
-}
-
-void
-intel_dp_mst_encoder_cleanup(struct intel_digital_port *dig_port)
-{
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (!intel_dp_mst_source_support(intel_dp))
- return;
-
- drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
- /* encoders will get killed by normal cleanup */
-
- intel_dp->mst_mgr.cbs = NULL;
-}
-
-bool intel_dp_mst_is_master_trans(const struct intel_crtc_state *crtc_state)
-{
- return crtc_state->mst_master_transcoder == crtc_state->cpu_transcoder;
-}
-
-bool intel_dp_mst_is_slave_trans(const struct intel_crtc_state *crtc_state)
-{
- return crtc_state->mst_master_transcoder != INVALID_TRANSCODER &&
- crtc_state->mst_master_transcoder != crtc_state->cpu_transcoder;
-}
-
-/**
- * intel_dp_mst_add_topology_state_for_connector - add MST topology state for a connector
- * @state: atomic state
- * @connector: connector to add the state for
- * @crtc: the CRTC @connector is attached to
- *
- * Add the MST topology state for @connector to @state.
- *
- * Returns 0 on success, negative error code on failure.
- */
-static int
-intel_dp_mst_add_topology_state_for_connector(struct intel_atomic_state *state,
- struct intel_connector *connector,
- struct intel_crtc *crtc)
-{
- struct drm_dp_mst_topology_state *mst_state;
-
- if (!connector->mst_port)
- return 0;
-
- mst_state = drm_atomic_get_mst_topology_state(&state->base,
- &connector->mst_port->mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- mst_state->pending_crtc_mask |= drm_crtc_mask(&crtc->base);
-
- return 0;
-}
-
-/**
- * intel_dp_mst_add_topology_state_for_crtc - add MST topology state for a CRTC
- * @state: atomic state
- * @crtc: CRTC to add the state for
- *
- * Add the MST topology state for @crtc to @state.
- *
- * Returns 0 on success, negative error code on failure.
- */
-int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state *state,
- struct intel_crtc *crtc)
-{
- struct drm_connector *_connector;
- struct drm_connector_state *conn_state;
- int i;
-
- for_each_new_connector_in_state(&state->base, _connector, conn_state, i) {
- struct intel_connector *connector = to_intel_connector(_connector);
- int ret;
-
- if (conn_state->crtc != &crtc->base)
- continue;
-
- ret = intel_dp_mst_add_topology_state_for_connector(state, connector, crtc);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static struct intel_connector *
-get_connector_in_state_for_crtc(struct intel_atomic_state *state,
- const struct intel_crtc *crtc)
-{
- struct drm_connector_state *old_conn_state;
- struct drm_connector_state *new_conn_state;
- struct drm_connector *_connector;
- int i;
-
- for_each_oldnew_connector_in_state(&state->base, _connector,
- old_conn_state, new_conn_state, i) {
- struct intel_connector *connector =
- to_intel_connector(_connector);
-
- if (old_conn_state->crtc == &crtc->base ||
- new_conn_state->crtc == &crtc->base)
- return connector;
- }
-
- return NULL;
-}
-
-/**
- * intel_dp_mst_crtc_needs_modeset - check if changes in topology need to modeset the given CRTC
- * @state: atomic state
- * @crtc: CRTC for which to check the modeset requirement
- *
- * Check if any change in a MST topology requires a forced modeset on @crtc in
- * this topology. One such change is enabling/disabling the DSC decompression
- * state in the first branch device's UFP DPCD as required by one CRTC, while
- * the other @crtc in the same topology is still active, requiring a full modeset
- * on @crtc.
- */
-bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
- struct intel_crtc *crtc)
-{
- const struct intel_connector *crtc_connector;
- const struct drm_connector_state *conn_state;
- const struct drm_connector *_connector;
- int i;
-
- if (!intel_crtc_has_type(intel_atomic_get_new_crtc_state(state, crtc),
- INTEL_OUTPUT_DP_MST))
- return false;
-
- crtc_connector = get_connector_in_state_for_crtc(state, crtc);
-
- if (!crtc_connector)
- /* None of the connectors in the topology needs modeset */
- return false;
-
- for_each_new_connector_in_state(&state->base, _connector, conn_state, i) {
- const struct intel_connector *connector =
- to_intel_connector(_connector);
- const struct intel_crtc_state *new_crtc_state;
- const struct intel_crtc_state *old_crtc_state;
- struct intel_crtc *crtc_iter;
-
- if (connector->mst_port != crtc_connector->mst_port ||
- !conn_state->crtc)
- continue;
-
- crtc_iter = to_intel_crtc(conn_state->crtc);
-
- new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc_iter);
- old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc_iter);
-
- if (!intel_crtc_needs_modeset(new_crtc_state))
- continue;
-
- if (old_crtc_state->dsc.compression_enable ==
- new_crtc_state->dsc.compression_enable)
- continue;
- /*
- * Toggling the decompression flag because of this stream in
- * the first downstream branch device's UFP DPCD may reset the
- * whole branch device. To avoid the reset while other streams
- * are also active modeset the whole MST topology in this
- * case.
- */
- if (connector->dp.dsc_decompression_aux ==
- &connector->mst_port->aux)
- return true;
- }
-
- return false;
-}
-
-<<<<<<<
-/*
- * intel_dp_mst_verify_dpcd_state - verify the MST SW enabled state wrt. the DPCD
- * @intel_dp: DP port object
- *
- * Verify if @intel_dp's MST enabled SW state matches the corresponding DPCD
- * state. A long HPD pulse - not long enough to be detected as a disconnected
- * state - could've reset the DPCD state, which requires tearing
- * down/recreating the MST topology.
- *
- * Returns %true if the SW MST enabled and DPCD states match, %false
- * otherwise.
- */
-bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp)
-{
- struct intel_display *display = to_intel_display(intel_dp);
- struct intel_connector *connector = intel_dp->attached_connector;
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct intel_encoder *encoder = &dig_port->base;
- int ret;
- u8 val;
-
- if (!intel_dp->is_mst)
- return true;
-
- ret = drm_dp_dpcd_readb(intel_dp->mst_mgr.aux, DP_MSTM_CTRL, &val);
-
- /* Adjust the expected register value for SST + SideBand. */
- if (ret < 0 || val != (DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC)) {
- drm_dbg_kms(display->drm,
- "[CONNECTOR:%d:%s][ENCODER:%d:%s] MST mode got reset, removing topology (ret=%d, ctrl=0x%02x)\n",
- connector->base.base.id, connector->base.name,
- encoder->base.base.id, encoder->base.name,
- ret, val);
-
- return false;
- }
-
- return true;
-=======
-/**
- * intel_dp_mst_prepare_probe - Prepare an MST link for topology probing
- * @intel_dp: DP port object
- *
- * Prepare an MST link for topology probing, programming the target
- * link parameters to DPCD. This step is a requirement of the enumaration
- * of path resources during probing.
- */
-void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp)
-{
- int link_rate = intel_dp_max_link_rate(intel_dp);
- int lane_count = intel_dp_max_lane_count(intel_dp);
- u8 rate_select;
- u8 link_bw;
-
- if (intel_dp->link_trained)
- return;
-
- if (intel_mst_probed_link_params_valid(intel_dp, link_rate, lane_count))
- return;
-
- intel_dp_compute_rate(intel_dp, link_rate, &link_bw, &rate_select);
-
- intel_dp_link_training_set_mode(intel_dp, link_rate, false);
- intel_dp_link_training_set_bw(intel_dp, link_bw, rate_select, lane_count,
- drm_dp_enhanced_frame_cap(intel_dp->dpcd));
-
- intel_mst_set_probed_link_params(intel_dp, link_rate, lane_count);
->>>>>>>
-}
diff --git a/rr-cache/184514ddd9867f1a8f27d1e7de47d36242b6ad90/postimage b/rr-cache/184514ddd9867f1a8f27d1e7de47d36242b6ad90/postimage
deleted file mode 100644
index 4b433e16206d..000000000000
--- a/rr-cache/184514ddd9867f1a8f27d1e7de47d36242b6ad90/postimage
+++ /dev/null
@@ -1,12343 +0,0 @@
-/*
- * Copyright 2015 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
- *
- */
-
-/* The caprices of the preprocessor require that this be declared right here */
-#define CREATE_TRACE_POINTS
-
-#include "dm_services_types.h"
-#include "dc.h"
-#include "link_enc_cfg.h"
-#include "dc/inc/core_types.h"
-#include "dal_asic_id.h"
-#include "dmub/dmub_srv.h"
-#include "dc/inc/hw/dmcu.h"
-#include "dc/inc/hw/abm.h"
-#include "dc/dc_dmub_srv.h"
-#include "dc/dc_edid_parser.h"
-#include "dc/dc_stat.h"
-#include "dc/dc_state.h"
-#include "amdgpu_dm_trace.h"
-#include "dpcd_defs.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 "vid.h"
-#include "amdgpu.h"
-#include "amdgpu_display.h"
-#include "amdgpu_ucode.h"
-#include "atom.h"
-#include "amdgpu_dm.h"
-#include "amdgpu_dm_plane.h"
-#include "amdgpu_dm_crtc.h"
-#include "amdgpu_dm_hdcp.h"
-#include <drm/display/drm_hdcp_helper.h>
-#include "amdgpu_dm_wb.h"
-#include "amdgpu_pm.h"
-#include "amdgpu_atombios.h"
-
-#include "amd_shared.h"
-#include "amdgpu_dm_irq.h"
-#include "dm_helpers.h"
-#include "amdgpu_dm_mst_types.h"
-#if defined(CONFIG_DEBUG_FS)
-#include "amdgpu_dm_debugfs.h"
-#endif
-#include "amdgpu_dm_psr.h"
-#include "amdgpu_dm_replay.h"
-
-#include "ivsrcid/ivsrcid_vislands30.h"
-
-#include <linux/backlight.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/pm_runtime.h>
-#include <linux/pci.h>
-#include <linux/power_supply.h>
-#include <linux/firmware.h>
-#include <linux/component.h>
-#include <linux/dmi.h>
-#include <linux/sort.h>
-
-#include <drm/display/drm_dp_mst_helper.h>
-#include <drm/display/drm_hdmi_helper.h>
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_uapi.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_blend.h>
-#include <drm/drm_fixed.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_eld.h>
-#include <drm/drm_vblank.h>
-#include <drm/drm_audio_component.h>
-#include <drm/drm_gem_atomic_helper.h>
-
-#include <acpi/video.h>
-
-#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h"
-
-#include "dcn/dcn_1_0_offset.h"
-#include "dcn/dcn_1_0_sh_mask.h"
-#include "soc15_hw_ip.h"
-#include "soc15_common.h"
-#include "vega10_ip_offset.h"
-
-#include "gc/gc_11_0_0_offset.h"
-#include "gc/gc_11_0_0_sh_mask.h"
-
-#include "modules/inc/mod_freesync.h"
-#include "modules/power/power_helpers.h"
-
-#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_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
-
-/* Number of bytes in PSP header for firmware. */
-#define PSP_HEADER_BYTES 0x100
-
-/* Number of bytes in PSP footer for firmware. */
-#define PSP_FOOTER_BYTES 0x100
-
-/**
- * DOC: overview
- *
- * The AMDgpu display manager, **amdgpu_dm** (or even simpler,
- * **dm**) sits between DRM and DC. It acts as a liaison, converting DRM
- * requests into DC requests, and DC responses into DRM responses.
- *
- * The root control structure is &struct amdgpu_display_manager.
- */
-
-/* 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 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
- * provided by DAL. The drm strcutures are: drm_crtc, drm_connector,
- * drm_encoder, drm_mode_config
- *
- * Returns 0 on success
- */
-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 void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state);
-
-static int amdgpu_dm_atomic_check(struct drm_device *dev,
- struct drm_atomic_state *state);
-
-static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector);
-static void handle_hpd_rx_irq(void *param);
-
-static bool
-is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
- struct drm_crtc_state *new_crtc_state);
-/*
- * dm_vblank_get_counter
- *
- * @brief
- * Get counter for number of vertical blanks
- *
- * @param
- * struct amdgpu_device *adev - [in] desired amdgpu device
- * int disp_idx - [in] which CRTC to get the counter from
- *
- * @return
- * Counter for vertical blanks
- */
-static u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc)
-{
- struct amdgpu_crtc *acrtc = NULL;
-
- if (crtc >= adev->mode_info.num_crtc)
- return 0;
-
- acrtc = adev->mode_info.crtcs[crtc];
-
- if (!acrtc->dm_irq_params.stream) {
- DRM_ERROR("dc_stream_state is NULL for crtc '%d'!\n",
- crtc);
- return 0;
- }
-
- return dc_stream_get_vblank_counter(acrtc->dm_irq_params.stream);
-}
-
-static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
- u32 *vbl, u32 *position)
-{
- u32 v_blank_start = 0, v_blank_end = 0, h_position = 0, v_position = 0;
- struct amdgpu_crtc *acrtc = NULL;
- struct dc *dc = adev->dm.dc;
-
- if ((crtc < 0) || (crtc >= adev->mode_info.num_crtc))
- return -EINVAL;
-
- acrtc = adev->mode_info.crtcs[crtc];
-
- if (!acrtc->dm_irq_params.stream) {
- DRM_ERROR("dc_stream_state is NULL for crtc '%d'!\n",
- crtc);
- return 0;
- }
-
- if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed)
- dc_allow_idle_optimizations(dc, false);
-
- /*
- * TODO rework base driver to use values directly.
- * for now parse it back into reg-format
- */
- dc_stream_get_scanoutpos(acrtc->dm_irq_params.stream,
- &v_blank_start,
- &v_blank_end,
- &h_position,
- &v_position);
-
- *position = v_position | (h_position << 16);
- *vbl = v_blank_start | (v_blank_end << 16);
-
- return 0;
-}
-
-static bool dm_is_idle(void *handle)
-{
- /* XXX todo */
- return true;
-}
-
-static int dm_wait_for_idle(void *handle)
-{
- /* XXX todo */
- return 0;
-}
-
-static bool dm_check_soft_reset(void *handle)
-{
- return false;
-}
-
-static int dm_soft_reset(void *handle)
-{
- /* 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)
-{
- if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)
- return true;
- else if (amdgpu_dm_crtc_vrr_active(old_state) != amdgpu_dm_crtc_vrr_active(new_state))
- return true;
- else
- return false;
-}
-
-/*
- * 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)
-{
- const struct dc_surface_update *sa = (struct dc_surface_update *)a;
- const struct dc_surface_update *sb = (struct dc_surface_update *)b;
-
- /* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */
- return sb->surface->layer_index - sa->surface->layer_index;
-}
-
-/**
- * update_planes_and_stream_adapter() - Send planes to be updated in DC
- *
- * DC has a generic way to update planes and stream via
- * dc_update_planes_and_stream function; however, DM might need some
- * adjustments and preparation before calling it. This function is a wrapper
- * for the dc_update_planes_and_stream that does any required configuration
- * before passing control to DC.
- *
- * @dc: Display Core control structure
- * @update_type: specify whether it is FULL/MEDIUM/FAST update
- * @planes_count: planes count to update
- * @stream: stream state
- * @stream_update: stream update
- * @array_of_surface_update: dc surface update pointer
- *
- */
-static inline bool update_planes_and_stream_adapter(struct dc *dc,
- int update_type,
- int planes_count,
- struct dc_stream_state *stream,
- struct dc_stream_update *stream_update,
- struct dc_surface_update *array_of_surface_update)
-{
- sort(array_of_surface_update, planes_count,
- sizeof(*array_of_surface_update), dm_plane_layer_index_cmp, NULL);
-
- /*
- * Previous frame finished and HW is ready for optimization.
- */
- if (update_type == UPDATE_TYPE_FAST)
- dc_post_update_surfaces_to_stream(dc);
-
- return dc_update_planes_and_stream(dc,
- array_of_surface_update,
- planes_count,
- stream,
- 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_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) {
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /* BTR processing for pre-DCE12 ASICs */
- if (acrtc->dm_irq_params.stream &&
- 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);
-
- dc_stream_adjust_vmin_vmax(
- adev->dm.dc,
- 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 &&
- 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);
-
- dc_stream_adjust_vmin_vmax(adev->dm.dc, 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);
-}
-
-/**
- * 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_ERROR("DMUB HPD callback notification was NULL");
- return;
- }
-
- if (notify->link_index > adev->dm.dc->link_count) {
- DRM_ERROR("DMUB HPD index (%u)is abnormal", notify->link_index);
- 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("DMUB HPD IRQ callback: link_index=%u\n", link_index);
- else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ)
- DRM_INFO("DMUB HPD RX IRQ callback: link_index=%u\n", link_index);
- else
- DRM_WARN("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("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);
- }
- }
-}
-
-/**
- * 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_ERROR("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);
-
-}
-
-#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;
- static const char *const event_type[] = {
- "NO_DATA",
- "AUX_REPLY",
- "HPD",
- "HPD_IRQ",
- "SET_CONFIGC_REPLY",
- "DPIA_NOTIFICATION",
- "HPD_SENSE_NOTIFY",
- };
-
- 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_DEBUG_DRIVER("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_DEBUG_DRIVER("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, &notify);
- if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) {
- DRM_ERROR("DM: notify type %d invalid!", notify.type);
- continue;
- }
- if (!dm->dmub_callback[notify.type]) {
- DRM_WARN("DMUB notification skipped due to no handler: type=%s\n",
- event_type[notify.type]);
- continue;
- }
- if (dm->dmub_thread_offload[notify.type] == true) {
- dmub_hpd_wrk = kzalloc(sizeof(*dmub_hpd_wrk), GFP_ATOMIC);
- if (!dmub_hpd_wrk) {
- DRM_ERROR("Failed to allocate dmub_hpd_wrk");
- return;
- }
- dmub_hpd_wrk->dmub_notify = kmemdup(&notify, sizeof(struct dmub_notification),
- GFP_ATOMIC);
- if (!dmub_hpd_wrk->dmub_notify) {
- kfree(dmub_hpd_wrk);
- DRM_ERROR("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, &notify);
- }
- } while (notify.pending_notification);
- }
-}
-
-static int dm_set_clockgating_state(void *handle,
- enum amd_clockgating_state state)
-{
- return 0;
-}
-
-static int dm_set_powergating_state(void *handle,
- enum amd_powergating_state state)
-{
- return 0;
-}
-
-/* Prototypes of private functions */
-static int dm_early_init(void *handle);
-
-/* 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_ERROR("DM: Failed to initialize FBC\n");
- else {
- adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr;
- DRM_INFO("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;
- ret = drm_eld_size(connector->eld);
- memcpy(buf, connector->eld, min(max_bytes, ret));
-
- break;
- }
- drm_connector_list_iter_end(&conn_iter);
-
- mutex_unlock(&adev->dm.audio_lock);
-
- DRM_DEBUG_KMS("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_DEBUG_KMS("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 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_ERROR("No framebuffer info for DMUB service.\n");
- return -EINVAL;
- }
-
- if (!dmub_fw) {
- /* Firmware required for DMUB support. */
- DRM_ERROR("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_ERROR("Error checking HW support for DMUB: %d\n", status);
- return -EINVAL;
- }
-
- if (!has_hw_support) {
- DRM_INFO("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("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;
-
- 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 = le32_to_cpu(hdr->inst_const_bytes) -
- PSP_HEADER_BYTES - PSP_FOOTER_BYTES;
-
- 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.fb_base = adev->gmc.fb_start;
- hw_params.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];
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- hw_params.dpia_supported = true;
- hw_params.disable_dpia = adev->dm.dc->debug.dpia_debug.bits.disable_dpia;
- break;
- default:
- break;
- }
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- hw_params.ips_sequential_ono = adev->external_rev_id > 0x10;
- break;
- default:
- break;
- }
-
- status = dmub_srv_hw_init(dmub_srv, &hw_params);
- if (status != DMUB_STATUS_OK) {
- DRM_ERROR("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("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_ERROR("Couldn't allocate DC DMUB server!\n");
- return -ENOMEM;
- }
-
- DRM_INFO("DMUB hardware initialized: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
-
- 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("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("Wait for DMUB auto-load failed: %d\n", status);
- } else {
- /* Perform the full hardware initialization. */
- r = dm_dmub_hw_init(adev);
- if (r)
- DRM_ERROR("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;
- u32 logical_addr_low;
- u32 logical_addr_high;
- u32 agp_base, agp_bot, agp_top;
- PHYSICAL_ADDRESS_LOC page_table_start, page_table_end, page_table_base;
-
- memset(pa_config, 0, sizeof(*pa_config));
-
- agp_base = 0;
- agp_bot = adev->gmc.agp_start >> 24;
- agp_top = adev->gmc.agp_end >> 24;
-
- /* AGP aperture is disabled */
- if (agp_bot > agp_top) {
- logical_addr_low = adev->gmc.fb_start >> 18;
- if (adev->apu_flags & (AMD_APU_IS_RAVEN2 |
- AMD_APU_IS_RENOIR |
- AMD_APU_IS_GREEN_SARDINE))
- /*
- * Raven2 has a HW issue that it is unable to use the vram which
- * is out of MC_VM_SYSTEM_APERTURE_HIGH_ADDR. So here is the
- * workaround that increase system aperture high address (add 1)
- * to get rid of the VM fault and hardware hang.
- */
- logical_addr_high = (adev->gmc.fb_end >> 18) + 0x1;
- else
- logical_addr_high = adev->gmc.fb_end >> 18;
- } else {
- logical_addr_low = min(adev->gmc.fb_start, adev->gmc.agp_start) >> 18;
- if (adev->apu_flags & (AMD_APU_IS_RAVEN2 |
- AMD_APU_IS_RENOIR |
- AMD_APU_IS_GREEN_SARDINE))
- /*
- * Raven2 has a HW issue that it is unable to use the vram which
- * is out of MC_VM_SYSTEM_APERTURE_HIGH_ADDR. So here is the
- * workaround that increase system aperture high address (add 1)
- * to get rid of the VM fault and hardware hang.
- */
- logical_addr_high = max((adev->gmc.fb_end >> 18) + 0x1, adev->gmc.agp_end >> 18);
- else
- logical_addr_high = max(adev->gmc.fb_end, adev->gmc.agp_end) >> 18;
- }
-
- pt_base = amdgpu_gmc_pd_addr(adev->gart.bo);
-
- page_table_start.high_part = upper_32_bits(adev->gmc.gart_start >>
- AMDGPU_GPU_PAGE_SHIFT);
- page_table_start.low_part = lower_32_bits(adev->gmc.gart_start >>
- AMDGPU_GPU_PAGE_SHIFT);
- page_table_end.high_part = upper_32_bits(adev->gmc.gart_end >>
- AMDGPU_GPU_PAGE_SHIFT);
- page_table_end.low_part = lower_32_bits(adev->gmc.gart_end >>
- AMDGPU_GPU_PAGE_SHIFT);
- page_table_base.high_part = upper_32_bits(pt_base);
- page_table_base.low_part = lower_32_bits(pt_base);
-
- pa_config->system_aperture.start_addr = (uint64_t)logical_addr_low << 18;
- pa_config->system_aperture.end_addr = (uint64_t)logical_addr_high << 18;
-
- pa_config->system_aperture.agp_base = (uint64_t)agp_base << 24;
- pa_config->system_aperture.agp_bot = (uint64_t)agp_bot << 24;
- pa_config->system_aperture.agp_top = (uint64_t)agp_top << 24;
-
- pa_config->system_aperture.fb_base = adev->gmc.fb_start;
- pa_config->system_aperture.fb_offset = adev->vm_manager.vram_base_offset;
- pa_config->system_aperture.fb_top = adev->gmc.fb_end;
-
- pa_config->gart_config.page_table_start_addr = page_table_start.quad_part << 12;
- pa_config->gart_config.page_table_end_addr = page_table_end.quad_part << 12;
- pa_config->gart_config.page_table_base_addr = page_table_base.quad_part;
-
- pa_config->is_hvm_enabled = adev->mode_info.gpu_vm_support;
-
-}
-
-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;
-
- if (!aconnector) {
- DRM_ERROR("Can't retrieve aconnector in hpd_rx_irq_offload_work");
- goto skip;
- }
-
- adev = drm_to_adev(aconnector->base.dev);
- dc_link = aconnector->dc_link;
-
- mutex_lock(&aconnector->hpd_lock);
- if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
- DRM_ERROR("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 dc *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 = kcalloc(max_caps, sizeof(*hpd_rx_offload_wq), GFP_KERNEL);
-
- 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_ERROR("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;
- u16 subsys_vendor;
- u16 subsys_device;
- u8 revision;
-};
-
-static const struct amdgpu_stutter_quirk amdgpu_stutter_quirk_list[] = {
- /* https://bugzilla.kernel.org/show_bug.cgi?id=214417 */
- { 0x1002, 0x15dd, 0x1002, 0x15dd, 0xc8 },
- { 0, 0, 0, 0, 0 },
-};
-
-static bool dm_should_disable_stutter(struct pci_dev *pdev)
-{
- const struct amdgpu_stutter_quirk *p = amdgpu_stutter_quirk_list;
-
- while (p && p->chip_device != 0) {
- if (pdev->vendor == p->chip_vendor &&
- pdev->device == p->chip_device &&
- pdev->subsystem_vendor == p->subsys_vendor &&
- pdev->subsystem_device == p->subsys_device &&
- pdev->revision == p->revision) {
- return true;
- }
- ++p;
- }
- return false;
-}
-
-static const struct dmi_system_id hpd_disconnect_quirk_table[] = {
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3660"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3260"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3460"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex Tower Plus 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex Tower 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex SFF Plus 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex SFF 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex Micro Plus 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex Micro 7010"),
- },
- },
- {}
- /* TODO: refactor this from a fixed table to a dynamic option */
-};
-
-static void retrieve_dmi_info(struct amdgpu_display_manager *dm)
-{
- const struct dmi_system_id *dmi_id;
-
- dm->aux_hpd_discon_quirk = false;
-
- dmi_id = dmi_first_match(hpd_disconnect_quirk_table);
- if (dmi_id) {
- dm->aux_hpd_discon_quirk = true;
- DRM_INFO("aux_hpd_discon_quirk attached\n");
- }
-}
-
-void*
-dm_allocate_gpu_mem(
- struct amdgpu_device *adev,
- enum dc_gpu_mem_alloc_type type,
- size_t size,
- long long *addr)
-{
- struct dal_allocation *da;
- u32 domain = (type == DC_MEM_ALLOC_TYPE_GART) ?
- AMDGPU_GEM_DOMAIN_GTT : AMDGPU_GEM_DOMAIN_VRAM;
- int ret;
-
- da = kzalloc(sizeof(struct dal_allocation), GFP_KERNEL);
- if (!da)
- return NULL;
-
- ret = amdgpu_bo_create_kernel(adev, size, PAGE_SIZE,
- domain, &da->bo,
- &da->gpu_addr, &da->cpu_ptr);
-
- *addr = da->gpu_addr;
-
- if (ret) {
- kfree(da);
- return NULL;
- }
-
- /* add da to list in dm */
- list_add(&da->list, &adev->dm.da_list);
-
- return da->cpu_ptr;
-}
-
-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 struct dml2_soc_bb *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev)
-{
- struct dml2_soc_bb *bb;
- long long addr;
- 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):
- break;
- default:
- return NULL;
- }
-
- bb = dm_allocate_gpu_mem(adev,
- DC_MEM_ALLOC_TYPE_GART,
- sizeof(struct dml2_soc_bb),
- &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)
- /* No need to free bb here since it shall be done in dm_sw_fini() */
- return NULL;
- }
-
- /* 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)
- return NULL;
-
- return bb;
-}
-
-static int amdgpu_dm_init(struct amdgpu_device *adev)
-{
- struct dc_init_data init_data;
- struct dc_callback_init init_params;
- int r;
-
- adev->dm.ddev = adev_to_drm(adev);
- adev->dm.adev = adev;
-
- /* Zero all the fields */
- memset(&init_data, 0, sizeof(init_data));
- memset(&init_params, 0, sizeof(init_params));
-
- mutex_init(&adev->dm.dpia_aux_lock);
- mutex_init(&adev->dm.dc_lock);
- mutex_init(&adev->dm.audio_lock);
-
- if (amdgpu_dm_irq_init(adev)) {
- DRM_ERROR("amdgpu: failed to initialize DM IRQ support.\n");
- goto error;
- }
-
- init_data.asic_id.chip_family = adev->family;
-
- init_data.asic_id.pci_revision_id = adev->pdev->revision;
- init_data.asic_id.hw_internal_rev = adev->external_rev_id;
- init_data.asic_id.chip_id = adev->pdev->device;
-
- init_data.asic_id.vram_width = adev->gmc.vram_width;
- /* TODO: initialize init_data.asic_id.vram_type here!!!! */
- init_data.asic_id.atombios_base_address =
- adev->mode_info.atom_context->bios;
-
- init_data.driver = adev;
-
- /* cgs_device was created in dm_sw_init() */
- init_data.cgs_device = adev->dm.cgs_device;
-
- init_data.dce_environment = DCE_ENV_PRODUCTION_DRV;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- switch (adev->dm.dmcub_fw_version) {
- case 0: /* development */
- case 0x1: /* linux-firmware.git hash 6d9f399 */
- case 0x01000000: /* linux-firmware.git hash 9a0b0f4 */
- init_data.flags.disable_dmcu = false;
- break;
- default:
- init_data.flags.disable_dmcu = true;
- }
- break;
- case IP_VERSION(2, 0, 3):
- init_data.flags.disable_dmcu = true;
- break;
- default:
- break;
- }
-
- /* APU support S/G display by default except:
- * ASICs before Carrizo,
- * RAVEN1 (Users reported stability issue)
- */
-
- if (adev->asic_type < CHIP_CARRIZO) {
- init_data.flags.gpu_vm_support = false;
- } else if (adev->asic_type == CHIP_RAVEN) {
- if (adev->apu_flags & AMD_APU_IS_RAVEN)
- init_data.flags.gpu_vm_support = false;
- else
- init_data.flags.gpu_vm_support = (amdgpu_sg_display != 0);
- } else {
- init_data.flags.gpu_vm_support = (amdgpu_sg_display != 0) && (adev->flags & AMD_IS_APU);
- }
-
- adev->mode_info.gpu_vm_support = init_data.flags.gpu_vm_support;
-
- if (amdgpu_dc_feature_mask & DC_FBC_MASK)
- init_data.flags.fbc_support = true;
-
- if (amdgpu_dc_feature_mask & DC_MULTI_MON_PP_MCLK_SWITCH_MASK)
- init_data.flags.multi_mon_pp_mclk_switch = true;
-
- if (amdgpu_dc_feature_mask & DC_DISABLE_FRACTIONAL_PWM_MASK)
- init_data.flags.disable_fractional_pwm = true;
-
- if (amdgpu_dc_feature_mask & DC_EDP_NO_POWER_SEQUENCING)
- init_data.flags.edp_no_power_sequencing = true;
-
- if (amdgpu_dc_feature_mask & DC_DISABLE_LTTPR_DP1_4A)
- init_data.flags.allow_lttpr_non_transparent_mode.bits.DP1_4A = true;
- if (amdgpu_dc_feature_mask & DC_DISABLE_LTTPR_DP2_0)
- init_data.flags.allow_lttpr_non_transparent_mode.bits.DP2_0 = true;
-
- init_data.flags.seamless_boot_edp_requested = false;
-
- if (amdgpu_device_seamless_boot_supported(adev)) {
- init_data.flags.seamless_boot_edp_requested = true;
- init_data.flags.allow_seamless_boot_optimization = true;
- DRM_INFO("Seamless boot condition check passed\n");
- }
-
- init_data.flags.enable_mipi_converter_optimization = true;
-
- init_data.dcn_reg_offsets = adev->reg_offset[DCE_HWIP][0];
- init_data.nbio_reg_offsets = adev->reg_offset[NBIO_HWIP][0];
- init_data.clk_reg_offsets = adev->reg_offset[CLK_HWIP][0];
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_IPS)
- init_data.flags.disable_ips = DMUB_IPS_DISABLE_ALL;
- else
- init_data.flags.disable_ips = DMUB_IPS_ENABLE;
-
- init_data.flags.disable_ips_in_vpb = 0;
-
- /* Enable DWB for tested platforms only */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 0, 0))
- init_data.num_virtual_links = 1;
-
- retrieve_dmi_info(&adev->dm);
-
- if (adev->dm.bb_from_dmub)
- init_data.bb_from_dmub = adev->dm.bb_from_dmub;
- else
- init_data.bb_from_dmub = NULL;
-
- /* Display Core create. */
- adev->dm.dc = dc_create(&init_data);
-
- if (adev->dm.dc) {
- DRM_INFO("Display Core v%s initialized on %s\n", DC_VER,
- dce_version_to_string(adev->dm.dc->ctx->dce_version));
- } else {
- DRM_INFO("Display Core failed to initialize with v%s!\n", DC_VER);
- goto error;
- }
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_PIPE_SPLIT) {
- adev->dm.dc->debug.force_single_disp_pipe_split = false;
- adev->dm.dc->debug.pipe_split_policy = MPC_SPLIT_AVOID;
- }
-
- if (adev->asic_type != CHIP_CARRIZO && adev->asic_type != CHIP_STONEY)
- adev->dm.dc->debug.disable_stutter = amdgpu_pp_feature_mask & PP_STUTTER_MODE ? false : true;
- if (dm_should_disable_stutter(adev->pdev))
- adev->dm.dc->debug.disable_stutter = true;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_STUTTER)
- adev->dm.dc->debug.disable_stutter = true;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_DSC)
- adev->dm.dc->debug.disable_dsc = true;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_CLOCK_GATING)
- adev->dm.dc->debug.disable_clock_gate = true;
-
- if (amdgpu_dc_debug_mask & DC_FORCE_SUBVP_MCLK_SWITCH)
- adev->dm.dc->debug.force_subvp_mclk_switch = true;
-
- if (amdgpu_dc_debug_mask & DC_ENABLE_DML2) {
- adev->dm.dc->debug.using_dml2 = true;
- adev->dm.dc->debug.using_dml21 = true;
- }
-
- adev->dm.dc->debug.visual_confirm = amdgpu_dc_visual_confirm;
-
- /* TODO: Remove after DP2 receiver gets proper support of Cable ID feature */
- adev->dm.dc->debug.ignore_cable_id = true;
-
- if (adev->dm.dc->caps.dp_hdmi21_pcon_support)
- DRM_INFO("DP-HDMI FRL PCON supported\n");
-
- r = dm_dmub_hw_init(adev);
- if (r) {
- DRM_ERROR("DMUB interface failed to initialize: status=%d\n", r);
- goto error;
- }
-
- dc_hardware_init(adev->dm.dc);
-
- adev->dm.hpd_rx_offload_wq = hpd_rx_irq_create_workqueue(adev->dm.dc);
- if (!adev->dm.hpd_rx_offload_wq) {
- DRM_ERROR("amdgpu: failed to create hpd rx offload workqueue.\n");
- goto error;
- }
-
- if ((adev->flags & AMD_IS_APU) && (adev->asic_type >= CHIP_CARRIZO)) {
- struct dc_phy_addr_space_config pa_config;
-
- mmhub_read_system_context(adev, &pa_config);
-
- // Call the DC init_memory func
- dc_setup_system_context(adev->dm.dc, &pa_config);
- }
-
- adev->dm.freesync_module = mod_freesync_create(adev->dm.dc);
- if (!adev->dm.freesync_module) {
- DRM_ERROR(
- "amdgpu: failed to initialize freesync_module.\n");
- } else
- DRM_DEBUG_DRIVER("amdgpu: freesync_module init done %p.\n",
- adev->dm.freesync_module);
-
- amdgpu_dm_init_color_mod();
-
- if (adev->dm.dc->caps.max_links > 0) {
- adev->dm.vblank_control_workqueue =
- create_singlethread_workqueue("dm_vblank_control_workqueue");
- if (!adev->dm.vblank_control_workqueue)
- DRM_ERROR("amdgpu: failed to initialize vblank_workqueue.\n");
- }
-
- if (adev->dm.dc->caps.ips_support && adev->dm.dc->config.disable_ips == DMUB_IPS_ENABLE)
- adev->dm.idle_workqueue = idle_create_workqueue(adev);
-
- if (adev->dm.dc->caps.max_links > 0 && adev->family >= AMDGPU_FAMILY_RV) {
- adev->dm.hdcp_workqueue = hdcp_create_workqueue(adev, &init_params.cp_psp, adev->dm.dc);
-
- if (!adev->dm.hdcp_workqueue)
- DRM_ERROR("amdgpu: failed to initialize hdcp_workqueue.\n");
- else
- DRM_DEBUG_DRIVER("amdgpu: hdcp_workqueue init done %p.\n", adev->dm.hdcp_workqueue);
-
- dc_init_callbacks(adev->dm.dc, &init_params);
- }
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- init_completion(&adev->dm.dmub_aux_transfer_done);
- adev->dm.dmub_notify = kzalloc(sizeof(struct dmub_notification), GFP_KERNEL);
- if (!adev->dm.dmub_notify) {
- DRM_INFO("amdgpu: fail to allocate adev->dm.dmub_notify");
- goto error;
- }
-
- adev->dm.delayed_hpd_wq = create_singlethread_workqueue("amdgpu_dm_hpd_wq");
- if (!adev->dm.delayed_hpd_wq) {
- DRM_ERROR("amdgpu: failed to create hpd offload workqueue.\n");
- goto error;
- }
-
- amdgpu_dm_outbox_init(adev);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
- dmub_aux_setconfig_callback, false)) {
- DRM_ERROR("amdgpu: fail to register dmub aux 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.
- */
- dc_enable_dmub_outbox(adev->dm.dc);
-
- /* DPIA trace goes to dmesg logs only if outbox is enabled */
- if (amdgpu_dc_debug_mask & DC_ENABLE_DPIA_TRACE)
- dc_dmub_srv_enable_dpia_trace(adev->dm.dc);
- }
-
- if (amdgpu_dm_initialize_drm_device(adev)) {
- DRM_ERROR(
- "amdgpu: failed to initialize sw for display support.\n");
- goto error;
- }
-
- /* create fake encoders for MST */
- dm_dp_create_fake_mst_encoders(adev);
-
- /* TODO: Add_display_info? */
-
- /* TODO use dynamic cursor width */
- adev_to_drm(adev)->mode_config.cursor_width = adev->dm.dc->caps.max_cursor_size;
- adev_to_drm(adev)->mode_config.cursor_height = adev->dm.dc->caps.max_cursor_size;
-
- if (drm_vblank_init(adev_to_drm(adev), adev->dm.display_indexes_num)) {
- DRM_ERROR(
- "amdgpu: failed to initialize sw for display support.\n");
- goto error;
- }
-
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- adev->dm.secure_display_ctxs = amdgpu_dm_crtc_secure_display_create_contexts(adev);
- if (!adev->dm.secure_display_ctxs)
- DRM_ERROR("amdgpu: failed to initialize secure display contexts.\n");
-#endif
-
- DRM_DEBUG_DRIVER("KMS initialized.\n");
-
- return 0;
-error:
- amdgpu_dm_fini(adev);
-
- return -EINVAL;
-}
-
-static int amdgpu_dm_early_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- amdgpu_dm_audio_fini(adev);
-
- return 0;
-}
-
-static void amdgpu_dm_fini(struct amdgpu_device *adev)
-{
- int i;
-
- if (adev->dm.vblank_control_workqueue) {
- destroy_workqueue(adev->dm.vblank_control_workqueue);
- adev->dm.vblank_control_workqueue = NULL;
- }
-
- if (adev->dm.idle_workqueue) {
- if (adev->dm.idle_workqueue->running) {
- adev->dm.idle_workqueue->enable = false;
- flush_work(&adev->dm.idle_workqueue->work);
- }
-
- kfree(adev->dm.idle_workqueue);
- adev->dm.idle_workqueue = NULL;
- }
-
- amdgpu_dm_destroy_drm_device(&adev->dm);
-
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- if (adev->dm.secure_display_ctxs) {
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- if (adev->dm.secure_display_ctxs[i].crtc) {
- flush_work(&adev->dm.secure_display_ctxs[i].notify_ta_work);
- flush_work(&adev->dm.secure_display_ctxs[i].forward_roi_work);
- }
- }
- kfree(adev->dm.secure_display_ctxs);
- adev->dm.secure_display_ctxs = NULL;
- }
-#endif
- if (adev->dm.hdcp_workqueue) {
- hdcp_destroy(&adev->dev->kobj, adev->dm.hdcp_workqueue);
- adev->dm.hdcp_workqueue = NULL;
- }
-
- if (adev->dm.dc) {
- dc_deinit_callbacks(adev->dm.dc);
- dc_dmub_srv_destroy(&adev->dm.dc->ctx->dmub_srv);
- if (dc_enable_dmub_notifications(adev->dm.dc)) {
- kfree(adev->dm.dmub_notify);
- adev->dm.dmub_notify = NULL;
- destroy_workqueue(adev->dm.delayed_hpd_wq);
- adev->dm.delayed_hpd_wq = NULL;
- }
- }
-
- if (adev->dm.dmub_bo)
- amdgpu_bo_free_kernel(&adev->dm.dmub_bo,
- &adev->dm.dmub_bo_gpu_addr,
- &adev->dm.dmub_bo_cpu_addr);
-
- if (adev->dm.hpd_rx_offload_wq && adev->dm.dc) {
- for (i = 0; i < adev->dm.dc->caps.max_links; i++) {
- if (adev->dm.hpd_rx_offload_wq[i].wq) {
- destroy_workqueue(adev->dm.hpd_rx_offload_wq[i].wq);
- adev->dm.hpd_rx_offload_wq[i].wq = NULL;
- }
- }
-
- kfree(adev->dm.hpd_rx_offload_wq);
- adev->dm.hpd_rx_offload_wq = NULL;
- }
-
- /* DC Destroy TODO: Replace destroy DAL */
- if (adev->dm.dc)
- dc_destroy(&adev->dm.dc);
- /*
- * TODO: pageflip, vlank interrupt
- *
- * amdgpu_dm_irq_fini(adev);
- */
-
- if (adev->dm.cgs_device) {
- amdgpu_cgs_destroy_device(adev->dm.cgs_device);
- adev->dm.cgs_device = NULL;
- }
- if (adev->dm.freesync_module) {
- mod_freesync_destroy(adev->dm.freesync_module);
- adev->dm.freesync_module = NULL;
- }
-
- mutex_destroy(&adev->dm.audio_lock);
- mutex_destroy(&adev->dm.dc_lock);
- mutex_destroy(&adev->dm.dpia_aux_lock);
-}
-
-static int load_dmcu_fw(struct amdgpu_device *adev)
-{
- const char *fw_name_dmcu = NULL;
- int r;
- const struct dmcu_firmware_header_v1_0 *hdr;
-
- switch (adev->asic_type) {
-#if defined(CONFIG_DRM_AMD_DC_SI)
- case CHIP_TAHITI:
- case CHIP_PITCAIRN:
- case CHIP_VERDE:
- case CHIP_OLAND:
-#endif
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- case CHIP_KAVERI:
- case CHIP_KABINI:
- case CHIP_MULLINS:
- case CHIP_TONGA:
- case CHIP_FIJI:
- case CHIP_CARRIZO:
- case CHIP_STONEY:
- case CHIP_POLARIS11:
- case CHIP_POLARIS10:
- case CHIP_POLARIS12:
- case CHIP_VEGAM:
- case CHIP_VEGA10:
- case CHIP_VEGA12:
- case CHIP_VEGA20:
- return 0;
- case CHIP_NAVI12:
- fw_name_dmcu = FIRMWARE_NAVI12_DMCU;
- break;
- case CHIP_RAVEN:
- if (ASICREV_IS_PICASSO(adev->external_rev_id))
- fw_name_dmcu = FIRMWARE_RAVEN_DMCU;
- else if (ASICREV_IS_RAVEN2(adev->external_rev_id))
- fw_name_dmcu = FIRMWARE_RAVEN_DMCU;
- else
- return 0;
- break;
- default:
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(2, 0, 3):
- case IP_VERSION(2, 0, 0):
- case IP_VERSION(2, 1, 0):
- case IP_VERSION(3, 0, 0):
- case IP_VERSION(3, 0, 2):
- case IP_VERSION(3, 0, 3):
- case IP_VERSION(3, 0, 1):
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- return 0;
- default:
- break;
- }
- DRM_ERROR("Unsupported ASIC type: 0x%X\n", adev->asic_type);
- return -EINVAL;
- }
-
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
- DRM_DEBUG_KMS("dm: DMCU firmware not supported on direct or SMU loading\n");
- return 0;
- }
-
- r = amdgpu_ucode_request(adev, &adev->dm.fw_dmcu, "%s", fw_name_dmcu);
- if (r == -ENODEV) {
- /* DMCU firmware is not necessary, so don't raise a fuss if it's missing */
- DRM_DEBUG_KMS("dm: DMCU firmware not found\n");
- adev->dm.fw_dmcu = NULL;
- return 0;
- }
- if (r) {
- dev_err(adev->dev, "amdgpu_dm: Can't validate firmware \"%s\"\n",
- fw_name_dmcu);
- amdgpu_ucode_release(&adev->dm.fw_dmcu);
- return r;
- }
-
- hdr = (const struct dmcu_firmware_header_v1_0 *)adev->dm.fw_dmcu->data;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_ERAM].ucode_id = AMDGPU_UCODE_ID_DMCU_ERAM;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_ERAM].fw = adev->dm.fw_dmcu;
- adev->firmware.fw_size +=
- ALIGN(le32_to_cpu(hdr->header.ucode_size_bytes) - le32_to_cpu(hdr->intv_size_bytes), PAGE_SIZE);
-
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_INTV].ucode_id = AMDGPU_UCODE_ID_DMCU_INTV;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_INTV].fw = adev->dm.fw_dmcu;
- adev->firmware.fw_size +=
- ALIGN(le32_to_cpu(hdr->intv_size_bytes), PAGE_SIZE);
-
- adev->dm.dmcu_fw_version = le32_to_cpu(hdr->header.ucode_version);
-
- DRM_DEBUG_KMS("PSP loading DMCU firmware\n");
-
- 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_region_params region_params;
- struct dmub_srv_region_info region_info;
- struct dmub_srv_memory_params memory_params;
- 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_SHARED_STATE
- };
- 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(4, 0, 1):
- dmub_asic = DMUB_ASIC_DCN401;
- 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("Loading DMUB firmware via PSP: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
- }
-
-
- adev->dm.dmub_srv = kzalloc(sizeof(*adev->dm.dmub_srv), GFP_KERNEL);
- dmub_srv = adev->dm.dmub_srv;
-
- if (!dmub_srv) {
- DRM_ERROR("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_ERROR("Error creating DMUB service: %d\n", status);
- return -EINVAL;
- }
-
- /* Calculate the size of all the regions for the DMUB service. */
- memset(&region_params, 0, sizeof(region_params));
-
- region_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
- PSP_HEADER_BYTES - PSP_FOOTER_BYTES;
- region_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
- region_params.vbios_size = adev->bios_size;
- region_params.fw_bss_data = region_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;
- region_params.fw_inst_const =
- adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES;
- region_params.window_memory_type = window_memory_type;
-
- status = dmub_srv_calc_region_info(dmub_srv, &region_params,
- &region_info);
-
- if (status != DMUB_STATUS_OK) {
- DRM_ERROR("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 = &region_info;
- memory_params.window_memory_type = window_memory_type;
-
- adev->dm.dmub_fb_info =
- kzalloc(sizeof(*adev->dm.dmub_fb_info), GFP_KERNEL);
- fb_info = adev->dm.dmub_fb_info;
-
- if (!fb_info) {
- DRM_ERROR(
- "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_ERROR("Error calculating DMUB FB info: %d\n", status);
- return -EINVAL;
- }
-
- adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
-
- return 0;
-}
-
-static int dm_sw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- adev->dm.cgs_device = amdgpu_cgs_create_device(adev);
-
- if (!adev->dm.cgs_device) {
- DRM_ERROR("amdgpu: failed to create cgs device.\n");
- return -EINVAL;
- }
-
- /* Moved from dm init since we need to use allocations for storing bounding box data */
- INIT_LIST_HEAD(&adev->dm.da_list);
-
- r = dm_dmub_sw_init(adev);
- if (r)
- return r;
-
- return load_dmcu_fw(adev);
-}
-
-static int dm_sw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct dal_allocation *da;
-
- list_for_each_entry(da, &adev->dm.da_list, list) {
- if (adev->dm.bb_from_dmub == (void *) da->cpu_ptr) {
- amdgpu_bo_free_kernel(&da->bo, &da->gpu_addr, &da->cpu_ptr);
- list_del(&da->list);
- kfree(da);
- break;
- }
- }
-
- adev->dm.bb_from_dmub = NULL;
-
- kfree(adev->dm.dmub_fb_info);
- adev->dm.dmub_fb_info = NULL;
-
- if (adev->dm.dmub_srv) {
- dmub_srv_destroy(adev->dm.dmub_srv);
- kfree(adev->dm.dmub_srv);
- adev->dm.dmub_srv = NULL;
- }
-
- amdgpu_ucode_release(&adev->dm.dmub_fw);
- amdgpu_ucode_release(&adev->dm.fw_dmcu);
-
- 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 int dm_late_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- struct dmcu_iram_parameters params;
- unsigned int linear_lut[16];
- int i;
- struct dmcu *dmcu = NULL;
-
- dmcu = adev->dm.dc->res_pool->dmcu;
-
- for (i = 0; i < 16; i++)
- linear_lut[i] = 0xFFFF * i / 15;
-
- params.set = 0;
- params.backlight_ramping_override = false;
- params.backlight_ramping_start = 0xCCCC;
- params.backlight_ramping_reduction = 0xCCCCCCCC;
- params.backlight_lut_array_size = 16;
- params.backlight_lut_array = linear_lut;
-
- /* Min backlight level after ABM reduction, Don't allow below 1%
- * 0xFFFF x 0.01 = 0x28F
- */
- params.min_abm_backlight = 0x28F;
- /* In the case where abm is implemented on dmcub,
- * dmcu object will be null.
- * ABM 2.4 and up are implemented on dmcub.
- */
- if (dmcu) {
- if (!dmcu_load_iram(dmcu, params))
- return -EINVAL;
- } else if (adev->dm.dc->ctx->dmub_srv) {
- struct dc_link *edp_links[MAX_NUM_EDP];
- int edp_num;
-
- dc_get_edp_links(adev->dm.dc, edp_links, &edp_num);
- for (i = 0; i < edp_num; i++) {
- if (!dmub_init_abm_config(adev->dm.dc->res_pool, params, i))
- return -EINVAL;
- }
- }
-
- return detect_mst_link_for_all_connectors(adev_to_drm(adev));
-}
-
-static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
-{
- int ret;
- u8 guid[16];
- u64 tmp64;
-
- mutex_lock(&mgr->lock);
- if (!mgr->mst_primary)
- goto out_fail;
-
- if (drm_dp_read_dpcd_caps(mgr->aux, mgr->dpcd) < 0) {
- drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
- goto out_fail;
- }
-
- ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
- DP_MST_EN |
- DP_UP_REQ_EN |
- DP_UPSTREAM_IS_SRC);
- if (ret < 0) {
- drm_dbg_kms(mgr->dev, "mst write failed - undocked during suspend?\n");
- goto out_fail;
- }
-
- /* Some hubs forget their guids after they resume */
- ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
- if (ret != 16) {
- drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
- goto out_fail;
- }
-
- if (memchr_inv(guid, 0, 16) == NULL) {
- tmp64 = get_jiffies_64();
- memcpy(&guid[0], &tmp64, sizeof(u64));
- memcpy(&guid[8], &tmp64, sizeof(u64));
-
- ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, guid, 16);
-
- if (ret != 16) {
- drm_dbg_kms(mgr->dev, "check mstb guid failed - undocked during suspend?\n");
- goto out_fail;
- }
- }
-
- memcpy(mgr->mst_primary->guid, guid, 16);
-
-out_fail:
- mutex_unlock(&mgr->lock);
-}
-
-static void s3_handle_mst(struct drm_device *dev, bool suspend)
-{
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- struct drm_dp_mst_topology_mgr *mgr;
-
- 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_root)
- continue;
-
- mgr = &aconnector->mst_mgr;
-
- if (suspend) {
- drm_dp_mst_topology_mgr_suspend(mgr);
- } else {
- /* if extended timeout is supported in hardware,
- * default to LTTPR timeout (3.2ms) first as a W/A for DP link layer
- * CTS 4.2.1.1 regression introduced by CTS specs requirement update.
- */
- try_to_configure_aux_timeout(aconnector->dc_link->ddc, LINK_AUX_DEFAULT_LTTPR_TIMEOUT_PERIOD);
- if (!dp_is_lttpr_present(aconnector->dc_link))
- try_to_configure_aux_timeout(aconnector->dc_link->ddc, LINK_AUX_DEFAULT_TIMEOUT_PERIOD);
-
- /* TODO: move resume_mst_branch_status() into drm mst resume again
- * once topology probing work is pulled out from mst resume into mst
- * resume 2nd step. mst resume 2nd step should be called after old
- * state getting restored (i.e. drm_atomic_helper_resume()).
- */
- resume_mst_branch_status(mgr);
- }
- }
- drm_connector_list_iter_end(&iter);
-}
-
-static int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev)
-{
- int ret = 0;
-
- /* This interface is for dGPU Navi1x.Linux dc-pplib interface depends
- * on window driver dc implementation.
- * For Navi1x, clock settings of dcn watermarks are fixed. the settings
- * should be passed to smu during boot up and resume from s3.
- * boot up: dc calculate dcn watermark clock settings within dc_create,
- * dcn20_resource_construct
- * then call pplib functions below to pass the settings to smu:
- * smu_set_watermarks_for_clock_ranges
- * smu_set_watermarks_table
- * navi10_set_watermarks_table
- * smu_write_watermarks_table
- *
- * For Renoir, clock settings of dcn watermark are also fixed values.
- * dc has implemented different flow for window driver:
- * dc_hardware_init / dc_set_power_state
- * dcn10_init_hw
- * notify_wm_ranges
- * set_wm_ranges
- * -- Linux
- * smu_set_watermarks_for_clock_ranges
- * renoir_set_watermarks_table
- * smu_write_watermarks_table
- *
- * For Linux,
- * dc_hardware_init -> amdgpu_dm_init
- * dc_set_power_state --> dm_resume
- *
- * therefore, this function apply to navi10/12/14 but not Renoir
- * *
- */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(2, 0, 0):
- break;
- default:
- return 0;
- }
-
- ret = amdgpu_dpm_write_watermarks_table(adev);
- if (ret) {
- DRM_ERROR("Failed to update WMTABLE!\n");
- return ret;
- }
-
- return 0;
-}
-
-/**
- * dm_hw_init() - Initialize DC device
- * @handle: The base driver device containing the amdgpu_dm device.
- *
- * Initialize the &struct amdgpu_display_manager device. This involves calling
- * the initializers of each DM component, then populating the struct with them.
- *
- * Although the function implies hardware initialization, both hardware and
- * software are initialized here. Splitting them out to their relevant init
- * hooks is a future TODO item.
- *
- * Some notable things that are initialized here:
- *
- * - Display Core, both software and hardware
- * - DC modules that we need (freesync and color management)
- * - DRM software states
- * - Interrupt sources and handlers
- * - Vblank support
- * - Debug FS entries, if enabled
- */
-static int dm_hw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- /* Create DAL display manager */
- r = amdgpu_dm_init(adev);
- if (r)
- return r;
- amdgpu_dm_hpd_init(adev);
-
- return 0;
-}
-
-/**
- * dm_hw_fini() - Teardown DC device
- * @handle: The base driver device containing the amdgpu_dm device.
- *
- * Teardown components within &struct amdgpu_display_manager that require
- * cleanup. This involves cleaning up the DRM device, DC, and any modules that
- * were loaded. Also flush IRQ workqueues and disable them.
- */
-static int dm_hw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- amdgpu_dm_hpd_fini(adev);
-
- amdgpu_dm_irq_fini(adev);
- amdgpu_dm_fini(adev);
- return 0;
-}
-
-
-static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
- struct dc_state *state, bool enable)
-{
- enum dc_irq_source irq_source;
- struct amdgpu_crtc *acrtc;
- int rc = -EBUSY;
- int i = 0;
-
- for (i = 0; i < state->stream_count; i++) {
- acrtc = get_crtc_by_otg_inst(
- adev, state->stream_status[i].primary_otg_inst);
-
- if (acrtc && state->stream_status[i].plane_count != 0) {
- irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst;
- rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
- if (rc)
- DRM_WARN("Failed to %s pflip interrupts\n",
- enable ? "enable" : "disable");
-
- if (enable) {
- if (amdgpu_dm_crtc_vrr_active(to_dm_crtc_state(acrtc->base.state)))
- rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, true);
- } else
- rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, false);
-
- if (rc)
- DRM_WARN("Failed to %sable vupdate interrupt\n", enable ? "en" : "dis");
-
- irq_source = IRQ_TYPE_VBLANK + acrtc->otg_inst;
- /* During gpu-reset we disable and then enable vblank irq, so
- * don't use amdgpu_irq_get/put() to avoid refcount change.
- */
- if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
- DRM_WARN("Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
- }
- }
-
-}
-
-static enum dc_status amdgpu_dm_commit_zero_streams(struct dc *dc)
-{
- struct dc_state *context = NULL;
- enum dc_status res = DC_ERROR_UNEXPECTED;
- int i;
- struct dc_stream_state *del_streams[MAX_PIPES];
- int del_streams_count = 0;
- struct dc_commit_streams_params params = {};
-
- memset(del_streams, 0, sizeof(del_streams));
-
- context = dc_state_create_current_copy(dc);
- if (context == NULL)
- goto context_alloc_fail;
-
- /* First remove from context all streams */
- for (i = 0; i < context->stream_count; i++) {
- struct dc_stream_state *stream = context->streams[i];
-
- del_streams[del_streams_count++] = stream;
- }
-
- /* Remove all planes for removed streams and then remove the streams */
- for (i = 0; i < del_streams_count; i++) {
- if (!dc_state_rem_all_planes_for_stream(dc, del_streams[i], context)) {
- res = DC_FAIL_DETACH_SURFACES;
- goto fail;
- }
-
- res = dc_state_remove_stream(dc, context, del_streams[i]);
- if (res != DC_OK)
- goto fail;
- }
-
- params.streams = context->streams;
- params.stream_count = context->stream_count;
- res = dc_commit_streams(dc, &params);
-
-fail:
- dc_state_release(context);
-
-context_alloc_fail:
- return res;
-}
-
-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_suspend(void *handle)
-{
- struct amdgpu_device *adev = handle;
- struct amdgpu_display_manager *dm = &adev->dm;
- int ret = 0;
-
- if (amdgpu_in_reset(adev)) {
- mutex_lock(&dm->dc_lock);
-
- dc_allow_idle_optimizations(adev->dm.dc, false);
-
- dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state);
-
- if (dm->cached_dc_state)
- dm_gpureset_toggle_interrupts(adev, dm->cached_dc_state, false);
-
- amdgpu_dm_commit_zero_streams(dm->dc);
-
- amdgpu_dm_irq_suspend(adev);
-
- hpd_rx_irq_work_suspend(dm);
-
- return ret;
- }
-
- WARN_ON(adev->dm.cached_state);
- adev->dm.cached_state = drm_atomic_helper_suspend(adev_to_drm(adev));
- if (IS_ERR(adev->dm.cached_state))
- return PTR_ERR(adev->dm.cached_state);
-
- s3_handle_mst(adev_to_drm(adev), true);
-
- amdgpu_dm_irq_suspend(adev);
-
- hpd_rx_irq_work_suspend(dm);
-
- if (adev->dm.dc->caps.ips_support)
- dc_allow_idle_optimizations(adev->dm.dc, true);
-
- dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3);
- dc_dmub_srv_set_power_state(dm->dc->ctx->dmub_srv, DC_ACPI_CM_POWER_STATE_D3);
-
- return 0;
-}
-
-struct drm_connector *
-amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_state *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)
-{
- struct dc_sink_init_data sink_init_data = { 0 };
- struct display_sink_capability sink_caps = { 0 };
- enum dc_edid_status edid_status;
- struct dc_context *dc_ctx = link->ctx;
- struct drm_device *dev = adev_to_drm(dc_ctx->driver_context);
- struct dc_sink *sink = NULL;
- struct dc_sink *prev_sink = NULL;
-
- link->type = dc_connection_none;
- prev_sink = link->local_sink;
-
- if (prev_sink)
- dc_sink_release(prev_sink);
-
- switch (link->connector_signal) {
- case SIGNAL_TYPE_HDMI_TYPE_A: {
- sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
- sink_caps.signal = SIGNAL_TYPE_HDMI_TYPE_A;
- break;
- }
-
- case SIGNAL_TYPE_DVI_SINGLE_LINK: {
- sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
- sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
- break;
- }
-
- case SIGNAL_TYPE_DVI_DUAL_LINK: {
- sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
- sink_caps.signal = SIGNAL_TYPE_DVI_DUAL_LINK;
- break;
- }
-
- case SIGNAL_TYPE_LVDS: {
- sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
- sink_caps.signal = SIGNAL_TYPE_LVDS;
- break;
- }
-
- case SIGNAL_TYPE_EDP: {
- sink_caps.transaction_type =
- DDC_TRANSACTION_TYPE_I2C_OVER_AUX;
- sink_caps.signal = SIGNAL_TYPE_EDP;
- break;
- }
-
- case SIGNAL_TYPE_DISPLAY_PORT: {
- sink_caps.transaction_type =
- DDC_TRANSACTION_TYPE_I2C_OVER_AUX;
- sink_caps.signal = SIGNAL_TYPE_VIRTUAL;
- break;
- }
-
- default:
- drm_err(dev, "Invalid connector type! signal:%d\n",
- link->connector_signal);
- return;
- }
-
- sink_init_data.link = link;
- sink_init_data.sink_signal = sink_caps.signal;
-
- sink = dc_sink_create(&sink_init_data);
- if (!sink) {
- drm_err(dev, "Failed to create sink!\n");
- return;
- }
-
- /* dc_sink_create returns a new reference */
- link->local_sink = sink;
-
- edid_status = dm_helpers_read_local_edid(
- link->ctx,
- link,
- sink);
-
- if (edid_status != EDID_OK)
- drm_err(dev, "Failed to read EDID\n");
-
-}
-
-static void dm_gpureset_commit_state(struct dc_state *dc_state,
- struct amdgpu_display_manager *dm)
-{
- struct {
- struct dc_surface_update surface_updates[MAX_SURFACES];
- struct dc_plane_info plane_infos[MAX_SURFACES];
- struct dc_scaling_info scaling_infos[MAX_SURFACES];
- struct dc_flip_addrs flip_addrs[MAX_SURFACES];
- struct dc_stream_update stream_update;
- } *bundle;
- int k, m;
-
- bundle = kzalloc(sizeof(*bundle), GFP_KERNEL);
-
- if (!bundle) {
- drm_err(dm->ddev, "Failed to allocate update bundle\n");
- goto cleanup;
- }
-
- for (k = 0; k < dc_state->stream_count; k++) {
- bundle->stream_update.stream = dc_state->streams[k];
-
- for (m = 0; m < dc_state->stream_status->plane_count; m++) {
- bundle->surface_updates[m].surface =
- dc_state->stream_status->plane_states[m];
- bundle->surface_updates[m].surface->force_full_update =
- true;
- }
-
- update_planes_and_stream_adapter(dm->dc,
- UPDATE_TYPE_FULL,
- dc_state->stream_status->plane_count,
- dc_state->streams[k],
- &bundle->stream_update,
- bundle->surface_updates);
- }
-
-cleanup:
- kfree(bundle);
-}
-
-static int dm_resume(void *handle)
-{
- struct amdgpu_device *adev = handle;
- struct drm_device *ddev = adev_to_drm(adev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- struct drm_crtc *crtc;
- struct drm_crtc_state *new_crtc_state;
- struct dm_crtc_state *dm_new_crtc_state;
- struct drm_plane *plane;
- struct drm_plane_state *new_plane_state;
- struct dm_plane_state *dm_new_plane_state;
- struct dm_atomic_state *dm_state = to_dm_atomic_state(dm->atomic_obj.state);
- enum dc_connection_type new_connection_type = dc_connection_none;
- struct dc_state *dc_state;
- int i, r, j, ret;
- bool need_hotplug = false;
- struct dc_commit_streams_params commit_params = {};
-
- if (dm->dc->caps.ips_support) {
- dc_dmub_srv_apply_idle_power_optimizations(dm->dc, false);
- }
-
- if (amdgpu_in_reset(adev)) {
- dc_state = dm->cached_dc_state;
-
- /*
- * The dc->current_state is backed up into dm->cached_dc_state
- * before we commit 0 streams.
- *
- * DC will clear link encoder assignments on the real state
- * but the changes won't propagate over to the copy we made
- * before the 0 streams commit.
- *
- * DC expects that link encoder assignments are *not* valid
- * when committing a state, so as a workaround we can copy
- * off of the current state.
- *
- * We lose the previous assignments, but we had already
- * commit 0 streams anyway.
- */
- link_enc_cfg_copy(adev->dm.dc->current_state, dc_state);
-
- r = dm_dmub_hw_init(adev);
- if (r)
- DRM_ERROR("DMUB interface failed to initialize: status=%d\n", r);
-
- dc_dmub_srv_set_power_state(dm->dc->ctx->dmub_srv, DC_ACPI_CM_POWER_STATE_D0);
- dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D0);
-
- dc_resume(dm->dc);
-
- amdgpu_dm_irq_resume_early(adev);
-
- 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;
- }
- }
-
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- amdgpu_dm_outbox_init(adev);
- dc_enable_dmub_outbox(adev->dm.dc);
- }
-
- commit_params.streams = dc_state->streams;
- commit_params.stream_count = dc_state->stream_count;
- dc_exit_ips_for_hw_access(dm->dc);
- WARN_ON(!dc_commit_streams(dm->dc, &commit_params));
-
- dm_gpureset_commit_state(dm->cached_dc_state, dm);
-
- dm_gpureset_toggle_interrupts(adev, dm->cached_dc_state, true);
-
- dc_state_release(dm->cached_dc_state);
- dm->cached_dc_state = NULL;
-
- amdgpu_dm_irq_resume_late(adev);
-
- mutex_unlock(&dm->dc_lock);
-
- return 0;
- }
- /* Recreate dc_state - DC invalidates it when setting power state to S3. */
- dc_state_release(dm_state->context);
- dm_state->context = dc_state_create(dm->dc, NULL);
- /* TODO: Remove dc_state->dccg, use dc->dccg directly. */
-
- /* Before powering on DC we need to re-initialize DMUB. */
- dm_dmub_hw_resume(adev);
-
- /* Re-enable outbox interrupts for DPIA. */
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- amdgpu_dm_outbox_init(adev);
- dc_enable_dmub_outbox(adev->dm.dc);
- }
-
- /* power on hardware */
- dc_dmub_srv_set_power_state(dm->dc->ctx->dmub_srv, DC_ACPI_CM_POWER_STATE_D0);
- dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D0);
-
- /* program HPD filter */
- dc_resume(dm->dc);
-
- /*
- * early enable HPD Rx IRQ, should be done before set mode as short
- * pulse interrupts are used for MST
- */
- amdgpu_dm_irq_resume_early(adev);
-
- /* On resume we need to rewrite the MSTM control bits to enable MST*/
- s3_handle_mst(ddev, false);
-
- /* Do detection*/
- drm_connector_list_iter_begin(ddev, &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)
- continue;
-
- /*
- * this is the case when traversing through already created end sink
- * MST connectors, should be skipped
- */
- if (aconnector->mst_root)
- continue;
-
- mutex_lock(&aconnector->hpd_lock);
- if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type))
- DRM_ERROR("KMS: Failed to detect connector\n");
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(aconnector->dc_link);
- } else {
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- dc_link_detect(aconnector->dc_link, DETECT_REASON_RESUMEFROMS3S4);
- mutex_unlock(&dm->dc_lock);
- }
-
- if (aconnector->fake_enable && aconnector->dc_link->local_sink)
- aconnector->fake_enable = false;
-
- if (aconnector->dc_sink)
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
- amdgpu_dm_update_connector_after_detect(aconnector);
- mutex_unlock(&aconnector->hpd_lock);
- }
- drm_connector_list_iter_end(&iter);
-
- /* Force mode set in atomic commit */
- for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
- new_crtc_state->active_changed = true;
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- reset_freesync_config_for_crtc(dm_new_crtc_state);
- }
-
- /*
- * atomic_check is expected to create the dc states. We need to release
- * them here, since they were duplicated as part of the suspend
- * procedure.
- */
- for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (dm_new_crtc_state->stream) {
- WARN_ON(kref_read(&dm_new_crtc_state->stream->refcount) > 1);
- dc_stream_release(dm_new_crtc_state->stream);
- dm_new_crtc_state->stream = NULL;
- }
- dm_new_crtc_state->base.color_mgmt_changed = true;
- }
-
- for_each_new_plane_in_state(dm->cached_state, plane, new_plane_state, i) {
- dm_new_plane_state = to_dm_plane_state(new_plane_state);
- if (dm_new_plane_state->dc_state) {
- WARN_ON(kref_read(&dm_new_plane_state->dc_state->refcount) > 1);
- dc_plane_state_release(dm_new_plane_state->dc_state);
- dm_new_plane_state->dc_state = NULL;
- }
- }
-
- drm_atomic_helper_resume(ddev, dm->cached_state);
-
- dm->cached_state = NULL;
-
- /* Do mst topology probing after resuming cached state*/
- drm_connector_list_iter_begin(ddev, &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_root)
- continue;
-
- ret = drm_dp_mst_topology_mgr_resume(&aconnector->mst_mgr, true);
-
- if (ret < 0) {
- dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx,
- aconnector->dc_link);
- need_hotplug = true;
- }
- }
- drm_connector_list_iter_end(&iter);
-
- if (need_hotplug)
- drm_kms_helper_hotplug_event(ddev);
-
- amdgpu_dm_irq_resume_late(adev);
-
- amdgpu_dm_smu_write_watermarks_table(adev);
-
- return 0;
-}
-
-/**
- * DOC: DM Lifecycle
- *
- * DM (and consequently DC) is registered in the amdgpu base driver as a IP
- * block. When CONFIG_DRM_AMD_DC is enabled, the DM device IP block is added to
- * the base driver's device list to be initialized and torn down accordingly.
- *
- * The functions to do so are provided as hooks in &struct amd_ip_funcs.
- */
-
-static const struct amd_ip_funcs amdgpu_dm_funcs = {
- .name = "dm",
- .early_init = dm_early_init,
- .late_init = dm_late_init,
- .sw_init = dm_sw_init,
- .sw_fini = dm_sw_fini,
- .early_fini = amdgpu_dm_early_fini,
- .hw_init = dm_hw_init,
- .hw_fini = dm_hw_fini,
- .suspend = dm_suspend,
- .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,
- .dump_ip_state = NULL,
- .print_ip_state = NULL,
-};
-
-const struct amdgpu_ip_block_version dm_ip_block = {
- .type = AMD_IP_BLOCK_TYPE_DCE,
- .major = 1,
- .minor = 0,
- .rev = 0,
- .funcs = &amdgpu_dm_funcs,
-};
-
-
-/**
- * DOC: atomic
- *
- * *WIP*
- */
-
-static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
- .fb_create = amdgpu_display_user_framebuffer_create,
- .get_format_info = amdgpu_dm_plane_get_format_info,
- .atomic_check = amdgpu_dm_atomic_check,
- .atomic_commit = drm_atomic_helper_commit,
-};
-
-static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
- .atomic_commit_tail = amdgpu_dm_atomic_commit_tail,
- .atomic_commit_setup = drm_dp_mst_atomic_setup_commit,
-};
-
-static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
-{
- struct amdgpu_dm_backlight_caps *caps;
- struct drm_connector *conn_base;
- struct amdgpu_device *adev;
- struct drm_luminance_range_info *luminance_range;
-
- if (aconnector->bl_idx == -1 ||
- aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP)
- return;
-
- conn_base = &aconnector->base;
- adev = drm_to_adev(conn_base->dev);
-
- 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;
-
- luminance_range = &conn_base->display_info.luminance_range;
-
- if (luminance_range->max_luminance) {
- caps->aux_min_input_signal = luminance_range->min_luminance;
- caps->aux_max_input_signal = luminance_range->max_luminance;
- } else {
- caps->aux_min_input_signal = 0;
- caps->aux_max_input_signal = 512;
- }
-}
-
-void amdgpu_dm_update_connector_after_detect(
- struct amdgpu_dm_connector *aconnector)
-{
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct dc_sink *sink;
-
- /* 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
- */
- mutex_lock(&dev->mode_config.mutex);
-
- if (sink) {
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(connector, NULL);
- /*
- * 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->edid);
- } else {
- amdgpu_dm_update_freesync_caps(connector, NULL);
- if (!aconnector->dc_sink) {
- aconnector->dc_sink = aconnector->dc_em_sink;
- dc_sink_retain(aconnector->dc_sink);
- }
- }
-
- mutex_unlock(&dev->mode_config.mutex);
-
- if (sink)
- dc_sink_release(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) {
- dc_sink_release(sink);
- 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);
- if (sink)
- dc_sink_release(sink);
- return;
- }
-
- drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n",
- aconnector->connector_id, aconnector->dc_sink, sink);
-
- 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);
- dc_sink_release(aconnector->dc_sink);
- }
-
- aconnector->dc_sink = sink;
- dc_sink_retain(aconnector->dc_sink);
- if (sink->dc_edid.length == 0) {
- aconnector->edid = NULL;
- if (aconnector->dc_link->aux_mode) {
- drm_dp_cec_unset_edid(
- &aconnector->dm_dp_aux.aux);
- }
- } else {
- aconnector->edid =
- (struct edid *)sink->dc_edid.raw_edid;
-
- if (aconnector->dc_link->aux_mode)
- drm_dp_cec_set_edid(&aconnector->dm_dp_aux.aux,
- aconnector->edid);
- }
-
- if (!aconnector->timing_requested) {
- aconnector->timing_requested =
- kzalloc(sizeof(struct dc_crtc_timing), GFP_KERNEL);
- if (!aconnector->timing_requested)
- drm_err(dev,
- "failed to create aconnector->requested_timing\n");
- }
-
- drm_connector_update_edid_property(connector, aconnector->edid);
- amdgpu_dm_update_freesync_caps(connector, aconnector->edid);
- update_connector_ext_caps(aconnector);
- } else {
- drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
- amdgpu_dm_update_freesync_caps(connector, NULL);
- drm_connector_update_edid_property(connector, NULL);
- aconnector->num_modes = 0;
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
- aconnector->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;
- }
-
- mutex_unlock(&dev->mode_config.mutex);
-
- update_subconnector_property(aconnector);
-
- if (sink)
- dc_sink_release(sink);
-}
-
-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;
-
- 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.
- */
- mutex_lock(&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_ERROR("KMS: Failed to detect connector\n");
-
- 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 {
- mutex_lock(&adev->dm.dc_lock);
- dc_exit_ips_for_hw_access(dc);
- ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
- mutex_unlock(&adev->dm.dc_lock);
- if (ret) {
- 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);
- }
- }
- mutex_unlock(&aconnector->hpd_lock);
-
-}
-
-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 hpd_rx_irq_offload_work_queue *offload_wq,
- union hpd_irq_data hpd_irq_data)
-{
- struct hpd_rx_irq_offload_work *offload_work =
- kzalloc(sizeof(*offload_work), GFP_KERNEL);
-
- if (!offload_work) {
- DRM_ERROR("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;
-
- queue_work(offload_wq->wq, &offload_work->work);
- DRM_DEBUG_KMS("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(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(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(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_ERROR("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_ERROR("amdgpu: fail to register dmub hpd callback");
- return -EINVAL;
- }
-
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ,
- dmub_hpd_callback, true)) {
- DRM_ERROR("amdgpu: fail to register dmub hpd 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_ERROR("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_ERROR("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;
-}
-
-#if defined(CONFIG_DRM_AMD_DC_SI)
-/* Register IRQ sources and initialize IRQ callbacks */
-static int dce60_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 client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
-
- 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++) {
- r = amdgpu_irq_add_id(adev, client_id, i + 1, &adev->crtc_irq);
- if (r) {
- DRM_ERROR("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 + 1, 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_ERROR("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 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_ERROR("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_ERROR("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_ERROR("Failed to add hpd irq id!\n");
- return r;
- }
-
- r = register_hpd_handlers(adev);
-
- return r;
-}
-#endif
-
-/* 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 client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
-
- 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 = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0; i <= VISLANDS30_IV_SRCID_D6_VERTICAL_INTERRUPT0; i++) {
- r = amdgpu_irq_add_id(adev, client_id, i, &adev->crtc_irq);
- if (r) {
- DRM_ERROR("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_ERROR("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 VUPDATE interrupt */
- for (i = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT; i <= VISLANDS30_IV_SRCID_D6_V_UPDATE_INT; i += 2) {
- r = amdgpu_irq_add_id(adev, client_id, i, &adev->vupdate_irq);
- if (r) {
- DRM_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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.
- *
- * This should only be called during atomic check.
- */
-int dm_atomic_get_state(struct drm_atomic_state *state,
- struct dm_atomic_state **dm_state)
-{
- struct drm_device *dev = state->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct drm_private_state *priv_state;
-
- if (*dm_state)
- return 0;
-
- priv_state = drm_atomic_get_private_obj_state(state, &dm->atomic_obj);
- if (IS_ERR(priv_state))
- return PTR_ERR(priv_state);
-
- *dm_state = to_dm_atomic_state(priv_state);
-
- return 0;
-}
-
-static struct dm_atomic_state *
-dm_atomic_get_new_state(struct drm_atomic_state *state)
-{
- struct drm_device *dev = state->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct drm_private_obj *obj;
- struct drm_private_state *new_obj_state;
- int i;
-
- for_each_new_private_obj_in_state(state, obj, new_obj_state, i) {
- if (obj->funcs == dm->atomic_obj.funcs)
- return to_dm_atomic_state(new_obj_state);
- }
-
- return NULL;
-}
-
-static struct drm_private_state *
-dm_atomic_duplicate_state(struct drm_private_obj *obj)
-{
- struct dm_atomic_state *old_state, *new_state;
-
- new_state = kzalloc(sizeof(*new_state), GFP_KERNEL);
- if (!new_state)
- return NULL;
-
- __drm_atomic_helper_private_obj_duplicate_state(obj, &new_state->base);
-
- old_state = to_dm_atomic_state(obj->state);
-
- if (old_state && old_state->context)
- new_state->context = dc_state_create_copy(old_state->context);
-
- if (!new_state->context) {
- kfree(new_state);
- return NULL;
- }
-
- return &new_state->base;
-}
-
-static void dm_atomic_destroy_state(struct drm_private_obj *obj,
- struct drm_private_state *state)
-{
- struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
-
- if (dm_state && dm_state->context)
- dc_state_release(dm_state->context);
-
- kfree(dm_state);
-}
-
-static struct drm_private_state_funcs dm_atomic_state_funcs = {
- .atomic_duplicate_state = dm_atomic_duplicate_state,
- .atomic_destroy_state = dm_atomic_destroy_state,
-};
-
-static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
-{
- struct dm_atomic_state *state;
- int r;
-
- adev->mode_info.mode_config_initialized = true;
-
- adev_to_drm(adev)->mode_config.funcs = (void *)&amdgpu_dm_mode_funcs;
- adev_to_drm(adev)->mode_config.helper_private = &amdgpu_dm_mode_config_helperfuncs;
-
- adev_to_drm(adev)->mode_config.max_width = 16384;
- adev_to_drm(adev)->mode_config.max_height = 16384;
-
- adev_to_drm(adev)->mode_config.preferred_depth = 24;
- if (adev->asic_type == CHIP_HAWAII)
- /* disable prefer shadow for now due to hibernation issues */
- adev_to_drm(adev)->mode_config.prefer_shadow = 0;
- else
- adev_to_drm(adev)->mode_config.prefer_shadow = 1;
- /* indicates support for immediate flip */
- adev_to_drm(adev)->mode_config.async_page_flip = true;
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- state->context = dc_state_create_current_copy(adev->dm.dc);
- if (!state->context) {
- kfree(state);
- return -ENOMEM;
- }
-
- drm_atomic_private_obj_init(adev_to_drm(adev),
- &adev->dm.atomic_obj,
- &state->base,
- &dm_atomic_state_funcs);
-
- r = amdgpu_display_modeset_create_props(adev);
- if (r) {
- dc_state_release(state->context);
- kfree(state);
- return r;
- }
-
-#ifdef AMD_PRIVATE_COLOR
- if (amdgpu_dm_create_color_properties(adev)) {
- dc_state_release(state->context);
- kfree(state);
- return -ENOMEM;
- }
-#endif
-
- r = amdgpu_dm_audio_init(adev);
- if (r) {
- dc_state_release(state->context);
- kfree(state);
- return r;
- }
-
- return 0;
-}
-
-#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
-#define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
-#define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
-
-static void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
- int bl_idx)
-{
-#if defined(CONFIG_ACPI)
- struct amdgpu_dm_backlight_caps caps;
-
- memset(&caps, 0, sizeof(caps));
-
- if (dm->backlight_caps[bl_idx].caps_valid)
- return;
-
- amdgpu_acpi_get_backlight_caps(&caps);
- if (caps.caps_valid) {
- dm->backlight_caps[bl_idx].caps_valid = true;
- if (caps.aux_support)
- return;
- dm->backlight_caps[bl_idx].min_input_signal = caps.min_input_signal;
- dm->backlight_caps[bl_idx].max_input_signal = caps.max_input_signal;
- } else {
- dm->backlight_caps[bl_idx].min_input_signal =
- AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- dm->backlight_caps[bl_idx].max_input_signal =
- AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
- }
-#else
- if (dm->backlight_caps[bl_idx].aux_support)
- return;
-
- dm->backlight_caps[bl_idx].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- dm->backlight_caps[bl_idx].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
-#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;
-}
-
-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;
-
- // Rescale 0..255 to min..max
- return min + DIV_ROUND_CLOSEST((max - min) * brightness,
- AMDGPU_MAX_BL_LEVEL);
-}
-
-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..255
- return DIV_ROUND_CLOSEST(AMDGPU_MAX_BL_LEVEL * (brightness - min),
- max - min);
-}
-
-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;
- bool rc;
-
- 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];
-
- /* Change brightness based on AUX property */
- if (caps.aux_support) {
- rc = dc_link_set_backlight_level_nits(link, true, brightness,
- AUX_BL_DEFAULT_TRANSITION_TIME_MS);
- if (!rc)
- DRM_DEBUG("DM: Failed to update backlight via AUX on eDP[%d]\n", bl_idx);
- } else {
- rc = dc_link_set_backlight_level(link, brightness, 0);
- if (!rc)
- DRM_DEBUG("DM: Failed to update backlight on eDP[%d]\n", bl_idx);
- }
-
- 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;
- bool rc;
-
- rc = dc_link_get_backlight_level_nits(link, &avg, &peak);
- if (!rc)
- 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 = { 0 };
- char bl_name[16];
-
- 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;
- }
-
- amdgpu_acpi_get_backlight_caps(&caps);
- if (caps.caps_valid) {
- if (power_supply_is_system_supplied() > 0)
- props.brightness = caps.ac_level;
- else
- props.brightness = caps.dc_level;
- } else
- props.brightness = AMDGPU_MAX_BL_LEVEL;
-
- props.max_brightness = AMDGPU_MAX_BL_LEVEL;
- 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);
-
- if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) {
- DRM_ERROR("DM: Backlight registration failed!\n");
- dm->backlight_dev[aconnector->bl_idx] = NULL;
- } else
- DRM_DEBUG_DRIVER("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,
- const struct dc_plane_cap *plane_cap)
-{
- struct drm_plane *plane;
- unsigned long possible_crtcs;
- int ret = 0;
-
- plane = kzalloc(sizeof(struct drm_plane), GFP_KERNEL);
- if (!plane) {
- DRM_ERROR("KMS: Failed to allocate plane\n");
- return -ENOMEM;
- }
- plane->type = plane_type;
-
- /*
- * HACK: IGT tests expect that the primary plane for a CRTC
- * can only have one possible CRTC. Only expose support for
- * any CRTC if they're not going to be used as a primary plane
- * for a CRTC - like overlay or underlay planes.
- */
- possible_crtcs = 1 << plane_id;
- if (plane_id >= dm->dc->caps.max_streams)
- possible_crtcs = 0xff;
-
- ret = amdgpu_dm_plane_init(dm, plane, possible_crtcs, plane_cap);
-
- if (ret) {
- DRM_ERROR("KMS: Failed to initialize plane\n");
- kfree(plane);
- return ret;
- }
-
- if (mode_info)
- mode_info->planes[plane_id] = plane;
-
- return ret;
-}
-
-
-static void setup_backlight_device(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector)
-{
- 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->brightness[bl_idx] = AMDGPU_MAX_BL_LEVEL;
- dm->backlight_link[bl_idx] = link;
- dm->num_of_edps++;
-
- update_connector_ext_caps(aconnector);
-}
-
-static void amdgpu_set_panel_orientation(struct drm_connector *connector);
-
-/*
- * In this architecture, the association
- * connector -> encoder -> crtc
- * id not really requried. The crtc and connector will hold the
- * display_index as an abstraction to use with DAL component
- *
- * Returns 0 on success
- */
-static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
-{
- struct amdgpu_display_manager *dm = &adev->dm;
- s32 i;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct amdgpu_encoder *aencoder = NULL;
- struct amdgpu_mode_info *mode_info = &adev->mode_info;
- u32 link_cnt;
- s32 primary_planes;
- enum dc_connection_type new_connection_type = dc_connection_none;
- const struct dc_plane_cap *plane;
- bool psr_feature_enabled = false;
- bool replay_feature_enabled = false;
- int max_overlay = dm->dc->caps.max_slave_planes;
-
- dm->display_indexes_num = dm->dc->caps.max_streams;
- /* Update the actual used number of crtc */
- adev->mode_info.num_crtc = adev->dm.display_indexes_num;
-
- amdgpu_dm_set_irq_funcs(adev);
-
- link_cnt = dm->dc->caps.max_links;
- if (amdgpu_dm_mode_config_init(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize mode config\n");
- return -EINVAL;
- }
-
- /* There is one primary plane per CRTC */
- primary_planes = dm->dc->caps.max_streams;
- if (primary_planes > AMDGPU_MAX_PLANES) {
- DRM_ERROR("DM: Plane nums out of 6 planes\n");
- return -EINVAL;
- }
-
- /*
- * Initialize primary planes, implicit planes for legacy IOCTLS.
- * Order is reversed to match iteration order in atomic check.
- */
- for (i = (primary_planes - 1); i >= 0; i--) {
- plane = &dm->dc->caps.planes[i];
-
- if (initialize_plane(dm, mode_info, i,
- DRM_PLANE_TYPE_PRIMARY, plane)) {
- DRM_ERROR("KMS: Failed to initialize primary plane\n");
- goto fail;
- }
- }
-
- /*
- * Initialize overlay planes, index starting after primary planes.
- * These planes have a higher DRM index than the primary planes since
- * they should be considered as having a higher z-order.
- * Order is reversed to match iteration order in atomic check.
- *
- * Only support DCN for now, and only expose one so we don't encourage
- * userspace to use up all the pipes.
- */
- for (i = 0; i < dm->dc->caps.max_planes; ++i) {
- struct dc_plane_cap *plane = &dm->dc->caps.planes[i];
-
- /* Do not create overlay if MPO disabled */
- if (amdgpu_dc_debug_mask & DC_DISABLE_MPO)
- break;
-
- if (plane->type != DC_PLANE_TYPE_DCN_UNIVERSAL)
- continue;
-
- if (!plane->pixel_format_support.argb8888)
- continue;
-
- if (max_overlay-- == 0)
- break;
-
- if (initialize_plane(dm, NULL, primary_planes + i,
- DRM_PLANE_TYPE_OVERLAY, plane)) {
- DRM_ERROR("KMS: Failed to initialize overlay plane\n");
- goto fail;
- }
- }
-
- for (i = 0; i < dm->dc->caps.max_streams; i++)
- if (amdgpu_dm_crtc_init(dm, mode_info->planes[i], i)) {
- DRM_ERROR("KMS: Failed to initialize crtc\n");
- goto fail;
- }
-
- /* Use Outbox interrupt */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 0, 0):
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(2, 1, 0):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- if (register_outbox_irq_handlers(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize IRQ\n");
- goto fail;
- }
- break;
- default:
- DRM_DEBUG_KMS("Unsupported DCN IP version for outbox: 0x%X\n",
- amdgpu_ip_version(adev, DCE_HWIP, 0));
- }
-
- /* Determine whether to enable PSR support by default. */
- if (!(amdgpu_dc_debug_mask & DC_DISABLE_PSR)) {
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- psr_feature_enabled = true;
- break;
- default:
- psr_feature_enabled = amdgpu_dc_feature_mask & DC_PSR_MASK;
- break;
- }
- }
-
- /* Determine whether to enable Replay support by default. */
- if (!(amdgpu_dc_debug_mask & DC_DISABLE_REPLAY)) {
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- replay_feature_enabled = true;
- break;
-
- default:
- replay_feature_enabled = amdgpu_dc_feature_mask & DC_REPLAY_MASK;
- break;
- }
- }
-
- if (link_cnt > MAX_LINKS) {
- DRM_ERROR(
- "KMS: Cannot support more than %d display indexes\n",
- MAX_LINKS);
- goto fail;
- }
-
- /* loops over all connectors on the board */
- for (i = 0; i < link_cnt; i++) {
- struct dc_link *link = NULL;
-
- link = dc_get_link_at_index(dm->dc, i);
-
- if (link->connector_signal == SIGNAL_TYPE_VIRTUAL) {
- struct amdgpu_dm_wb_connector *wbcon = kzalloc(sizeof(*wbcon), GFP_KERNEL);
-
- if (!wbcon) {
- DRM_ERROR("KMS: Failed to allocate writeback connector\n");
- continue;
- }
-
- if (amdgpu_dm_wb_connector_init(dm, wbcon, i)) {
- DRM_ERROR("KMS: Failed to initialize writeback connector\n");
- kfree(wbcon);
- continue;
- }
-
- link->psr_settings.psr_feature_enabled = false;
- link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
-
- continue;
- }
-
- aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL);
- if (!aconnector)
- goto fail;
-
- aencoder = kzalloc(sizeof(*aencoder), GFP_KERNEL);
- if (!aencoder)
- goto fail;
-
- if (amdgpu_dm_encoder_init(dm->ddev, aencoder, i)) {
- DRM_ERROR("KMS: Failed to initialize encoder\n");
- goto fail;
- }
-
- if (amdgpu_dm_connector_init(dm, aconnector, i, aencoder)) {
- DRM_ERROR("KMS: Failed to initialize connector\n");
- goto fail;
- }
-
- if (dm->hpd_rx_offload_wq)
- dm->hpd_rx_offload_wq[aconnector->base.index].aconnector =
- aconnector;
-
- if (!dc_link_detect_connection_type(link, &new_connection_type))
- DRM_ERROR("KMS: Failed to detect connector\n");
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(link);
- amdgpu_dm_update_connector_after_detect(aconnector);
- } else {
- bool ret = false;
-
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- ret = dc_link_detect(link, DETECT_REASON_BOOT);
- mutex_unlock(&dm->dc_lock);
-
- if (ret) {
- amdgpu_dm_update_connector_after_detect(aconnector);
- setup_backlight_device(dm, aconnector);
-
- /* Disable PSR if Replay can be enabled */
- if (replay_feature_enabled)
- if (amdgpu_dm_set_replay_caps(link, aconnector))
- psr_feature_enabled = false;
-
- if (psr_feature_enabled)
- amdgpu_dm_set_psr_caps(link);
- }
- }
- amdgpu_set_panel_orientation(&aconnector->base);
- }
-
- /* Software is initialized. Now we can register interrupt handlers. */
- switch (adev->asic_type) {
-#if defined(CONFIG_DRM_AMD_DC_SI)
- case CHIP_TAHITI:
- case CHIP_PITCAIRN:
- case CHIP_VERDE:
- case CHIP_OLAND:
- if (dce60_register_irq_handlers(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize IRQ\n");
- goto fail;
- }
- break;
-#endif
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- case CHIP_KAVERI:
- case CHIP_KABINI:
- case CHIP_MULLINS:
- case CHIP_TONGA:
- case CHIP_FIJI:
- case CHIP_CARRIZO:
- case CHIP_STONEY:
- case CHIP_POLARIS11:
- case CHIP_POLARIS10:
- case CHIP_POLARIS12:
- case CHIP_VEGAM:
- case CHIP_VEGA10:
- case CHIP_VEGA12:
- case CHIP_VEGA20:
- if (dce110_register_irq_handlers(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize IRQ\n");
- goto fail;
- }
- break;
- default:
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(1, 0, 0):
- case IP_VERSION(1, 0, 1):
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(2, 0, 3):
- case IP_VERSION(2, 0, 0):
- case IP_VERSION(2, 1, 0):
- case IP_VERSION(3, 0, 0):
- case IP_VERSION(3, 0, 2):
- case IP_VERSION(3, 0, 3):
- case IP_VERSION(3, 0, 1):
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- if (dcn10_register_irq_handlers(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize IRQ\n");
- goto fail;
- }
- break;
- default:
- DRM_ERROR("Unsupported DCE IP versions: 0x%X\n",
- amdgpu_ip_version(adev, DCE_HWIP, 0));
- goto fail;
- }
- break;
- }
-
- return 0;
-fail:
- kfree(aencoder);
- kfree(aconnector);
-
- return -EINVAL;
-}
-
-static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm)
-{
- drm_atomic_private_obj_fini(&dm->atomic_obj);
-}
-
-/******************************************************************************
- * amdgpu_display_funcs functions
- *****************************************************************************/
-
-/*
- * dm_bandwidth_update - program display watermarks
- *
- * @adev: amdgpu_device pointer
- *
- * Calculate and program the display watermarks and line buffer allocation.
- */
-static void dm_bandwidth_update(struct amdgpu_device *adev)
-{
- /* TODO: implement later */
-}
-
-static const struct amdgpu_display_funcs dm_display_funcs = {
- .bandwidth_update = dm_bandwidth_update, /* called unconditionally */
- .vblank_get_counter = dm_vblank_get_counter,/* called unconditionally */
- .backlight_set_level = NULL, /* never called for DC */
- .backlight_get_level = NULL, /* never called for DC */
- .hpd_sense = NULL,/* called unconditionally */
- .hpd_set_polarity = NULL, /* called unconditionally */
- .hpd_get_gpio_reg = NULL, /* VBIOS parsing. DAL does it. */
- .page_flip_get_scanoutpos =
- dm_crtc_get_scanoutpos,/* called unconditionally */
- .add_encoder = NULL, /* VBIOS parsing. DAL does it. */
- .add_connector = NULL, /* VBIOS parsing. DAL does it. */
-};
-
-#if defined(CONFIG_DEBUG_KERNEL_DC)
-
-static ssize_t s3_debug_store(struct device *device,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- int ret;
- int s3_state;
- struct drm_device *drm_dev = dev_get_drvdata(device);
- struct amdgpu_device *adev = drm_to_adev(drm_dev);
-
- ret = kstrtoint(buf, 0, &s3_state);
-
- if (ret == 0) {
- if (s3_state) {
- dm_resume(adev);
- drm_kms_helper_hotplug_event(adev_to_drm(adev));
- } else
- dm_suspend(adev);
- }
-
- return ret == 0 ? count : 0;
-}
-
-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(4, 0, 1):
- fw_name_dmub = FIRMWARE_DCN_401_DMUB;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
- r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, "%s", fw_name_dmub);
- return r;
-}
-
-static int dm_early_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_mode_info *mode_info = &adev->mode_info;
- struct atom_context *ctx = mode_info->atom_context;
- int index = GetIndexIntoMasterTable(DATA, Object_Header);
- u16 data_offset;
-
- /* if there is no object header, skip DM */
- if (!amdgpu_atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) {
- adev->harvest_ip_mask |= AMD_HARVEST_IP_DMU_MASK;
- dev_info(adev->dev, "No object header, skipping DM\n");
- return -ENOENT;
- }
-
- switch (adev->asic_type) {
-#if defined(CONFIG_DRM_AMD_DC_SI)
- case CHIP_TAHITI:
- case CHIP_PITCAIRN:
- case CHIP_VERDE:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case CHIP_OLAND:
- adev->mode_info.num_crtc = 2;
- adev->mode_info.num_hpd = 2;
- adev->mode_info.num_dig = 2;
- break;
-#endif
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case CHIP_KAVERI:
- adev->mode_info.num_crtc = 4;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 7;
- break;
- case CHIP_KABINI:
- case CHIP_MULLINS:
- adev->mode_info.num_crtc = 2;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case CHIP_FIJI:
- case CHIP_TONGA:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 7;
- break;
- case CHIP_CARRIZO:
- adev->mode_info.num_crtc = 3;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 9;
- break;
- case CHIP_STONEY:
- adev->mode_info.num_crtc = 2;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 9;
- break;
- case CHIP_POLARIS11:
- case CHIP_POLARIS12:
- adev->mode_info.num_crtc = 5;
- adev->mode_info.num_hpd = 5;
- adev->mode_info.num_dig = 5;
- break;
- case CHIP_POLARIS10:
- case CHIP_VEGAM:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case CHIP_VEGA10:
- case CHIP_VEGA12:
- case CHIP_VEGA20:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- default:
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(3, 0, 0):
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case IP_VERSION(2, 0, 0):
- case IP_VERSION(3, 0, 2):
- adev->mode_info.num_crtc = 5;
- adev->mode_info.num_hpd = 5;
- adev->mode_info.num_dig = 5;
- break;
- case IP_VERSION(2, 0, 3):
- case IP_VERSION(3, 0, 3):
- adev->mode_info.num_crtc = 2;
- adev->mode_info.num_hpd = 2;
- adev->mode_info.num_dig = 2;
- break;
- case IP_VERSION(1, 0, 0):
- case IP_VERSION(1, 0, 1):
- case IP_VERSION(3, 0, 1):
- case IP_VERSION(2, 1, 0):
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- adev->mode_info.num_crtc = 4;
- adev->mode_info.num_hpd = 4;
- adev->mode_info.num_dig = 4;
- break;
- default:
- DRM_ERROR("Unsupported DCE IP versions: 0x%x\n",
- amdgpu_ip_version(adev, DCE_HWIP, 0));
- return -EINVAL;
- }
- break;
- }
-
- if (adev->mode_info.funcs == NULL)
- adev->mode_info.funcs = &dm_display_funcs;
-
- /*
- * Note: Do NOT change adev->audio_endpt_rreg and
- * adev->audio_endpt_wreg because they are initialised in
- * amdgpu_device_init()
- */
-#if defined(CONFIG_DEBUG_KERNEL_DC)
- device_create_file(
- adev_to_drm(adev)->dev,
- &dev_attr_s3_debug);
-#endif
- adev->dc_enabled = true;
-
- return dm_init_microcode(adev);
-}
-
-static bool modereset_required(struct drm_crtc_state *crtc_state)
-{
- return !crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
-}
-
-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
-fill_plane_color_attributes(const struct drm_plane_state *plane_state,
- const enum surface_pixel_format format,
- enum dc_color_space *color_space)
-{
- bool full_range;
-
- *color_space = COLOR_SPACE_SRGB;
-
- /* DRM color properties only affect non-RGB formats. */
- if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN)
- return 0;
-
- full_range = (plane_state->color_range == DRM_COLOR_YCBCR_FULL_RANGE);
-
- switch (plane_state->color_encoding) {
- case DRM_COLOR_YCBCR_BT601:
- if (full_range)
- *color_space = COLOR_SPACE_YCBCR601;
- else
- *color_space = COLOR_SPACE_YCBCR601_LIMITED;
- break;
-
- case DRM_COLOR_YCBCR_BT709:
- if (full_range)
- *color_space = COLOR_SPACE_YCBCR709;
- else
- *color_space = COLOR_SPACE_YCBCR709_LIMITED;
- break;
-
- case DRM_COLOR_YCBCR_BT2020:
- if (full_range)
- *color_space = COLOR_SPACE_2020_YCBCR;
- else
- return -EINVAL;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int
-fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
- const struct drm_plane_state *plane_state,
- const u64 tiling_flags,
- struct dc_plane_info *plane_info,
- struct dc_plane_address *address,
- bool tmz_surface,
- bool force_disable_dcc)
-{
- const struct drm_framebuffer *fb = plane_state->fb;
- const struct amdgpu_framebuffer *afb =
- to_amdgpu_framebuffer(plane_state->fb);
- int ret;
-
- memset(plane_info, 0, sizeof(*plane_info));
-
- switch (fb->format->format) {
- case DRM_FORMAT_C8:
- plane_info->format =
- SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS;
- break;
- case DRM_FORMAT_RGB565:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_RGB565;
- break;
- case DRM_FORMAT_XRGB8888:
- case DRM_FORMAT_ARGB8888:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888;
- break;
- case DRM_FORMAT_XRGB2101010:
- case DRM_FORMAT_ARGB2101010:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010;
- break;
- case DRM_FORMAT_XBGR2101010:
- case DRM_FORMAT_ABGR2101010:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010;
- break;
- case DRM_FORMAT_XBGR8888:
- case DRM_FORMAT_ABGR8888:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR8888;
- break;
- case DRM_FORMAT_NV21:
- plane_info->format = SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr;
- break;
- case DRM_FORMAT_NV12:
- plane_info->format = SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb;
- break;
- case DRM_FORMAT_P010:
- plane_info->format = SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb;
- break;
- case DRM_FORMAT_XRGB16161616F:
- case DRM_FORMAT_ARGB16161616F:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F;
- break;
- case DRM_FORMAT_XBGR16161616F:
- case DRM_FORMAT_ABGR16161616F:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F;
- break;
- case DRM_FORMAT_XRGB16161616:
- case DRM_FORMAT_ARGB16161616:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616;
- break;
- case DRM_FORMAT_XBGR16161616:
- case DRM_FORMAT_ABGR16161616:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616;
- break;
- default:
- DRM_ERROR(
- "Unsupported screen format %p4cc\n",
- &fb->format->format);
- return -EINVAL;
- }
-
- switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) {
- case DRM_MODE_ROTATE_0:
- plane_info->rotation = ROTATION_ANGLE_0;
- break;
- case DRM_MODE_ROTATE_90:
- plane_info->rotation = ROTATION_ANGLE_90;
- break;
- case DRM_MODE_ROTATE_180:
- plane_info->rotation = ROTATION_ANGLE_180;
- break;
- case DRM_MODE_ROTATE_270:
- plane_info->rotation = ROTATION_ANGLE_270;
- break;
- default:
- plane_info->rotation = ROTATION_ANGLE_0;
- break;
- }
-
-
- plane_info->visible = true;
- plane_info->stereo_format = PLANE_STEREO_FORMAT_NONE;
-
- plane_info->layer_index = plane_state->normalized_zpos;
-
- ret = fill_plane_color_attributes(plane_state, plane_info->format,
- &plane_info->color_space);
- if (ret)
- return ret;
-
- ret = amdgpu_dm_plane_fill_plane_buffer_attributes(adev, afb, plane_info->format,
- plane_info->rotation, tiling_flags,
- &plane_info->tiling_info,
- &plane_info->plane_size,
- &plane_info->dcc, address,
- tmz_surface, force_disable_dcc);
- if (ret)
- return ret;
-
- amdgpu_dm_plane_fill_blending_from_plane_state(
- plane_state, &plane_info->per_pixel_alpha, &plane_info->pre_multiplied_alpha,
- &plane_info->global_alpha, &plane_info->global_alpha_value);
-
- return 0;
-}
-
-static int fill_dc_plane_attributes(struct amdgpu_device *adev,
- struct dc_plane_state *dc_plane_state,
- struct drm_plane_state *plane_state,
- struct drm_crtc_state *crtc_state)
-{
- struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state);
- struct amdgpu_framebuffer *afb = (struct amdgpu_framebuffer *)plane_state->fb;
- struct dc_scaling_info scaling_info;
- struct dc_plane_info plane_info;
- int ret;
- bool force_disable_dcc = false;
-
- ret = amdgpu_dm_plane_fill_dc_scaling_info(adev, plane_state, &scaling_info);
- if (ret)
- return ret;
-
- dc_plane_state->src_rect = scaling_info.src_rect;
- dc_plane_state->dst_rect = scaling_info.dst_rect;
- dc_plane_state->clip_rect = scaling_info.clip_rect;
- dc_plane_state->scaling_quality = scaling_info.scaling_quality;
-
- force_disable_dcc = adev->asic_type == CHIP_RAVEN && adev->in_suspend;
- ret = fill_dc_plane_info_and_addr(adev, plane_state,
- afb->tiling_flags,
- &plane_info,
- &dc_plane_state->address,
- afb->tmz_surface,
- force_disable_dcc);
- if (ret)
- return ret;
-
- dc_plane_state->format = plane_info.format;
- dc_plane_state->color_space = plane_info.color_space;
- dc_plane_state->format = plane_info.format;
- dc_plane_state->plane_size = plane_info.plane_size;
- dc_plane_state->rotation = plane_info.rotation;
- dc_plane_state->horizontal_mirror = plane_info.horizontal_mirror;
- dc_plane_state->stereo_format = plane_info.stereo_format;
- dc_plane_state->tiling_info = plane_info.tiling_info;
- dc_plane_state->visible = plane_info.visible;
- dc_plane_state->per_pixel_alpha = plane_info.per_pixel_alpha;
- dc_plane_state->pre_multiplied_alpha = plane_info.pre_multiplied_alpha;
- dc_plane_state->global_alpha = plane_info.global_alpha;
- dc_plane_state->global_alpha_value = plane_info.global_alpha_value;
- dc_plane_state->dcc = plane_info.dcc;
- dc_plane_state->layer_index = plane_info.layer_index;
- dc_plane_state->flip_int_enabled = true;
-
- /*
- * Always set input transfer function, since plane state is refreshed
- * every time.
- */
- ret = amdgpu_dm_update_plane_color_mgmt(dm_crtc_state,
- plane_state,
- dc_plane_state);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static inline void fill_dc_dirty_rect(struct drm_plane *plane,
- struct rect *dirty_rect, int32_t x,
- s32 y, s32 width, s32 height,
- int *i, bool ffu)
-{
- WARN_ON(*i >= DC_MAX_DIRTY_RECTS);
-
- dirty_rect->x = x;
- dirty_rect->y = y;
- dirty_rect->width = width;
- dirty_rect->height = height;
-
- if (ffu)
- drm_dbg(plane->dev,
- "[PLANE:%d] PSR FFU dirty rect size (%d, %d)\n",
- plane->base.id, width, height);
- else
- drm_dbg(plane->dev,
- "[PLANE:%d] PSR SU dirty rect at (%d, %d) size (%d, %d)",
- plane->base.id, x, y, width, height);
-
- (*i)++;
-}
-
-/**
- * fill_dc_dirty_rects() - Fill DC dirty regions for PSR selective updates
- *
- * @plane: DRM plane containing dirty regions that need to be flushed to the eDP
- * remote fb
- * @old_plane_state: Old state of @plane
- * @new_plane_state: New state of @plane
- * @crtc_state: New state of CRTC connected to the @plane
- * @flip_addrs: DC flip tracking struct, which also tracts dirty rects
- * @is_psr_su: Flag indicating whether Panel Self Refresh Selective Update (PSR SU) is enabled.
- * If PSR SU is enabled and damage clips are available, only the regions of the screen
- * that have changed will be updated. If PSR SU is not enabled,
- * or if damage clips are not available, the entire screen will be updated.
- * @dirty_regions_changed: dirty regions changed
- *
- * For PSR SU, DC informs the DMUB uController of dirty rectangle regions
- * (referred to as "damage clips" in DRM nomenclature) that require updating on
- * the eDP remote buffer. The responsibility of specifying the dirty regions is
- * amdgpu_dm's.
- *
- * A damage-aware DRM client should fill the FB_DAMAGE_CLIPS property on the
- * plane with regions that require flushing to the eDP remote buffer. In
- * addition, certain use cases - such as cursor and multi-plane overlay (MPO) -
- * implicitly provide damage clips without any client support via the plane
- * bounds.
- */
-static void fill_dc_dirty_rects(struct drm_plane *plane,
- struct drm_plane_state *old_plane_state,
- struct drm_plane_state *new_plane_state,
- struct drm_crtc_state *crtc_state,
- struct dc_flip_addrs *flip_addrs,
- bool is_psr_su,
- bool *dirty_regions_changed)
-{
- 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;
- bool bb_changed;
- bool fb_changed;
- u32 i = 0;
- *dirty_regions_changed = false;
-
- /*
- * Cursor plane has it's own dirty rect update interface. See
- * dcn10_dmub_update_cursor_data and dmub_cmd_update_cursor_info_data
- */
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- return;
-
- 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 (num_clips && (!amdgpu_damage_clips || (amdgpu_damage_clips < 0 &&
- is_psr_su)))
- goto ffu;
-
- if (!dm_crtc_state->mpo_requested) {
- if (!num_clips || num_clips > DC_MAX_DIRTY_RECTS)
- goto ffu;
-
- for (; flip_addrs->dirty_rect_count < num_clips; clips++)
- fill_dc_dirty_rect(new_plane_state->plane,
- &dirty_rects[flip_addrs->dirty_rect_count],
- clips->x1, clips->y1,
- clips->x2 - clips->x1, clips->y2 - clips->y1,
- &flip_addrs->dirty_rect_count,
- false);
- return;
- }
-
- /*
- * MPO is requested. Add entire plane bounding box to dirty rects if
- * flipped to or damaged.
- *
- * If plane is moved or resized, also add old bounding box to dirty
- * rects.
- */
- fb_changed = old_plane_state->fb->base.id !=
- new_plane_state->fb->base.id;
- bb_changed = (old_plane_state->crtc_x != new_plane_state->crtc_x ||
- old_plane_state->crtc_y != new_plane_state->crtc_y ||
- old_plane_state->crtc_w != new_plane_state->crtc_w ||
- old_plane_state->crtc_h != new_plane_state->crtc_h);
-
- drm_dbg(plane->dev,
- "[PLANE:%d] PSR bb_changed:%d fb_changed:%d num_clips:%d\n",
- new_plane_state->plane->base.id,
- bb_changed, fb_changed, num_clips);
-
- *dirty_regions_changed = bb_changed;
-
- if ((num_clips + (bb_changed ? 2 : 0)) > DC_MAX_DIRTY_RECTS)
- goto ffu;
-
- if (bb_changed) {
- fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[i],
- new_plane_state->crtc_x,
- new_plane_state->crtc_y,
- new_plane_state->crtc_w,
- new_plane_state->crtc_h, &i, false);
-
- /* Add old plane bounding-box if plane is moved or resized */
- fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[i],
- old_plane_state->crtc_x,
- old_plane_state->crtc_y,
- old_plane_state->crtc_w,
- old_plane_state->crtc_h, &i, false);
- }
-
- if (num_clips) {
- for (; i < num_clips; clips++)
- fill_dc_dirty_rect(new_plane_state->plane,
- &dirty_rects[i], clips->x1,
- clips->y1, clips->x2 - clips->x1,
- clips->y2 - clips->y1, &i, false);
- } else if (fb_changed && !bb_changed) {
- fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[i],
- new_plane_state->crtc_x,
- new_plane_state->crtc_y,
- new_plane_state->crtc_w,
- new_plane_state->crtc_h, &i, false);
- }
-
- flip_addrs->dirty_rect_count = i;
- return;
-
-ffu:
- fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[0], 0, 0,
- dm_crtc_state->base.mode.crtc_hdisplay,
- dm_crtc_state->base.mode.crtc_vdisplay,
- &flip_addrs->dirty_rect_count, true);
-}
-
-static void update_stream_scaling_settings(const struct drm_display_mode *mode,
- const struct dm_connector_state *dm_state,
- struct dc_stream_state *stream)
-{
- enum amdgpu_rmx_type rmx_type;
-
- struct rect src = { 0 }; /* viewport in composition space*/
- struct rect dst = { 0 }; /* stream addressable area */
-
- /* no mode. nothing to be done */
- if (!mode)
- return;
-
- /* Full screen scaling by default */
- src.width = mode->hdisplay;
- src.height = mode->vdisplay;
- dst.width = stream->timing.h_addressable;
- dst.height = stream->timing.v_addressable;
-
- if (dm_state) {
- rmx_type = dm_state->scaling;
- if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) {
- if (src.width * dst.height <
- src.height * dst.width) {
- /* height needs less upscaling/more downscaling */
- dst.width = src.width *
- dst.height / src.height;
- } else {
- /* width needs less upscaling/more downscaling */
- dst.height = src.height *
- dst.width / src.width;
- }
- } else if (rmx_type == RMX_CENTER) {
- dst = src;
- }
-
- dst.x = (stream->timing.h_addressable - dst.width) / 2;
- dst.y = (stream->timing.v_addressable - dst.height) / 2;
-
- if (dm_state->underscan_enable) {
- dst.x += dm_state->underscan_hborder / 2;
- dst.y += dm_state->underscan_vborder / 2;
- dst.width -= dm_state->underscan_hborder;
- dst.height -= dm_state->underscan_vborder;
- }
- }
-
- stream->src = src;
- stream->dst = dst;
-
- DRM_DEBUG_KMS("Destination Rectangle x:%d y:%d width:%d height:%d\n",
- dst.x, dst.y, dst.width, dst.height);
-
-}
-
-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;
- break;
- case DRM_MODE_COLORIMETRY_DEFAULT: // ITU601
- default:
- if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
- color_space = COLOR_SPACE_SRGB;
- /*
- * 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)
-{
- 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;
-
- 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;
- /* TODO: un-hardcode */
- if (drm_mode_is_420_only(info, mode_in)
- && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if (drm_mode_is_420_also(info, mode_in)
- && aconnector
- && aconnector->force_yuv420_output)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR444)
- && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
- 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) {
- drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, (struct drm_connector *)connector, mode_in);
- timing_out->vic = avi_frame.video_code;
- drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame, (struct drm_connector *)connector, mode_in);
- 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) {
- copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode);
- } else if (native_mode->clock == drm_mode->clock &&
- native_mode->htotal == drm_mode->htotal &&
- native_mode->vtotal == drm_mode->vtotal) {
- 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 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_ERROR("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_DEBUG_DRIVER("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 ||
- sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER)
- 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);
- }
-}
-
-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;
- 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_DEBUG_DRIVER("%s: [%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));
- 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_DEBUG_DRIVER("%s: [%s] DSC is selected from DP-HDMI PCON\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 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;
-#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 == NULL) {
- DRM_ERROR("connector is NULL!\n");
- return stream;
- }
-
- 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(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_ERROR("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_DEBUG_DRIVER("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(&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)
- mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket);
-
- if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
- stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ||
- stream->signal == SIGNAL_TYPE_EDP) {
- //
- // 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;
-
- 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->psr_skip_count = AMDGPU_DM_PSR_ENTRY_DELAY;
-
- }
-finish:
- dc_sink_release(sink);
-
- return stream;
-}
-
-static enum drm_connector_status
-amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
-{
- bool connected;
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- /*
- * 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.
- */
-
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED &&
- !aconnector->fake_enable)
- connected = (aconnector->dc_sink != NULL);
- else
- connected = (aconnector->base.force == DRM_FORCE_ON ||
- aconnector->base.force == DRM_FORCE_ON_DIGITAL);
-
- update_subconnector_property(aconnector);
-
- return (connected ? 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;
- }
-
- 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;
- }
-
- 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);
- to_dm_connector_state(connector->state)->abm_level = val ?:
- ABM_LEVEL_IMMEDIATE_DISABLE;
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- 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);
-
- 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);
-
- 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);
- if (aconnector->i2c) {
- i2c_del_adapter(&aconnector->i2c->base);
- kfree(aconnector->i2c);
- }
- 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(sizeof(*state), GFP_KERNEL);
-
- 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;
- 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;
-
- /*
- * Note: drm_get_edid gets edid in the following order:
- * 1) override EDID if set via edid_override debugfs,
- * 2) firmware EDID if set via edid_firmware module parameter
- * 3) regular DDC read.
- */
- edid = drm_get_edid(connector, ddc);
- if (!edid) {
- DRM_ERROR("No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- aconnector->edid = edid;
-
- /* Update emulated (virtual) sink's EDID */
- if (dc_em_sink && dc_link) {
- 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
- };
- struct edid *edid;
- struct i2c_adapter *ddc;
-
- if (dc_link->aux_mode)
- ddc = &aconnector->dm_dp_aux.aux.ddc;
- else
- ddc = &aconnector->i2c->base;
-
- /*
- * Note: drm_get_edid gets edid in the following order:
- * 1) override EDID if set via edid_override debugfs,
- * 2) firmware EDID if set via edid_firmware module parameter
- * 3) regular DDC read.
- */
- edid = drm_get_edid(connector, ddc);
- if (!edid) {
- DRM_ERROR("No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- if (drm_detect_hdmi_monitor(edid))
- init_params.sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
-
- aconnector->edid = 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, true);
-
-cleanup:
- if (dc_state)
- dc_state_release(dc_state);
-
- if (dc_plane_state)
- dc_plane_state_release(dc_plane_state);
-
- return dc_result;
-}
-
-struct dc_stream_state *
-create_validate_stream_for_sink(struct amdgpu_dm_connector *aconnector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream)
-{
- struct drm_connector *connector = &aconnector->base;
- 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;
-
- if (!dm_state)
- return NULL;
-
- do {
- stream = create_stream_for_sink(connector, drm_mode,
- dm_state, old_stream,
- requested_bpc);
- if (stream == NULL) {
- DRM_ERROR("Failed to create stream for sink!\n");
- break;
- }
-
- if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return stream;
-
- dc_result = dc_validate_stream(adev->dm.dc, 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) {
- DRM_DEBUG_KMS("Mode %dx%d (clk %d) failed DC validation with error %d (%s)\n",
- drm_mode->hdisplay,
- drm_mode->vdisplay,
- drm_mode->clock,
- dc_result,
- 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 >= 6);
-
- if (dc_result == DC_FAIL_ENC_VALIDATE && !aconnector->force_yuv420_output) {
- DRM_DEBUG_KMS("Retry forcing YCbCr420 encoding\n");
-
- aconnector->force_yuv420_output = true;
- stream = create_validate_stream_for_sink(aconnector, drm_mode,
- dm_state, old_stream);
- aconnector->force_yuv420_output = false;
- }
-
- return stream;
-}
-
-enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- int result = MODE_ERROR;
- struct dc_sink *dc_sink;
- /* TODO: Unhardcode stream count */
- struct dc_stream_state *stream;
- 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_ERROR("dc_sink is NULL!\n");
- goto fail;
- }
-
- drm_mode_set_crtcinfo(mode, 0);
-
- stream = create_validate_stream_for_sink(aconnector, mode,
- to_dm_connector_state(connector->state),
- NULL);
- 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_state *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;
-
- 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->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_state *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 (!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 = dfixed_const(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_DEBUG_ATOMIC("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_state *state,
- struct dc_state *dc_state,
- struct dsc_mst_fairness_vars *vars)
-{
- struct dc_stream_state *stream = NULL;
- struct drm_connector *connector;
- struct drm_connector_state *new_con_state;
- struct amdgpu_dm_connector *aconnector;
- struct dm_connector_state *dm_conn_state;
- int i, j, ret;
- int vcpi, pbn_div, pbn = 0, slot_num = 0;
-
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (!aconnector->mst_output_port)
- continue;
-
- if (!new_con_state || !new_con_state->crtc)
- continue;
-
- dm_conn_state = to_dm_connector_state(new_con_state);
-
- for (j = 0; j < dc_state->stream_count; j++) {
- stream = dc_state->streams[j];
- if (!stream)
- continue;
-
- if ((struct amdgpu_dm_connector *)stream->dm_stream_context == aconnector)
- break;
-
- stream = NULL;
- }
-
- if (!stream)
- continue;
-
- pbn_div = dm_mst_get_pbn_divider(stream->link);
- /* pbn is calculated by compute_mst_dsc_configs_for_state*/
- for (j = 0; j < dc_state->stream_count; j++) {
- if (vars[j].aconnector == aconnector) {
- pbn = vars[j].pbn;
- break;
- }
- }
-
- if (j == dc_state->stream_count || pbn_div == 0)
- continue;
-
- slot_num = DIV_ROUND_UP(pbn, pbn_div);
-
- if (stream->timing.flags.DSC != 1) {
- dm_conn_state->pbn = pbn;
- dm_conn_state->vcpi_slots = slot_num;
-
- ret = drm_dp_mst_atomic_enable_dsc(state, aconnector->mst_output_port,
- dm_conn_state->pbn, false);
- if (ret < 0)
- return ret;
-
- continue;
- }
-
- vcpi = drm_dp_mst_atomic_enable_dsc(state, aconnector->mst_output_port, pbn, true);
- if (vcpi < 0)
- return vcpi;
-
- dm_conn_state->pbn = pbn;
- dm_conn_state->vcpi_slots = vcpi;
- }
- return 0;
-}
-
-static int to_drm_connector_type(enum signal_type st)
-{
- 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:
- return DRM_MODE_CONNECTOR_DisplayPort;
- case SIGNAL_TYPE_DVI_DUAL_LINK:
- case SIGNAL_TYPE_DVI_SINGLE_LINK:
- 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,
- 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 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;
- struct 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}
- };
-
- 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,
- struct edid *edid)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (edid) {
- /* empty probed_modes */
- INIT_LIST_HEAD(&connector->probed_modes);
- amdgpu_dm_connector->num_modes =
- drm_add_edid_modes(connector, edid);
-
- /* 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_add_edid_modes() and need to be
- * restored here.
- */
- amdgpu_dm_update_freesync_caps(connector, edid);
- } 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,
- struct edid *edid)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (!(amdgpu_freesync_vid_mode && edid))
- 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 drm_encoder *encoder;
- struct edid *edid = amdgpu_dm_connector->edid;
- struct dc_link_settings *verified_link_cap =
- &amdgpu_dm_connector->dc_link->verified_link_cap;
- const struct dc *dc = amdgpu_dm_connector->dc_link->dc;
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
-
- if (!drm_edid_is_valid(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);
- } else {
- amdgpu_dm_connector_ddc_get_modes(connector, edid);
- if (encoder)
- amdgpu_dm_connector_add_common_modes(encoder, connector);
- amdgpu_dm_connector_add_freesync_modes(connector, 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);
-
-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);
-
- /*
- * 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;
- default:
- break;
- }
-
- drm_object_attach_property(&aconnector->base.base,
- dm->ddev->mode_config.scaling_mode_property,
- DRM_MODE_SCALE_NONE);
-
- 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);
-
- if (adev->dm.hdcp_workqueue)
- drm_connector_attach_content_protection_property(&aconnector->base, true);
- }
-}
-
-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 || !ddc_service->ddc_pin->hw_info.hw_supported)
- return result;
-
- cmd.payloads = kcalloc(num, sizeof(struct i2c_payload), GFP_KERNEL);
-
- 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 (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,
- int link_index,
- int *res)
-{
- struct amdgpu_device *adev = ddc_service->ctx->driver_context;
- struct amdgpu_i2c_adapter *i2c;
-
- i2c = kzalloc(sizeof(struct amdgpu_i2c_adapter), GFP_KERNEL);
- if (!i2c)
- return NULL;
- i2c->base.owner = THIS_MODULE;
- i2c->base.dev.parent = &adev->pdev->dev;
- i2c->base.algo = &amdgpu_dm_i2c_algo;
- snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d", link_index);
- i2c_set_adapdata(&i2c->base, i2c);
- i2c->ddc_service = ddc_service;
-
- return i2c;
-}
-
-
-/*
- * 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, link->link_index, &res);
- if (!i2c) {
- DRM_ERROR("Failed to create i2c adapter data\n");
- return -ENOMEM;
- }
-
- aconnector->i2c = i2c;
- res = i2c_add_adapter(&i2c->base);
-
- if (res) {
- DRM_ERROR("Failed to register hw i2c %d\n", link->link_index);
- goto out_free;
- }
-
- connector_type = to_drm_connector_type(link->connector_signal);
-
- res = drm_connector_init_with_ddc(
- dm->ddev,
- &aconnector->base,
- &amdgpu_dm_connector_funcs,
- connector_type,
- &i2c->base);
-
- if (res) {
- DRM_ERROR("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_DisplayPort
- || connector_type == DRM_MODE_CONNECTOR_eDP)
- 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)
-{
- /*
- * We have no guarantee that the frontend index maps to the same
- * backend index - some even map to more than one.
- *
- * TODO: Use a different interrupt or check DC itself for the mapping.
- */
- int irq_type =
- amdgpu_display_crtc_idx_to_irq_type(
- adev,
- acrtc->crtc_id);
- struct drm_vblank_crtc_config config = {0};
- struct dc_crtc_timing *timing;
- int offdelay;
-
- if (acrtc_state) {
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
- IP_VERSION(3, 5, 0) ||
- acrtc_state->stream->link->psr_settings.psr_version <
- DC_PSR_VERSION_UNSUPPORTED) {
- timing = &acrtc_state->stream->timing;
-
- /* at least 2 frames */
- offdelay = DIV64_U64_ROUND_UP((u64)20 *
- timing->v_total *
- timing->h_total,
- timing->pix_clk_100hz);
-
- config.offdelay_ms = offdelay ?: 30;
- } else {
- config.disable_immediate = true;
- }
-
- drm_crtc_vblank_on_config(&acrtc->base,
- &config);
-
- amdgpu_irq_get(
- adev,
- &adev->pageflip_irq,
- irq_type);
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- amdgpu_irq_get(
- adev,
- &adev->vline0_irq,
- irq_type);
-#endif
- } else {
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- amdgpu_irq_put(
- adev,
- &adev->vline0_irq,
- irq_type);
-#endif
- amdgpu_irq_put(
- adev,
- &adev->pageflip_irq,
- irq_type);
- drm_crtc_vblank_off(&acrtc->base);
- }
-}
-
-static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
- struct amdgpu_crtc *acrtc)
-{
- int irq_type =
- amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
-
- /**
- * This reads the current state for the IRQ and force reapplies
- * the setting to hardware.
- */
- amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type);
-}
-
-static bool
-is_scaling_state_different(const struct dm_connector_state *dm_state,
- const struct dm_connector_state *old_dm_state)
-{
- if (dm_state->scaling != old_dm_state->scaling)
- return true;
- if (!dm_state->underscan_enable && old_dm_state->underscan_enable) {
- if (old_dm_state->underscan_hborder != 0 && old_dm_state->underscan_vborder != 0)
- return true;
- } else if (dm_state->underscan_enable && !old_dm_state->underscan_enable) {
- if (dm_state->underscan_hborder != 0 && dm_state->underscan_vborder != 0)
- return true;
- } else if (dm_state->underscan_hborder != old_dm_state->underscan_hborder ||
- dm_state->underscan_vborder != old_dm_state->underscan_vborder)
- return true;
- return false;
-}
-
-static bool is_content_protection_different(struct drm_crtc_state *new_crtc_state,
- struct drm_crtc_state *old_crtc_state,
- struct drm_connector_state *new_conn_state,
- struct drm_connector_state *old_conn_state,
- const struct drm_connector *connector,
- struct hdcp_workqueue *hdcp_w)
-{
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state);
-
- pr_debug("[HDCP_DM] connector->index: %x connect_status: %x dpms: %x\n",
- connector->index, connector->status, connector->dpms);
- pr_debug("[HDCP_DM] state protection old: %x new: %x\n",
- old_conn_state->content_protection, new_conn_state->content_protection);
-
- if (old_crtc_state)
- pr_debug("[HDCP_DM] old crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n",
- old_crtc_state->enable,
- old_crtc_state->active,
- old_crtc_state->mode_changed,
- old_crtc_state->active_changed,
- old_crtc_state->connectors_changed);
-
- if (new_crtc_state)
- pr_debug("[HDCP_DM] NEW crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n",
- new_crtc_state->enable,
- new_crtc_state->active,
- new_crtc_state->mode_changed,
- new_crtc_state->active_changed,
- new_crtc_state->connectors_changed);
-
- /* hdcp content type change */
- if (old_conn_state->hdcp_content_type != new_conn_state->hdcp_content_type &&
- new_conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- new_conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- pr_debug("[HDCP_DM] Type0/1 change %s :true\n", __func__);
- return true;
- }
-
- /* CP is being re enabled, ignore this */
- if (old_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED &&
- new_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) {
- if (new_crtc_state && new_crtc_state->mode_changed) {
- new_conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- pr_debug("[HDCP_DM] ENABLED->DESIRED & mode_changed %s :true\n", __func__);
- return true;
- }
- new_conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- pr_debug("[HDCP_DM] ENABLED -> DESIRED %s :false\n", __func__);
- return false;
- }
-
- /* S3 resume case, since old state will always be 0 (UNDESIRED) and the restored state will be ENABLED
- *
- * Handles: UNDESIRED -> ENABLED
- */
- if (old_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_UNDESIRED &&
- new_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
- new_conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
-
- /* Stream removed and re-enabled
- *
- * Can sometimes overlap with the HPD case,
- * thus set update_hdcp to false to avoid
- * setting HDCP multiple times.
- *
- * Handles: DESIRED -> DESIRED (Special case)
- */
- if (!(old_conn_state->crtc && old_conn_state->crtc->enabled) &&
- new_conn_state->crtc && new_conn_state->crtc->enabled &&
- connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) {
- dm_con_state->update_hdcp = false;
- pr_debug("[HDCP_DM] DESIRED->DESIRED (Stream removed and re-enabled) %s :true\n",
- __func__);
- return true;
- }
-
- /* Hot-plug, headless s3, dpms
- *
- * Only start HDCP if the display is connected/enabled.
- * update_hdcp flag will be set to false until the next
- * HPD comes in.
- *
- * Handles: DESIRED -> DESIRED (Special case)
- */
- if (dm_con_state->update_hdcp &&
- new_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
- connector->dpms == DRM_MODE_DPMS_ON && aconnector->dc_sink != NULL) {
- dm_con_state->update_hdcp = false;
- pr_debug("[HDCP_DM] DESIRED->DESIRED (Hot-plug, headless s3, dpms) %s :true\n",
- __func__);
- return true;
- }
-
- if (old_conn_state->content_protection == new_conn_state->content_protection) {
- if (new_conn_state->content_protection >= DRM_MODE_CONTENT_PROTECTION_DESIRED) {
- if (new_crtc_state && new_crtc_state->mode_changed) {
- pr_debug("[HDCP_DM] DESIRED->DESIRED or ENABLE->ENABLE mode_change %s :true\n",
- __func__);
- return true;
- }
- pr_debug("[HDCP_DM] DESIRED->DESIRED & ENABLE->ENABLE %s :false\n",
- __func__);
- return false;
- }
-
- pr_debug("[HDCP_DM] UNDESIRED->UNDESIRED %s :false\n", __func__);
- return false;
- }
-
- if (new_conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED) {
- pr_debug("[HDCP_DM] UNDESIRED->DESIRED or DESIRED->UNDESIRED or ENABLED->UNDESIRED %s :true\n",
- __func__);
- return true;
- }
-
- pr_debug("[HDCP_DM] DESIRED->ENABLED %s :false\n", __func__);
- return false;
-}
-
-static void remove_stream(struct amdgpu_device *adev,
- struct amdgpu_crtc *acrtc,
- struct dc_stream_state *stream)
-{
- /* this is the update mode case */
-
- acrtc->otg_inst = -1;
- acrtc->enabled = false;
-}
-
-static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
-{
-
- assert_spin_locked(&acrtc->base.dev->event_lock);
- WARN_ON(acrtc->event);
-
- acrtc->event = acrtc->base.state->event;
-
- /* Set the flip status */
- acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
-
- /* Mark this event as consumed */
- acrtc->base.state->event = NULL;
-
- drm_dbg_state(acrtc->base.dev,
- "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
- acrtc->crtc_id);
-}
-
-static void update_freesync_state_on_stream(
- struct amdgpu_display_manager *dm,
- struct dm_crtc_state *new_crtc_state,
- struct dc_stream_state *new_stream,
- struct dc_plane_state *surface,
- u32 flip_timestamp_in_us)
-{
- struct mod_vrr_params vrr_params;
- struct dc_info_packet vrr_infopacket = {0};
- struct amdgpu_device *adev = dm->adev;
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
- unsigned long flags;
- bool pack_sdp_v1_3 = false;
- struct amdgpu_dm_connector *aconn;
- enum vrr_packet_type packet_type = PACKET_TYPE_VRR;
-
- if (!new_stream)
- return;
-
- /*
- * TODO: Determine why min/max totals and vrefresh can be 0 here.
- * For now it's sufficient to just guard against these conditions.
- */
-
- if (!new_stream->timing.h_total || !new_stream->timing.v_total)
- return;
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- vrr_params = acrtc->dm_irq_params.vrr_params;
-
- if (surface) {
- mod_freesync_handle_preflip(
- dm->freesync_module,
- surface,
- new_stream,
- flip_timestamp_in_us,
- &vrr_params);
-
- if (adev->family < AMDGPU_FAMILY_AI &&
- amdgpu_dm_crtc_vrr_active(new_crtc_state)) {
- mod_freesync_handle_v_update(dm->freesync_module,
- new_stream, &vrr_params);
-
- /* Need to call this before the frame ends. */
- dc_stream_adjust_vmin_vmax(dm->dc,
- new_crtc_state->stream,
- &vrr_params.adjust);
- }
- }
-
- aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context;
-
- if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) {
- pack_sdp_v1_3 = aconn->pack_sdp_v1_3;
-
- if (aconn->vsdb_info.amd_vsdb_version == 1)
- packet_type = PACKET_TYPE_FS_V1;
- else if (aconn->vsdb_info.amd_vsdb_version == 2)
- packet_type = PACKET_TYPE_FS_V2;
- else if (aconn->vsdb_info.amd_vsdb_version == 3)
- packet_type = PACKET_TYPE_FS_V3;
-
- mod_build_adaptive_sync_infopacket(new_stream, aconn->as_type, NULL,
- &new_stream->adaptive_sync_infopacket);
- }
-
- mod_freesync_build_vrr_infopacket(
- dm->freesync_module,
- new_stream,
- &vrr_params,
- packet_type,
- TRANSFER_FUNC_UNKNOWN,
- &vrr_infopacket,
- pack_sdp_v1_3);
-
- new_crtc_state->freesync_vrr_info_changed |=
- (memcmp(&new_crtc_state->vrr_infopacket,
- &vrr_infopacket,
- sizeof(vrr_infopacket)) != 0);
-
- acrtc->dm_irq_params.vrr_params = vrr_params;
- new_crtc_state->vrr_infopacket = vrr_infopacket;
-
- new_stream->vrr_infopacket = vrr_infopacket;
- new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params);
-
- if (new_crtc_state->freesync_vrr_info_changed)
- DRM_DEBUG_KMS("VRR packet update: crtc=%u enabled=%d state=%d",
- new_crtc_state->base.crtc->base.id,
- (int)new_crtc_state->base.vrr_enabled,
- (int)vrr_params.state);
-
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-}
-
-static void update_stream_irq_parameters(
- struct amdgpu_display_manager *dm,
- struct dm_crtc_state *new_crtc_state)
-{
- struct dc_stream_state *new_stream = new_crtc_state->stream;
- struct mod_vrr_params vrr_params;
- struct mod_freesync_config config = new_crtc_state->freesync_config;
- struct amdgpu_device *adev = dm->adev;
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
- unsigned long flags;
-
- if (!new_stream)
- return;
-
- /*
- * TODO: Determine why min/max totals and vrefresh can be 0 here.
- * For now it's sufficient to just guard against these conditions.
- */
- if (!new_stream->timing.h_total || !new_stream->timing.v_total)
- return;
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- vrr_params = acrtc->dm_irq_params.vrr_params;
-
- if (new_crtc_state->vrr_supported &&
- config.min_refresh_in_uhz &&
- config.max_refresh_in_uhz) {
- /*
- * if freesync compatible mode was set, config.state will be set
- * in atomic check
- */
- if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz &&
- (!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) ||
- new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) {
- vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz;
- vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz;
- vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz;
- vrr_params.state = VRR_STATE_ACTIVE_FIXED;
- } else {
- config.state = new_crtc_state->base.vrr_enabled ?
- VRR_STATE_ACTIVE_VARIABLE :
- VRR_STATE_INACTIVE;
- }
- } else {
- config.state = VRR_STATE_UNSUPPORTED;
- }
-
- mod_freesync_build_vrr_params(dm->freesync_module,
- new_stream,
- &config, &vrr_params);
-
- new_crtc_state->freesync_config = config;
- /* Copy state for access from DM IRQ handler */
- acrtc->dm_irq_params.freesync_config = config;
- acrtc->dm_irq_params.active_planes = new_crtc_state->active_planes;
- acrtc->dm_irq_params.vrr_params = vrr_params;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-}
-
-static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
- struct dm_crtc_state *new_state)
-{
- bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
- bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
-
- if (!old_vrr_active && new_vrr_active) {
- /* Transition VRR inactive -> active:
- * While VRR is active, we must not disable vblank irq, as a
- * reenable after disable would compute bogus vblank/pflip
- * timestamps if it likely happened inside display front-porch.
- *
- * We also need vupdate irq for the actual core vblank handling
- * at end of vblank.
- */
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
- WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
- DRM_DEBUG_DRIVER("%s: crtc=%u VRR off->on: Get vblank ref\n",
- __func__, new_state->base.crtc->base.id);
- } else if (old_vrr_active && !new_vrr_active) {
- /* Transition VRR active -> inactive:
- * Allow vblank irq disable again for fixed refresh rate.
- */
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
- drm_crtc_vblank_put(new_state->base.crtc);
- DRM_DEBUG_DRIVER("%s: crtc=%u VRR on->off: Drop vblank ref\n",
- __func__, new_state->base.crtc->base.id);
- }
-}
-
-static void amdgpu_dm_commit_cursors(struct drm_atomic_state *state)
-{
- struct drm_plane *plane;
- struct drm_plane_state *old_plane_state;
- int i;
-
- /*
- * TODO: Make this per-stream so we don't issue redundant updates for
- * commits with multiple streams.
- */
- for_each_old_plane_in_state(state, plane, old_plane_state, i)
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- amdgpu_dm_plane_handle_cursor_update(plane, old_plane_state);
-}
-
-static inline uint32_t get_mem_type(struct drm_framebuffer *fb)
-{
- struct amdgpu_bo *abo = gem_to_amdgpu_bo(fb->obj[0]);
-
- return abo->tbo.resource ? abo->tbo.resource->mem_type : 0;
-}
-
-static void amdgpu_dm_update_cursor(struct drm_plane *plane,
- struct drm_plane_state *old_plane_state,
- struct dc_stream_update *update)
-{
- struct amdgpu_device *adev = drm_to_adev(plane->dev);
- struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(plane->state->fb);
- struct drm_crtc *crtc = afb ? plane->state->crtc : old_plane_state->crtc;
- struct dm_crtc_state *crtc_state = crtc ? to_dm_crtc_state(crtc->state) : NULL;
- struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
- uint64_t address = afb ? afb->address : 0;
- struct dc_cursor_position position = {0};
- struct dc_cursor_attributes attributes;
- int ret;
-
- if (!plane->state->fb && !old_plane_state->fb)
- return;
-
- drm_dbg_atomic(plane->dev, "crtc_id=%d with size %d to %d\n",
- amdgpu_crtc->crtc_id, plane->state->crtc_w,
- plane->state->crtc_h);
-
- ret = amdgpu_dm_plane_get_cursor_position(plane, crtc, &position);
- if (ret)
- return;
-
- if (!position.enable) {
- /* turn off cursor */
- if (crtc_state && crtc_state->stream) {
- dc_stream_set_cursor_position(crtc_state->stream,
- &position);
- update->cursor_position = &crtc_state->stream->cursor_position;
- }
- return;
- }
-
- amdgpu_crtc->cursor_width = plane->state->crtc_w;
- amdgpu_crtc->cursor_height = plane->state->crtc_h;
-
- memset(&attributes, 0, sizeof(attributes));
- attributes.address.high_part = upper_32_bits(address);
- attributes.address.low_part = lower_32_bits(address);
- attributes.width = plane->state->crtc_w;
- attributes.height = plane->state->crtc_h;
- attributes.color_format = CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA;
- attributes.rotation_angle = 0;
- attributes.attribute_flags.value = 0;
-
- /* Enable cursor degamma ROM on DCN3+ for implicit sRGB degamma in DRM
- * legacy gamma setup.
- */
- if (crtc_state->cm_is_degamma_srgb &&
- adev->dm.dc->caps.color.dpp.gamma_corr)
- attributes.attribute_flags.bits.ENABLE_CURSOR_DEGAMMA = 1;
-
- if (afb)
- attributes.pitch = afb->base.pitches[0] / afb->base.format->cpp[0];
-
- if (crtc_state->stream) {
- if (!dc_stream_set_cursor_attributes(crtc_state->stream,
- &attributes))
- DRM_ERROR("DC failed to set cursor attributes\n");
-
- update->cursor_attributes = &crtc_state->stream->cursor_attributes;
-
- if (!dc_stream_set_cursor_position(crtc_state->stream,
- &position))
- DRM_ERROR("DC failed to set cursor position\n");
-
- update->cursor_position = &crtc_state->stream->cursor_position;
- }
-}
-
-static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
- struct drm_device *dev,
- struct amdgpu_display_manager *dm,
- struct drm_crtc *pcrtc,
- bool wait_for_vblank)
-{
- u32 i;
- u64 timestamp_ns = ktime_get_ns();
- struct drm_plane *plane;
- struct drm_plane_state *old_plane_state, *new_plane_state;
- struct amdgpu_crtc *acrtc_attach = to_amdgpu_crtc(pcrtc);
- struct drm_crtc_state *new_pcrtc_state =
- drm_atomic_get_new_crtc_state(state, pcrtc);
- struct dm_crtc_state *acrtc_state = to_dm_crtc_state(new_pcrtc_state);
- struct dm_crtc_state *dm_old_crtc_state =
- to_dm_crtc_state(drm_atomic_get_old_crtc_state(state, pcrtc));
- int planes_count = 0, vpos, hpos;
- unsigned long flags;
- u32 target_vblank, last_flip_vblank;
- bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state);
- bool cursor_update = false;
- bool pflip_present = false;
- bool dirty_rects_changed = false;
- bool updated_planes_and_streams = false;
- struct {
- struct dc_surface_update surface_updates[MAX_SURFACES];
- struct dc_plane_info plane_infos[MAX_SURFACES];
- struct dc_scaling_info scaling_infos[MAX_SURFACES];
- struct dc_flip_addrs flip_addrs[MAX_SURFACES];
- struct dc_stream_update stream_update;
- } *bundle;
-
- bundle = kzalloc(sizeof(*bundle), GFP_KERNEL);
-
- if (!bundle) {
- drm_err(dev, "Failed to allocate update bundle\n");
- goto cleanup;
- }
-
- /*
- * Disable the cursor first if we're disabling all the planes.
- * It'll remain on the screen after the planes are re-enabled
- * if we don't.
- *
- * If the cursor is transitioning from native to overlay mode, the
- * native cursor needs to be disabled first.
- */
- if (acrtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE &&
- dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) {
- struct dc_cursor_position cursor_position = {0};
-
- if (!dc_stream_set_cursor_position(acrtc_state->stream,
- &cursor_position))
- drm_err(dev, "DC failed to disable native cursor\n");
-
- bundle->stream_update.cursor_position =
- &acrtc_state->stream->cursor_position;
- }
-
- if (acrtc_state->active_planes == 0 &&
- dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
- amdgpu_dm_commit_cursors(state);
-
- /* update planes when needed */
- for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
- struct drm_crtc *crtc = new_plane_state->crtc;
- struct drm_crtc_state *new_crtc_state;
- struct drm_framebuffer *fb = new_plane_state->fb;
- struct amdgpu_framebuffer *afb = (struct amdgpu_framebuffer *)fb;
- bool plane_needs_flip;
- struct dc_plane_state *dc_plane;
- struct dm_plane_state *dm_new_plane_state = to_dm_plane_state(new_plane_state);
-
- /* Cursor plane is handled after stream updates */
- if (plane->type == DRM_PLANE_TYPE_CURSOR &&
- acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) {
- if ((fb && crtc == pcrtc) ||
- (old_plane_state->fb && old_plane_state->crtc == pcrtc)) {
- cursor_update = true;
- if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0)
- amdgpu_dm_update_cursor(plane, old_plane_state, &bundle->stream_update);
- }
-
- continue;
- }
-
- if (!fb || !crtc || pcrtc != crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
- if (!new_crtc_state->active)
- continue;
-
- dc_plane = dm_new_plane_state->dc_state;
- if (!dc_plane)
- continue;
-
- bundle->surface_updates[planes_count].surface = dc_plane;
- 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;
- }
-
- amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state,
- &bundle->scaling_infos[planes_count]);
-
- bundle->surface_updates[planes_count].scaling_info =
- &bundle->scaling_infos[planes_count];
-
- plane_needs_flip = old_plane_state->fb && new_plane_state->fb;
-
- pflip_present = pflip_present || plane_needs_flip;
-
- if (!plane_needs_flip) {
- planes_count += 1;
- continue;
- }
-
- fill_dc_plane_info_and_addr(
- dm->adev, new_plane_state,
- afb->tiling_flags,
- &bundle->plane_infos[planes_count],
- &bundle->flip_addrs[planes_count].address,
- afb->tmz_surface, false);
-
- drm_dbg_state(state->dev, "plane: id=%d dcc_en=%d\n",
- new_plane_state->plane->index,
- bundle->plane_infos[planes_count].dcc.enable);
-
- bundle->surface_updates[planes_count].plane_info =
- &bundle->plane_infos[planes_count];
-
- if (acrtc_state->stream->link->psr_settings.psr_feature_enabled ||
- acrtc_state->stream->link->replay_settings.replay_feature_enabled) {
- fill_dc_dirty_rects(plane, old_plane_state,
- new_plane_state, new_crtc_state,
- &bundle->flip_addrs[planes_count],
- acrtc_state->stream->link->psr_settings.psr_version ==
- DC_PSR_VERSION_SU_1,
- &dirty_rects_changed);
-
- /*
- * If the dirty regions changed, PSR-SU need to be disabled temporarily
- * and enabled it again after dirty regions are stable to avoid video glitch.
- * PSR-SU will be enabled in vblank_control_worker() if user pause the video
- * during the PSR-SU was disabled.
- */
- if (acrtc_state->stream->link->psr_settings.psr_version >= DC_PSR_VERSION_SU_1 &&
- acrtc_attach->dm_irq_params.allow_psr_entry &&
-#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
- !amdgpu_dm_crc_window_is_activated(acrtc_state->base.crtc) &&
-#endif
- dirty_rects_changed) {
- mutex_lock(&dm->dc_lock);
- acrtc_state->stream->link->psr_settings.psr_dirty_rects_change_timestamp_ns =
- timestamp_ns;
- if (acrtc_state->stream->link->psr_settings.psr_allow_active)
- amdgpu_dm_psr_disable(acrtc_state->stream);
- mutex_unlock(&dm->dc_lock);
- }
- }
-
- /*
- * Only allow immediate flips for fast updates that don't
- * change memory domain, FB pitch, DCC state, rotation or
- * mirroring.
- *
- * dm_crtc_helper_atomic_check() only accepts async flips with
- * fast updates.
- */
- if (crtc->state->async_flip &&
- (acrtc_state->update_type != UPDATE_TYPE_FAST ||
- get_mem_type(old_plane_state->fb) != get_mem_type(fb)))
- drm_warn_once(state->dev,
- "[PLANE:%d:%s] async flip with non-fast update\n",
- plane->base.id, plane->name);
-
- bundle->flip_addrs[planes_count].flip_immediate =
- crtc->state->async_flip &&
- acrtc_state->update_type == UPDATE_TYPE_FAST &&
- get_mem_type(old_plane_state->fb) == get_mem_type(fb);
-
- timestamp_ns = ktime_get_ns();
- bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000);
- bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count];
- bundle->surface_updates[planes_count].surface = dc_plane;
-
- if (!bundle->surface_updates[planes_count].surface) {
- DRM_ERROR("No surface for CRTC: id=%d\n",
- acrtc_attach->crtc_id);
- continue;
- }
-
- if (plane == pcrtc->primary)
- update_freesync_state_on_stream(
- dm,
- acrtc_state,
- acrtc_state->stream,
- dc_plane,
- bundle->flip_addrs[planes_count].flip_timestamp_in_us);
-
- drm_dbg_state(state->dev, "%s Flipping to hi: 0x%x, low: 0x%x\n",
- __func__,
- bundle->flip_addrs[planes_count].address.grph.addr.high_part,
- bundle->flip_addrs[planes_count].address.grph.addr.low_part);
-
- planes_count += 1;
-
- }
-
- if (pflip_present) {
- if (!vrr_active) {
- /* Use old throttling in non-vrr fixed refresh rate mode
- * to keep flip scheduling based on target vblank counts
- * working in a backwards compatible way, e.g., for
- * clients using the GLX_OML_sync_control extension or
- * DRI3/Present extension with defined target_msc.
- */
- last_flip_vblank = amdgpu_get_vblank_counter_kms(pcrtc);
- } else {
- /* For variable refresh rate mode only:
- * Get vblank of last completed flip to avoid > 1 vrr
- * flips per video frame by use of throttling, but allow
- * flip programming anywhere in the possibly large
- * variable vrr vblank interval for fine-grained flip
- * timing control and more opportunity to avoid stutter
- * on late submission of flips.
- */
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- last_flip_vblank = acrtc_attach->dm_irq_params.last_flip_vblank;
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
-
- target_vblank = last_flip_vblank + wait_for_vblank;
-
- /*
- * Wait until we're out of the vertical blank period before the one
- * targeted by the flip
- */
- while ((acrtc_attach->enabled &&
- (amdgpu_display_get_crtc_scanoutpos(dm->ddev, acrtc_attach->crtc_id,
- 0, &vpos, &hpos, NULL,
- NULL, &pcrtc->hwmode)
- & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
- (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
- (int)(target_vblank -
- amdgpu_get_vblank_counter_kms(pcrtc)) > 0)) {
- usleep_range(1000, 1100);
- }
-
- /**
- * Prepare the flip event for the pageflip interrupt to handle.
- *
- * This only works in the case where we've already turned on the
- * appropriate hardware blocks (eg. HUBP) so in the transition case
- * from 0 -> n planes we have to skip a hardware generated event
- * and rely on sending it from software.
- */
- if (acrtc_attach->base.state->event &&
- acrtc_state->active_planes > 0) {
- drm_crtc_vblank_get(pcrtc);
-
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
-
- WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE);
- prepare_flip_isr(acrtc_attach);
-
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
-
- if (acrtc_state->stream) {
- if (acrtc_state->freesync_vrr_info_changed)
- bundle->stream_update.vrr_infopacket =
- &acrtc_state->stream->vrr_infopacket;
- }
- } else if (cursor_update && acrtc_state->active_planes > 0) {
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- if (acrtc_attach->base.state->event) {
- drm_crtc_vblank_get(pcrtc);
- acrtc_attach->event = acrtc_attach->base.state->event;
- acrtc_attach->base.state->event = NULL;
- }
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
-
- /* Update the planes if changed or disable if we don't have any. */
- if ((planes_count || acrtc_state->active_planes == 0) &&
- acrtc_state->stream) {
- /*
- * If PSR or idle optimizations are enabled then flush out
- * any pending work before hardware programming.
- */
- if (dm->vblank_control_workqueue)
- flush_workqueue(dm->vblank_control_workqueue);
-
- bundle->stream_update.stream = acrtc_state->stream;
- if (new_pcrtc_state->mode_changed) {
- bundle->stream_update.src = acrtc_state->stream->src;
- bundle->stream_update.dst = acrtc_state->stream->dst;
- }
-
- if (new_pcrtc_state->color_mgmt_changed) {
- /*
- * TODO: This isn't fully correct since we've actually
- * already modified the stream in place.
- */
- bundle->stream_update.gamut_remap =
- &acrtc_state->stream->gamut_remap_matrix;
- bundle->stream_update.output_csc_transform =
- &acrtc_state->stream->csc_color_matrix;
- bundle->stream_update.out_transfer_func =
- &acrtc_state->stream->out_transfer_func;
- bundle->stream_update.lut3d_func =
- (struct dc_3dlut *) acrtc_state->stream->lut3d_func;
- bundle->stream_update.func_shaper =
- (struct dc_transfer_func *) acrtc_state->stream->func_shaper;
- }
-
- acrtc_state->stream->abm_level = acrtc_state->abm_level;
- if (acrtc_state->abm_level != dm_old_crtc_state->abm_level)
- bundle->stream_update.abm_level = &acrtc_state->abm_level;
-
- mutex_lock(&dm->dc_lock);
- if ((acrtc_state->update_type > UPDATE_TYPE_FAST) &&
- acrtc_state->stream->link->psr_settings.psr_allow_active)
- amdgpu_dm_psr_disable(acrtc_state->stream);
- mutex_unlock(&dm->dc_lock);
-
- /*
- * If FreeSync state on the stream has changed then we need to
- * re-adjust the min/max bounds now that DC doesn't handle this
- * as part of commit.
- */
- if (is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) {
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- dc_stream_adjust_vmin_vmax(
- dm->dc, acrtc_state->stream,
- &acrtc_attach->dm_irq_params.vrr_params.adjust);
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
- mutex_lock(&dm->dc_lock);
- update_planes_and_stream_adapter(dm->dc,
- acrtc_state->update_type,
- planes_count,
- acrtc_state->stream,
- &bundle->stream_update,
- bundle->surface_updates);
- updated_planes_and_streams = true;
-
- /**
- * Enable or disable the interrupts on the backend.
- *
- * Most pipes are put into power gating when unused.
- *
- * When power gating is enabled on a pipe we lose the
- * interrupt enablement state when power gating is disabled.
- *
- * So we need to update the IRQ control state in hardware
- * whenever the pipe turns on (since it could be previously
- * power gated) or off (since some pipes can't be power gated
- * on some ASICs).
- */
- if (dm_old_crtc_state->active_planes != acrtc_state->active_planes)
- dm_update_pflip_irq_state(drm_to_adev(dev),
- acrtc_attach);
-
- if (acrtc_state->update_type > UPDATE_TYPE_FAST) {
- if (acrtc_state->stream->link->replay_settings.config.replay_supported &&
- !acrtc_state->stream->link->replay_settings.replay_feature_enabled) {
- struct amdgpu_dm_connector *aconn =
- (struct amdgpu_dm_connector *)acrtc_state->stream->dm_stream_context;
- amdgpu_dm_link_setup_replay(acrtc_state->stream->link, aconn);
- } else if (acrtc_state->stream->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED &&
- !acrtc_state->stream->link->psr_settings.psr_feature_enabled) {
-
- struct amdgpu_dm_connector *aconn = (struct amdgpu_dm_connector *)
- acrtc_state->stream->dm_stream_context;
-
- if (!aconn->disallow_edp_enter_psr)
- amdgpu_dm_link_setup_psr(acrtc_state->stream);
- }
- }
-
- /* Decrement skip count when PSR is enabled and we're doing fast updates. */
- if (acrtc_state->update_type == UPDATE_TYPE_FAST &&
- acrtc_state->stream->link->psr_settings.psr_feature_enabled) {
- struct amdgpu_dm_connector *aconn =
- (struct amdgpu_dm_connector *)acrtc_state->stream->dm_stream_context;
-
- if (aconn->psr_skip_count > 0)
- aconn->psr_skip_count--;
-
- /* Allow PSR when skip count is 0. */
- acrtc_attach->dm_irq_params.allow_psr_entry = !aconn->psr_skip_count;
-
- /*
- * If sink supports PSR SU, there is no need to rely on
- * a vblank event disable request to enable PSR. PSR SU
- * can be enabled immediately once OS demonstrates an
- * adequate number of fast atomic commits to notify KMD
- * of update events. See `vblank_control_worker()`.
- */
- if (acrtc_state->stream->link->psr_settings.psr_version >= DC_PSR_VERSION_SU_1 &&
- acrtc_attach->dm_irq_params.allow_psr_entry &&
-#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
- !amdgpu_dm_crc_window_is_activated(acrtc_state->base.crtc) &&
-#endif
- !acrtc_state->stream->link->psr_settings.psr_allow_active &&
- !aconn->disallow_edp_enter_psr &&
- (timestamp_ns -
- acrtc_state->stream->link->psr_settings.psr_dirty_rects_change_timestamp_ns) >
- 500000000)
- amdgpu_dm_psr_enable(acrtc_state->stream);
- } else {
- acrtc_attach->dm_irq_params.allow_psr_entry = false;
- }
-
- mutex_unlock(&dm->dc_lock);
- }
-
- /*
- * Update cursor state *after* programming all the planes.
- * This avoids redundant programming in the case where we're going
- * to be disabling a single plane - those pipes are being disabled.
- */
- if (acrtc_state->active_planes &&
- (!updated_planes_and_streams || amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) &&
- acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
- amdgpu_dm_commit_cursors(state);
-
-cleanup:
- kfree(bundle);
-}
-
-static void amdgpu_dm_commit_audio(struct drm_device *dev,
- struct drm_atomic_state *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
- * @stream_state: the DC stream state.
- *
- * Copy the mirrored transient state flags from DRM, to DC. It is used to bring
- * a dc_stream_state's flags in sync with a drm_crtc_state's flags.
- */
-static void amdgpu_dm_crtc_copy_transient_flags(struct drm_crtc_state *crtc_state,
- struct dc_stream_state *stream_state)
-{
- stream_state->mode_changed = drm_atomic_crtc_needs_modeset(crtc_state);
-}
-
-static void dm_clear_writeback(struct amdgpu_display_manager *dm,
- struct dm_crtc_state *crtc_state)
-{
- dc_stream_remove_writeback(dm->dc, crtc_state->stream, 0);
-}
-
-static void amdgpu_dm_commit_streams(struct drm_atomic_state *state,
- struct dc_state *dc_state)
-{
- struct drm_device *dev = state->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct drm_crtc *crtc;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
- struct drm_connector_state *old_con_state;
- struct drm_connector *connector;
- bool mode_set_reset_required = false;
- u32 i;
- struct dc_commit_streams_params params = {dc_state->streams, dc_state->stream_count};
-
- /* Disable writeback */
- for_each_old_connector_in_state(state, connector, old_con_state, i) {
- struct dm_connector_state *dm_old_con_state;
- struct amdgpu_crtc *acrtc;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- old_crtc_state = NULL;
-
- dm_old_con_state = to_dm_connector_state(old_con_state);
- if (!dm_old_con_state->base.crtc)
- continue;
-
- acrtc = to_amdgpu_crtc(dm_old_con_state->base.crtc);
- if (acrtc)
- old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base);
-
- if (!acrtc || !acrtc->wb_enabled)
- continue;
-
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- dm_clear_writeback(dm, dm_old_crtc_state);
- acrtc->wb_enabled = false;
- }
-
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state,
- new_crtc_state, i) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- if (old_crtc_state->active &&
- (!new_crtc_state->active ||
- drm_atomic_crtc_needs_modeset(new_crtc_state))) {
- manage_dm_interrupts(adev, acrtc, NULL);
- dc_stream_release(dm_old_crtc_state->stream);
- }
- }
-
- drm_atomic_helper_calc_timestamping_constants(state);
-
- /* update changed items */
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- drm_dbg_state(state->dev,
- "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, planes_changed:%d, mode_changed:%d,active_changed:%d,connectors_changed:%d\n",
- acrtc->crtc_id,
- new_crtc_state->enable,
- new_crtc_state->active,
- new_crtc_state->planes_changed,
- new_crtc_state->mode_changed,
- new_crtc_state->active_changed,
- new_crtc_state->connectors_changed);
-
- /* Disable cursor if disabling crtc */
- if (old_crtc_state->active && !new_crtc_state->active) {
- struct dc_cursor_position position;
-
- memset(&position, 0, sizeof(position));
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- dc_stream_program_cursor_position(dm_old_crtc_state->stream, &position);
- mutex_unlock(&dm->dc_lock);
- }
-
- /* Copy all transient state flags into dc state */
- if (dm_new_crtc_state->stream) {
- amdgpu_dm_crtc_copy_transient_flags(&dm_new_crtc_state->base,
- dm_new_crtc_state->stream);
- }
-
- /* handles headless hotplug case, updating new_state and
- * aconnector as needed
- */
-
- if (amdgpu_dm_crtc_modeset_required(new_crtc_state, dm_new_crtc_state->stream, dm_old_crtc_state->stream)) {
-
- drm_dbg_atomic(dev,
- "Atomic commit: SET crtc id %d: [%p]\n",
- acrtc->crtc_id, acrtc);
-
- if (!dm_new_crtc_state->stream) {
- /*
- * this could happen because of issues with
- * userspace notifications delivery.
- * In this case userspace tries to set mode on
- * display which is disconnected in fact.
- * dc_sink is NULL in this case on aconnector.
- * We expect reset mode will come soon.
- *
- * This can also happen when unplug is done
- * during resume sequence ended
- *
- * In this case, we want to pretend we still
- * have a sink to keep the pipe running so that
- * hw state is consistent with the sw state
- */
- drm_dbg_atomic(dev,
- "Failed to create new stream for crtc %d\n",
- acrtc->base.base.id);
- continue;
- }
-
- if (dm_old_crtc_state->stream)
- remove_stream(adev, acrtc, dm_old_crtc_state->stream);
-
- pm_runtime_get_noresume(dev->dev);
-
- acrtc->enabled = true;
- acrtc->hw_mode = new_crtc_state->mode;
- crtc->hwmode = new_crtc_state->mode;
- mode_set_reset_required = true;
- } else if (modereset_required(new_crtc_state)) {
- drm_dbg_atomic(dev,
- "Atomic commit: RESET. crtc id %d:[%p]\n",
- acrtc->crtc_id, acrtc);
- /* i.e. reset mode */
- if (dm_old_crtc_state->stream)
- remove_stream(adev, acrtc, dm_old_crtc_state->stream);
-
- mode_set_reset_required = true;
- }
- } /* for_each_crtc_in_state() */
-
- /* if there mode set or reset, disable eDP PSR, Replay */
- if (mode_set_reset_required) {
- if (dm->vblank_control_workqueue)
- flush_workqueue(dm->vblank_control_workqueue);
-
- amdgpu_dm_replay_disable_all(dm);
- amdgpu_dm_psr_disable_all(dm);
- }
-
- dm_enable_per_frame_crtc_master_sync(dc_state);
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- WARN_ON(!dc_commit_streams(dm->dc, &params));
-
- /* Allow idle optimization when vblank count is 0 for display off */
- if (dm->active_vblank_irq_count == 0)
- dc_allow_idle_optimizations(dm->dc, true);
- mutex_unlock(&dm->dc_lock);
-
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- if (dm_new_crtc_state->stream != NULL) {
- const struct dc_stream_status *status =
- dc_stream_get_status(dm_new_crtc_state->stream);
-
- if (!status)
- status = dc_state_get_stream_status(dc_state,
- dm_new_crtc_state->stream);
- if (!status)
- drm_err(dev,
- "got no status for stream %p on acrtc%p\n",
- dm_new_crtc_state->stream, acrtc);
- else
- acrtc->otg_inst = status->primary_otg_inst;
- }
- }
-}
-
-static void dm_set_writeback(struct amdgpu_display_manager *dm,
- struct dm_crtc_state *crtc_state,
- struct drm_connector *connector,
- struct drm_connector_state *new_con_state)
-{
- struct drm_writeback_connector *wb_conn = drm_connector_to_writeback(connector);
- struct amdgpu_device *adev = dm->adev;
- struct amdgpu_crtc *acrtc;
- struct dc_writeback_info *wb_info;
- struct pipe_ctx *pipe = NULL;
- struct amdgpu_framebuffer *afb;
- int i = 0;
-
- wb_info = kzalloc(sizeof(*wb_info), GFP_KERNEL);
- if (!wb_info) {
- DRM_ERROR("Failed to allocate wb_info\n");
- return;
- }
-
- acrtc = to_amdgpu_crtc(wb_conn->encoder.crtc);
- if (!acrtc) {
- DRM_ERROR("no amdgpu_crtc found\n");
- kfree(wb_info);
- return;
- }
-
- afb = to_amdgpu_framebuffer(new_con_state->writeback_job->fb);
- if (!afb) {
- DRM_ERROR("No amdgpu_framebuffer found\n");
- kfree(wb_info);
- return;
- }
-
- for (i = 0; i < MAX_PIPES; i++) {
- if (dm->dc->current_state->res_ctx.pipe_ctx[i].stream == crtc_state->stream) {
- pipe = &dm->dc->current_state->res_ctx.pipe_ctx[i];
- break;
- }
- }
-
- /* fill in wb_info */
- wb_info->wb_enabled = true;
-
- wb_info->dwb_pipe_inst = 0;
- wb_info->dwb_params.dwbscl_black_color = 0;
- wb_info->dwb_params.hdr_mult = 0x1F000;
- wb_info->dwb_params.csc_params.gamut_adjust_type = CM_GAMUT_ADJUST_TYPE_BYPASS;
- wb_info->dwb_params.csc_params.gamut_coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S2_13;
- wb_info->dwb_params.output_depth = DWB_OUTPUT_PIXEL_DEPTH_10BPC;
- wb_info->dwb_params.cnv_params.cnv_out_bpc = DWB_CNV_OUT_BPC_10BPC;
-
- /* width & height from crtc */
- wb_info->dwb_params.cnv_params.src_width = acrtc->base.mode.crtc_hdisplay;
- wb_info->dwb_params.cnv_params.src_height = acrtc->base.mode.crtc_vdisplay;
- wb_info->dwb_params.dest_width = acrtc->base.mode.crtc_hdisplay;
- wb_info->dwb_params.dest_height = acrtc->base.mode.crtc_vdisplay;
-
- wb_info->dwb_params.cnv_params.crop_en = false;
- wb_info->dwb_params.stereo_params.stereo_enabled = false;
-
- wb_info->dwb_params.cnv_params.out_max_pix_val = 0x3ff; // 10 bits
- wb_info->dwb_params.cnv_params.out_min_pix_val = 0;
- wb_info->dwb_params.cnv_params.fc_out_format = DWB_OUT_FORMAT_32BPP_ARGB;
- wb_info->dwb_params.cnv_params.out_denorm_mode = DWB_OUT_DENORM_BYPASS;
-
- wb_info->dwb_params.out_format = dwb_scaler_mode_bypass444;
-
- wb_info->dwb_params.capture_rate = dwb_capture_rate_0;
-
- wb_info->dwb_params.scaler_taps.h_taps = 4;
- wb_info->dwb_params.scaler_taps.v_taps = 4;
- wb_info->dwb_params.scaler_taps.h_taps_c = 2;
- wb_info->dwb_params.scaler_taps.v_taps_c = 2;
- wb_info->dwb_params.subsample_position = DWB_INTERSTITIAL_SUBSAMPLING;
-
- wb_info->mcif_buf_params.luma_pitch = afb->base.pitches[0];
- wb_info->mcif_buf_params.chroma_pitch = afb->base.pitches[1];
-
- for (i = 0; i < DWB_MCIF_BUF_COUNT; i++) {
- wb_info->mcif_buf_params.luma_address[i] = afb->address;
- wb_info->mcif_buf_params.chroma_address[i] = 0;
- }
-
- wb_info->mcif_buf_params.p_vmid = 1;
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 0, 0)) {
- wb_info->mcif_warmup_params.start_address.quad_part = afb->address;
- wb_info->mcif_warmup_params.region_size =
- wb_info->mcif_buf_params.luma_pitch * wb_info->dwb_params.dest_height;
- }
- wb_info->mcif_warmup_params.p_vmid = 1;
- wb_info->writeback_source_plane = pipe->plane_state;
-
- 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);
-}
-
-/**
- * amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation.
- * @state: The atomic state to commit
- *
- * This will tell DC to commit the constructed DC state from atomic_check,
- * programming the hardware. Any failures here implies a hardware failure, since
- * atomic check should have filtered anything non-kosher.
- */
-static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
-{
- struct drm_device *dev = state->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct dm_atomic_state *dm_state;
- struct dc_state *dc_state = NULL;
- u32 i, j;
- struct drm_crtc *crtc;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- unsigned long flags;
- bool wait_for_vblank = true;
- struct drm_connector *connector;
- struct drm_connector_state *old_con_state, *new_con_state;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
- int crtc_disable_count = 0;
-
- trace_amdgpu_dm_atomic_commit_tail_begin(state);
-
- drm_atomic_helper_update_legacy_modeset_state(dev, state);
- drm_dp_mst_atomic_wait_for_dependencies(state);
-
- dm_state = dm_atomic_get_new_state(state);
- if (dm_state && dm_state->context) {
- dc_state = dm_state->context;
- amdgpu_dm_commit_streams(state, dc_state);
- }
-
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
- struct amdgpu_dm_connector *aconnector;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (!adev->dm.hdcp_workqueue)
- continue;
-
- pr_debug("[HDCP_DM] -------------- i : %x ----------\n", i);
-
- if (!connector)
- continue;
-
- pr_debug("[HDCP_DM] connector->index: %x connect_status: %x dpms: %x\n",
- connector->index, connector->status, connector->dpms);
- pr_debug("[HDCP_DM] state protection old: %x new: %x\n",
- old_con_state->content_protection, new_con_state->content_protection);
-
- if (aconnector->dc_sink) {
- if (aconnector->dc_sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
- aconnector->dc_sink->sink_signal != SIGNAL_TYPE_NONE) {
- pr_debug("[HDCP_DM] pipe_ctx dispname=%s\n",
- aconnector->dc_sink->edid_caps.display_name);
- }
- }
-
- new_crtc_state = NULL;
- old_crtc_state = NULL;
-
- if (acrtc) {
- new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base);
- old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base);
- }
-
- if (old_crtc_state)
- pr_debug("old crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n",
- old_crtc_state->enable,
- old_crtc_state->active,
- old_crtc_state->mode_changed,
- old_crtc_state->active_changed,
- old_crtc_state->connectors_changed);
-
- if (new_crtc_state)
- pr_debug("NEW crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n",
- new_crtc_state->enable,
- new_crtc_state->active,
- new_crtc_state->mode_changed,
- new_crtc_state->active_changed,
- new_crtc_state->connectors_changed);
- }
-
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- if (!adev->dm.hdcp_workqueue)
- continue;
-
- new_crtc_state = NULL;
- old_crtc_state = NULL;
-
- if (acrtc) {
- new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base);
- old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base);
- }
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- if (dm_new_crtc_state && dm_new_crtc_state->stream == NULL &&
- connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
- hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index);
- new_con_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- dm_new_con_state->update_hdcp = true;
- continue;
- }
-
- if (is_content_protection_different(new_crtc_state, old_crtc_state, new_con_state,
- old_con_state, connector, adev->dm.hdcp_workqueue)) {
- /* when display is unplugged from mst hub, connctor will
- * be destroyed within dm_dp_mst_connector_destroy. connector
- * hdcp perperties, like type, undesired, desired, enabled,
- * will be lost. So, save hdcp properties into hdcp_work within
- * amdgpu_dm_atomic_commit_tail. if the same display is
- * plugged back with same display index, its hdcp properties
- * will be retrieved from hdcp_work within dm_dp_mst_get_modes
- */
-
- bool enable_encryption = false;
-
- if (new_con_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED)
- enable_encryption = true;
-
- if (aconnector->dc_link && aconnector->dc_sink &&
- aconnector->dc_link->type == dc_connection_mst_branch) {
- struct hdcp_workqueue *hdcp_work = adev->dm.hdcp_workqueue;
- struct hdcp_workqueue *hdcp_w =
- &hdcp_work[aconnector->dc_link->link_index];
-
- hdcp_w->hdcp_content_type[connector->index] =
- new_con_state->hdcp_content_type;
- hdcp_w->content_protection[connector->index] =
- new_con_state->content_protection;
- }
-
- if (new_crtc_state && new_crtc_state->mode_changed &&
- new_con_state->content_protection >= DRM_MODE_CONTENT_PROTECTION_DESIRED)
- enable_encryption = true;
-
- DRM_INFO("[HDCP_DM] hdcp_update_display enable_encryption = %x\n", enable_encryption);
-
- if (aconnector->dc_link)
- hdcp_update_display(
- adev->dm.hdcp_workqueue, aconnector->dc_link->link_index, aconnector,
- new_con_state->hdcp_content_type, enable_encryption);
- }
- }
-
- /* Handle connector state changes */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
- struct dc_surface_update *dummy_updates;
- struct dc_stream_update stream_update;
- struct dc_info_packet hdr_packet;
- struct dc_stream_status *status = NULL;
- bool abm_changed, hdr_changed, scaling_changed;
-
- memset(&stream_update, 0, sizeof(stream_update));
-
- if (acrtc) {
- new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base);
- old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base);
- }
-
- /* Skip any modesets/resets */
- if (!acrtc || drm_atomic_crtc_needs_modeset(new_crtc_state))
- continue;
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- scaling_changed = is_scaling_state_different(dm_new_con_state,
- dm_old_con_state);
-
- abm_changed = dm_new_crtc_state->abm_level !=
- dm_old_crtc_state->abm_level;
-
- hdr_changed =
- !drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state);
-
- if (!scaling_changed && !abm_changed && !hdr_changed)
- continue;
-
- stream_update.stream = dm_new_crtc_state->stream;
- if (scaling_changed) {
- update_stream_scaling_settings(&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;
- stream_update.dst = dm_new_crtc_state->stream->dst;
- }
-
- if (abm_changed) {
- dm_new_crtc_state->stream->abm_level = dm_new_crtc_state->abm_level;
-
- stream_update.abm_level = &dm_new_crtc_state->abm_level;
- }
-
- if (hdr_changed) {
- fill_hdr_info_packet(new_con_state, &hdr_packet);
- stream_update.hdr_static_metadata = &hdr_packet;
- }
-
- status = dc_stream_get_status(dm_new_crtc_state->stream);
-
- if (WARN_ON(!status))
- continue;
-
- WARN_ON(!status->plane_count);
-
- /*
- * TODO: DC refuses to perform stream updates without a dc_surface_update.
- * Here we create an empty update on each plane.
- * To fix this, DC should permit updating only stream properties.
- */
- dummy_updates = kzalloc(sizeof(struct dc_surface_update) * MAX_SURFACES, GFP_ATOMIC);
- if (!dummy_updates) {
- DRM_ERROR("Failed to allocate memory for dummy_updates.\n");
- continue;
- }
- for (j = 0; j < status->plane_count; j++)
- dummy_updates[j].surface = status->plane_states[0];
-
- sort(dummy_updates, status->plane_count,
- sizeof(*dummy_updates), dm_plane_layer_index_cmp, NULL);
-
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- dc_update_planes_and_stream(dm->dc,
- dummy_updates,
- status->plane_count,
- dm_new_crtc_state->stream,
- &stream_update);
- mutex_unlock(&dm->dc_lock);
- kfree(dummy_updates);
- }
-
- /**
- * Enable interrupts for CRTCs that are newly enabled or went through
- * a modeset. It was intentionally deferred until after the front end
- * state was modified to wait until the OTG was on and so the IRQ
- * handlers didn't access stale or invalid state.
- */
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-#ifdef CONFIG_DEBUG_FS
- enum amdgpu_dm_pipe_crc_source cur_crc_src;
-#endif
- /* Count number of newly disabled CRTCs for dropping PM refs later. */
- if (old_crtc_state->active && !new_crtc_state->active)
- crtc_disable_count++;
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- /* For freesync config update on crtc state and params for irq */
- update_stream_irq_parameters(dm, dm_new_crtc_state);
-
-#ifdef CONFIG_DEBUG_FS
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- cur_crc_src = acrtc->dm_irq_params.crc_src;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-#endif
-
- if (new_crtc_state->active &&
- (!old_crtc_state->active ||
- drm_atomic_crtc_needs_modeset(new_crtc_state))) {
- dc_stream_retain(dm_new_crtc_state->stream);
- acrtc->dm_irq_params.stream = dm_new_crtc_state->stream;
- manage_dm_interrupts(adev, acrtc, dm_new_crtc_state);
- }
- /* Handle vrr on->off / off->on transitions */
- amdgpu_dm_handle_vrr_transition(dm_old_crtc_state, dm_new_crtc_state);
-
-#ifdef CONFIG_DEBUG_FS
- if (new_crtc_state->active &&
- (!old_crtc_state->active ||
- drm_atomic_crtc_needs_modeset(new_crtc_state))) {
- /**
- * Frontend may have changed so reapply the CRC capture
- * settings for the stream.
- */
- if (amdgpu_dm_is_valid_crc_source(cur_crc_src)) {
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- if (amdgpu_dm_crc_window_is_activated(crtc)) {
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- acrtc->dm_irq_params.window_param.update_win = true;
-
- /**
- * It takes 2 frames for HW to stably generate CRC when
- * resuming from suspend, so we set skip_frame_cnt 2.
- */
- acrtc->dm_irq_params.window_param.skip_frame_cnt = 2;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
-#endif
- if (amdgpu_dm_crtc_configure_crc_source(
- crtc, dm_new_crtc_state, cur_crc_src))
- drm_dbg_atomic(dev, "Failed to configure crc source");
- }
- }
-#endif
- }
-
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, j)
- if (new_crtc_state->async_flip)
- wait_for_vblank = false;
-
- /* update planes when needed per crtc*/
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, j) {
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- if (dm_new_crtc_state->stream)
- amdgpu_dm_commit_planes(state, dev, dm, crtc, wait_for_vblank);
- }
-
- /* Enable writeback */
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- if (!new_con_state->writeback_job)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base);
-
- if (!new_crtc_state)
- continue;
-
- if (acrtc->wb_enabled)
- continue;
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- dm_set_writeback(dm, dm_new_crtc_state, connector, new_con_state);
- acrtc->wb_enabled = true;
- }
-
- /* Update audio instances for each connector. */
- amdgpu_dm_commit_audio(dev, state);
-
- /* restore the backlight level */
- for (i = 0; i < dm->num_of_edps; i++) {
- if (dm->backlight_dev[i] &&
- (dm->actual_brightness[i] != dm->brightness[i]))
- amdgpu_dm_backlight_set_level(dm, i, dm->brightness[i]);
- }
-
- /*
- * send vblank event on all events not handled in flip and
- * mark consumed event for drm_atomic_helper_commit_hw_done
- */
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
-
- if (new_crtc_state->event)
- drm_send_event_locked(dev, &new_crtc_state->event->base);
-
- new_crtc_state->event = NULL;
- }
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-
- /* Signal HW programming completion */
- drm_atomic_helper_commit_hw_done(state);
-
- if (wait_for_vblank)
- drm_atomic_helper_wait_for_flip_done(dev, state);
-
- drm_atomic_helper_cleanup_planes(dev, state);
-
- /* Don't free the memory if we are hitting this as part of suspend.
- * This way we don't free any memory during suspend; see
- * amdgpu_bo_free_kernel(). The memory will be freed in the first
- * non-suspend modeset or when the driver is torn down.
- */
- if (!adev->in_suspend) {
- /* return the stolen vga memory back to VRAM */
- if (!adev->mman.keep_stolen_vga_memory)
- amdgpu_bo_free_kernel(&adev->mman.stolen_vga_memory, NULL, NULL);
- amdgpu_bo_free_kernel(&adev->mman.stolen_extended_memory, NULL, NULL);
- }
-
- /*
- * Finally, drop a runtime PM reference for each newly disabled CRTC,
- * so we can put the GPU into runtime suspend if we're not driving any
- * displays anymore
- */
- for (i = 0; i < crtc_disable_count; i++)
- pm_runtime_put_autosuspend(dev->dev);
- pm_runtime_mark_last_busy(dev->dev);
-}
-
-static int dm_force_atomic_commit(struct drm_connector *connector)
-{
- int ret = 0;
- struct drm_device *ddev = connector->dev;
- struct drm_atomic_state *state = drm_atomic_state_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_state
- */
- conn_state = drm_atomic_get_connector_state(state, connector);
-
- ret = PTR_ERR_OR_ZERO(conn_state);
- if (ret)
- goto out;
-
- /* Attach crtc to drm_atomic_state*/
- crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base);
-
- ret = PTR_ERR_OR_ZERO(crtc_state);
- if (ret)
- goto out;
-
- /* force a restore */
- crtc_state->mode_changed = true;
-
- /* Attach plane to drm_atomic_state */
- plane_state = drm_atomic_get_plane_state(state, plane);
-
- ret = PTR_ERR_OR_ZERO(plane_state);
- if (ret)
- goto out;
-
- /* Call commit internally with the state we just constructed */
- ret = drm_atomic_commit(state);
-
-out:
- drm_atomic_state_put(state);
- if (ret)
- DRM_ERROR("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.
- */
-static int do_aquire_global_lock(struct drm_device *dev,
- struct drm_atomic_state *state)
-{
- struct drm_crtc *crtc;
- struct drm_crtc_commit *commit;
- long ret;
-
- /*
- * Adding all modeset locks to aquire_ctx will
- * ensure that when the framework release it the
- * extra locks we are locking here will get released to
- */
- ret = drm_modeset_lock_all_ctx(dev, state->acquire_ctx);
- if (ret)
- return ret;
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- spin_lock(&crtc->commit_lock);
- commit = list_first_entry_or_null(&crtc->commit_list,
- struct drm_crtc_commit, commit_entry);
- if (commit)
- drm_crtc_commit_get(commit);
- spin_unlock(&crtc->commit_lock);
-
- if (!commit)
- continue;
-
- /*
- * Make sure all pending HW programming completed and
- * page flips done
- */
- ret = wait_for_completion_interruptible_timeout(&commit->hw_done, 10*HZ);
-
- if (ret > 0)
- ret = wait_for_completion_interruptible_timeout(
- &commit->flip_done, 10*HZ);
-
- if (ret == 0)
- DRM_ERROR("[CRTC:%d:%s] hw_done or flip_done timed out\n",
- crtc->base.id, crtc->name);
-
- drm_crtc_commit_put(commit);
- }
-
- return ret < 0 ? ret : 0;
-}
-
-static void get_freesync_config_for_crtc(
- struct dm_crtc_state *new_crtc_state,
- struct dm_connector_state *new_con_state)
-{
- struct mod_freesync_config config = {0};
- struct amdgpu_dm_connector *aconnector;
- struct drm_display_mode *mode = &new_crtc_state->base.mode;
- int vrefresh = drm_mode_vrefresh(mode);
- bool fs_vid_mode = false;
-
- if (new_con_state->base.connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return;
-
- aconnector = to_amdgpu_dm_connector(new_con_state->base.connector);
-
- new_crtc_state->vrr_supported = new_con_state->freesync_capable &&
- vrefresh >= aconnector->min_vfreq &&
- vrefresh <= aconnector->max_vfreq;
-
- if (new_crtc_state->vrr_supported) {
- new_crtc_state->stream->ignore_msa_timing_param = true;
- fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED;
-
- config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000;
- config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000;
- config.vsif_supported = true;
- config.btr = true;
-
- if (fs_vid_mode) {
- config.state = VRR_STATE_ACTIVE_FIXED;
- config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz;
- goto out;
- } else if (new_crtc_state->base.vrr_enabled) {
- config.state = VRR_STATE_ACTIVE_VARIABLE;
- } else {
- config.state = VRR_STATE_INACTIVE;
- }
- }
-out:
- new_crtc_state->freesync_config = config;
-}
-
-static void reset_freesync_config_for_crtc(
- struct dm_crtc_state *new_crtc_state)
-{
- new_crtc_state->vrr_supported = false;
-
- memset(&new_crtc_state->vrr_infopacket, 0,
- sizeof(new_crtc_state->vrr_infopacket));
-}
-
-static bool
-is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
- struct drm_crtc_state *new_crtc_state)
-{
- const struct drm_display_mode *old_mode, *new_mode;
-
- if (!old_crtc_state || !new_crtc_state)
- return false;
-
- old_mode = &old_crtc_state->mode;
- new_mode = &new_crtc_state->mode;
-
- if (old_mode->clock == new_mode->clock &&
- old_mode->hdisplay == new_mode->hdisplay &&
- old_mode->vdisplay == new_mode->vdisplay &&
- old_mode->htotal == new_mode->htotal &&
- old_mode->vtotal != new_mode->vtotal &&
- old_mode->hsync_start == new_mode->hsync_start &&
- old_mode->vsync_start != new_mode->vsync_start &&
- old_mode->hsync_end == new_mode->hsync_end &&
- old_mode->vsync_end != new_mode->vsync_end &&
- old_mode->hskew == new_mode->hskew &&
- old_mode->vscan == new_mode->vscan &&
- (old_mode->vsync_end - old_mode->vsync_start) ==
- (new_mode->vsync_end - new_mode->vsync_start))
- return true;
-
- return false;
-}
-
-static 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;
-
- dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
-
- num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000;
- den = (unsigned long long)new_crtc_state->mode.htotal *
- (unsigned long long)new_crtc_state->mode.vtotal;
-
- res = div_u64(num, den);
- dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
-}
-
-static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
- struct drm_atomic_state *state,
- struct drm_crtc *crtc,
- struct drm_crtc_state *old_crtc_state,
- struct drm_crtc_state *new_crtc_state,
- bool enable,
- bool *lock_and_validation_needed)
-{
- struct dm_atomic_state *dm_state = NULL;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
- struct dc_stream_state *new_stream;
- int ret = 0;
-
- /*
- * TODO Move this code into dm_crtc_atomic_check once we get rid of dc_validation_set
- * update changed items
- */
- struct amdgpu_crtc *acrtc = NULL;
- struct drm_connector *connector = NULL;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct drm_connector_state *drm_new_conn_state = NULL, *drm_old_conn_state = NULL;
- struct dm_connector_state *dm_new_conn_state = NULL, *dm_old_conn_state = NULL;
-
- new_stream = NULL;
-
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- acrtc = to_amdgpu_crtc(crtc);
- connector = amdgpu_dm_find_first_crtc_matching_connector(state, crtc);
- if (connector)
- aconnector = to_amdgpu_dm_connector(connector);
-
- /* TODO This hack should go away */
- if (connector && enable) {
- /* Make sure fake sink is created in plug-in scenario */
- drm_new_conn_state = drm_atomic_get_new_connector_state(state,
- connector);
- drm_old_conn_state = drm_atomic_get_old_connector_state(state,
- connector);
-
- if (IS_ERR(drm_new_conn_state)) {
- ret = PTR_ERR_OR_ZERO(drm_new_conn_state);
- goto fail;
- }
-
- dm_new_conn_state = to_dm_connector_state(drm_new_conn_state);
- dm_old_conn_state = to_dm_connector_state(drm_old_conn_state);
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- goto skip_modeset;
-
- new_stream = create_validate_stream_for_sink(aconnector,
- &new_crtc_state->mode,
- dm_new_conn_state,
- dm_old_crtc_state->stream);
-
- /*
- * we can have no stream on ACTION_SET if a display
- * was disconnected during S3, in this case it is not an
- * error, the OS will be updated after detection, and
- * will do the right thing on next atomic commit
- */
-
- if (!new_stream) {
- DRM_DEBUG_DRIVER("%s: Failed to create new stream for crtc %d\n",
- __func__, acrtc->base.base.id);
- ret = -ENOMEM;
- goto fail;
- }
-
- /*
- * TODO: Check VSDB bits to decide whether this should
- * be enabled or not.
- */
- new_stream->triggered_crtc_reset.enabled =
- dm->force_timing_sync;
-
- dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level;
-
- ret = fill_hdr_info_packet(drm_new_conn_state,
- &new_stream->hdr_static_metadata);
- if (ret)
- goto fail;
-
- /*
- * If we already removed the old stream from the context
- * (and set the new stream to NULL) then we can't reuse
- * the old stream even if the stream and scaling are unchanged.
- * We'll hit the BUG_ON and black screen.
- *
- * TODO: Refactor this function to allow this check to work
- * in all conditions.
- */
- if (amdgpu_freesync_vid_mode &&
- dm_new_crtc_state->stream &&
- is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state))
- goto skip_modeset;
-
- if (dm_new_crtc_state->stream &&
- dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) &&
- dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream)) {
- new_crtc_state->mode_changed = false;
- DRM_DEBUG_DRIVER("Mode change not required, setting mode_changed to %d",
- new_crtc_state->mode_changed);
- }
- }
-
- /* mode_changed flag may get updated above, need to check again */
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- goto skip_modeset;
-
- drm_dbg_state(state->dev,
- "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, planes_changed:%d, mode_changed:%d,active_changed:%d,connectors_changed:%d\n",
- acrtc->crtc_id,
- new_crtc_state->enable,
- new_crtc_state->active,
- new_crtc_state->planes_changed,
- new_crtc_state->mode_changed,
- new_crtc_state->active_changed,
- new_crtc_state->connectors_changed);
-
- /* Remove stream for any changed/disabled CRTC */
- if (!enable) {
-
- if (!dm_old_crtc_state->stream)
- goto skip_modeset;
-
- /* Unset freesync video if it was active before */
- if (dm_old_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED) {
- dm_new_crtc_state->freesync_config.state = VRR_STATE_INACTIVE;
- dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = 0;
- }
-
- /* Now check if we should set freesync video mode */
- if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream &&
- dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) &&
- dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream) &&
- is_timing_unchanged_for_freesync(new_crtc_state,
- old_crtc_state)) {
- new_crtc_state->mode_changed = false;
- DRM_DEBUG_DRIVER(
- "Mode change not required for front porch change, setting mode_changed to %d",
- new_crtc_state->mode_changed);
-
- set_freesync_fixed_config(dm_new_crtc_state);
-
- goto skip_modeset;
- } else if (amdgpu_freesync_vid_mode && aconnector &&
- is_freesync_video_mode(&new_crtc_state->mode,
- aconnector)) {
- struct drm_display_mode *high_mode;
-
- high_mode = 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);
- }
-
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret)
- goto fail;
-
- DRM_DEBUG_DRIVER("Disabling DRM crtc: %d\n",
- crtc->base.id);
-
- /* i.e. reset mode */
- if (dc_state_remove_stream(
- dm->dc,
- dm_state->context,
- dm_old_crtc_state->stream) != DC_OK) {
- ret = -EINVAL;
- goto fail;
- }
-
- dc_stream_release(dm_old_crtc_state->stream);
- dm_new_crtc_state->stream = NULL;
-
- reset_freesync_config_for_crtc(dm_new_crtc_state);
-
- *lock_and_validation_needed = true;
-
- } else {/* Add stream for any updated/enabled CRTC */
- /*
- * Quick fix to prevent NULL pointer on new_stream when
- * added MST connectors not found in existing crtc_state in the chained mode
- * TODO: need to dig out the root cause of that
- */
- if (!connector)
- goto skip_modeset;
-
- if (modereset_required(new_crtc_state))
- goto skip_modeset;
-
- if (amdgpu_dm_crtc_modeset_required(new_crtc_state, new_stream,
- dm_old_crtc_state->stream)) {
-
- WARN_ON(dm_new_crtc_state->stream);
-
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret)
- goto fail;
-
- dm_new_crtc_state->stream = new_stream;
-
- dc_stream_retain(new_stream);
-
- DRM_DEBUG_ATOMIC("Enabling DRM crtc: %d\n",
- crtc->base.id);
-
- if (dc_state_add_stream(
- dm->dc,
- dm_state->context,
- dm_new_crtc_state->stream) != DC_OK) {
- ret = -EINVAL;
- goto fail;
- }
-
- *lock_and_validation_needed = true;
- }
- }
-
-skip_modeset:
- /* Release extra reference */
- if (new_stream)
- dc_stream_release(new_stream);
-
- /*
- * We want to do dc stream updates that do not require a
- * full modeset below.
- */
- if (!(enable && connector && new_crtc_state->active))
- return 0;
- /*
- * Given above conditions, the dc state cannot be NULL because:
- * 1. We're in the process of enabling CRTCs (just been added
- * to the dc context, or already is on the context)
- * 2. Has a valid connector attached, and
- * 3. Is currently active and enabled.
- * => The dc stream state currently exists.
- */
- BUG_ON(dm_new_crtc_state->stream == NULL);
-
- /* 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(
- &new_crtc_state->mode, dm_new_conn_state, dm_new_crtc_state->stream);
-
- /* ABM settings */
- dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level;
-
- /*
- * Color management settings. We also update color properties
- * when a modeset is needed, to ensure it gets reprogrammed.
- */
- if (dm_new_crtc_state->base.color_mgmt_changed ||
- dm_old_crtc_state->regamma_tf != dm_new_crtc_state->regamma_tf ||
- drm_atomic_crtc_needs_modeset(new_crtc_state)) {
- ret = amdgpu_dm_update_crtc_color_mgmt(dm_new_crtc_state);
- if (ret)
- goto fail;
- }
-
- /* Update Freesync settings. */
- get_freesync_config_for_crtc(dm_new_crtc_state,
- dm_new_conn_state);
-
- return ret;
-
-fail:
- if (new_stream)
- dc_stream_release(new_stream);
- return ret;
-}
-
-static bool should_reset_plane(struct drm_atomic_state *state,
- struct drm_plane *plane,
- struct drm_plane_state *old_plane_state,
- struct drm_plane_state *new_plane_state)
-{
- struct drm_plane *other;
- struct drm_plane_state *old_other_state, *new_other_state;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *old_dm_crtc_state, *new_dm_crtc_state;
- struct amdgpu_device *adev = drm_to_adev(plane->dev);
- int i;
-
- /*
- * TODO: Remove this hack for all asics once it proves that the
- * fast updates works fine on DCN3.2+.
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 2, 0) &&
- state->allow_modeset)
- return true;
-
- /* Exit early if we know that we're adding or removing the plane. */
- if (old_plane_state->crtc != new_plane_state->crtc)
- return true;
-
- /* old crtc == new_crtc == NULL, plane not in context. */
- if (!new_plane_state->crtc)
- return false;
-
- new_crtc_state =
- drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
- old_crtc_state =
- drm_atomic_get_old_crtc_state(state, old_plane_state->crtc);
-
- if (!new_crtc_state)
- return true;
-
- /*
- * A change in cursor mode means a new dc pipe needs to be acquired or
- * released from the state
- */
- old_dm_crtc_state = to_dm_crtc_state(old_crtc_state);
- new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (plane->type == DRM_PLANE_TYPE_CURSOR &&
- old_dm_crtc_state != NULL &&
- old_dm_crtc_state->cursor_mode != new_dm_crtc_state->cursor_mode) {
- return true;
- }
-
- /* CRTC Degamma changes currently require us to recreate planes. */
- if (new_crtc_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.
- *
- * TODO: We can likely skip bandwidth validation if the only thing that
- * changed about the plane was it'z z-ordering.
- */
- if (new_crtc_state->zpos_changed)
- return true;
-
- if (drm_atomic_crtc_needs_modeset(new_crtc_state))
- return true;
-
- /*
- * If there are any new primary or overlay planes being added or
- * removed then the z-order can potentially change. To ensure
- * correct z-order and pipe acquisition the current DC architecture
- * requires us to remove and recreate all existing planes.
- *
- * TODO: Come up with a more elegant solution for this.
- */
- for_each_oldnew_plane_in_state(state, other, old_other_state, new_other_state, i) {
- struct amdgpu_framebuffer *old_afb, *new_afb;
- struct dm_plane_state *dm_new_other_state, *dm_old_other_state;
-
- dm_new_other_state = to_dm_plane_state(new_other_state);
- dm_old_other_state = to_dm_plane_state(old_other_state);
-
- if (other->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- if (old_other_state->crtc != new_plane_state->crtc &&
- new_other_state->crtc != new_plane_state->crtc)
- continue;
-
- if (old_other_state->crtc != new_other_state->crtc)
- return true;
-
- /* Src/dst size and scaling updates. */
- if (old_other_state->src_w != new_other_state->src_w ||
- old_other_state->src_h != new_other_state->src_h ||
- old_other_state->crtc_w != new_other_state->crtc_w ||
- old_other_state->crtc_h != new_other_state->crtc_h)
- return true;
-
- /* Rotation / mirroring updates. */
- if (old_other_state->rotation != new_other_state->rotation)
- return true;
-
- /* Blending updates. */
- if (old_other_state->pixel_blend_mode !=
- new_other_state->pixel_blend_mode)
- return true;
-
- /* Alpha updates. */
- if (old_other_state->alpha != new_other_state->alpha)
- return true;
-
- /* Colorspace changes. */
- if (old_other_state->color_range != new_other_state->color_range ||
- old_other_state->color_encoding != new_other_state->color_encoding)
- return true;
-
- /* HDR/Transfer Function changes. */
- if (dm_old_other_state->degamma_tf != dm_new_other_state->degamma_tf ||
- dm_old_other_state->degamma_lut != dm_new_other_state->degamma_lut ||
- dm_old_other_state->hdr_mult != dm_new_other_state->hdr_mult ||
- dm_old_other_state->ctm != dm_new_other_state->ctm ||
- dm_old_other_state->shaper_lut != dm_new_other_state->shaper_lut ||
- dm_old_other_state->shaper_tf != dm_new_other_state->shaper_tf ||
- dm_old_other_state->lut3d != dm_new_other_state->lut3d ||
- dm_old_other_state->blend_lut != dm_new_other_state->blend_lut ||
- dm_old_other_state->blend_tf != dm_new_other_state->blend_tf)
- return true;
-
- /* Framebuffer checks fall at the end. */
- if (!old_other_state->fb || !new_other_state->fb)
- continue;
-
- /* Pixel format changes can require bandwidth updates. */
- if (old_other_state->fb->format != new_other_state->fb->format)
- return true;
-
- old_afb = (struct amdgpu_framebuffer *)old_other_state->fb;
- new_afb = (struct amdgpu_framebuffer *)new_other_state->fb;
-
- /* Tiling and DCC changes also require bandwidth updates. */
- if (old_afb->tiling_flags != new_afb->tiling_flags ||
- old_afb->base.modifier != new_afb->base.modifier)
- return true;
- }
-
- return false;
-}
-
-static int dm_check_cursor_fb(struct amdgpu_crtc *new_acrtc,
- struct drm_plane_state *new_plane_state,
- struct drm_framebuffer *fb)
-{
- struct amdgpu_device *adev = drm_to_adev(new_acrtc->base.dev);
- struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(fb);
- unsigned int pitch;
- bool linear;
-
- if (fb->width > new_acrtc->max_cursor_width ||
- fb->height > new_acrtc->max_cursor_height) {
- DRM_DEBUG_ATOMIC("Bad cursor FB size %dx%d\n",
- new_plane_state->fb->width,
- new_plane_state->fb->height);
- return -EINVAL;
- }
- if (new_plane_state->src_w != fb->width << 16 ||
- new_plane_state->src_h != fb->height << 16) {
- DRM_DEBUG_ATOMIC("Cropping not supported for cursor plane\n");
- return -EINVAL;
- }
-
- /* Pitch in pixels */
- pitch = fb->pitches[0] / fb->format->cpp[0];
-
- if (fb->width != pitch) {
- DRM_DEBUG_ATOMIC("Cursor FB width %d doesn't match pitch %d",
- fb->width, pitch);
- return -EINVAL;
- }
-
- switch (pitch) {
- case 64:
- case 128:
- case 256:
- /* FB pitch is supported by cursor plane */
- break;
- default:
- DRM_DEBUG_ATOMIC("Bad cursor FB pitch %d px\n", pitch);
- return -EINVAL;
- }
-
- /* Core DRM takes care of checking FB modifiers, so we only need to
- * check tiling flags when the FB doesn't have a modifier.
- */
- if (!(fb->flags & DRM_MODE_FB_MODIFIERS)) {
- if (adev->family >= AMDGPU_FAMILY_GC_12_0_0) {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, GFX12_SWIZZLE_MODE) == 0;
- } else if (adev->family >= AMDGPU_FAMILY_AI) {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE) == 0;
- } else {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_2D_TILED_THIN1 &&
- AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_1D_TILED_THIN1 &&
- AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE) == 0;
- }
- if (!linear) {
- DRM_DEBUG_ATOMIC("Cursor FB not linear");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-/*
- * Helper function for checking the cursor in native mode
- */
-static int dm_check_native_cursor_state(struct drm_crtc *new_plane_crtc,
- struct drm_plane *plane,
- struct drm_plane_state *new_plane_state,
- bool enable)
-{
-
- struct amdgpu_crtc *new_acrtc;
- int ret;
-
- if (!enable || !new_plane_crtc ||
- drm_atomic_plane_disabling(plane->state, new_plane_state))
- return 0;
-
- new_acrtc = to_amdgpu_crtc(new_plane_crtc);
-
- if (new_plane_state->src_x != 0 || new_plane_state->src_y != 0) {
- DRM_DEBUG_ATOMIC("Cropping not supported for cursor plane\n");
- return -EINVAL;
- }
-
- if (new_plane_state->fb) {
- ret = dm_check_cursor_fb(new_acrtc, new_plane_state,
- new_plane_state->fb);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static bool dm_should_update_native_cursor(struct drm_atomic_state *state,
- struct drm_crtc *old_plane_crtc,
- struct drm_crtc *new_plane_crtc,
- bool enable)
-{
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
-
- if (!enable) {
- if (old_plane_crtc == NULL)
- return true;
-
- old_crtc_state = drm_atomic_get_old_crtc_state(
- state, old_plane_crtc);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- return dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE;
- } else {
- if (new_plane_crtc == NULL)
- return true;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_plane_crtc);
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- return dm_new_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE;
- }
-}
-
-static int dm_update_plane_state(struct dc *dc,
- struct drm_atomic_state *state,
- struct drm_plane *plane,
- struct drm_plane_state *old_plane_state,
- struct drm_plane_state *new_plane_state,
- bool enable,
- bool *lock_and_validation_needed,
- bool *is_top_most_overlay)
-{
-
- struct dm_atomic_state *dm_state = NULL;
- struct drm_crtc *new_plane_crtc, *old_plane_crtc;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *dm_new_crtc_state, *dm_old_crtc_state;
- struct dm_plane_state *dm_new_plane_state, *dm_old_plane_state;
- bool needs_reset, update_native_cursor;
- int ret = 0;
-
-
- new_plane_crtc = new_plane_state->crtc;
- old_plane_crtc = old_plane_state->crtc;
- dm_new_plane_state = to_dm_plane_state(new_plane_state);
- dm_old_plane_state = to_dm_plane_state(old_plane_state);
-
- update_native_cursor = dm_should_update_native_cursor(state,
- old_plane_crtc,
- new_plane_crtc,
- enable);
-
- if (plane->type == DRM_PLANE_TYPE_CURSOR && update_native_cursor) {
- ret = dm_check_native_cursor_state(new_plane_crtc, plane,
- new_plane_state, enable);
- if (ret)
- return ret;
-
- return 0;
- }
-
- needs_reset = should_reset_plane(state, plane, old_plane_state,
- new_plane_state);
-
- /* Remove any changed/removed planes */
- if (!enable) {
- if (!needs_reset)
- return 0;
-
- if (!old_plane_crtc)
- return 0;
-
- old_crtc_state = drm_atomic_get_old_crtc_state(
- state, old_plane_crtc);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- if (!dm_old_crtc_state->stream)
- return 0;
-
- DRM_DEBUG_ATOMIC("Disabling DRM plane: %d on DRM crtc %d\n",
- plane->base.id, old_plane_crtc->base.id);
-
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret)
- return ret;
-
- if (!dc_state_remove_plane(
- dc,
- dm_old_crtc_state->stream,
- dm_old_plane_state->dc_state,
- dm_state->context)) {
-
- return -EINVAL;
- }
-
- if (dm_old_plane_state->dc_state)
- dc_plane_state_release(dm_old_plane_state->dc_state);
-
- dm_new_plane_state->dc_state = NULL;
-
- *lock_and_validation_needed = true;
-
- } else { /* Add new planes */
- struct dc_plane_state *dc_new_plane_state;
-
- if (drm_atomic_plane_disabling(plane->state, new_plane_state))
- return 0;
-
- if (!new_plane_crtc)
- return 0;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_crtc);
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- if (!dm_new_crtc_state->stream)
- return 0;
-
- if (!needs_reset)
- return 0;
-
- ret = amdgpu_dm_plane_helper_check_state(new_plane_state, new_crtc_state);
- if (ret)
- goto out;
-
- WARN_ON(dm_new_plane_state->dc_state);
-
- dc_new_plane_state = dc_create_plane_state(dc);
- if (!dc_new_plane_state) {
- ret = -ENOMEM;
- goto out;
- }
-
- DRM_DEBUG_ATOMIC("Enabling DRM plane: %d on DRM crtc %d\n",
- plane->base.id, new_plane_crtc->base.id);
-
- ret = fill_dc_plane_attributes(
- drm_to_adev(new_plane_crtc->dev),
- dc_new_plane_state,
- new_plane_state,
- new_crtc_state);
- if (ret) {
- dc_plane_state_release(dc_new_plane_state);
- goto out;
- }
-
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret) {
- dc_plane_state_release(dc_new_plane_state);
- goto out;
- }
-
- /*
- * Any atomic check errors that occur after this will
- * not need a release. The plane state will be attached
- * to the stream, and therefore part of the atomic
- * state. It'll be released when the atomic state is
- * cleaned.
- */
- if (!dc_state_add_plane(
- dc,
- dm_new_crtc_state->stream,
- dc_new_plane_state,
- dm_state->context)) {
-
- dc_plane_state_release(dc_new_plane_state);
- ret = -EINVAL;
- goto out;
- }
-
- dm_new_plane_state->dc_state = dc_new_plane_state;
-
- dm_new_crtc_state->mpo_requested |= (plane->type == DRM_PLANE_TYPE_OVERLAY);
-
- /* 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;
-
- *lock_and_validation_needed = true;
- }
-
-out:
- /* If enabling cursor overlay failed, attempt fallback to native mode */
- if (enable && ret == -EINVAL && plane->type == DRM_PLANE_TYPE_CURSOR) {
- ret = dm_check_native_cursor_state(new_plane_crtc, plane,
- new_plane_state, enable);
- if (ret)
- return ret;
-
- dm_new_crtc_state->cursor_mode = DM_CURSOR_NATIVE_MODE;
- }
-
- return ret;
-}
-
-static 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:
- case DRM_MODE_ROTATE_270:
- *src_w = plane_state->src_h >> 16;
- *src_h = plane_state->src_w >> 16;
- break;
- case DRM_MODE_ROTATE_0:
- case DRM_MODE_ROTATE_180:
- default:
- *src_w = plane_state->src_w >> 16;
- *src_h = plane_state->src_h >> 16;
- break;
- }
-}
-
-static void
-dm_get_plane_scale(struct drm_plane_state *plane_state,
- int *out_plane_scale_w, int *out_plane_scale_h)
-{
- int plane_src_w, plane_src_h;
-
- dm_get_oriented_plane_size(plane_state, &plane_src_w, &plane_src_h);
- *out_plane_scale_w = plane_state->crtc_w * 1000 / plane_src_w;
- *out_plane_scale_h = plane_state->crtc_h * 1000 / plane_src_h;
-}
-
-/*
- * The normalized_zpos value cannot be used by this iterator directly. It's only
- * calculated for enabled planes, potentially causing normalized_zpos collisions
- * between enabled/disabled planes in the atomic state. We need a unique value
- * so that the iterator will not generate the same object twice, or loop
- * indefinitely.
- */
-static inline struct __drm_planes_state *__get_next_zpos(
- struct drm_atomic_state *state,
- struct __drm_planes_state *prev)
-{
- unsigned int highest_zpos = 0, prev_zpos = 256;
- uint32_t highest_id = 0, prev_id = UINT_MAX;
- struct drm_plane_state *new_plane_state;
- struct drm_plane *plane;
- int i, highest_i = -1;
-
- if (prev != NULL) {
- prev_zpos = prev->new_state->zpos;
- prev_id = prev->ptr->base.id;
- }
-
- for_each_new_plane_in_state(state, plane, new_plane_state, i) {
- /* Skip planes with higher zpos than the previously returned */
- if (new_plane_state->zpos > prev_zpos ||
- (new_plane_state->zpos == prev_zpos &&
- plane->base.id >= prev_id))
- continue;
-
- /* Save the index of the plane with highest zpos */
- if (new_plane_state->zpos > highest_zpos ||
- (new_plane_state->zpos == highest_zpos &&
- plane->base.id > highest_id)) {
- highest_zpos = new_plane_state->zpos;
- highest_id = plane->base.id;
- highest_i = i;
- }
- }
-
- if (highest_i < 0)
- return NULL;
-
- return &state->planes[highest_i];
-}
-
-/*
- * Use the uniqueness of the plane's (zpos, drm obj ID) combination to iterate
- * by descending zpos, as read from the new plane state. This is the same
- * ordering as defined by drm_atomic_normalize_zpos().
- */
-#define for_each_oldnew_plane_in_descending_zpos(__state, plane, old_plane_state, new_plane_state) \
- for (struct __drm_planes_state *__i = __get_next_zpos((__state), NULL); \
- __i != NULL; __i = __get_next_zpos((__state), __i)) \
- for_each_if(((plane) = __i->ptr, \
- (void)(plane) /* Only to avoid unused-but-set-variable warning */, \
- (old_plane_state) = __i->old_state, \
- (new_plane_state) = __i->new_state, 1))
-
-static int add_affected_mst_dsc_crtcs(struct drm_atomic_state *state, struct drm_crtc *crtc)
-{
- struct drm_connector *connector;
- struct drm_connector_state *conn_state, *old_conn_state;
- struct amdgpu_dm_connector *aconnector = NULL;
- int i;
-
- for_each_oldnew_connector_in_state(state, connector, old_conn_state, conn_state, i) {
- if (!conn_state->crtc)
- conn_state = old_conn_state;
-
- if (conn_state->crtc != crtc)
- continue;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (!aconnector->mst_output_port || !aconnector->mst_root)
- aconnector = NULL;
- else
- break;
- }
-
- if (!aconnector)
- return 0;
-
- return drm_dp_mst_add_affected_dsc_crtcs(state, &aconnector->mst_root->mst_mgr);
-}
-
-/**
- * DOC: Cursor Modes - Native vs Overlay
- *
- * In native mode, the cursor uses a integrated cursor pipe within each DCN hw
- * plane. It does not require a dedicated hw plane to enable, but it is
- * subjected to the same z-order and scaling as the hw plane. It also has format
- * restrictions, a RGB cursor in native mode cannot be enabled within a non-RGB
- * hw plane.
- *
- * In overlay mode, the cursor uses a separate DCN hw plane, and thus has its
- * own scaling and z-pos. It also has no blending restrictions. It lends to a
- * cursor behavior more akin to a DRM client's expectations. However, it does
- * occupy an extra DCN plane, and therefore will only be used if a DCN plane is
- * available.
- */
-
-/**
- * dm_crtc_get_cursor_mode() - Determine the required cursor mode on crtc
- * @adev: amdgpu device
- * @state: DRM atomic state
- * @dm_crtc_state: amdgpu state for the CRTC containing the cursor
- * @cursor_mode: Returns the required cursor mode on dm_crtc_state
- *
- * Get whether the cursor should be enabled in native mode, or overlay mode, on
- * the dm_crtc_state.
- *
- * The cursor should be enabled in overlay mode if there exists an underlying
- * plane - on which the cursor may be blended - that is either YUV formatted, or
- * scaled differently from the cursor.
- *
- * Since zpos info is required, drm_atomic_normalize_zpos must be called before
- * calling this function.
- *
- * Return: 0 on success, or an error code if getting the cursor plane state
- * failed.
- */
-static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev,
- struct drm_atomic_state *state,
- struct dm_crtc_state *dm_crtc_state,
- enum amdgpu_dm_cursor_mode *cursor_mode)
-{
- struct drm_plane_state *old_plane_state, *plane_state, *cursor_state;
- struct drm_crtc_state *crtc_state = &dm_crtc_state->base;
- struct drm_plane *plane;
- bool consider_mode_change = false;
- bool entire_crtc_covered = false;
- bool cursor_changed = false;
- int underlying_scale_w, underlying_scale_h;
- int cursor_scale_w, cursor_scale_h;
- int i;
-
- /* Overlay cursor not supported on HW before DCN
- * DCN401 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions
- * as previous DCN generations, so enable native mode on DCN401 in addition to DCE
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0 ||
- amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1)) {
- *cursor_mode = DM_CURSOR_NATIVE_MODE;
- return 0;
- }
-
- /* Init cursor_mode to be the same as current */
- *cursor_mode = dm_crtc_state->cursor_mode;
-
- /*
- * Cursor mode can change if a plane's format changes, scale changes, is
- * enabled/disabled, or z-order changes.
- */
- for_each_oldnew_plane_in_state(state, plane, old_plane_state, plane_state, i) {
- int new_scale_w, new_scale_h, old_scale_w, old_scale_h;
-
- /* Only care about planes on this CRTC */
- if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0)
- continue;
-
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- cursor_changed = true;
-
- if (drm_atomic_plane_enabling(old_plane_state, plane_state) ||
- drm_atomic_plane_disabling(old_plane_state, plane_state) ||
- old_plane_state->fb->format != plane_state->fb->format) {
- consider_mode_change = true;
- break;
- }
-
- dm_get_plane_scale(plane_state, &new_scale_w, &new_scale_h);
- dm_get_plane_scale(old_plane_state, &old_scale_w, &old_scale_h);
- if (new_scale_w != old_scale_w || new_scale_h != old_scale_h) {
- consider_mode_change = true;
- break;
- }
- }
-
- if (!consider_mode_change && !crtc_state->zpos_changed)
- return 0;
-
- /*
- * If no cursor change on this CRTC, and not enabled on this CRTC, then
- * no need to set cursor mode. This avoids needlessly locking the cursor
- * state.
- */
- if (!cursor_changed &&
- !(drm_plane_mask(crtc_state->crtc->cursor) & crtc_state->plane_mask)) {
- return 0;
- }
-
- cursor_state = drm_atomic_get_plane_state(state,
- crtc_state->crtc->cursor);
- if (IS_ERR(cursor_state))
- return PTR_ERR(cursor_state);
-
- /* Cursor is disabled */
- if (!cursor_state->fb)
- return 0;
-
- /* For all planes in descending z-order (all of which are below cursor
- * as per zpos definitions), check their scaling and format
- */
- for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, plane_state) {
-
- /* Only care about non-cursor planes on this CRTC */
- if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0 ||
- plane->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- /* Underlying plane is YUV format - use overlay cursor */
- if (amdgpu_dm_plane_is_video_format(plane_state->fb->format->format)) {
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
- return 0;
- }
-
- dm_get_plane_scale(plane_state,
- &underlying_scale_w, &underlying_scale_h);
- dm_get_plane_scale(cursor_state,
- &cursor_scale_w, &cursor_scale_h);
-
- /* Underlying plane has different scale - use overlay cursor */
- if (cursor_scale_w != underlying_scale_w &&
- cursor_scale_h != underlying_scale_h) {
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
- return 0;
- }
-
- /* If this plane covers the whole CRTC, no need to check planes underneath */
- if (plane_state->crtc_x <= 0 && plane_state->crtc_y <= 0 &&
- plane_state->crtc_x + plane_state->crtc_w >= crtc_state->mode.hdisplay &&
- plane_state->crtc_y + plane_state->crtc_h >= crtc_state->mode.vdisplay) {
- entire_crtc_covered = true;
- break;
- }
- }
-
- /* If planes do not cover the entire CRTC, use overlay mode to enable
- * cursor over holes
- */
- if (entire_crtc_covered)
- *cursor_mode = DM_CURSOR_NATIVE_MODE;
- else
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
-
- return 0;
-}
-
-/**
- * amdgpu_dm_atomic_check() - Atomic check implementation for AMDgpu DM.
- *
- * @dev: The DRM device
- * @state: The atomic state to commit
- *
- * Validate that the given atomic state is programmable by DC into hardware.
- * This involves constructing a &struct dc_state reflecting the new hardware
- * state we wish to commit, then querying DC to see if it is programmable. It's
- * important not to modify the existing DC state. Otherwise, atomic_check
- * may unexpectedly commit hardware changes.
- *
- * When validating the DC state, it's important that the right locks are
- * acquired. For full updates case which removes/adds/updates streams on one
- * CRTC while flipping on another CRTC, acquiring global lock will guarantee
- * that any such full update commit will wait for completion of any outstanding
- * flip using DRMs synchronization events.
- *
- * Note that DM adds the affected connectors for all CRTCs in state, when that
- * might not seem necessary. This is because DC stream creation requires the
- * DC sink, which is tied to the DRM connector state. Cleaning this up should
- * be possible but non-trivial - a possible TODO item.
- *
- * Return: -Error code if validation failed.
- */
-static int amdgpu_dm_atomic_check(struct drm_device *dev,
- struct drm_atomic_state *state)
-{
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_atomic_state *dm_state = NULL;
- struct dc *dc = adev->dm.dc;
- struct drm_connector *connector;
- struct drm_connector_state *old_con_state, *new_con_state;
- struct drm_crtc *crtc;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct drm_plane *plane;
- struct drm_plane_state *old_plane_state, *new_plane_state, *new_cursor_state;
- enum dc_status status;
- int ret, i;
- bool lock_and_validation_needed = false;
- bool is_top_most_overlay = true;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
- struct drm_dp_mst_topology_mgr *mgr;
- struct drm_dp_mst_topology_state *mst_state;
- struct dsc_mst_fairness_vars vars[MAX_PIPES] = {0};
-
- trace_amdgpu_dm_atomic_check_begin(state);
-
- ret = drm_atomic_helper_check_modeset(dev, state);
- if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_helper_check_modeset() failed\n");
- goto fail;
- }
-
- /* Check connector changes */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state);
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
-
- /* Skip connectors that are disabled or part of modeset already. */
- if (!new_con_state->crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_crtc_state(state, new_con_state->crtc);
- if (IS_ERR(new_crtc_state)) {
- drm_dbg_atomic(dev, "drm_atomic_get_crtc_state() failed\n");
- ret = PTR_ERR(new_crtc_state);
- goto fail;
- }
-
- if (dm_old_con_state->abm_level != dm_new_con_state->abm_level ||
- dm_old_con_state->scaling != dm_new_con_state->scaling)
- new_crtc_state->connectors_changed = true;
- }
-
- if (dc_resource_is_dsc_encoding_supported(dc)) {
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- if (drm_atomic_crtc_needs_modeset(new_crtc_state)) {
- ret = add_affected_mst_dsc_crtcs(state, crtc);
- if (ret) {
- drm_dbg_atomic(dev, "add_affected_mst_dsc_crtcs() failed\n");
- goto fail;
- }
- }
- }
- }
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state) &&
- !new_crtc_state->color_mgmt_changed &&
- old_crtc_state->vrr_enabled == new_crtc_state->vrr_enabled &&
- dm_old_crtc_state->dsc_force_changed == false)
- continue;
-
- ret = amdgpu_dm_verify_lut_sizes(new_crtc_state);
- if (ret) {
- drm_dbg_atomic(dev, "amdgpu_dm_verify_lut_sizes() failed\n");
- goto fail;
- }
-
- if (!new_crtc_state->enable)
- continue;
-
- ret = drm_atomic_add_affected_connectors(state, crtc);
- if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_add_affected_connectors() failed\n");
- goto fail;
- }
-
- ret = drm_atomic_add_affected_planes(state, crtc);
- if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_add_affected_planes() failed\n");
- goto fail;
- }
-
- if (dm_old_crtc_state->dsc_force_changed)
- new_crtc_state->mode_changed = true;
- }
-
- /*
- * Add all primary and overlay planes on the CRTC to the state
- * whenever a plane is enabled to maintain correct z-ordering
- * and to enable fast surface updates.
- */
- drm_for_each_crtc(crtc, dev) {
- bool modified = false;
-
- for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- if (new_plane_state->crtc == crtc ||
- old_plane_state->crtc == crtc) {
- modified = true;
- break;
- }
- }
-
- if (!modified)
- continue;
-
- drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) {
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- new_plane_state =
- drm_atomic_get_plane_state(state, plane);
-
- if (IS_ERR(new_plane_state)) {
- ret = PTR_ERR(new_plane_state);
- drm_dbg_atomic(dev, "new_plane_state is BAD\n");
- goto fail;
- }
- }
- }
-
- /*
- * DC consults the zpos (layer_index in DC terminology) to determine the
- * hw plane on which to enable the hw cursor (see
- * `dcn10_can_pipe_disable_cursor`). By now, all modified planes are in
- * atomic state, so call drm helper to normalize zpos.
- */
- ret = drm_atomic_normalize_zpos(dev, state);
- if (ret) {
- drm_dbg(dev, "drm_atomic_normalize_zpos() failed\n");
- goto fail;
- }
-
- /*
- * Determine whether cursors on each CRTC should be enabled in native or
- * overlay mode.
- */
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
- &dm_new_crtc_state->cursor_mode);
- if (ret) {
- drm_dbg(dev, "Failed to determine cursor mode\n");
- goto fail;
- }
- }
-
- /* Remove exiting planes if they are modified */
- for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, new_plane_state) {
- if (old_plane_state->fb && new_plane_state->fb &&
- get_mem_type(old_plane_state->fb) !=
- get_mem_type(new_plane_state->fb))
- lock_and_validation_needed = true;
-
- ret = dm_update_plane_state(dc, state, plane,
- old_plane_state,
- new_plane_state,
- false,
- &lock_and_validation_needed,
- &is_top_most_overlay);
- if (ret) {
- drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
- goto fail;
- }
- }
-
- /* Disable all crtcs which require disable */
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- ret = dm_update_crtc_state(&adev->dm, state, crtc,
- old_crtc_state,
- new_crtc_state,
- false,
- &lock_and_validation_needed);
- if (ret) {
- drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed\n");
- goto fail;
- }
- }
-
- /* Enable all crtcs which require enable */
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- ret = dm_update_crtc_state(&adev->dm, state, crtc,
- old_crtc_state,
- new_crtc_state,
- true,
- &lock_and_validation_needed);
- if (ret) {
- drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed\n");
- goto fail;
- }
- }
-
- /* Add new/modified planes */
- for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, new_plane_state) {
- ret = dm_update_plane_state(dc, state, plane,
- old_plane_state,
- new_plane_state,
- true,
- &lock_and_validation_needed,
- &is_top_most_overlay);
- if (ret) {
- drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
- goto fail;
- }
- }
-
-#if defined(CONFIG_DRM_AMD_DC_FP)
- if (dc_resource_is_dsc_encoding_supported(dc)) {
- ret = pre_validate_dsc(state, &dm_state, vars);
- if (ret != 0)
- goto fail;
- }
-#endif
-
- /* Run this here since we want to validate the streams we created */
- ret = drm_atomic_helper_check_planes(dev, state);
- if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_helper_check_planes() failed\n");
- goto fail;
- }
-
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (dm_new_crtc_state->mpo_requested)
- drm_dbg_atomic(dev, "MPO enablement requested on crtc:[%p]\n", crtc);
- }
-
- /* Check cursor restrictions */
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- enum amdgpu_dm_cursor_mode required_cursor_mode;
- int is_rotated, is_scaled;
-
- /* Overlay cusor not subject to native cursor restrictions */
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (dm_new_crtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE)
- continue;
-
- /* Check if rotation or scaling is enabled on DCN401 */
- if ((drm_plane_mask(crtc->cursor) & new_crtc_state->plane_mask) &&
- amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1)) {
- new_cursor_state = drm_atomic_get_new_plane_state(state, crtc->cursor);
-
- is_rotated = new_cursor_state &&
- ((new_cursor_state->rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_0);
- is_scaled = new_cursor_state && ((new_cursor_state->src_w >> 16 != new_cursor_state->crtc_w) ||
- (new_cursor_state->src_h >> 16 != new_cursor_state->crtc_h));
-
- if (is_rotated || is_scaled) {
- drm_dbg_driver(
- crtc->dev,
- "[CRTC:%d:%s] cannot enable hardware cursor due to rotation/scaling\n",
- crtc->base.id, crtc->name);
- ret = -EINVAL;
- goto fail;
- }
- }
-
- /* If HW can only do native cursor, check restrictions again */
- ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
- &required_cursor_mode);
- if (ret) {
- drm_dbg_driver(crtc->dev,
- "[CRTC:%d:%s] Checking cursor mode failed\n",
- crtc->base.id, crtc->name);
- goto fail;
- } else if (required_cursor_mode == DM_CURSOR_OVERLAY_MODE) {
- drm_dbg_driver(crtc->dev,
- "[CRTC:%d:%s] Cannot enable native cursor due to scaling or YUV restrictions\n",
- crtc->base.id, crtc->name);
- ret = -EINVAL;
- goto fail;
- }
- }
-
- if (state->legacy_cursor_update) {
- /*
- * This is a fast cursor update coming from the plane update
- * helper, check if it can be done asynchronously for better
- * performance.
- */
- state->async_update =
- !drm_atomic_helper_async_check(dev, state);
-
- /*
- * Skip the remaining global validation if this is an async
- * update. Cursor updates can be done without affecting
- * state or bandwidth calcs and this avoids the performance
- * penalty of locking the private state object and
- * allocating a new dc_state.
- */
- if (state->async_update)
- return 0;
- }
-
- /* Check scaling and underscan changes*/
- /* TODO Removed scaling changes validation due to inability to commit
- * new stream into context w\o causing full reset. Need to
- * decide how to handle.
- */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state);
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
-
- /* Skip any modesets/resets */
- if (!acrtc || drm_atomic_crtc_needs_modeset(
- drm_atomic_get_new_crtc_state(state, &acrtc->base)))
- continue;
-
- /* Skip any thing not scale or underscan changes */
- if (!is_scaling_state_different(dm_new_con_state, dm_old_con_state))
- continue;
-
- lock_and_validation_needed = true;
- }
-
- /* set the slot info for each mst_state based on the link encoding format */
- for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- u8 link_coding_cap;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
- if (connector->index == mst_state->mgr->conn_base_id) {
- aconnector = to_amdgpu_dm_connector(connector);
- link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(aconnector->dc_link);
- drm_dp_mst_update_slots(mst_state, link_coding_cap);
-
- break;
- }
- }
- drm_connector_list_iter_end(&iter);
- }
-
- /**
- * Streams and planes are reset when there are changes that affect
- * bandwidth. Anything that affects bandwidth needs to go through
- * DC global validation to ensure that the configuration can be applied
- * to hardware.
- *
- * We have to currently stall out here in atomic_check for outstanding
- * commits to finish in this case because our IRQ handlers reference
- * DRM state directly - we can end up disabling interrupts too early
- * if we don't.
- *
- * TODO: Remove this stall and drop DM state private objects.
- */
- if (lock_and_validation_needed) {
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret) {
- drm_dbg_atomic(dev, "dm_atomic_get_state() failed\n");
- goto fail;
- }
-
- ret = do_aquire_global_lock(dev, state);
- if (ret) {
- drm_dbg_atomic(dev, "do_aquire_global_lock() failed\n");
- goto fail;
- }
-
-#if defined(CONFIG_DRM_AMD_DC_FP)
- if (dc_resource_is_dsc_encoding_supported(dc)) {
- ret = compute_mst_dsc_configs_for_state(state, dm_state->context, vars);
- if (ret) {
- drm_dbg_atomic(dev, "compute_mst_dsc_configs_for_state() failed\n");
- ret = -EINVAL;
- goto fail;
- }
- }
-#endif
-
- ret = dm_update_mst_vcpi_slots_for_dsc(state, dm_state->context, vars);
- if (ret) {
- drm_dbg_atomic(dev, "dm_update_mst_vcpi_slots_for_dsc() failed\n");
- goto fail;
- }
-
- /*
- * Perform validation of MST topology in the state:
- * We need to perform MST atomic check before calling
- * dc_validate_global_state(), or there is a chance
- * to get stuck in an infinite loop and hang eventually.
- */
- ret = drm_dp_mst_atomic_check(state);
- if (ret) {
- drm_dbg_atomic(dev, "drm_dp_mst_atomic_check() failed\n");
- goto fail;
- }
- status = dc_validate_global_state(dc, dm_state->context, true);
- if (status != DC_OK) {
- drm_dbg_atomic(dev, "DC global validation failure: %s (%d)",
- dc_status_to_str(status), status);
- ret = -EINVAL;
- goto fail;
- }
- } else {
- /*
- * The commit is a fast update. Fast updates shouldn't change
- * the DC context, affect global validation, and can have their
- * commit work done in parallel with other commits not touching
- * the same resource. If we have a new DC context as part of
- * the DM atomic state from validation we need to free it and
- * retain the existing one instead.
- *
- * Furthermore, since the DM atomic state only contains the DC
- * context and can safely be annulled, we can free the state
- * and clear the associated private object now to free
- * some memory and avoid a possible use-after-free later.
- */
-
- for (i = 0; i < state->num_private_objs; i++) {
- struct drm_private_obj *obj = state->private_objs[i].ptr;
-
- if (obj->funcs == adev->dm.atomic_obj.funcs) {
- int j = state->num_private_objs-1;
-
- dm_atomic_destroy_state(obj,
- state->private_objs[i].state);
-
- /* If i is not at the end of the array then the
- * last element needs to be moved to where i was
- * before the array can safely be truncated.
- */
- if (i != j)
- state->private_objs[i] =
- state->private_objs[j];
-
- state->private_objs[j].ptr = NULL;
- state->private_objs[j].state = NULL;
- state->private_objs[j].old_state = NULL;
- state->private_objs[j].new_state = NULL;
-
- state->num_private_objs = j;
- break;
- }
- }
- }
-
- /* Store the overall update type for use later in atomic check. */
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- struct dm_crtc_state *dm_new_crtc_state =
- to_dm_crtc_state(new_crtc_state);
-
- /*
- * Only allow async flips for fast updates that don't change
- * the FB pitch, the DCC state, rotation, etc.
- */
- if (new_crtc_state->async_flip && lock_and_validation_needed) {
- drm_dbg_atomic(crtc->dev,
- "[CRTC:%d:%s] async flips are only supported for fast updates\n",
- crtc->base.id, crtc->name);
- ret = -EINVAL;
- goto fail;
- }
-
- dm_new_crtc_state->update_type = lock_and_validation_needed ?
- UPDATE_TYPE_FULL : UPDATE_TYPE_FAST;
- }
-
- /* Must be success */
- WARN_ON(ret);
-
- trace_amdgpu_dm_atomic_check_finish(state, ret);
-
- return ret;
-
-fail:
- if (ret == -EDEADLK)
- drm_dbg_atomic(dev, "Atomic check stopped to avoid deadlock.\n");
- else if (ret == -EINTR || ret == -EAGAIN || ret == -ERESTARTSYS)
- drm_dbg_atomic(dev, "Atomic check stopped due to signal.\n");
- else
- drm_dbg_atomic(dev, "Atomic check failed with err: %d\n", ret);
-
- trace_amdgpu_dm_atomic_check_finish(state, ret);
-
- 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_ERROR("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_ERROR("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;
- } else {
- DRM_WARN("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;
- 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,
- struct edid *edid)
-{
- u8 *edid_ext = NULL;
- int i;
- int j = 0;
- u16 min_vfreq;
- u16 max_vfreq;
-
- if (edid == NULL || edid->extensions == 0)
- 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 (edid_ext == NULL)
- 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 parse_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- u8 *edid_ext = NULL;
- int i;
- int j = 0;
-
- if (edid == NULL || edid->extensions == 0)
- return -ENODEV;
-
- /* Find DisplayID extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (void *)(edid + (i + 1));
- if (edid_ext[0] == DISPLAYID_EXT)
- break;
- }
-
- while (j < EDID_LENGTH) {
- struct amd_vsdb_block *amd_vsdb = (struct amd_vsdb_block *)&edid_ext[j];
- unsigned int ieeeId = (amd_vsdb->ieee_id[2] << 16) | (amd_vsdb->ieee_id[1] << 8) | (amd_vsdb->ieee_id[0]);
-
- if (ieeeId == HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID &&
- amd_vsdb->version == HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3) {
- vsdb_info->replay_mode = (amd_vsdb->feature_caps & AMD_VSDB_VERSION_3_FEATURECAP_REPLAYMODE) ? true : false;
- vsdb_info->amd_vsdb_version = HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3;
- DRM_DEBUG_KMS("Panel supports Replay Mode: %d\n", vsdb_info->replay_mode);
-
- return true;
- }
- j++;
- }
-
- return false;
-}
-
-static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- 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.
- * @edid: EDID from monitor
- *
- * 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,
- struct edid *edid)
-{
- int i = 0;
- struct detailed_timing *timing;
- struct detailed_non_pixel *data;
- struct detailed_data_monitor_range *range;
- 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};
- bool freesync_capable = false;
- enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE;
-
- if (!connector->state) {
- DRM_ERROR("%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;
-
- if (!edid || !sink) {
- dm_con_state = to_dm_connector_state(connector->state);
-
- amdgpu_dm_connector->min_vfreq = 0;
- amdgpu_dm_connector->max_vfreq = 0;
- connector->display_info.monitor_range.min_vfreq = 0;
- connector->display_info.monitor_range.max_vfreq = 0;
- freesync_capable = false;
-
- goto update;
- }
-
- dm_con_state = to_dm_connector_state(connector->state);
-
- if (!adev->dm.freesync_module)
- goto update;
-
- /* 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)) {
- bool edid_check_required = false;
-
- if (amdgpu_dm_connector->dc_link &&
- amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
- if (edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ) {
- 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;
- } else {
- edid_check_required = edid->version > 1 ||
- (edid->version == 1 &&
- edid->revision > 1);
- }
- }
-
- if (edid_check_required) {
- for (i = 0; i < 4; i++) {
-
- timing = &edid->detailed_timings[i];
- data = &timing->data.other_data;
- range = &data->data.range;
- /*
- * Check if monitor has continuous frequency mode
- */
- if (data->type != EDID_DETAIL_MONITOR_RANGE)
- continue;
- /*
- * Check for flag range limits only. If flag == 1 then
- * no additional timing information provided.
- * Default GTF, GTF Secondary curve and CVT are not
- * supported
- */
- if (range->flags != 1)
- continue;
-
- connector->display_info.monitor_range.min_vfreq = range->min_vfreq;
- connector->display_info.monitor_range.max_vfreq = range->max_vfreq;
-
- if (edid->revision >= 4) {
- if (data->pad2 & DRM_EDID_RANGE_OFFSET_MIN_VFREQ)
- connector->display_info.monitor_range.min_vfreq += 255;
- if (data->pad2 & DRM_EDID_RANGE_OFFSET_MAX_VFREQ)
- connector->display_info.monitor_range.max_vfreq += 255;
- }
-
- amdgpu_dm_connector->min_vfreq =
- connector->display_info.monitor_range.min_vfreq;
- amdgpu_dm_connector->max_vfreq =
- connector->display_info.monitor_range.max_vfreq;
-
- break;
- }
-
- if (amdgpu_dm_connector->max_vfreq -
- amdgpu_dm_connector->min_vfreq > 10) {
-
- freesync_capable = true;
- }
- }
- parse_amd_vsdb(amdgpu_dm_connector, edid, &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 (edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0 && vsdb_info.freesync_supported) {
- timing = &edid->detailed_timings[i];
- data = &timing->data.other_data;
-
- 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 && 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->vsdb_info = vsdb_info;
-
- 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;
- }
- }
-
-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);
- struct dc *dc = adev->dm.dc;
- int i;
-
- mutex_lock(&adev->dm.dc_lock);
- if (dc->current_state) {
- for (i = 0; i < dc->current_state->stream_count; ++i)
- dc->current_state->streams[i]
- ->triggered_crtc_reset.enabled =
- adev->dm.force_timing_sync;
-
- dm_enable_per_frame_crtc_master_sync(dc->current_state);
- dc_trigger_sync(dc, dc->current_state);
- }
- 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)
-{
-#ifdef DM_CHECK_ADDR_0
- if (address == 0) {
- drm_err(adev_to_drm(ctx->driver_context),
- "invalid register write. address = 0");
- return;
- }
-#endif
-
- amdgpu_dm_exit_ips_for_hw_access(ctx->dc);
- cgs_write_register(ctx->cgs_device, address, value);
- trace_amdgpu_dc_wreg(&ctx->perf_trace->write_count, address, value);
-}
-
-uint32_t dm_read_reg_func(const struct dc_context *ctx, uint32_t address,
- const char *func_name)
-{
- u32 value;
-#ifdef DM_CHECK_ADDR_0
- if (address == 0) {
- drm_err(adev_to_drm(ctx->driver_context),
- "invalid register read; address = 0\n");
- return 0;
- }
-#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);
-
- trace_amdgpu_dc_rreg(&ctx->perf_trace->read_count, address, value);
-
- 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_ERROR("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("DPIA AUX failed on 0x%x(%d), error %d\n",
- payload->address, payload->length,
- p_notify->result);
- }
- *operation_result = AUX_RET_ERROR_INVALID_REPLY;
- goto out;
- }
-
-
- payload->reply[0] = adev->dm.dmub_notify->aux_reply.command;
- if (!payload->write && p_notify->aux_reply.length &&
- (payload->reply[0] == AUX_TRANSACTION_REPLY_AUX_ACK)) {
-
- if (payload->length != p_notify->aux_reply.length) {
- DRM_WARN("invalid read length %d from DPIA AUX 0x%x(%d)!\n",
- p_notify->aux_reply.length,
- payload->address, payload->length);
- *operation_result = AUX_RET_ERROR_INVALID_REPLY;
- goto out;
- }
-
- 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;
-}
-
-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_ERROR("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)
-{
- 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)
-{
- return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type);
-}
diff --git a/rr-cache/184514ddd9867f1a8f27d1e7de47d36242b6ad90/preimage b/rr-cache/184514ddd9867f1a8f27d1e7de47d36242b6ad90/preimage
deleted file mode 100644
index c18b34f873fd..000000000000
--- a/rr-cache/184514ddd9867f1a8f27d1e7de47d36242b6ad90/preimage
+++ /dev/null
@@ -1,12348 +0,0 @@
-/*
- * Copyright 2015 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
- *
- */
-
-/* The caprices of the preprocessor require that this be declared right here */
-#define CREATE_TRACE_POINTS
-
-#include "dm_services_types.h"
-#include "dc.h"
-#include "link_enc_cfg.h"
-#include "dc/inc/core_types.h"
-#include "dal_asic_id.h"
-#include "dmub/dmub_srv.h"
-#include "dc/inc/hw/dmcu.h"
-#include "dc/inc/hw/abm.h"
-#include "dc/dc_dmub_srv.h"
-#include "dc/dc_edid_parser.h"
-#include "dc/dc_stat.h"
-#include "dc/dc_state.h"
-#include "amdgpu_dm_trace.h"
-#include "dpcd_defs.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 "vid.h"
-#include "amdgpu.h"
-#include "amdgpu_display.h"
-#include "amdgpu_ucode.h"
-#include "atom.h"
-#include "amdgpu_dm.h"
-#include "amdgpu_dm_plane.h"
-#include "amdgpu_dm_crtc.h"
-#include "amdgpu_dm_hdcp.h"
-#include <drm/display/drm_hdcp_helper.h>
-#include "amdgpu_dm_wb.h"
-#include "amdgpu_pm.h"
-#include "amdgpu_atombios.h"
-
-#include "amd_shared.h"
-#include "amdgpu_dm_irq.h"
-#include "dm_helpers.h"
-#include "amdgpu_dm_mst_types.h"
-#if defined(CONFIG_DEBUG_FS)
-#include "amdgpu_dm_debugfs.h"
-#endif
-#include "amdgpu_dm_psr.h"
-#include "amdgpu_dm_replay.h"
-
-#include "ivsrcid/ivsrcid_vislands30.h"
-
-#include <linux/backlight.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/pm_runtime.h>
-#include <linux/pci.h>
-#include <linux/power_supply.h>
-#include <linux/firmware.h>
-#include <linux/component.h>
-#include <linux/dmi.h>
-#include <linux/sort.h>
-
-#include <drm/display/drm_dp_mst_helper.h>
-#include <drm/display/drm_hdmi_helper.h>
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_uapi.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_blend.h>
-#include <drm/drm_fixed.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_eld.h>
-#include <drm/drm_vblank.h>
-#include <drm/drm_audio_component.h>
-#include <drm/drm_gem_atomic_helper.h>
-
-#include <acpi/video.h>
-
-#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h"
-
-#include "dcn/dcn_1_0_offset.h"
-#include "dcn/dcn_1_0_sh_mask.h"
-#include "soc15_hw_ip.h"
-#include "soc15_common.h"
-#include "vega10_ip_offset.h"
-
-#include "gc/gc_11_0_0_offset.h"
-#include "gc/gc_11_0_0_sh_mask.h"
-
-#include "modules/inc/mod_freesync.h"
-#include "modules/power/power_helpers.h"
-
-#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_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
-
-/* Number of bytes in PSP header for firmware. */
-#define PSP_HEADER_BYTES 0x100
-
-/* Number of bytes in PSP footer for firmware. */
-#define PSP_FOOTER_BYTES 0x100
-
-/**
- * DOC: overview
- *
- * The AMDgpu display manager, **amdgpu_dm** (or even simpler,
- * **dm**) sits between DRM and DC. It acts as a liaison, converting DRM
- * requests into DC requests, and DC responses into DRM responses.
- *
- * The root control structure is &struct amdgpu_display_manager.
- */
-
-/* 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 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
- * provided by DAL. The drm strcutures are: drm_crtc, drm_connector,
- * drm_encoder, drm_mode_config
- *
- * Returns 0 on success
- */
-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 void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state);
-
-static int amdgpu_dm_atomic_check(struct drm_device *dev,
- struct drm_atomic_state *state);
-
-static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector);
-static void handle_hpd_rx_irq(void *param);
-
-static bool
-is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
- struct drm_crtc_state *new_crtc_state);
-/*
- * dm_vblank_get_counter
- *
- * @brief
- * Get counter for number of vertical blanks
- *
- * @param
- * struct amdgpu_device *adev - [in] desired amdgpu device
- * int disp_idx - [in] which CRTC to get the counter from
- *
- * @return
- * Counter for vertical blanks
- */
-static u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc)
-{
- struct amdgpu_crtc *acrtc = NULL;
-
- if (crtc >= adev->mode_info.num_crtc)
- return 0;
-
- acrtc = adev->mode_info.crtcs[crtc];
-
- if (!acrtc->dm_irq_params.stream) {
- DRM_ERROR("dc_stream_state is NULL for crtc '%d'!\n",
- crtc);
- return 0;
- }
-
- return dc_stream_get_vblank_counter(acrtc->dm_irq_params.stream);
-}
-
-static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
- u32 *vbl, u32 *position)
-{
- u32 v_blank_start = 0, v_blank_end = 0, h_position = 0, v_position = 0;
- struct amdgpu_crtc *acrtc = NULL;
- struct dc *dc = adev->dm.dc;
-
- if ((crtc < 0) || (crtc >= adev->mode_info.num_crtc))
- return -EINVAL;
-
- acrtc = adev->mode_info.crtcs[crtc];
-
- if (!acrtc->dm_irq_params.stream) {
- DRM_ERROR("dc_stream_state is NULL for crtc '%d'!\n",
- crtc);
- return 0;
- }
-
- if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed)
- dc_allow_idle_optimizations(dc, false);
-
- /*
- * TODO rework base driver to use values directly.
- * for now parse it back into reg-format
- */
- dc_stream_get_scanoutpos(acrtc->dm_irq_params.stream,
- &v_blank_start,
- &v_blank_end,
- &h_position,
- &v_position);
-
- *position = v_position | (h_position << 16);
- *vbl = v_blank_start | (v_blank_end << 16);
-
- return 0;
-}
-
-static bool dm_is_idle(void *handle)
-{
- /* XXX todo */
- return true;
-}
-
-static int dm_wait_for_idle(void *handle)
-{
- /* XXX todo */
- return 0;
-}
-
-static bool dm_check_soft_reset(void *handle)
-{
- return false;
-}
-
-static int dm_soft_reset(void *handle)
-{
- /* 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)
-{
- if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)
- return true;
- else if (amdgpu_dm_crtc_vrr_active(old_state) != amdgpu_dm_crtc_vrr_active(new_state))
- return true;
- else
- return false;
-}
-
-/*
- * 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)
-{
- const struct dc_surface_update *sa = (struct dc_surface_update *)a;
- const struct dc_surface_update *sb = (struct dc_surface_update *)b;
-
- /* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */
- return sb->surface->layer_index - sa->surface->layer_index;
-}
-
-/**
- * update_planes_and_stream_adapter() - Send planes to be updated in DC
- *
- * DC has a generic way to update planes and stream via
- * dc_update_planes_and_stream function; however, DM might need some
- * adjustments and preparation before calling it. This function is a wrapper
- * for the dc_update_planes_and_stream that does any required configuration
- * before passing control to DC.
- *
- * @dc: Display Core control structure
- * @update_type: specify whether it is FULL/MEDIUM/FAST update
- * @planes_count: planes count to update
- * @stream: stream state
- * @stream_update: stream update
- * @array_of_surface_update: dc surface update pointer
- *
- */
-static inline bool update_planes_and_stream_adapter(struct dc *dc,
- int update_type,
- int planes_count,
- struct dc_stream_state *stream,
- struct dc_stream_update *stream_update,
- struct dc_surface_update *array_of_surface_update)
-{
- sort(array_of_surface_update, planes_count,
- sizeof(*array_of_surface_update), dm_plane_layer_index_cmp, NULL);
-
- /*
- * Previous frame finished and HW is ready for optimization.
- */
- if (update_type == UPDATE_TYPE_FAST)
- dc_post_update_surfaces_to_stream(dc);
-
- return dc_update_planes_and_stream(dc,
- array_of_surface_update,
- planes_count,
- stream,
- 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_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) {
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /* BTR processing for pre-DCE12 ASICs */
- if (acrtc->dm_irq_params.stream &&
- 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);
-
- dc_stream_adjust_vmin_vmax(
- adev->dm.dc,
- 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 &&
- 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);
-
- dc_stream_adjust_vmin_vmax(adev->dm.dc, 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);
-}
-
-/**
- * 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_ERROR("DMUB HPD callback notification was NULL");
- return;
- }
-
- if (notify->link_index > adev->dm.dc->link_count) {
- DRM_ERROR("DMUB HPD index (%u)is abnormal", notify->link_index);
- 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("DMUB HPD IRQ callback: link_index=%u\n", link_index);
- else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ)
- DRM_INFO("DMUB HPD RX IRQ callback: link_index=%u\n", link_index);
- else
- DRM_WARN("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("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);
- }
- }
-}
-
-/**
- * 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_ERROR("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);
-
-}
-
-#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;
- static const char *const event_type[] = {
- "NO_DATA",
- "AUX_REPLY",
- "HPD",
- "HPD_IRQ",
- "SET_CONFIGC_REPLY",
- "DPIA_NOTIFICATION",
- "HPD_SENSE_NOTIFY",
- };
-
- 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_DEBUG_DRIVER("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_DEBUG_DRIVER("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, &notify);
- if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) {
- DRM_ERROR("DM: notify type %d invalid!", notify.type);
- continue;
- }
- if (!dm->dmub_callback[notify.type]) {
- DRM_WARN("DMUB notification skipped due to no handler: type=%s\n",
- event_type[notify.type]);
- continue;
- }
- if (dm->dmub_thread_offload[notify.type] == true) {
- dmub_hpd_wrk = kzalloc(sizeof(*dmub_hpd_wrk), GFP_ATOMIC);
- if (!dmub_hpd_wrk) {
- DRM_ERROR("Failed to allocate dmub_hpd_wrk");
- return;
- }
- dmub_hpd_wrk->dmub_notify = kmemdup(&notify, sizeof(struct dmub_notification),
- GFP_ATOMIC);
- if (!dmub_hpd_wrk->dmub_notify) {
- kfree(dmub_hpd_wrk);
- DRM_ERROR("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, &notify);
- }
- } while (notify.pending_notification);
- }
-}
-
-static int dm_set_clockgating_state(void *handle,
- enum amd_clockgating_state state)
-{
- return 0;
-}
-
-static int dm_set_powergating_state(void *handle,
- enum amd_powergating_state state)
-{
- return 0;
-}
-
-/* Prototypes of private functions */
-static int dm_early_init(void *handle);
-
-/* 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_ERROR("DM: Failed to initialize FBC\n");
- else {
- adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr;
- DRM_INFO("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;
- ret = drm_eld_size(connector->eld);
- memcpy(buf, connector->eld, min(max_bytes, ret));
-
- break;
- }
- drm_connector_list_iter_end(&conn_iter);
-
- mutex_unlock(&adev->dm.audio_lock);
-
- DRM_DEBUG_KMS("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_DEBUG_KMS("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 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_ERROR("No framebuffer info for DMUB service.\n");
- return -EINVAL;
- }
-
- if (!dmub_fw) {
- /* Firmware required for DMUB support. */
- DRM_ERROR("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_ERROR("Error checking HW support for DMUB: %d\n", status);
- return -EINVAL;
- }
-
- if (!has_hw_support) {
- DRM_INFO("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("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;
-
- 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 = le32_to_cpu(hdr->inst_const_bytes) -
- PSP_HEADER_BYTES - PSP_FOOTER_BYTES;
-
- 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.fb_base = adev->gmc.fb_start;
- hw_params.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];
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- hw_params.dpia_supported = true;
- hw_params.disable_dpia = adev->dm.dc->debug.dpia_debug.bits.disable_dpia;
- break;
- default:
- break;
- }
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- hw_params.ips_sequential_ono = adev->external_rev_id > 0x10;
- break;
- default:
- break;
- }
-
- status = dmub_srv_hw_init(dmub_srv, &hw_params);
- if (status != DMUB_STATUS_OK) {
- DRM_ERROR("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("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_ERROR("Couldn't allocate DC DMUB server!\n");
- return -ENOMEM;
- }
-
- DRM_INFO("DMUB hardware initialized: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
-
- 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("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("Wait for DMUB auto-load failed: %d\n", status);
- } else {
- /* Perform the full hardware initialization. */
- r = dm_dmub_hw_init(adev);
- if (r)
- DRM_ERROR("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;
- u32 logical_addr_low;
- u32 logical_addr_high;
- u32 agp_base, agp_bot, agp_top;
- PHYSICAL_ADDRESS_LOC page_table_start, page_table_end, page_table_base;
-
- memset(pa_config, 0, sizeof(*pa_config));
-
- agp_base = 0;
- agp_bot = adev->gmc.agp_start >> 24;
- agp_top = adev->gmc.agp_end >> 24;
-
- /* AGP aperture is disabled */
- if (agp_bot > agp_top) {
- logical_addr_low = adev->gmc.fb_start >> 18;
- if (adev->apu_flags & (AMD_APU_IS_RAVEN2 |
- AMD_APU_IS_RENOIR |
- AMD_APU_IS_GREEN_SARDINE))
- /*
- * Raven2 has a HW issue that it is unable to use the vram which
- * is out of MC_VM_SYSTEM_APERTURE_HIGH_ADDR. So here is the
- * workaround that increase system aperture high address (add 1)
- * to get rid of the VM fault and hardware hang.
- */
- logical_addr_high = (adev->gmc.fb_end >> 18) + 0x1;
- else
- logical_addr_high = adev->gmc.fb_end >> 18;
- } else {
- logical_addr_low = min(adev->gmc.fb_start, adev->gmc.agp_start) >> 18;
- if (adev->apu_flags & (AMD_APU_IS_RAVEN2 |
- AMD_APU_IS_RENOIR |
- AMD_APU_IS_GREEN_SARDINE))
- /*
- * Raven2 has a HW issue that it is unable to use the vram which
- * is out of MC_VM_SYSTEM_APERTURE_HIGH_ADDR. So here is the
- * workaround that increase system aperture high address (add 1)
- * to get rid of the VM fault and hardware hang.
- */
- logical_addr_high = max((adev->gmc.fb_end >> 18) + 0x1, adev->gmc.agp_end >> 18);
- else
- logical_addr_high = max(adev->gmc.fb_end, adev->gmc.agp_end) >> 18;
- }
-
- pt_base = amdgpu_gmc_pd_addr(adev->gart.bo);
-
- page_table_start.high_part = upper_32_bits(adev->gmc.gart_start >>
- AMDGPU_GPU_PAGE_SHIFT);
- page_table_start.low_part = lower_32_bits(adev->gmc.gart_start >>
- AMDGPU_GPU_PAGE_SHIFT);
- page_table_end.high_part = upper_32_bits(adev->gmc.gart_end >>
- AMDGPU_GPU_PAGE_SHIFT);
- page_table_end.low_part = lower_32_bits(adev->gmc.gart_end >>
- AMDGPU_GPU_PAGE_SHIFT);
- page_table_base.high_part = upper_32_bits(pt_base);
- page_table_base.low_part = lower_32_bits(pt_base);
-
- pa_config->system_aperture.start_addr = (uint64_t)logical_addr_low << 18;
- pa_config->system_aperture.end_addr = (uint64_t)logical_addr_high << 18;
-
- pa_config->system_aperture.agp_base = (uint64_t)agp_base << 24;
- pa_config->system_aperture.agp_bot = (uint64_t)agp_bot << 24;
- pa_config->system_aperture.agp_top = (uint64_t)agp_top << 24;
-
- pa_config->system_aperture.fb_base = adev->gmc.fb_start;
- pa_config->system_aperture.fb_offset = adev->vm_manager.vram_base_offset;
- pa_config->system_aperture.fb_top = adev->gmc.fb_end;
-
- pa_config->gart_config.page_table_start_addr = page_table_start.quad_part << 12;
- pa_config->gart_config.page_table_end_addr = page_table_end.quad_part << 12;
- pa_config->gart_config.page_table_base_addr = page_table_base.quad_part;
-
- pa_config->is_hvm_enabled = adev->mode_info.gpu_vm_support;
-
-}
-
-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;
-
- if (!aconnector) {
- DRM_ERROR("Can't retrieve aconnector in hpd_rx_irq_offload_work");
- goto skip;
- }
-
- adev = drm_to_adev(aconnector->base.dev);
- dc_link = aconnector->dc_link;
-
- mutex_lock(&aconnector->hpd_lock);
- if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
- DRM_ERROR("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 dc *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 = kcalloc(max_caps, sizeof(*hpd_rx_offload_wq), GFP_KERNEL);
-
- 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_ERROR("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;
- u16 subsys_vendor;
- u16 subsys_device;
- u8 revision;
-};
-
-static const struct amdgpu_stutter_quirk amdgpu_stutter_quirk_list[] = {
- /* https://bugzilla.kernel.org/show_bug.cgi?id=214417 */
- { 0x1002, 0x15dd, 0x1002, 0x15dd, 0xc8 },
- { 0, 0, 0, 0, 0 },
-};
-
-static bool dm_should_disable_stutter(struct pci_dev *pdev)
-{
- const struct amdgpu_stutter_quirk *p = amdgpu_stutter_quirk_list;
-
- while (p && p->chip_device != 0) {
- if (pdev->vendor == p->chip_vendor &&
- pdev->device == p->chip_device &&
- pdev->subsystem_vendor == p->subsys_vendor &&
- pdev->subsystem_device == p->subsys_device &&
- pdev->revision == p->revision) {
- return true;
- }
- ++p;
- }
- return false;
-}
-
-static const struct dmi_system_id hpd_disconnect_quirk_table[] = {
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3660"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3260"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Precision 3460"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex Tower Plus 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex Tower 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex SFF Plus 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex SFF 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex Micro Plus 7010"),
- },
- },
- {
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex Micro 7010"),
- },
- },
- {}
- /* TODO: refactor this from a fixed table to a dynamic option */
-};
-
-static void retrieve_dmi_info(struct amdgpu_display_manager *dm)
-{
- const struct dmi_system_id *dmi_id;
-
- dm->aux_hpd_discon_quirk = false;
-
- dmi_id = dmi_first_match(hpd_disconnect_quirk_table);
- if (dmi_id) {
- dm->aux_hpd_discon_quirk = true;
- DRM_INFO("aux_hpd_discon_quirk attached\n");
- }
-}
-
-void*
-dm_allocate_gpu_mem(
- struct amdgpu_device *adev,
- enum dc_gpu_mem_alloc_type type,
- size_t size,
- long long *addr)
-{
- struct dal_allocation *da;
- u32 domain = (type == DC_MEM_ALLOC_TYPE_GART) ?
- AMDGPU_GEM_DOMAIN_GTT : AMDGPU_GEM_DOMAIN_VRAM;
- int ret;
-
- da = kzalloc(sizeof(struct dal_allocation), GFP_KERNEL);
- if (!da)
- return NULL;
-
- ret = amdgpu_bo_create_kernel(adev, size, PAGE_SIZE,
- domain, &da->bo,
- &da->gpu_addr, &da->cpu_ptr);
-
- *addr = da->gpu_addr;
-
- if (ret) {
- kfree(da);
- return NULL;
- }
-
- /* add da to list in dm */
- list_add(&da->list, &adev->dm.da_list);
-
- return da->cpu_ptr;
-}
-
-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 struct dml2_soc_bb *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev)
-{
- struct dml2_soc_bb *bb;
- long long addr;
- 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):
- break;
- default:
- return NULL;
- }
-
- bb = dm_allocate_gpu_mem(adev,
- DC_MEM_ALLOC_TYPE_GART,
- sizeof(struct dml2_soc_bb),
- &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)
- /* No need to free bb here since it shall be done in dm_sw_fini() */
- return NULL;
- }
-
- /* 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)
- return NULL;
-
- return bb;
-}
-
-static int amdgpu_dm_init(struct amdgpu_device *adev)
-{
- struct dc_init_data init_data;
- struct dc_callback_init init_params;
- int r;
-
- adev->dm.ddev = adev_to_drm(adev);
- adev->dm.adev = adev;
-
- /* Zero all the fields */
- memset(&init_data, 0, sizeof(init_data));
- memset(&init_params, 0, sizeof(init_params));
-
- mutex_init(&adev->dm.dpia_aux_lock);
- mutex_init(&adev->dm.dc_lock);
- mutex_init(&adev->dm.audio_lock);
-
- if (amdgpu_dm_irq_init(adev)) {
- DRM_ERROR("amdgpu: failed to initialize DM IRQ support.\n");
- goto error;
- }
-
- init_data.asic_id.chip_family = adev->family;
-
- init_data.asic_id.pci_revision_id = adev->pdev->revision;
- init_data.asic_id.hw_internal_rev = adev->external_rev_id;
- init_data.asic_id.chip_id = adev->pdev->device;
-
- init_data.asic_id.vram_width = adev->gmc.vram_width;
- /* TODO: initialize init_data.asic_id.vram_type here!!!! */
- init_data.asic_id.atombios_base_address =
- adev->mode_info.atom_context->bios;
-
- init_data.driver = adev;
-
- /* cgs_device was created in dm_sw_init() */
- init_data.cgs_device = adev->dm.cgs_device;
-
- init_data.dce_environment = DCE_ENV_PRODUCTION_DRV;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- switch (adev->dm.dmcub_fw_version) {
- case 0: /* development */
- case 0x1: /* linux-firmware.git hash 6d9f399 */
- case 0x01000000: /* linux-firmware.git hash 9a0b0f4 */
- init_data.flags.disable_dmcu = false;
- break;
- default:
- init_data.flags.disable_dmcu = true;
- }
- break;
- case IP_VERSION(2, 0, 3):
- init_data.flags.disable_dmcu = true;
- break;
- default:
- break;
- }
-
- /* APU support S/G display by default except:
- * ASICs before Carrizo,
- * RAVEN1 (Users reported stability issue)
- */
-
- if (adev->asic_type < CHIP_CARRIZO) {
- init_data.flags.gpu_vm_support = false;
- } else if (adev->asic_type == CHIP_RAVEN) {
- if (adev->apu_flags & AMD_APU_IS_RAVEN)
- init_data.flags.gpu_vm_support = false;
- else
- init_data.flags.gpu_vm_support = (amdgpu_sg_display != 0);
- } else {
- init_data.flags.gpu_vm_support = (amdgpu_sg_display != 0) && (adev->flags & AMD_IS_APU);
- }
-
- adev->mode_info.gpu_vm_support = init_data.flags.gpu_vm_support;
-
- if (amdgpu_dc_feature_mask & DC_FBC_MASK)
- init_data.flags.fbc_support = true;
-
- if (amdgpu_dc_feature_mask & DC_MULTI_MON_PP_MCLK_SWITCH_MASK)
- init_data.flags.multi_mon_pp_mclk_switch = true;
-
- if (amdgpu_dc_feature_mask & DC_DISABLE_FRACTIONAL_PWM_MASK)
- init_data.flags.disable_fractional_pwm = true;
-
- if (amdgpu_dc_feature_mask & DC_EDP_NO_POWER_SEQUENCING)
- init_data.flags.edp_no_power_sequencing = true;
-
- if (amdgpu_dc_feature_mask & DC_DISABLE_LTTPR_DP1_4A)
- init_data.flags.allow_lttpr_non_transparent_mode.bits.DP1_4A = true;
- if (amdgpu_dc_feature_mask & DC_DISABLE_LTTPR_DP2_0)
- init_data.flags.allow_lttpr_non_transparent_mode.bits.DP2_0 = true;
-
- init_data.flags.seamless_boot_edp_requested = false;
-
- if (amdgpu_device_seamless_boot_supported(adev)) {
- init_data.flags.seamless_boot_edp_requested = true;
- init_data.flags.allow_seamless_boot_optimization = true;
- DRM_INFO("Seamless boot condition check passed\n");
- }
-
- init_data.flags.enable_mipi_converter_optimization = true;
-
- init_data.dcn_reg_offsets = adev->reg_offset[DCE_HWIP][0];
- init_data.nbio_reg_offsets = adev->reg_offset[NBIO_HWIP][0];
- init_data.clk_reg_offsets = adev->reg_offset[CLK_HWIP][0];
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_IPS)
- init_data.flags.disable_ips = DMUB_IPS_DISABLE_ALL;
- else
- init_data.flags.disable_ips = DMUB_IPS_ENABLE;
-
- init_data.flags.disable_ips_in_vpb = 0;
-
- /* Enable DWB for tested platforms only */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 0, 0))
- init_data.num_virtual_links = 1;
-
- retrieve_dmi_info(&adev->dm);
-
- if (adev->dm.bb_from_dmub)
- init_data.bb_from_dmub = adev->dm.bb_from_dmub;
- else
- init_data.bb_from_dmub = NULL;
-
- /* Display Core create. */
- adev->dm.dc = dc_create(&init_data);
-
- if (adev->dm.dc) {
- DRM_INFO("Display Core v%s initialized on %s\n", DC_VER,
- dce_version_to_string(adev->dm.dc->ctx->dce_version));
- } else {
- DRM_INFO("Display Core failed to initialize with v%s!\n", DC_VER);
- goto error;
- }
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_PIPE_SPLIT) {
- adev->dm.dc->debug.force_single_disp_pipe_split = false;
- adev->dm.dc->debug.pipe_split_policy = MPC_SPLIT_AVOID;
- }
-
- if (adev->asic_type != CHIP_CARRIZO && adev->asic_type != CHIP_STONEY)
- adev->dm.dc->debug.disable_stutter = amdgpu_pp_feature_mask & PP_STUTTER_MODE ? false : true;
- if (dm_should_disable_stutter(adev->pdev))
- adev->dm.dc->debug.disable_stutter = true;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_STUTTER)
- adev->dm.dc->debug.disable_stutter = true;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_DSC)
- adev->dm.dc->debug.disable_dsc = true;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_CLOCK_GATING)
- adev->dm.dc->debug.disable_clock_gate = true;
-
- if (amdgpu_dc_debug_mask & DC_FORCE_SUBVP_MCLK_SWITCH)
- adev->dm.dc->debug.force_subvp_mclk_switch = true;
-
- if (amdgpu_dc_debug_mask & DC_ENABLE_DML2) {
- adev->dm.dc->debug.using_dml2 = true;
- adev->dm.dc->debug.using_dml21 = true;
- }
-
- adev->dm.dc->debug.visual_confirm = amdgpu_dc_visual_confirm;
-
- /* TODO: Remove after DP2 receiver gets proper support of Cable ID feature */
- adev->dm.dc->debug.ignore_cable_id = true;
-
- if (adev->dm.dc->caps.dp_hdmi21_pcon_support)
- DRM_INFO("DP-HDMI FRL PCON supported\n");
-
- r = dm_dmub_hw_init(adev);
- if (r) {
- DRM_ERROR("DMUB interface failed to initialize: status=%d\n", r);
- goto error;
- }
-
- dc_hardware_init(adev->dm.dc);
-
- adev->dm.hpd_rx_offload_wq = hpd_rx_irq_create_workqueue(adev->dm.dc);
- if (!adev->dm.hpd_rx_offload_wq) {
- DRM_ERROR("amdgpu: failed to create hpd rx offload workqueue.\n");
- goto error;
- }
-
- if ((adev->flags & AMD_IS_APU) && (adev->asic_type >= CHIP_CARRIZO)) {
- struct dc_phy_addr_space_config pa_config;
-
- mmhub_read_system_context(adev, &pa_config);
-
- // Call the DC init_memory func
- dc_setup_system_context(adev->dm.dc, &pa_config);
- }
-
- adev->dm.freesync_module = mod_freesync_create(adev->dm.dc);
- if (!adev->dm.freesync_module) {
- DRM_ERROR(
- "amdgpu: failed to initialize freesync_module.\n");
- } else
- DRM_DEBUG_DRIVER("amdgpu: freesync_module init done %p.\n",
- adev->dm.freesync_module);
-
- amdgpu_dm_init_color_mod();
-
- if (adev->dm.dc->caps.max_links > 0) {
- adev->dm.vblank_control_workqueue =
- create_singlethread_workqueue("dm_vblank_control_workqueue");
- if (!adev->dm.vblank_control_workqueue)
- DRM_ERROR("amdgpu: failed to initialize vblank_workqueue.\n");
- }
-
- if (adev->dm.dc->caps.ips_support && adev->dm.dc->config.disable_ips == DMUB_IPS_ENABLE)
- adev->dm.idle_workqueue = idle_create_workqueue(adev);
-
- if (adev->dm.dc->caps.max_links > 0 && adev->family >= AMDGPU_FAMILY_RV) {
- adev->dm.hdcp_workqueue = hdcp_create_workqueue(adev, &init_params.cp_psp, adev->dm.dc);
-
- if (!adev->dm.hdcp_workqueue)
- DRM_ERROR("amdgpu: failed to initialize hdcp_workqueue.\n");
- else
- DRM_DEBUG_DRIVER("amdgpu: hdcp_workqueue init done %p.\n", adev->dm.hdcp_workqueue);
-
- dc_init_callbacks(adev->dm.dc, &init_params);
- }
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- init_completion(&adev->dm.dmub_aux_transfer_done);
- adev->dm.dmub_notify = kzalloc(sizeof(struct dmub_notification), GFP_KERNEL);
- if (!adev->dm.dmub_notify) {
- DRM_INFO("amdgpu: fail to allocate adev->dm.dmub_notify");
- goto error;
- }
-
- adev->dm.delayed_hpd_wq = create_singlethread_workqueue("amdgpu_dm_hpd_wq");
- if (!adev->dm.delayed_hpd_wq) {
- DRM_ERROR("amdgpu: failed to create hpd offload workqueue.\n");
- goto error;
- }
-
- amdgpu_dm_outbox_init(adev);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
- dmub_aux_setconfig_callback, false)) {
- DRM_ERROR("amdgpu: fail to register dmub aux 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.
- */
- dc_enable_dmub_outbox(adev->dm.dc);
-
- /* DPIA trace goes to dmesg logs only if outbox is enabled */
- if (amdgpu_dc_debug_mask & DC_ENABLE_DPIA_TRACE)
- dc_dmub_srv_enable_dpia_trace(adev->dm.dc);
- }
-
- if (amdgpu_dm_initialize_drm_device(adev)) {
- DRM_ERROR(
- "amdgpu: failed to initialize sw for display support.\n");
- goto error;
- }
-
- /* create fake encoders for MST */
- dm_dp_create_fake_mst_encoders(adev);
-
- /* TODO: Add_display_info? */
-
- /* TODO use dynamic cursor width */
- adev_to_drm(adev)->mode_config.cursor_width = adev->dm.dc->caps.max_cursor_size;
- adev_to_drm(adev)->mode_config.cursor_height = adev->dm.dc->caps.max_cursor_size;
-
- if (drm_vblank_init(adev_to_drm(adev), adev->dm.display_indexes_num)) {
- DRM_ERROR(
- "amdgpu: failed to initialize sw for display support.\n");
- goto error;
- }
-
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- adev->dm.secure_display_ctxs = amdgpu_dm_crtc_secure_display_create_contexts(adev);
- if (!adev->dm.secure_display_ctxs)
- DRM_ERROR("amdgpu: failed to initialize secure display contexts.\n");
-#endif
-
- DRM_DEBUG_DRIVER("KMS initialized.\n");
-
- return 0;
-error:
- amdgpu_dm_fini(adev);
-
- return -EINVAL;
-}
-
-static int amdgpu_dm_early_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- amdgpu_dm_audio_fini(adev);
-
- return 0;
-}
-
-static void amdgpu_dm_fini(struct amdgpu_device *adev)
-{
- int i;
-
- if (adev->dm.vblank_control_workqueue) {
- destroy_workqueue(adev->dm.vblank_control_workqueue);
- adev->dm.vblank_control_workqueue = NULL;
- }
-
- if (adev->dm.idle_workqueue) {
- if (adev->dm.idle_workqueue->running) {
- adev->dm.idle_workqueue->enable = false;
- flush_work(&adev->dm.idle_workqueue->work);
- }
-
- kfree(adev->dm.idle_workqueue);
- adev->dm.idle_workqueue = NULL;
- }
-
- amdgpu_dm_destroy_drm_device(&adev->dm);
-
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- if (adev->dm.secure_display_ctxs) {
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- if (adev->dm.secure_display_ctxs[i].crtc) {
- flush_work(&adev->dm.secure_display_ctxs[i].notify_ta_work);
- flush_work(&adev->dm.secure_display_ctxs[i].forward_roi_work);
- }
- }
- kfree(adev->dm.secure_display_ctxs);
- adev->dm.secure_display_ctxs = NULL;
- }
-#endif
- if (adev->dm.hdcp_workqueue) {
- hdcp_destroy(&adev->dev->kobj, adev->dm.hdcp_workqueue);
- adev->dm.hdcp_workqueue = NULL;
- }
-
- if (adev->dm.dc) {
- dc_deinit_callbacks(adev->dm.dc);
- dc_dmub_srv_destroy(&adev->dm.dc->ctx->dmub_srv);
- if (dc_enable_dmub_notifications(adev->dm.dc)) {
- kfree(adev->dm.dmub_notify);
- adev->dm.dmub_notify = NULL;
- destroy_workqueue(adev->dm.delayed_hpd_wq);
- adev->dm.delayed_hpd_wq = NULL;
- }
- }
-
- if (adev->dm.dmub_bo)
- amdgpu_bo_free_kernel(&adev->dm.dmub_bo,
- &adev->dm.dmub_bo_gpu_addr,
- &adev->dm.dmub_bo_cpu_addr);
-
- if (adev->dm.hpd_rx_offload_wq && adev->dm.dc) {
- for (i = 0; i < adev->dm.dc->caps.max_links; i++) {
- if (adev->dm.hpd_rx_offload_wq[i].wq) {
- destroy_workqueue(adev->dm.hpd_rx_offload_wq[i].wq);
- adev->dm.hpd_rx_offload_wq[i].wq = NULL;
- }
- }
-
- kfree(adev->dm.hpd_rx_offload_wq);
- adev->dm.hpd_rx_offload_wq = NULL;
- }
-
- /* DC Destroy TODO: Replace destroy DAL */
- if (adev->dm.dc)
- dc_destroy(&adev->dm.dc);
- /*
- * TODO: pageflip, vlank interrupt
- *
- * amdgpu_dm_irq_fini(adev);
- */
-
- if (adev->dm.cgs_device) {
- amdgpu_cgs_destroy_device(adev->dm.cgs_device);
- adev->dm.cgs_device = NULL;
- }
- if (adev->dm.freesync_module) {
- mod_freesync_destroy(adev->dm.freesync_module);
- adev->dm.freesync_module = NULL;
- }
-
- mutex_destroy(&adev->dm.audio_lock);
- mutex_destroy(&adev->dm.dc_lock);
- mutex_destroy(&adev->dm.dpia_aux_lock);
-}
-
-static int load_dmcu_fw(struct amdgpu_device *adev)
-{
- const char *fw_name_dmcu = NULL;
- int r;
- const struct dmcu_firmware_header_v1_0 *hdr;
-
- switch (adev->asic_type) {
-#if defined(CONFIG_DRM_AMD_DC_SI)
- case CHIP_TAHITI:
- case CHIP_PITCAIRN:
- case CHIP_VERDE:
- case CHIP_OLAND:
-#endif
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- case CHIP_KAVERI:
- case CHIP_KABINI:
- case CHIP_MULLINS:
- case CHIP_TONGA:
- case CHIP_FIJI:
- case CHIP_CARRIZO:
- case CHIP_STONEY:
- case CHIP_POLARIS11:
- case CHIP_POLARIS10:
- case CHIP_POLARIS12:
- case CHIP_VEGAM:
- case CHIP_VEGA10:
- case CHIP_VEGA12:
- case CHIP_VEGA20:
- return 0;
- case CHIP_NAVI12:
- fw_name_dmcu = FIRMWARE_NAVI12_DMCU;
- break;
- case CHIP_RAVEN:
- if (ASICREV_IS_PICASSO(adev->external_rev_id))
- fw_name_dmcu = FIRMWARE_RAVEN_DMCU;
- else if (ASICREV_IS_RAVEN2(adev->external_rev_id))
- fw_name_dmcu = FIRMWARE_RAVEN_DMCU;
- else
- return 0;
- break;
- default:
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(2, 0, 3):
- case IP_VERSION(2, 0, 0):
- case IP_VERSION(2, 1, 0):
- case IP_VERSION(3, 0, 0):
- case IP_VERSION(3, 0, 2):
- case IP_VERSION(3, 0, 3):
- case IP_VERSION(3, 0, 1):
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- return 0;
- default:
- break;
- }
- DRM_ERROR("Unsupported ASIC type: 0x%X\n", adev->asic_type);
- return -EINVAL;
- }
-
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
- DRM_DEBUG_KMS("dm: DMCU firmware not supported on direct or SMU loading\n");
- return 0;
- }
-
- r = amdgpu_ucode_request(adev, &adev->dm.fw_dmcu, "%s", fw_name_dmcu);
- if (r == -ENODEV) {
- /* DMCU firmware is not necessary, so don't raise a fuss if it's missing */
- DRM_DEBUG_KMS("dm: DMCU firmware not found\n");
- adev->dm.fw_dmcu = NULL;
- return 0;
- }
- if (r) {
- dev_err(adev->dev, "amdgpu_dm: Can't validate firmware \"%s\"\n",
- fw_name_dmcu);
- amdgpu_ucode_release(&adev->dm.fw_dmcu);
- return r;
- }
-
- hdr = (const struct dmcu_firmware_header_v1_0 *)adev->dm.fw_dmcu->data;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_ERAM].ucode_id = AMDGPU_UCODE_ID_DMCU_ERAM;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_ERAM].fw = adev->dm.fw_dmcu;
- adev->firmware.fw_size +=
- ALIGN(le32_to_cpu(hdr->header.ucode_size_bytes) - le32_to_cpu(hdr->intv_size_bytes), PAGE_SIZE);
-
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_INTV].ucode_id = AMDGPU_UCODE_ID_DMCU_INTV;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCU_INTV].fw = adev->dm.fw_dmcu;
- adev->firmware.fw_size +=
- ALIGN(le32_to_cpu(hdr->intv_size_bytes), PAGE_SIZE);
-
- adev->dm.dmcu_fw_version = le32_to_cpu(hdr->header.ucode_version);
-
- DRM_DEBUG_KMS("PSP loading DMCU firmware\n");
-
- 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_region_params region_params;
- struct dmub_srv_region_info region_info;
- struct dmub_srv_memory_params memory_params;
- 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_SHARED_STATE
- };
- 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(4, 0, 1):
- dmub_asic = DMUB_ASIC_DCN401;
- 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("Loading DMUB firmware via PSP: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
- }
-
-
- adev->dm.dmub_srv = kzalloc(sizeof(*adev->dm.dmub_srv), GFP_KERNEL);
- dmub_srv = adev->dm.dmub_srv;
-
- if (!dmub_srv) {
- DRM_ERROR("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_ERROR("Error creating DMUB service: %d\n", status);
- return -EINVAL;
- }
-
- /* Calculate the size of all the regions for the DMUB service. */
- memset(&region_params, 0, sizeof(region_params));
-
- region_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
- PSP_HEADER_BYTES - PSP_FOOTER_BYTES;
- region_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
- region_params.vbios_size = adev->bios_size;
- region_params.fw_bss_data = region_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;
- region_params.fw_inst_const =
- adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES;
- region_params.window_memory_type = window_memory_type;
-
- status = dmub_srv_calc_region_info(dmub_srv, &region_params,
- &region_info);
-
- if (status != DMUB_STATUS_OK) {
- DRM_ERROR("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 = &region_info;
- memory_params.window_memory_type = window_memory_type;
-
- adev->dm.dmub_fb_info =
- kzalloc(sizeof(*adev->dm.dmub_fb_info), GFP_KERNEL);
- fb_info = adev->dm.dmub_fb_info;
-
- if (!fb_info) {
- DRM_ERROR(
- "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_ERROR("Error calculating DMUB FB info: %d\n", status);
- return -EINVAL;
- }
-
- adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
-
- return 0;
-}
-
-static int dm_sw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- adev->dm.cgs_device = amdgpu_cgs_create_device(adev);
-
- if (!adev->dm.cgs_device) {
- DRM_ERROR("amdgpu: failed to create cgs device.\n");
- return -EINVAL;
- }
-
- /* Moved from dm init since we need to use allocations for storing bounding box data */
- INIT_LIST_HEAD(&adev->dm.da_list);
-
- r = dm_dmub_sw_init(adev);
- if (r)
- return r;
-
- return load_dmcu_fw(adev);
-}
-
-static int dm_sw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct dal_allocation *da;
-
- list_for_each_entry(da, &adev->dm.da_list, list) {
- if (adev->dm.bb_from_dmub == (void *) da->cpu_ptr) {
- amdgpu_bo_free_kernel(&da->bo, &da->gpu_addr, &da->cpu_ptr);
- list_del(&da->list);
- kfree(da);
- break;
- }
- }
-
- adev->dm.bb_from_dmub = NULL;
-
- kfree(adev->dm.dmub_fb_info);
- adev->dm.dmub_fb_info = NULL;
-
- if (adev->dm.dmub_srv) {
- dmub_srv_destroy(adev->dm.dmub_srv);
- kfree(adev->dm.dmub_srv);
- adev->dm.dmub_srv = NULL;
- }
-
- amdgpu_ucode_release(&adev->dm.dmub_fw);
- amdgpu_ucode_release(&adev->dm.fw_dmcu);
-
- 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 int dm_late_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- struct dmcu_iram_parameters params;
- unsigned int linear_lut[16];
- int i;
- struct dmcu *dmcu = NULL;
-
- dmcu = adev->dm.dc->res_pool->dmcu;
-
- for (i = 0; i < 16; i++)
- linear_lut[i] = 0xFFFF * i / 15;
-
- params.set = 0;
- params.backlight_ramping_override = false;
- params.backlight_ramping_start = 0xCCCC;
- params.backlight_ramping_reduction = 0xCCCCCCCC;
- params.backlight_lut_array_size = 16;
- params.backlight_lut_array = linear_lut;
-
- /* Min backlight level after ABM reduction, Don't allow below 1%
- * 0xFFFF x 0.01 = 0x28F
- */
- params.min_abm_backlight = 0x28F;
- /* In the case where abm is implemented on dmcub,
- * dmcu object will be null.
- * ABM 2.4 and up are implemented on dmcub.
- */
- if (dmcu) {
- if (!dmcu_load_iram(dmcu, params))
- return -EINVAL;
- } else if (adev->dm.dc->ctx->dmub_srv) {
- struct dc_link *edp_links[MAX_NUM_EDP];
- int edp_num;
-
- dc_get_edp_links(adev->dm.dc, edp_links, &edp_num);
- for (i = 0; i < edp_num; i++) {
- if (!dmub_init_abm_config(adev->dm.dc->res_pool, params, i))
- return -EINVAL;
- }
- }
-
- return detect_mst_link_for_all_connectors(adev_to_drm(adev));
-}
-
-static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
-{
- int ret;
- u8 guid[16];
- u64 tmp64;
-
- mutex_lock(&mgr->lock);
- if (!mgr->mst_primary)
- goto out_fail;
-
- if (drm_dp_read_dpcd_caps(mgr->aux, mgr->dpcd) < 0) {
- drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
- goto out_fail;
- }
-
- ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
- DP_MST_EN |
- DP_UP_REQ_EN |
- DP_UPSTREAM_IS_SRC);
- if (ret < 0) {
- drm_dbg_kms(mgr->dev, "mst write failed - undocked during suspend?\n");
- goto out_fail;
- }
-
- /* Some hubs forget their guids after they resume */
- ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
- if (ret != 16) {
- drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n");
- goto out_fail;
- }
-
- if (memchr_inv(guid, 0, 16) == NULL) {
- tmp64 = get_jiffies_64();
- memcpy(&guid[0], &tmp64, sizeof(u64));
- memcpy(&guid[8], &tmp64, sizeof(u64));
-
- ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, guid, 16);
-
- if (ret != 16) {
- drm_dbg_kms(mgr->dev, "check mstb guid failed - undocked during suspend?\n");
- goto out_fail;
- }
- }
-
- memcpy(mgr->mst_primary->guid, guid, 16);
-
-out_fail:
- mutex_unlock(&mgr->lock);
-}
-
-static void s3_handle_mst(struct drm_device *dev, bool suspend)
-{
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- struct drm_dp_mst_topology_mgr *mgr;
-
- 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_root)
- continue;
-
- mgr = &aconnector->mst_mgr;
-
- if (suspend) {
- drm_dp_mst_topology_mgr_suspend(mgr);
- } else {
- /* if extended timeout is supported in hardware,
- * default to LTTPR timeout (3.2ms) first as a W/A for DP link layer
- * CTS 4.2.1.1 regression introduced by CTS specs requirement update.
- */
- try_to_configure_aux_timeout(aconnector->dc_link->ddc, LINK_AUX_DEFAULT_LTTPR_TIMEOUT_PERIOD);
- if (!dp_is_lttpr_present(aconnector->dc_link))
- try_to_configure_aux_timeout(aconnector->dc_link->ddc, LINK_AUX_DEFAULT_TIMEOUT_PERIOD);
-
- /* TODO: move resume_mst_branch_status() into drm mst resume again
- * once topology probing work is pulled out from mst resume into mst
- * resume 2nd step. mst resume 2nd step should be called after old
- * state getting restored (i.e. drm_atomic_helper_resume()).
- */
- resume_mst_branch_status(mgr);
- }
- }
- drm_connector_list_iter_end(&iter);
-}
-
-static int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev)
-{
- int ret = 0;
-
- /* This interface is for dGPU Navi1x.Linux dc-pplib interface depends
- * on window driver dc implementation.
- * For Navi1x, clock settings of dcn watermarks are fixed. the settings
- * should be passed to smu during boot up and resume from s3.
- * boot up: dc calculate dcn watermark clock settings within dc_create,
- * dcn20_resource_construct
- * then call pplib functions below to pass the settings to smu:
- * smu_set_watermarks_for_clock_ranges
- * smu_set_watermarks_table
- * navi10_set_watermarks_table
- * smu_write_watermarks_table
- *
- * For Renoir, clock settings of dcn watermark are also fixed values.
- * dc has implemented different flow for window driver:
- * dc_hardware_init / dc_set_power_state
- * dcn10_init_hw
- * notify_wm_ranges
- * set_wm_ranges
- * -- Linux
- * smu_set_watermarks_for_clock_ranges
- * renoir_set_watermarks_table
- * smu_write_watermarks_table
- *
- * For Linux,
- * dc_hardware_init -> amdgpu_dm_init
- * dc_set_power_state --> dm_resume
- *
- * therefore, this function apply to navi10/12/14 but not Renoir
- * *
- */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(2, 0, 0):
- break;
- default:
- return 0;
- }
-
- ret = amdgpu_dpm_write_watermarks_table(adev);
- if (ret) {
- DRM_ERROR("Failed to update WMTABLE!\n");
- return ret;
- }
-
- return 0;
-}
-
-/**
- * dm_hw_init() - Initialize DC device
- * @handle: The base driver device containing the amdgpu_dm device.
- *
- * Initialize the &struct amdgpu_display_manager device. This involves calling
- * the initializers of each DM component, then populating the struct with them.
- *
- * Although the function implies hardware initialization, both hardware and
- * software are initialized here. Splitting them out to their relevant init
- * hooks is a future TODO item.
- *
- * Some notable things that are initialized here:
- *
- * - Display Core, both software and hardware
- * - DC modules that we need (freesync and color management)
- * - DRM software states
- * - Interrupt sources and handlers
- * - Vblank support
- * - Debug FS entries, if enabled
- */
-static int dm_hw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- /* Create DAL display manager */
- r = amdgpu_dm_init(adev);
- if (r)
- return r;
- amdgpu_dm_hpd_init(adev);
-
- return 0;
-}
-
-/**
- * dm_hw_fini() - Teardown DC device
- * @handle: The base driver device containing the amdgpu_dm device.
- *
- * Teardown components within &struct amdgpu_display_manager that require
- * cleanup. This involves cleaning up the DRM device, DC, and any modules that
- * were loaded. Also flush IRQ workqueues and disable them.
- */
-static int dm_hw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- amdgpu_dm_hpd_fini(adev);
-
- amdgpu_dm_irq_fini(adev);
- amdgpu_dm_fini(adev);
- return 0;
-}
-
-
-static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
- struct dc_state *state, bool enable)
-{
- enum dc_irq_source irq_source;
- struct amdgpu_crtc *acrtc;
- int rc = -EBUSY;
- int i = 0;
-
- for (i = 0; i < state->stream_count; i++) {
- acrtc = get_crtc_by_otg_inst(
- adev, state->stream_status[i].primary_otg_inst);
-
- if (acrtc && state->stream_status[i].plane_count != 0) {
- irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst;
- rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
- if (rc)
- DRM_WARN("Failed to %s pflip interrupts\n",
- enable ? "enable" : "disable");
-
- if (enable) {
- if (amdgpu_dm_crtc_vrr_active(to_dm_crtc_state(acrtc->base.state)))
- rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, true);
- } else
- rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, false);
-
- if (rc)
- DRM_WARN("Failed to %sable vupdate interrupt\n", enable ? "en" : "dis");
-
- irq_source = IRQ_TYPE_VBLANK + acrtc->otg_inst;
- /* During gpu-reset we disable and then enable vblank irq, so
- * don't use amdgpu_irq_get/put() to avoid refcount change.
- */
- if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
- DRM_WARN("Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
- }
- }
-
-}
-
-static enum dc_status amdgpu_dm_commit_zero_streams(struct dc *dc)
-{
- struct dc_state *context = NULL;
- enum dc_status res = DC_ERROR_UNEXPECTED;
- int i;
- struct dc_stream_state *del_streams[MAX_PIPES];
- int del_streams_count = 0;
- struct dc_commit_streams_params params = {};
-
- memset(del_streams, 0, sizeof(del_streams));
-
- context = dc_state_create_current_copy(dc);
- if (context == NULL)
- goto context_alloc_fail;
-
- /* First remove from context all streams */
- for (i = 0; i < context->stream_count; i++) {
- struct dc_stream_state *stream = context->streams[i];
-
- del_streams[del_streams_count++] = stream;
- }
-
- /* Remove all planes for removed streams and then remove the streams */
- for (i = 0; i < del_streams_count; i++) {
- if (!dc_state_rem_all_planes_for_stream(dc, del_streams[i], context)) {
- res = DC_FAIL_DETACH_SURFACES;
- goto fail;
- }
-
- res = dc_state_remove_stream(dc, context, del_streams[i]);
- if (res != DC_OK)
- goto fail;
- }
-
- params.streams = context->streams;
- params.stream_count = context->stream_count;
- res = dc_commit_streams(dc, &params);
-
-fail:
- dc_state_release(context);
-
-context_alloc_fail:
- return res;
-}
-
-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_suspend(void *handle)
-{
- struct amdgpu_device *adev = handle;
- struct amdgpu_display_manager *dm = &adev->dm;
- int ret = 0;
-
- if (amdgpu_in_reset(adev)) {
- mutex_lock(&dm->dc_lock);
-
- dc_allow_idle_optimizations(adev->dm.dc, false);
-
- dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state);
-
- if (dm->cached_dc_state)
- dm_gpureset_toggle_interrupts(adev, dm->cached_dc_state, false);
-
- amdgpu_dm_commit_zero_streams(dm->dc);
-
- amdgpu_dm_irq_suspend(adev);
-
- hpd_rx_irq_work_suspend(dm);
-
- return ret;
- }
-
- WARN_ON(adev->dm.cached_state);
- adev->dm.cached_state = drm_atomic_helper_suspend(adev_to_drm(adev));
- if (IS_ERR(adev->dm.cached_state))
- return PTR_ERR(adev->dm.cached_state);
-
- s3_handle_mst(adev_to_drm(adev), true);
-
- amdgpu_dm_irq_suspend(adev);
-
- hpd_rx_irq_work_suspend(dm);
-
- if (adev->dm.dc->caps.ips_support)
- dc_allow_idle_optimizations(adev->dm.dc, true);
-
- dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3);
- dc_dmub_srv_set_power_state(dm->dc->ctx->dmub_srv, DC_ACPI_CM_POWER_STATE_D3);
-
- return 0;
-}
-
-struct drm_connector *
-amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_state *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)
-{
- struct dc_sink_init_data sink_init_data = { 0 };
- struct display_sink_capability sink_caps = { 0 };
- enum dc_edid_status edid_status;
- struct dc_context *dc_ctx = link->ctx;
- struct drm_device *dev = adev_to_drm(dc_ctx->driver_context);
- struct dc_sink *sink = NULL;
- struct dc_sink *prev_sink = NULL;
-
- link->type = dc_connection_none;
- prev_sink = link->local_sink;
-
- if (prev_sink)
- dc_sink_release(prev_sink);
-
- switch (link->connector_signal) {
- case SIGNAL_TYPE_HDMI_TYPE_A: {
- sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
- sink_caps.signal = SIGNAL_TYPE_HDMI_TYPE_A;
- break;
- }
-
- case SIGNAL_TYPE_DVI_SINGLE_LINK: {
- sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
- sink_caps.signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
- break;
- }
-
- case SIGNAL_TYPE_DVI_DUAL_LINK: {
- sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
- sink_caps.signal = SIGNAL_TYPE_DVI_DUAL_LINK;
- break;
- }
-
- case SIGNAL_TYPE_LVDS: {
- sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
- sink_caps.signal = SIGNAL_TYPE_LVDS;
- break;
- }
-
- case SIGNAL_TYPE_EDP: {
- sink_caps.transaction_type =
- DDC_TRANSACTION_TYPE_I2C_OVER_AUX;
- sink_caps.signal = SIGNAL_TYPE_EDP;
- break;
- }
-
- case SIGNAL_TYPE_DISPLAY_PORT: {
- sink_caps.transaction_type =
- DDC_TRANSACTION_TYPE_I2C_OVER_AUX;
- sink_caps.signal = SIGNAL_TYPE_VIRTUAL;
- break;
- }
-
- default:
- drm_err(dev, "Invalid connector type! signal:%d\n",
- link->connector_signal);
- return;
- }
-
- sink_init_data.link = link;
- sink_init_data.sink_signal = sink_caps.signal;
-
- sink = dc_sink_create(&sink_init_data);
- if (!sink) {
- drm_err(dev, "Failed to create sink!\n");
- return;
- }
-
- /* dc_sink_create returns a new reference */
- link->local_sink = sink;
-
- edid_status = dm_helpers_read_local_edid(
- link->ctx,
- link,
- sink);
-
- if (edid_status != EDID_OK)
- drm_err(dev, "Failed to read EDID\n");
-
-}
-
-static void dm_gpureset_commit_state(struct dc_state *dc_state,
- struct amdgpu_display_manager *dm)
-{
- struct {
- struct dc_surface_update surface_updates[MAX_SURFACES];
- struct dc_plane_info plane_infos[MAX_SURFACES];
- struct dc_scaling_info scaling_infos[MAX_SURFACES];
- struct dc_flip_addrs flip_addrs[MAX_SURFACES];
- struct dc_stream_update stream_update;
- } *bundle;
- int k, m;
-
- bundle = kzalloc(sizeof(*bundle), GFP_KERNEL);
-
- if (!bundle) {
- drm_err(dm->ddev, "Failed to allocate update bundle\n");
- goto cleanup;
- }
-
- for (k = 0; k < dc_state->stream_count; k++) {
- bundle->stream_update.stream = dc_state->streams[k];
-
- for (m = 0; m < dc_state->stream_status->plane_count; m++) {
- bundle->surface_updates[m].surface =
- dc_state->stream_status->plane_states[m];
- bundle->surface_updates[m].surface->force_full_update =
- true;
- }
-
- update_planes_and_stream_adapter(dm->dc,
- UPDATE_TYPE_FULL,
- dc_state->stream_status->plane_count,
- dc_state->streams[k],
- &bundle->stream_update,
- bundle->surface_updates);
- }
-
-cleanup:
- kfree(bundle);
-}
-
-static int dm_resume(void *handle)
-{
- struct amdgpu_device *adev = handle;
- struct drm_device *ddev = adev_to_drm(adev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- struct drm_crtc *crtc;
- struct drm_crtc_state *new_crtc_state;
- struct dm_crtc_state *dm_new_crtc_state;
- struct drm_plane *plane;
- struct drm_plane_state *new_plane_state;
- struct dm_plane_state *dm_new_plane_state;
- struct dm_atomic_state *dm_state = to_dm_atomic_state(dm->atomic_obj.state);
- enum dc_connection_type new_connection_type = dc_connection_none;
- struct dc_state *dc_state;
- int i, r, j, ret;
- bool need_hotplug = false;
- struct dc_commit_streams_params commit_params = {};
-
- if (dm->dc->caps.ips_support) {
- dc_dmub_srv_apply_idle_power_optimizations(dm->dc, false);
- }
-
- if (amdgpu_in_reset(adev)) {
- dc_state = dm->cached_dc_state;
-
- /*
- * The dc->current_state is backed up into dm->cached_dc_state
- * before we commit 0 streams.
- *
- * DC will clear link encoder assignments on the real state
- * but the changes won't propagate over to the copy we made
- * before the 0 streams commit.
- *
- * DC expects that link encoder assignments are *not* valid
- * when committing a state, so as a workaround we can copy
- * off of the current state.
- *
- * We lose the previous assignments, but we had already
- * commit 0 streams anyway.
- */
- link_enc_cfg_copy(adev->dm.dc->current_state, dc_state);
-
- r = dm_dmub_hw_init(adev);
- if (r)
- DRM_ERROR("DMUB interface failed to initialize: status=%d\n", r);
-
- dc_dmub_srv_set_power_state(dm->dc->ctx->dmub_srv, DC_ACPI_CM_POWER_STATE_D0);
- dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D0);
-
- dc_resume(dm->dc);
-
- amdgpu_dm_irq_resume_early(adev);
-
- 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;
- }
- }
-
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- amdgpu_dm_outbox_init(adev);
- dc_enable_dmub_outbox(adev->dm.dc);
- }
-
- commit_params.streams = dc_state->streams;
- commit_params.stream_count = dc_state->stream_count;
- dc_exit_ips_for_hw_access(dm->dc);
- WARN_ON(!dc_commit_streams(dm->dc, &commit_params));
-
- dm_gpureset_commit_state(dm->cached_dc_state, dm);
-
- dm_gpureset_toggle_interrupts(adev, dm->cached_dc_state, true);
-
- dc_state_release(dm->cached_dc_state);
- dm->cached_dc_state = NULL;
-
- amdgpu_dm_irq_resume_late(adev);
-
- mutex_unlock(&dm->dc_lock);
-
- return 0;
- }
- /* Recreate dc_state - DC invalidates it when setting power state to S3. */
- dc_state_release(dm_state->context);
- dm_state->context = dc_state_create(dm->dc, NULL);
- /* TODO: Remove dc_state->dccg, use dc->dccg directly. */
-
- /* Before powering on DC we need to re-initialize DMUB. */
- dm_dmub_hw_resume(adev);
-
- /* Re-enable outbox interrupts for DPIA. */
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- amdgpu_dm_outbox_init(adev);
- dc_enable_dmub_outbox(adev->dm.dc);
- }
-
- /* power on hardware */
- dc_dmub_srv_set_power_state(dm->dc->ctx->dmub_srv, DC_ACPI_CM_POWER_STATE_D0);
- dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D0);
-
- /* program HPD filter */
- dc_resume(dm->dc);
-
- /*
- * early enable HPD Rx IRQ, should be done before set mode as short
- * pulse interrupts are used for MST
- */
- amdgpu_dm_irq_resume_early(adev);
-
- /* On resume we need to rewrite the MSTM control bits to enable MST*/
- s3_handle_mst(ddev, false);
-
- /* Do detection*/
- drm_connector_list_iter_begin(ddev, &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)
- continue;
-
- /*
- * this is the case when traversing through already created end sink
- * MST connectors, should be skipped
- */
- if (aconnector->mst_root)
- continue;
-
- mutex_lock(&aconnector->hpd_lock);
- if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type))
- DRM_ERROR("KMS: Failed to detect connector\n");
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(aconnector->dc_link);
- } else {
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- dc_link_detect(aconnector->dc_link, DETECT_REASON_RESUMEFROMS3S4);
- mutex_unlock(&dm->dc_lock);
- }
-
- if (aconnector->fake_enable && aconnector->dc_link->local_sink)
- aconnector->fake_enable = false;
-
- if (aconnector->dc_sink)
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
- amdgpu_dm_update_connector_after_detect(aconnector);
- mutex_unlock(&aconnector->hpd_lock);
- }
- drm_connector_list_iter_end(&iter);
-
- /* Force mode set in atomic commit */
- for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
- new_crtc_state->active_changed = true;
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- reset_freesync_config_for_crtc(dm_new_crtc_state);
- }
-
- /*
- * atomic_check is expected to create the dc states. We need to release
- * them here, since they were duplicated as part of the suspend
- * procedure.
- */
- for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (dm_new_crtc_state->stream) {
- WARN_ON(kref_read(&dm_new_crtc_state->stream->refcount) > 1);
- dc_stream_release(dm_new_crtc_state->stream);
- dm_new_crtc_state->stream = NULL;
- }
- dm_new_crtc_state->base.color_mgmt_changed = true;
- }
-
- for_each_new_plane_in_state(dm->cached_state, plane, new_plane_state, i) {
- dm_new_plane_state = to_dm_plane_state(new_plane_state);
- if (dm_new_plane_state->dc_state) {
- WARN_ON(kref_read(&dm_new_plane_state->dc_state->refcount) > 1);
- dc_plane_state_release(dm_new_plane_state->dc_state);
- dm_new_plane_state->dc_state = NULL;
- }
- }
-
- drm_atomic_helper_resume(ddev, dm->cached_state);
-
- dm->cached_state = NULL;
-
- /* Do mst topology probing after resuming cached state*/
- drm_connector_list_iter_begin(ddev, &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_root)
- continue;
-
- ret = drm_dp_mst_topology_mgr_resume(&aconnector->mst_mgr, true);
-
- if (ret < 0) {
- dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx,
- aconnector->dc_link);
- need_hotplug = true;
- }
- }
- drm_connector_list_iter_end(&iter);
-
- if (need_hotplug)
- drm_kms_helper_hotplug_event(ddev);
-
- amdgpu_dm_irq_resume_late(adev);
-
- amdgpu_dm_smu_write_watermarks_table(adev);
-
- return 0;
-}
-
-/**
- * DOC: DM Lifecycle
- *
- * DM (and consequently DC) is registered in the amdgpu base driver as a IP
- * block. When CONFIG_DRM_AMD_DC is enabled, the DM device IP block is added to
- * the base driver's device list to be initialized and torn down accordingly.
- *
- * The functions to do so are provided as hooks in &struct amd_ip_funcs.
- */
-
-static const struct amd_ip_funcs amdgpu_dm_funcs = {
- .name = "dm",
- .early_init = dm_early_init,
- .late_init = dm_late_init,
- .sw_init = dm_sw_init,
- .sw_fini = dm_sw_fini,
- .early_fini = amdgpu_dm_early_fini,
- .hw_init = dm_hw_init,
- .hw_fini = dm_hw_fini,
- .suspend = dm_suspend,
- .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,
- .dump_ip_state = NULL,
- .print_ip_state = NULL,
-};
-
-const struct amdgpu_ip_block_version dm_ip_block = {
- .type = AMD_IP_BLOCK_TYPE_DCE,
- .major = 1,
- .minor = 0,
- .rev = 0,
- .funcs = &amdgpu_dm_funcs,
-};
-
-
-/**
- * DOC: atomic
- *
- * *WIP*
- */
-
-static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
- .fb_create = amdgpu_display_user_framebuffer_create,
- .get_format_info = amdgpu_dm_plane_get_format_info,
- .atomic_check = amdgpu_dm_atomic_check,
- .atomic_commit = drm_atomic_helper_commit,
-};
-
-static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
- .atomic_commit_tail = amdgpu_dm_atomic_commit_tail,
- .atomic_commit_setup = drm_dp_mst_atomic_setup_commit,
-};
-
-static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
-{
- struct amdgpu_dm_backlight_caps *caps;
- struct drm_connector *conn_base;
- struct amdgpu_device *adev;
- struct drm_luminance_range_info *luminance_range;
-
- if (aconnector->bl_idx == -1 ||
- aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP)
- return;
-
- conn_base = &aconnector->base;
- adev = drm_to_adev(conn_base->dev);
-
- 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;
-
- luminance_range = &conn_base->display_info.luminance_range;
-
- if (luminance_range->max_luminance) {
- caps->aux_min_input_signal = luminance_range->min_luminance;
- caps->aux_max_input_signal = luminance_range->max_luminance;
- } else {
- caps->aux_min_input_signal = 0;
- caps->aux_max_input_signal = 512;
- }
-}
-
-void amdgpu_dm_update_connector_after_detect(
- struct amdgpu_dm_connector *aconnector)
-{
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct dc_sink *sink;
-
- /* 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
- */
- mutex_lock(&dev->mode_config.mutex);
-
- if (sink) {
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(connector, NULL);
- /*
- * 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->edid);
- } else {
- amdgpu_dm_update_freesync_caps(connector, NULL);
- if (!aconnector->dc_sink) {
- aconnector->dc_sink = aconnector->dc_em_sink;
- dc_sink_retain(aconnector->dc_sink);
- }
- }
-
- mutex_unlock(&dev->mode_config.mutex);
-
- if (sink)
- dc_sink_release(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) {
- dc_sink_release(sink);
- 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);
- if (sink)
- dc_sink_release(sink);
- return;
- }
-
- drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n",
- aconnector->connector_id, aconnector->dc_sink, sink);
-
- 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);
- dc_sink_release(aconnector->dc_sink);
- }
-
- aconnector->dc_sink = sink;
- dc_sink_retain(aconnector->dc_sink);
- if (sink->dc_edid.length == 0) {
- aconnector->edid = NULL;
- if (aconnector->dc_link->aux_mode) {
- drm_dp_cec_unset_edid(
- &aconnector->dm_dp_aux.aux);
- }
- } else {
- aconnector->edid =
- (struct edid *)sink->dc_edid.raw_edid;
-
- if (aconnector->dc_link->aux_mode)
- drm_dp_cec_set_edid(&aconnector->dm_dp_aux.aux,
- aconnector->edid);
- }
-
- if (!aconnector->timing_requested) {
- aconnector->timing_requested =
- kzalloc(sizeof(struct dc_crtc_timing), GFP_KERNEL);
- if (!aconnector->timing_requested)
- drm_err(dev,
- "failed to create aconnector->requested_timing\n");
- }
-
- drm_connector_update_edid_property(connector, aconnector->edid);
- amdgpu_dm_update_freesync_caps(connector, aconnector->edid);
- update_connector_ext_caps(aconnector);
- } else {
- drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
- amdgpu_dm_update_freesync_caps(connector, NULL);
- drm_connector_update_edid_property(connector, NULL);
- aconnector->num_modes = 0;
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
- aconnector->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;
- }
-
- mutex_unlock(&dev->mode_config.mutex);
-
- update_subconnector_property(aconnector);
-
- if (sink)
- dc_sink_release(sink);
-}
-
-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;
-
- 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.
- */
- mutex_lock(&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_ERROR("KMS: Failed to detect connector\n");
-
- 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 {
- mutex_lock(&adev->dm.dc_lock);
- dc_exit_ips_for_hw_access(dc);
- ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
- mutex_unlock(&adev->dm.dc_lock);
- if (ret) {
- 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);
- }
- }
- mutex_unlock(&aconnector->hpd_lock);
-
-}
-
-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 hpd_rx_irq_offload_work_queue *offload_wq,
- union hpd_irq_data hpd_irq_data)
-{
- struct hpd_rx_irq_offload_work *offload_work =
- kzalloc(sizeof(*offload_work), GFP_KERNEL);
-
- if (!offload_work) {
- DRM_ERROR("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;
-
- queue_work(offload_wq->wq, &offload_work->work);
- DRM_DEBUG_KMS("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(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(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(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_ERROR("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_ERROR("amdgpu: fail to register dmub hpd callback");
- return -EINVAL;
- }
-
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ,
- dmub_hpd_callback, true)) {
- DRM_ERROR("amdgpu: fail to register dmub hpd 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_ERROR("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_ERROR("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;
-}
-
-#if defined(CONFIG_DRM_AMD_DC_SI)
-/* Register IRQ sources and initialize IRQ callbacks */
-static int dce60_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 client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
-
- 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++) {
- r = amdgpu_irq_add_id(adev, client_id, i + 1, &adev->crtc_irq);
- if (r) {
- DRM_ERROR("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 + 1, 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_ERROR("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 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_ERROR("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_ERROR("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_ERROR("Failed to add hpd irq id!\n");
- return r;
- }
-
- r = register_hpd_handlers(adev);
-
- return r;
-}
-#endif
-
-/* 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 client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
-
- 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 = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0; i <= VISLANDS30_IV_SRCID_D6_VERTICAL_INTERRUPT0; i++) {
- r = amdgpu_irq_add_id(adev, client_id, i, &adev->crtc_irq);
- if (r) {
- DRM_ERROR("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_ERROR("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 VUPDATE interrupt */
- for (i = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT; i <= VISLANDS30_IV_SRCID_D6_V_UPDATE_INT; i += 2) {
- r = amdgpu_irq_add_id(adev, client_id, i, &adev->vupdate_irq);
- if (r) {
- DRM_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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_ERROR("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.
- *
- * This should only be called during atomic check.
- */
-int dm_atomic_get_state(struct drm_atomic_state *state,
- struct dm_atomic_state **dm_state)
-{
- struct drm_device *dev = state->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct drm_private_state *priv_state;
-
- if (*dm_state)
- return 0;
-
- priv_state = drm_atomic_get_private_obj_state(state, &dm->atomic_obj);
- if (IS_ERR(priv_state))
- return PTR_ERR(priv_state);
-
- *dm_state = to_dm_atomic_state(priv_state);
-
- return 0;
-}
-
-static struct dm_atomic_state *
-dm_atomic_get_new_state(struct drm_atomic_state *state)
-{
- struct drm_device *dev = state->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct drm_private_obj *obj;
- struct drm_private_state *new_obj_state;
- int i;
-
- for_each_new_private_obj_in_state(state, obj, new_obj_state, i) {
- if (obj->funcs == dm->atomic_obj.funcs)
- return to_dm_atomic_state(new_obj_state);
- }
-
- return NULL;
-}
-
-static struct drm_private_state *
-dm_atomic_duplicate_state(struct drm_private_obj *obj)
-{
- struct dm_atomic_state *old_state, *new_state;
-
- new_state = kzalloc(sizeof(*new_state), GFP_KERNEL);
- if (!new_state)
- return NULL;
-
- __drm_atomic_helper_private_obj_duplicate_state(obj, &new_state->base);
-
- old_state = to_dm_atomic_state(obj->state);
-
- if (old_state && old_state->context)
- new_state->context = dc_state_create_copy(old_state->context);
-
- if (!new_state->context) {
- kfree(new_state);
- return NULL;
- }
-
- return &new_state->base;
-}
-
-static void dm_atomic_destroy_state(struct drm_private_obj *obj,
- struct drm_private_state *state)
-{
- struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
-
- if (dm_state && dm_state->context)
- dc_state_release(dm_state->context);
-
- kfree(dm_state);
-}
-
-static struct drm_private_state_funcs dm_atomic_state_funcs = {
- .atomic_duplicate_state = dm_atomic_duplicate_state,
- .atomic_destroy_state = dm_atomic_destroy_state,
-};
-
-static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
-{
- struct dm_atomic_state *state;
- int r;
-
- adev->mode_info.mode_config_initialized = true;
-
- adev_to_drm(adev)->mode_config.funcs = (void *)&amdgpu_dm_mode_funcs;
- adev_to_drm(adev)->mode_config.helper_private = &amdgpu_dm_mode_config_helperfuncs;
-
- adev_to_drm(adev)->mode_config.max_width = 16384;
- adev_to_drm(adev)->mode_config.max_height = 16384;
-
- adev_to_drm(adev)->mode_config.preferred_depth = 24;
- if (adev->asic_type == CHIP_HAWAII)
- /* disable prefer shadow for now due to hibernation issues */
- adev_to_drm(adev)->mode_config.prefer_shadow = 0;
- else
- adev_to_drm(adev)->mode_config.prefer_shadow = 1;
- /* indicates support for immediate flip */
- adev_to_drm(adev)->mode_config.async_page_flip = true;
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- state->context = dc_state_create_current_copy(adev->dm.dc);
- if (!state->context) {
- kfree(state);
- return -ENOMEM;
- }
-
- drm_atomic_private_obj_init(adev_to_drm(adev),
- &adev->dm.atomic_obj,
- &state->base,
- &dm_atomic_state_funcs);
-
- r = amdgpu_display_modeset_create_props(adev);
- if (r) {
- dc_state_release(state->context);
- kfree(state);
- return r;
- }
-
-#ifdef AMD_PRIVATE_COLOR
- if (amdgpu_dm_create_color_properties(adev)) {
- dc_state_release(state->context);
- kfree(state);
- return -ENOMEM;
- }
-#endif
-
- r = amdgpu_dm_audio_init(adev);
- if (r) {
- dc_state_release(state->context);
- kfree(state);
- return r;
- }
-
- return 0;
-}
-
-#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
-#define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
-#define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
-
-static void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
- int bl_idx)
-{
-#if defined(CONFIG_ACPI)
- struct amdgpu_dm_backlight_caps caps;
-
- memset(&caps, 0, sizeof(caps));
-
- if (dm->backlight_caps[bl_idx].caps_valid)
- return;
-
- amdgpu_acpi_get_backlight_caps(&caps);
- if (caps.caps_valid) {
- dm->backlight_caps[bl_idx].caps_valid = true;
- if (caps.aux_support)
- return;
- dm->backlight_caps[bl_idx].min_input_signal = caps.min_input_signal;
- dm->backlight_caps[bl_idx].max_input_signal = caps.max_input_signal;
- } else {
- dm->backlight_caps[bl_idx].min_input_signal =
- AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- dm->backlight_caps[bl_idx].max_input_signal =
- AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
- }
-#else
- if (dm->backlight_caps[bl_idx].aux_support)
- return;
-
- dm->backlight_caps[bl_idx].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- dm->backlight_caps[bl_idx].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
-#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;
-}
-
-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;
-
- // Rescale 0..255 to min..max
- return min + DIV_ROUND_CLOSEST((max - min) * brightness,
- AMDGPU_MAX_BL_LEVEL);
-}
-
-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..255
- return DIV_ROUND_CLOSEST(AMDGPU_MAX_BL_LEVEL * (brightness - min),
- max - min);
-}
-
-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;
- bool rc;
-
- 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];
-
- /* Change brightness based on AUX property */
- if (caps.aux_support) {
- rc = dc_link_set_backlight_level_nits(link, true, brightness,
- AUX_BL_DEFAULT_TRANSITION_TIME_MS);
- if (!rc)
- DRM_DEBUG("DM: Failed to update backlight via AUX on eDP[%d]\n", bl_idx);
- } else {
- rc = dc_link_set_backlight_level(link, brightness, 0);
- if (!rc)
- DRM_DEBUG("DM: Failed to update backlight on eDP[%d]\n", bl_idx);
- }
-
- 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;
- bool rc;
-
- rc = dc_link_get_backlight_level_nits(link, &avg, &peak);
- if (!rc)
- 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 = { 0 };
- char bl_name[16];
-
- 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;
- }
-
- amdgpu_acpi_get_backlight_caps(&caps);
- if (caps.caps_valid) {
- if (power_supply_is_system_supplied() > 0)
- props.brightness = caps.ac_level;
- else
- props.brightness = caps.dc_level;
- } else
- props.brightness = AMDGPU_MAX_BL_LEVEL;
-
- props.max_brightness = AMDGPU_MAX_BL_LEVEL;
- 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);
-
- if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) {
- DRM_ERROR("DM: Backlight registration failed!\n");
- dm->backlight_dev[aconnector->bl_idx] = NULL;
- } else
- DRM_DEBUG_DRIVER("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,
- const struct dc_plane_cap *plane_cap)
-{
- struct drm_plane *plane;
- unsigned long possible_crtcs;
- int ret = 0;
-
- plane = kzalloc(sizeof(struct drm_plane), GFP_KERNEL);
- if (!plane) {
- DRM_ERROR("KMS: Failed to allocate plane\n");
- return -ENOMEM;
- }
- plane->type = plane_type;
-
- /*
- * HACK: IGT tests expect that the primary plane for a CRTC
- * can only have one possible CRTC. Only expose support for
- * any CRTC if they're not going to be used as a primary plane
- * for a CRTC - like overlay or underlay planes.
- */
- possible_crtcs = 1 << plane_id;
- if (plane_id >= dm->dc->caps.max_streams)
- possible_crtcs = 0xff;
-
- ret = amdgpu_dm_plane_init(dm, plane, possible_crtcs, plane_cap);
-
- if (ret) {
- DRM_ERROR("KMS: Failed to initialize plane\n");
- kfree(plane);
- return ret;
- }
-
- if (mode_info)
- mode_info->planes[plane_id] = plane;
-
- return ret;
-}
-
-
-static void setup_backlight_device(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector)
-{
- 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->brightness[bl_idx] = AMDGPU_MAX_BL_LEVEL;
- dm->backlight_link[bl_idx] = link;
- dm->num_of_edps++;
-
- update_connector_ext_caps(aconnector);
-}
-
-static void amdgpu_set_panel_orientation(struct drm_connector *connector);
-
-/*
- * In this architecture, the association
- * connector -> encoder -> crtc
- * id not really requried. The crtc and connector will hold the
- * display_index as an abstraction to use with DAL component
- *
- * Returns 0 on success
- */
-static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
-{
- struct amdgpu_display_manager *dm = &adev->dm;
- s32 i;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct amdgpu_encoder *aencoder = NULL;
- struct amdgpu_mode_info *mode_info = &adev->mode_info;
- u32 link_cnt;
- s32 primary_planes;
- enum dc_connection_type new_connection_type = dc_connection_none;
- const struct dc_plane_cap *plane;
- bool psr_feature_enabled = false;
- bool replay_feature_enabled = false;
- int max_overlay = dm->dc->caps.max_slave_planes;
-
- dm->display_indexes_num = dm->dc->caps.max_streams;
- /* Update the actual used number of crtc */
- adev->mode_info.num_crtc = adev->dm.display_indexes_num;
-
- amdgpu_dm_set_irq_funcs(adev);
-
- link_cnt = dm->dc->caps.max_links;
- if (amdgpu_dm_mode_config_init(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize mode config\n");
- return -EINVAL;
- }
-
- /* There is one primary plane per CRTC */
- primary_planes = dm->dc->caps.max_streams;
- if (primary_planes > AMDGPU_MAX_PLANES) {
- DRM_ERROR("DM: Plane nums out of 6 planes\n");
- return -EINVAL;
- }
-
- /*
- * Initialize primary planes, implicit planes for legacy IOCTLS.
- * Order is reversed to match iteration order in atomic check.
- */
- for (i = (primary_planes - 1); i >= 0; i--) {
- plane = &dm->dc->caps.planes[i];
-
- if (initialize_plane(dm, mode_info, i,
- DRM_PLANE_TYPE_PRIMARY, plane)) {
- DRM_ERROR("KMS: Failed to initialize primary plane\n");
- goto fail;
- }
- }
-
- /*
- * Initialize overlay planes, index starting after primary planes.
- * These planes have a higher DRM index than the primary planes since
- * they should be considered as having a higher z-order.
- * Order is reversed to match iteration order in atomic check.
- *
- * Only support DCN for now, and only expose one so we don't encourage
- * userspace to use up all the pipes.
- */
- for (i = 0; i < dm->dc->caps.max_planes; ++i) {
- struct dc_plane_cap *plane = &dm->dc->caps.planes[i];
-
- /* Do not create overlay if MPO disabled */
- if (amdgpu_dc_debug_mask & DC_DISABLE_MPO)
- break;
-
- if (plane->type != DC_PLANE_TYPE_DCN_UNIVERSAL)
- continue;
-
- if (!plane->pixel_format_support.argb8888)
- continue;
-
- if (max_overlay-- == 0)
- break;
-
- if (initialize_plane(dm, NULL, primary_planes + i,
- DRM_PLANE_TYPE_OVERLAY, plane)) {
- DRM_ERROR("KMS: Failed to initialize overlay plane\n");
- goto fail;
- }
- }
-
- for (i = 0; i < dm->dc->caps.max_streams; i++)
- if (amdgpu_dm_crtc_init(dm, mode_info->planes[i], i)) {
- DRM_ERROR("KMS: Failed to initialize crtc\n");
- goto fail;
- }
-
- /* Use Outbox interrupt */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 0, 0):
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(2, 1, 0):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- if (register_outbox_irq_handlers(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize IRQ\n");
- goto fail;
- }
- break;
- default:
- DRM_DEBUG_KMS("Unsupported DCN IP version for outbox: 0x%X\n",
- amdgpu_ip_version(adev, DCE_HWIP, 0));
- }
-
- /* Determine whether to enable PSR support by default. */
- if (!(amdgpu_dc_debug_mask & DC_DISABLE_PSR)) {
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- psr_feature_enabled = true;
- break;
- default:
- psr_feature_enabled = amdgpu_dc_feature_mask & DC_PSR_MASK;
- break;
- }
- }
-
- /* Determine whether to enable Replay support by default. */
- if (!(amdgpu_dc_debug_mask & DC_DISABLE_REPLAY)) {
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- replay_feature_enabled = true;
- break;
-
- default:
- replay_feature_enabled = amdgpu_dc_feature_mask & DC_REPLAY_MASK;
- break;
- }
- }
-
- if (link_cnt > MAX_LINKS) {
- DRM_ERROR(
- "KMS: Cannot support more than %d display indexes\n",
- MAX_LINKS);
- goto fail;
- }
-
- /* loops over all connectors on the board */
- for (i = 0; i < link_cnt; i++) {
- struct dc_link *link = NULL;
-
- link = dc_get_link_at_index(dm->dc, i);
-
- if (link->connector_signal == SIGNAL_TYPE_VIRTUAL) {
- struct amdgpu_dm_wb_connector *wbcon = kzalloc(sizeof(*wbcon), GFP_KERNEL);
-
- if (!wbcon) {
- DRM_ERROR("KMS: Failed to allocate writeback connector\n");
- continue;
- }
-
- if (amdgpu_dm_wb_connector_init(dm, wbcon, i)) {
- DRM_ERROR("KMS: Failed to initialize writeback connector\n");
- kfree(wbcon);
- continue;
- }
-
- link->psr_settings.psr_feature_enabled = false;
- link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
-
- continue;
- }
-
- aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL);
- if (!aconnector)
- goto fail;
-
- aencoder = kzalloc(sizeof(*aencoder), GFP_KERNEL);
- if (!aencoder)
- goto fail;
-
- if (amdgpu_dm_encoder_init(dm->ddev, aencoder, i)) {
- DRM_ERROR("KMS: Failed to initialize encoder\n");
- goto fail;
- }
-
- if (amdgpu_dm_connector_init(dm, aconnector, i, aencoder)) {
- DRM_ERROR("KMS: Failed to initialize connector\n");
- goto fail;
- }
-
- if (dm->hpd_rx_offload_wq)
- dm->hpd_rx_offload_wq[aconnector->base.index].aconnector =
- aconnector;
-
- if (!dc_link_detect_connection_type(link, &new_connection_type))
- DRM_ERROR("KMS: Failed to detect connector\n");
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(link);
- amdgpu_dm_update_connector_after_detect(aconnector);
- } else {
- bool ret = false;
-
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- ret = dc_link_detect(link, DETECT_REASON_BOOT);
- mutex_unlock(&dm->dc_lock);
-
- if (ret) {
- amdgpu_dm_update_connector_after_detect(aconnector);
- setup_backlight_device(dm, aconnector);
-
- /* Disable PSR if Replay can be enabled */
- if (replay_feature_enabled)
- if (amdgpu_dm_set_replay_caps(link, aconnector))
- psr_feature_enabled = false;
-
- if (psr_feature_enabled)
- amdgpu_dm_set_psr_caps(link);
- }
- }
- amdgpu_set_panel_orientation(&aconnector->base);
- }
-
- /* Software is initialized. Now we can register interrupt handlers. */
- switch (adev->asic_type) {
-#if defined(CONFIG_DRM_AMD_DC_SI)
- case CHIP_TAHITI:
- case CHIP_PITCAIRN:
- case CHIP_VERDE:
- case CHIP_OLAND:
- if (dce60_register_irq_handlers(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize IRQ\n");
- goto fail;
- }
- break;
-#endif
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- case CHIP_KAVERI:
- case CHIP_KABINI:
- case CHIP_MULLINS:
- case CHIP_TONGA:
- case CHIP_FIJI:
- case CHIP_CARRIZO:
- case CHIP_STONEY:
- case CHIP_POLARIS11:
- case CHIP_POLARIS10:
- case CHIP_POLARIS12:
- case CHIP_VEGAM:
- case CHIP_VEGA10:
- case CHIP_VEGA12:
- case CHIP_VEGA20:
- if (dce110_register_irq_handlers(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize IRQ\n");
- goto fail;
- }
- break;
- default:
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(1, 0, 0):
- case IP_VERSION(1, 0, 1):
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(2, 0, 3):
- case IP_VERSION(2, 0, 0):
- case IP_VERSION(2, 1, 0):
- case IP_VERSION(3, 0, 0):
- case IP_VERSION(3, 0, 2):
- case IP_VERSION(3, 0, 3):
- case IP_VERSION(3, 0, 1):
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- if (dcn10_register_irq_handlers(dm->adev)) {
- DRM_ERROR("DM: Failed to initialize IRQ\n");
- goto fail;
- }
- break;
- default:
- DRM_ERROR("Unsupported DCE IP versions: 0x%X\n",
- amdgpu_ip_version(adev, DCE_HWIP, 0));
- goto fail;
- }
- break;
- }
-
- return 0;
-fail:
- kfree(aencoder);
- kfree(aconnector);
-
- return -EINVAL;
-}
-
-static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm)
-{
- drm_atomic_private_obj_fini(&dm->atomic_obj);
-}
-
-/******************************************************************************
- * amdgpu_display_funcs functions
- *****************************************************************************/
-
-/*
- * dm_bandwidth_update - program display watermarks
- *
- * @adev: amdgpu_device pointer
- *
- * Calculate and program the display watermarks and line buffer allocation.
- */
-static void dm_bandwidth_update(struct amdgpu_device *adev)
-{
- /* TODO: implement later */
-}
-
-static const struct amdgpu_display_funcs dm_display_funcs = {
- .bandwidth_update = dm_bandwidth_update, /* called unconditionally */
- .vblank_get_counter = dm_vblank_get_counter,/* called unconditionally */
- .backlight_set_level = NULL, /* never called for DC */
- .backlight_get_level = NULL, /* never called for DC */
- .hpd_sense = NULL,/* called unconditionally */
- .hpd_set_polarity = NULL, /* called unconditionally */
- .hpd_get_gpio_reg = NULL, /* VBIOS parsing. DAL does it. */
- .page_flip_get_scanoutpos =
- dm_crtc_get_scanoutpos,/* called unconditionally */
- .add_encoder = NULL, /* VBIOS parsing. DAL does it. */
- .add_connector = NULL, /* VBIOS parsing. DAL does it. */
-};
-
-#if defined(CONFIG_DEBUG_KERNEL_DC)
-
-static ssize_t s3_debug_store(struct device *device,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- int ret;
- int s3_state;
- struct drm_device *drm_dev = dev_get_drvdata(device);
- struct amdgpu_device *adev = drm_to_adev(drm_dev);
-
- ret = kstrtoint(buf, 0, &s3_state);
-
- if (ret == 0) {
- if (s3_state) {
- dm_resume(adev);
- drm_kms_helper_hotplug_event(adev_to_drm(adev));
- } else
- dm_suspend(adev);
- }
-
- return ret == 0 ? count : 0;
-}
-
-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(4, 0, 1):
- fw_name_dmub = FIRMWARE_DCN_401_DMUB;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
- r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, "%s", fw_name_dmub);
- return r;
-}
-
-static int dm_early_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_mode_info *mode_info = &adev->mode_info;
- struct atom_context *ctx = mode_info->atom_context;
- int index = GetIndexIntoMasterTable(DATA, Object_Header);
- u16 data_offset;
-
- /* if there is no object header, skip DM */
- if (!amdgpu_atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) {
- adev->harvest_ip_mask |= AMD_HARVEST_IP_DMU_MASK;
- dev_info(adev->dev, "No object header, skipping DM\n");
- return -ENOENT;
- }
-
- switch (adev->asic_type) {
-#if defined(CONFIG_DRM_AMD_DC_SI)
- case CHIP_TAHITI:
- case CHIP_PITCAIRN:
- case CHIP_VERDE:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case CHIP_OLAND:
- adev->mode_info.num_crtc = 2;
- adev->mode_info.num_hpd = 2;
- adev->mode_info.num_dig = 2;
- break;
-#endif
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case CHIP_KAVERI:
- adev->mode_info.num_crtc = 4;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 7;
- break;
- case CHIP_KABINI:
- case CHIP_MULLINS:
- adev->mode_info.num_crtc = 2;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case CHIP_FIJI:
- case CHIP_TONGA:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 7;
- break;
- case CHIP_CARRIZO:
- adev->mode_info.num_crtc = 3;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 9;
- break;
- case CHIP_STONEY:
- adev->mode_info.num_crtc = 2;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 9;
- break;
- case CHIP_POLARIS11:
- case CHIP_POLARIS12:
- adev->mode_info.num_crtc = 5;
- adev->mode_info.num_hpd = 5;
- adev->mode_info.num_dig = 5;
- break;
- case CHIP_POLARIS10:
- case CHIP_VEGAM:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case CHIP_VEGA10:
- case CHIP_VEGA12:
- case CHIP_VEGA20:
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- default:
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(3, 0, 0):
- adev->mode_info.num_crtc = 6;
- adev->mode_info.num_hpd = 6;
- adev->mode_info.num_dig = 6;
- break;
- case IP_VERSION(2, 0, 0):
- case IP_VERSION(3, 0, 2):
- adev->mode_info.num_crtc = 5;
- adev->mode_info.num_hpd = 5;
- adev->mode_info.num_dig = 5;
- break;
- case IP_VERSION(2, 0, 3):
- case IP_VERSION(3, 0, 3):
- adev->mode_info.num_crtc = 2;
- adev->mode_info.num_hpd = 2;
- adev->mode_info.num_dig = 2;
- break;
- case IP_VERSION(1, 0, 0):
- case IP_VERSION(1, 0, 1):
- case IP_VERSION(3, 0, 1):
- case IP_VERSION(2, 1, 0):
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- case IP_VERSION(3, 1, 4):
- case IP_VERSION(3, 1, 5):
- case IP_VERSION(3, 1, 6):
- case IP_VERSION(3, 2, 0):
- case IP_VERSION(3, 2, 1):
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(4, 0, 1):
- adev->mode_info.num_crtc = 4;
- adev->mode_info.num_hpd = 4;
- adev->mode_info.num_dig = 4;
- break;
- default:
- DRM_ERROR("Unsupported DCE IP versions: 0x%x\n",
- amdgpu_ip_version(adev, DCE_HWIP, 0));
- return -EINVAL;
- }
- break;
- }
-
- if (adev->mode_info.funcs == NULL)
- adev->mode_info.funcs = &dm_display_funcs;
-
- /*
- * Note: Do NOT change adev->audio_endpt_rreg and
- * adev->audio_endpt_wreg because they are initialised in
- * amdgpu_device_init()
- */
-#if defined(CONFIG_DEBUG_KERNEL_DC)
- device_create_file(
- adev_to_drm(adev)->dev,
- &dev_attr_s3_debug);
-#endif
- adev->dc_enabled = true;
-
- return dm_init_microcode(adev);
-}
-
-static bool modereset_required(struct drm_crtc_state *crtc_state)
-{
- return !crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
-}
-
-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
-fill_plane_color_attributes(const struct drm_plane_state *plane_state,
- const enum surface_pixel_format format,
- enum dc_color_space *color_space)
-{
- bool full_range;
-
- *color_space = COLOR_SPACE_SRGB;
-
- /* DRM color properties only affect non-RGB formats. */
- if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN)
- return 0;
-
- full_range = (plane_state->color_range == DRM_COLOR_YCBCR_FULL_RANGE);
-
- switch (plane_state->color_encoding) {
- case DRM_COLOR_YCBCR_BT601:
- if (full_range)
- *color_space = COLOR_SPACE_YCBCR601;
- else
- *color_space = COLOR_SPACE_YCBCR601_LIMITED;
- break;
-
- case DRM_COLOR_YCBCR_BT709:
- if (full_range)
- *color_space = COLOR_SPACE_YCBCR709;
- else
- *color_space = COLOR_SPACE_YCBCR709_LIMITED;
- break;
-
- case DRM_COLOR_YCBCR_BT2020:
- if (full_range)
- *color_space = COLOR_SPACE_2020_YCBCR;
- else
- return -EINVAL;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int
-fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
- const struct drm_plane_state *plane_state,
- const u64 tiling_flags,
- struct dc_plane_info *plane_info,
- struct dc_plane_address *address,
- bool tmz_surface,
- bool force_disable_dcc)
-{
- const struct drm_framebuffer *fb = plane_state->fb;
- const struct amdgpu_framebuffer *afb =
- to_amdgpu_framebuffer(plane_state->fb);
- int ret;
-
- memset(plane_info, 0, sizeof(*plane_info));
-
- switch (fb->format->format) {
- case DRM_FORMAT_C8:
- plane_info->format =
- SURFACE_PIXEL_FORMAT_GRPH_PALETA_256_COLORS;
- break;
- case DRM_FORMAT_RGB565:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_RGB565;
- break;
- case DRM_FORMAT_XRGB8888:
- case DRM_FORMAT_ARGB8888:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888;
- break;
- case DRM_FORMAT_XRGB2101010:
- case DRM_FORMAT_ARGB2101010:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB2101010;
- break;
- case DRM_FORMAT_XBGR2101010:
- case DRM_FORMAT_ABGR2101010:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR2101010;
- break;
- case DRM_FORMAT_XBGR8888:
- case DRM_FORMAT_ABGR8888:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR8888;
- break;
- case DRM_FORMAT_NV21:
- plane_info->format = SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr;
- break;
- case DRM_FORMAT_NV12:
- plane_info->format = SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb;
- break;
- case DRM_FORMAT_P010:
- plane_info->format = SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb;
- break;
- case DRM_FORMAT_XRGB16161616F:
- case DRM_FORMAT_ARGB16161616F:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616F;
- break;
- case DRM_FORMAT_XBGR16161616F:
- case DRM_FORMAT_ABGR16161616F:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616F;
- break;
- case DRM_FORMAT_XRGB16161616:
- case DRM_FORMAT_ARGB16161616:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616;
- break;
- case DRM_FORMAT_XBGR16161616:
- case DRM_FORMAT_ABGR16161616:
- plane_info->format = SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616;
- break;
- default:
- DRM_ERROR(
- "Unsupported screen format %p4cc\n",
- &fb->format->format);
- return -EINVAL;
- }
-
- switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) {
- case DRM_MODE_ROTATE_0:
- plane_info->rotation = ROTATION_ANGLE_0;
- break;
- case DRM_MODE_ROTATE_90:
- plane_info->rotation = ROTATION_ANGLE_90;
- break;
- case DRM_MODE_ROTATE_180:
- plane_info->rotation = ROTATION_ANGLE_180;
- break;
- case DRM_MODE_ROTATE_270:
- plane_info->rotation = ROTATION_ANGLE_270;
- break;
- default:
- plane_info->rotation = ROTATION_ANGLE_0;
- break;
- }
-
-
- plane_info->visible = true;
- plane_info->stereo_format = PLANE_STEREO_FORMAT_NONE;
-
- plane_info->layer_index = plane_state->normalized_zpos;
-
- ret = fill_plane_color_attributes(plane_state, plane_info->format,
- &plane_info->color_space);
- if (ret)
- return ret;
-
- ret = amdgpu_dm_plane_fill_plane_buffer_attributes(adev, afb, plane_info->format,
- plane_info->rotation, tiling_flags,
- &plane_info->tiling_info,
- &plane_info->plane_size,
- &plane_info->dcc, address,
- tmz_surface, force_disable_dcc);
- if (ret)
- return ret;
-
- amdgpu_dm_plane_fill_blending_from_plane_state(
- plane_state, &plane_info->per_pixel_alpha, &plane_info->pre_multiplied_alpha,
- &plane_info->global_alpha, &plane_info->global_alpha_value);
-
- return 0;
-}
-
-static int fill_dc_plane_attributes(struct amdgpu_device *adev,
- struct dc_plane_state *dc_plane_state,
- struct drm_plane_state *plane_state,
- struct drm_crtc_state *crtc_state)
-{
- struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state);
- struct amdgpu_framebuffer *afb = (struct amdgpu_framebuffer *)plane_state->fb;
- struct dc_scaling_info scaling_info;
- struct dc_plane_info plane_info;
- int ret;
- bool force_disable_dcc = false;
-
- ret = amdgpu_dm_plane_fill_dc_scaling_info(adev, plane_state, &scaling_info);
- if (ret)
- return ret;
-
- dc_plane_state->src_rect = scaling_info.src_rect;
- dc_plane_state->dst_rect = scaling_info.dst_rect;
- dc_plane_state->clip_rect = scaling_info.clip_rect;
- dc_plane_state->scaling_quality = scaling_info.scaling_quality;
-
- force_disable_dcc = adev->asic_type == CHIP_RAVEN && adev->in_suspend;
- ret = fill_dc_plane_info_and_addr(adev, plane_state,
- afb->tiling_flags,
- &plane_info,
- &dc_plane_state->address,
- afb->tmz_surface,
- force_disable_dcc);
- if (ret)
- return ret;
-
- dc_plane_state->format = plane_info.format;
- dc_plane_state->color_space = plane_info.color_space;
- dc_plane_state->format = plane_info.format;
- dc_plane_state->plane_size = plane_info.plane_size;
- dc_plane_state->rotation = plane_info.rotation;
- dc_plane_state->horizontal_mirror = plane_info.horizontal_mirror;
- dc_plane_state->stereo_format = plane_info.stereo_format;
- dc_plane_state->tiling_info = plane_info.tiling_info;
- dc_plane_state->visible = plane_info.visible;
- dc_plane_state->per_pixel_alpha = plane_info.per_pixel_alpha;
- dc_plane_state->pre_multiplied_alpha = plane_info.pre_multiplied_alpha;
- dc_plane_state->global_alpha = plane_info.global_alpha;
- dc_plane_state->global_alpha_value = plane_info.global_alpha_value;
- dc_plane_state->dcc = plane_info.dcc;
- dc_plane_state->layer_index = plane_info.layer_index;
- dc_plane_state->flip_int_enabled = true;
-
- /*
- * Always set input transfer function, since plane state is refreshed
- * every time.
- */
- ret = amdgpu_dm_update_plane_color_mgmt(dm_crtc_state,
- plane_state,
- dc_plane_state);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static inline void fill_dc_dirty_rect(struct drm_plane *plane,
- struct rect *dirty_rect, int32_t x,
- s32 y, s32 width, s32 height,
- int *i, bool ffu)
-{
- WARN_ON(*i >= DC_MAX_DIRTY_RECTS);
-
- dirty_rect->x = x;
- dirty_rect->y = y;
- dirty_rect->width = width;
- dirty_rect->height = height;
-
- if (ffu)
- drm_dbg(plane->dev,
- "[PLANE:%d] PSR FFU dirty rect size (%d, %d)\n",
- plane->base.id, width, height);
- else
- drm_dbg(plane->dev,
- "[PLANE:%d] PSR SU dirty rect at (%d, %d) size (%d, %d)",
- plane->base.id, x, y, width, height);
-
- (*i)++;
-}
-
-/**
- * fill_dc_dirty_rects() - Fill DC dirty regions for PSR selective updates
- *
- * @plane: DRM plane containing dirty regions that need to be flushed to the eDP
- * remote fb
- * @old_plane_state: Old state of @plane
- * @new_plane_state: New state of @plane
- * @crtc_state: New state of CRTC connected to the @plane
- * @flip_addrs: DC flip tracking struct, which also tracts dirty rects
- * @is_psr_su: Flag indicating whether Panel Self Refresh Selective Update (PSR SU) is enabled.
- * If PSR SU is enabled and damage clips are available, only the regions of the screen
- * that have changed will be updated. If PSR SU is not enabled,
- * or if damage clips are not available, the entire screen will be updated.
- * @dirty_regions_changed: dirty regions changed
- *
- * For PSR SU, DC informs the DMUB uController of dirty rectangle regions
- * (referred to as "damage clips" in DRM nomenclature) that require updating on
- * the eDP remote buffer. The responsibility of specifying the dirty regions is
- * amdgpu_dm's.
- *
- * A damage-aware DRM client should fill the FB_DAMAGE_CLIPS property on the
- * plane with regions that require flushing to the eDP remote buffer. In
- * addition, certain use cases - such as cursor and multi-plane overlay (MPO) -
- * implicitly provide damage clips without any client support via the plane
- * bounds.
- */
-static void fill_dc_dirty_rects(struct drm_plane *plane,
- struct drm_plane_state *old_plane_state,
- struct drm_plane_state *new_plane_state,
- struct drm_crtc_state *crtc_state,
- struct dc_flip_addrs *flip_addrs,
- bool is_psr_su,
- bool *dirty_regions_changed)
-{
- 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;
- bool bb_changed;
- bool fb_changed;
- u32 i = 0;
- *dirty_regions_changed = false;
-
- /*
- * Cursor plane has it's own dirty rect update interface. See
- * dcn10_dmub_update_cursor_data and dmub_cmd_update_cursor_info_data
- */
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- return;
-
- 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 (num_clips && (!amdgpu_damage_clips || (amdgpu_damage_clips < 0 &&
- is_psr_su)))
- goto ffu;
-
- if (!dm_crtc_state->mpo_requested) {
- if (!num_clips || num_clips > DC_MAX_DIRTY_RECTS)
- goto ffu;
-
- for (; flip_addrs->dirty_rect_count < num_clips; clips++)
- fill_dc_dirty_rect(new_plane_state->plane,
- &dirty_rects[flip_addrs->dirty_rect_count],
- clips->x1, clips->y1,
- clips->x2 - clips->x1, clips->y2 - clips->y1,
- &flip_addrs->dirty_rect_count,
- false);
- return;
- }
-
- /*
- * MPO is requested. Add entire plane bounding box to dirty rects if
- * flipped to or damaged.
- *
- * If plane is moved or resized, also add old bounding box to dirty
- * rects.
- */
- fb_changed = old_plane_state->fb->base.id !=
- new_plane_state->fb->base.id;
- bb_changed = (old_plane_state->crtc_x != new_plane_state->crtc_x ||
- old_plane_state->crtc_y != new_plane_state->crtc_y ||
- old_plane_state->crtc_w != new_plane_state->crtc_w ||
- old_plane_state->crtc_h != new_plane_state->crtc_h);
-
- drm_dbg(plane->dev,
- "[PLANE:%d] PSR bb_changed:%d fb_changed:%d num_clips:%d\n",
- new_plane_state->plane->base.id,
- bb_changed, fb_changed, num_clips);
-
- *dirty_regions_changed = bb_changed;
-
- if ((num_clips + (bb_changed ? 2 : 0)) > DC_MAX_DIRTY_RECTS)
- goto ffu;
-
- if (bb_changed) {
- fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[i],
- new_plane_state->crtc_x,
- new_plane_state->crtc_y,
- new_plane_state->crtc_w,
- new_plane_state->crtc_h, &i, false);
-
- /* Add old plane bounding-box if plane is moved or resized */
- fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[i],
- old_plane_state->crtc_x,
- old_plane_state->crtc_y,
- old_plane_state->crtc_w,
- old_plane_state->crtc_h, &i, false);
- }
-
- if (num_clips) {
- for (; i < num_clips; clips++)
- fill_dc_dirty_rect(new_plane_state->plane,
- &dirty_rects[i], clips->x1,
- clips->y1, clips->x2 - clips->x1,
- clips->y2 - clips->y1, &i, false);
- } else if (fb_changed && !bb_changed) {
- fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[i],
- new_plane_state->crtc_x,
- new_plane_state->crtc_y,
- new_plane_state->crtc_w,
- new_plane_state->crtc_h, &i, false);
- }
-
- flip_addrs->dirty_rect_count = i;
- return;
-
-ffu:
- fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[0], 0, 0,
- dm_crtc_state->base.mode.crtc_hdisplay,
- dm_crtc_state->base.mode.crtc_vdisplay,
- &flip_addrs->dirty_rect_count, true);
-}
-
-static void update_stream_scaling_settings(const struct drm_display_mode *mode,
- const struct dm_connector_state *dm_state,
- struct dc_stream_state *stream)
-{
- enum amdgpu_rmx_type rmx_type;
-
- struct rect src = { 0 }; /* viewport in composition space*/
- struct rect dst = { 0 }; /* stream addressable area */
-
- /* no mode. nothing to be done */
- if (!mode)
- return;
-
- /* Full screen scaling by default */
- src.width = mode->hdisplay;
- src.height = mode->vdisplay;
- dst.width = stream->timing.h_addressable;
- dst.height = stream->timing.v_addressable;
-
- if (dm_state) {
- rmx_type = dm_state->scaling;
- if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) {
- if (src.width * dst.height <
- src.height * dst.width) {
- /* height needs less upscaling/more downscaling */
- dst.width = src.width *
- dst.height / src.height;
- } else {
- /* width needs less upscaling/more downscaling */
- dst.height = src.height *
- dst.width / src.width;
- }
- } else if (rmx_type == RMX_CENTER) {
- dst = src;
- }
-
- dst.x = (stream->timing.h_addressable - dst.width) / 2;
- dst.y = (stream->timing.v_addressable - dst.height) / 2;
-
- if (dm_state->underscan_enable) {
- dst.x += dm_state->underscan_hborder / 2;
- dst.y += dm_state->underscan_vborder / 2;
- dst.width -= dm_state->underscan_hborder;
- dst.height -= dm_state->underscan_vborder;
- }
- }
-
- stream->src = src;
- stream->dst = dst;
-
- DRM_DEBUG_KMS("Destination Rectangle x:%d y:%d width:%d height:%d\n",
- dst.x, dst.y, dst.width, dst.height);
-
-}
-
-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;
- break;
- case DRM_MODE_COLORIMETRY_DEFAULT: // ITU601
- default:
- if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
- color_space = COLOR_SPACE_SRGB;
- /*
- * 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)
-{
- 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;
-
- 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;
- /* TODO: un-hardcode */
- if (drm_mode_is_420_only(info, mode_in)
- && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if (drm_mode_is_420_also(info, mode_in)
- && aconnector
- && aconnector->force_yuv420_output)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCBCR444)
- && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
- 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) {
- drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, (struct drm_connector *)connector, mode_in);
- timing_out->vic = avi_frame.video_code;
- drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame, (struct drm_connector *)connector, mode_in);
- 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) {
- copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode);
- } else if (native_mode->clock == drm_mode->clock &&
- native_mode->htotal == drm_mode->htotal &&
- native_mode->vtotal == drm_mode->vtotal) {
- 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 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_ERROR("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_DEBUG_DRIVER("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 ||
- sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER)
- 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);
- }
-}
-
-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;
- 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_DEBUG_DRIVER("%s: [%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));
- 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_DEBUG_DRIVER("%s: [%s] DSC is selected from DP-HDMI PCON\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 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;
-#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 == NULL) {
- DRM_ERROR("connector is NULL!\n");
- return stream;
- }
-
- 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(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_ERROR("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_DEBUG_DRIVER("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(&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)
- mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket);
-
- if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
- stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ||
- stream->signal == SIGNAL_TYPE_EDP) {
- //
- // 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;
-
- 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->psr_skip_count = AMDGPU_DM_PSR_ENTRY_DELAY;
-
- }
-finish:
- dc_sink_release(sink);
-
- return stream;
-}
-
-static enum drm_connector_status
-amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
-{
- bool connected;
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- /*
- * 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.
- */
-
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED &&
- !aconnector->fake_enable)
- connected = (aconnector->dc_sink != NULL);
- else
- connected = (aconnector->base.force == DRM_FORCE_ON ||
- aconnector->base.force == DRM_FORCE_ON_DIGITAL);
-
- update_subconnector_property(aconnector);
-
- return (connected ? 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;
- }
-
- 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;
- }
-
- 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);
- to_dm_connector_state(connector->state)->abm_level = val ?:
- ABM_LEVEL_IMMEDIATE_DISABLE;
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- 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);
-
- 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);
-
- 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);
- if (aconnector->i2c) {
- i2c_del_adapter(&aconnector->i2c->base);
- kfree(aconnector->i2c);
- }
- 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(sizeof(*state), GFP_KERNEL);
-
- 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;
- 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;
-
- /*
- * Note: drm_get_edid gets edid in the following order:
- * 1) override EDID if set via edid_override debugfs,
- * 2) firmware EDID if set via edid_firmware module parameter
- * 3) regular DDC read.
- */
- edid = drm_get_edid(connector, ddc);
- if (!edid) {
- DRM_ERROR("No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- aconnector->edid = edid;
-
- /* Update emulated (virtual) sink's EDID */
- if (dc_em_sink && dc_link) {
- 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
- };
- struct edid *edid;
- struct i2c_adapter *ddc;
-
- if (dc_link->aux_mode)
- ddc = &aconnector->dm_dp_aux.aux.ddc;
- else
- ddc = &aconnector->i2c->base;
-
- /*
- * Note: drm_get_edid gets edid in the following order:
- * 1) override EDID if set via edid_override debugfs,
- * 2) firmware EDID if set via edid_firmware module parameter
- * 3) regular DDC read.
- */
- edid = drm_get_edid(connector, ddc);
- if (!edid) {
- DRM_ERROR("No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- if (drm_detect_hdmi_monitor(edid))
- init_params.sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
-
- aconnector->edid = 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, true);
-
-cleanup:
- if (dc_state)
- dc_state_release(dc_state);
-
- if (dc_plane_state)
- dc_plane_state_release(dc_plane_state);
-
- return dc_result;
-}
-
-struct dc_stream_state *
-create_validate_stream_for_sink(struct amdgpu_dm_connector *aconnector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream)
-{
- struct drm_connector *connector = &aconnector->base;
- 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;
-
- if (!dm_state)
- return NULL;
-
- do {
- stream = create_stream_for_sink(connector, drm_mode,
- dm_state, old_stream,
- requested_bpc);
- if (stream == NULL) {
- DRM_ERROR("Failed to create stream for sink!\n");
- break;
- }
-
- if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return stream;
-
- dc_result = dc_validate_stream(adev->dm.dc, 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) {
- DRM_DEBUG_KMS("Mode %dx%d (clk %d) failed DC validation with error %d (%s)\n",
- drm_mode->hdisplay,
- drm_mode->vdisplay,
- drm_mode->clock,
- dc_result,
- 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 >= 6);
-
- if (dc_result == DC_FAIL_ENC_VALIDATE && !aconnector->force_yuv420_output) {
- DRM_DEBUG_KMS("Retry forcing YCbCr420 encoding\n");
-
- aconnector->force_yuv420_output = true;
- stream = create_validate_stream_for_sink(aconnector, drm_mode,
- dm_state, old_stream);
- aconnector->force_yuv420_output = false;
- }
-
- return stream;
-}
-
-enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- int result = MODE_ERROR;
- struct dc_sink *dc_sink;
- /* TODO: Unhardcode stream count */
- struct dc_stream_state *stream;
- 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_ERROR("dc_sink is NULL!\n");
- goto fail;
- }
-
- drm_mode_set_crtcinfo(mode, 0);
-
- stream = create_validate_stream_for_sink(aconnector, mode,
- to_dm_connector_state(connector->state),
- NULL);
- 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_state *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;
-
- 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->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_state *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 (!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 = dfixed_const(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_DEBUG_ATOMIC("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_state *state,
- struct dc_state *dc_state,
- struct dsc_mst_fairness_vars *vars)
-{
- struct dc_stream_state *stream = NULL;
- struct drm_connector *connector;
- struct drm_connector_state *new_con_state;
- struct amdgpu_dm_connector *aconnector;
- struct dm_connector_state *dm_conn_state;
- int i, j, ret;
- int vcpi, pbn_div, pbn = 0, slot_num = 0;
-
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (!aconnector->mst_output_port)
- continue;
-
- if (!new_con_state || !new_con_state->crtc)
- continue;
-
- dm_conn_state = to_dm_connector_state(new_con_state);
-
- for (j = 0; j < dc_state->stream_count; j++) {
- stream = dc_state->streams[j];
- if (!stream)
- continue;
-
- if ((struct amdgpu_dm_connector *)stream->dm_stream_context == aconnector)
- break;
-
- stream = NULL;
- }
-
- if (!stream)
- continue;
-
- pbn_div = dm_mst_get_pbn_divider(stream->link);
- /* pbn is calculated by compute_mst_dsc_configs_for_state*/
- for (j = 0; j < dc_state->stream_count; j++) {
- if (vars[j].aconnector == aconnector) {
- pbn = vars[j].pbn;
- break;
- }
- }
-
- if (j == dc_state->stream_count || pbn_div == 0)
- continue;
-
- slot_num = DIV_ROUND_UP(pbn, pbn_div);
-
- if (stream->timing.flags.DSC != 1) {
- dm_conn_state->pbn = pbn;
- dm_conn_state->vcpi_slots = slot_num;
-
- ret = drm_dp_mst_atomic_enable_dsc(state, aconnector->mst_output_port,
- dm_conn_state->pbn, false);
- if (ret < 0)
- return ret;
-
- continue;
- }
-
- vcpi = drm_dp_mst_atomic_enable_dsc(state, aconnector->mst_output_port, pbn, true);
- if (vcpi < 0)
- return vcpi;
-
- dm_conn_state->pbn = pbn;
- dm_conn_state->vcpi_slots = vcpi;
- }
- return 0;
-}
-
-static int to_drm_connector_type(enum signal_type st)
-{
- 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:
- return DRM_MODE_CONNECTOR_DisplayPort;
- case SIGNAL_TYPE_DVI_DUAL_LINK:
- case SIGNAL_TYPE_DVI_SINGLE_LINK:
- 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,
- 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 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;
- struct 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}
- };
-
- 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,
- struct edid *edid)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (edid) {
- /* empty probed_modes */
- INIT_LIST_HEAD(&connector->probed_modes);
- amdgpu_dm_connector->num_modes =
- drm_add_edid_modes(connector, edid);
-
- /* 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_add_edid_modes() and need to be
- * restored here.
- */
- amdgpu_dm_update_freesync_caps(connector, edid);
- } 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,
- struct edid *edid)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (!(amdgpu_freesync_vid_mode && edid))
- 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 drm_encoder *encoder;
- struct edid *edid = amdgpu_dm_connector->edid;
- struct dc_link_settings *verified_link_cap =
- &amdgpu_dm_connector->dc_link->verified_link_cap;
- const struct dc *dc = amdgpu_dm_connector->dc_link->dc;
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
-
- if (!drm_edid_is_valid(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);
- } else {
- amdgpu_dm_connector_ddc_get_modes(connector, edid);
- if (encoder)
- amdgpu_dm_connector_add_common_modes(encoder, connector);
- amdgpu_dm_connector_add_freesync_modes(connector, 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);
-
-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);
-
- /*
- * 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;
- default:
- break;
- }
-
- drm_object_attach_property(&aconnector->base.base,
- dm->ddev->mode_config.scaling_mode_property,
- DRM_MODE_SCALE_NONE);
-
- 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);
-
- if (adev->dm.hdcp_workqueue)
- drm_connector_attach_content_protection_property(&aconnector->base, true);
- }
-}
-
-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 || !ddc_service->ddc_pin->hw_info.hw_supported)
- return result;
-
- cmd.payloads = kcalloc(num, sizeof(struct i2c_payload), GFP_KERNEL);
-
- 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 (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,
- int link_index,
- int *res)
-{
- struct amdgpu_device *adev = ddc_service->ctx->driver_context;
- struct amdgpu_i2c_adapter *i2c;
-
- i2c = kzalloc(sizeof(struct amdgpu_i2c_adapter), GFP_KERNEL);
- if (!i2c)
- return NULL;
- i2c->base.owner = THIS_MODULE;
- i2c->base.dev.parent = &adev->pdev->dev;
- i2c->base.algo = &amdgpu_dm_i2c_algo;
- snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d", link_index);
- i2c_set_adapdata(&i2c->base, i2c);
- i2c->ddc_service = ddc_service;
-
- return i2c;
-}
-
-
-/*
- * 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, link->link_index, &res);
- if (!i2c) {
- DRM_ERROR("Failed to create i2c adapter data\n");
- return -ENOMEM;
- }
-
- aconnector->i2c = i2c;
- res = i2c_add_adapter(&i2c->base);
-
- if (res) {
- DRM_ERROR("Failed to register hw i2c %d\n", link->link_index);
- goto out_free;
- }
-
- connector_type = to_drm_connector_type(link->connector_signal);
-
- res = drm_connector_init_with_ddc(
- dm->ddev,
- &aconnector->base,
- &amdgpu_dm_connector_funcs,
- connector_type,
- &i2c->base);
-
- if (res) {
- DRM_ERROR("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_DisplayPort
- || connector_type == DRM_MODE_CONNECTOR_eDP)
- 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)
-{
-<<<<<<<
- /*
- * We have no guarantee that the frontend index maps to the same
- * backend index - some even map to more than one.
- *
- * TODO: Use a different interrupt or check DC itself for the mapping.
- */
- int irq_type =
- amdgpu_display_crtc_idx_to_irq_type(
- adev,
- acrtc->crtc_id);
- struct drm_vblank_crtc_config config = {0};
- struct dc_crtc_timing *timing;
- int offdelay;
-
- if (acrtc_state) {
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
- IP_VERSION(3, 5, 0) ||
- acrtc_state->stream->link->psr_settings.psr_version <
- DC_PSR_VERSION_UNSUPPORTED) {
- timing = &acrtc_state->stream->timing;
-
- /* at least 2 frames */
- offdelay = DIV64_U64_ROUND_UP((u64)20 *
- timing->v_total *
- timing->h_total,
- timing->pix_clk_100hz);
-
- config.offdelay_ms = offdelay ?: 30;
- } else {
- config.disable_immediate = true;
- }
-
- drm_crtc_vblank_on_config(&acrtc->base,
- &config);
-
- amdgpu_irq_get(
- adev,
- &adev->pageflip_irq,
- irq_type);
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- amdgpu_irq_get(
- adev,
- &adev->vline0_irq,
- irq_type);
-#endif
- } else {
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- amdgpu_irq_put(
- adev,
- &adev->vline0_irq,
- irq_type);
-#endif
- amdgpu_irq_put(
- adev,
- &adev->pageflip_irq,
- irq_type);
-=======
- if (enable)
- drm_crtc_vblank_on(&acrtc->base);
- else
->>>>>>>
- drm_crtc_vblank_off(&acrtc->base);
-}
-
-static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
- struct amdgpu_crtc *acrtc)
-{
- int irq_type =
- amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
-
- /**
- * This reads the current state for the IRQ and force reapplies
- * the setting to hardware.
- */
- amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type);
-}
-
-static bool
-is_scaling_state_different(const struct dm_connector_state *dm_state,
- const struct dm_connector_state *old_dm_state)
-{
- if (dm_state->scaling != old_dm_state->scaling)
- return true;
- if (!dm_state->underscan_enable && old_dm_state->underscan_enable) {
- if (old_dm_state->underscan_hborder != 0 && old_dm_state->underscan_vborder != 0)
- return true;
- } else if (dm_state->underscan_enable && !old_dm_state->underscan_enable) {
- if (dm_state->underscan_hborder != 0 && dm_state->underscan_vborder != 0)
- return true;
- } else if (dm_state->underscan_hborder != old_dm_state->underscan_hborder ||
- dm_state->underscan_vborder != old_dm_state->underscan_vborder)
- return true;
- return false;
-}
-
-static bool is_content_protection_different(struct drm_crtc_state *new_crtc_state,
- struct drm_crtc_state *old_crtc_state,
- struct drm_connector_state *new_conn_state,
- struct drm_connector_state *old_conn_state,
- const struct drm_connector *connector,
- struct hdcp_workqueue *hdcp_w)
-{
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state);
-
- pr_debug("[HDCP_DM] connector->index: %x connect_status: %x dpms: %x\n",
- connector->index, connector->status, connector->dpms);
- pr_debug("[HDCP_DM] state protection old: %x new: %x\n",
- old_conn_state->content_protection, new_conn_state->content_protection);
-
- if (old_crtc_state)
- pr_debug("[HDCP_DM] old crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n",
- old_crtc_state->enable,
- old_crtc_state->active,
- old_crtc_state->mode_changed,
- old_crtc_state->active_changed,
- old_crtc_state->connectors_changed);
-
- if (new_crtc_state)
- pr_debug("[HDCP_DM] NEW crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n",
- new_crtc_state->enable,
- new_crtc_state->active,
- new_crtc_state->mode_changed,
- new_crtc_state->active_changed,
- new_crtc_state->connectors_changed);
-
- /* hdcp content type change */
- if (old_conn_state->hdcp_content_type != new_conn_state->hdcp_content_type &&
- new_conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- new_conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- pr_debug("[HDCP_DM] Type0/1 change %s :true\n", __func__);
- return true;
- }
-
- /* CP is being re enabled, ignore this */
- if (old_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED &&
- new_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) {
- if (new_crtc_state && new_crtc_state->mode_changed) {
- new_conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- pr_debug("[HDCP_DM] ENABLED->DESIRED & mode_changed %s :true\n", __func__);
- return true;
- }
- new_conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- pr_debug("[HDCP_DM] ENABLED -> DESIRED %s :false\n", __func__);
- return false;
- }
-
- /* S3 resume case, since old state will always be 0 (UNDESIRED) and the restored state will be ENABLED
- *
- * Handles: UNDESIRED -> ENABLED
- */
- if (old_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_UNDESIRED &&
- new_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
- new_conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
-
- /* Stream removed and re-enabled
- *
- * Can sometimes overlap with the HPD case,
- * thus set update_hdcp to false to avoid
- * setting HDCP multiple times.
- *
- * Handles: DESIRED -> DESIRED (Special case)
- */
- if (!(old_conn_state->crtc && old_conn_state->crtc->enabled) &&
- new_conn_state->crtc && new_conn_state->crtc->enabled &&
- connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) {
- dm_con_state->update_hdcp = false;
- pr_debug("[HDCP_DM] DESIRED->DESIRED (Stream removed and re-enabled) %s :true\n",
- __func__);
- return true;
- }
-
- /* Hot-plug, headless s3, dpms
- *
- * Only start HDCP if the display is connected/enabled.
- * update_hdcp flag will be set to false until the next
- * HPD comes in.
- *
- * Handles: DESIRED -> DESIRED (Special case)
- */
- if (dm_con_state->update_hdcp &&
- new_conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
- connector->dpms == DRM_MODE_DPMS_ON && aconnector->dc_sink != NULL) {
- dm_con_state->update_hdcp = false;
- pr_debug("[HDCP_DM] DESIRED->DESIRED (Hot-plug, headless s3, dpms) %s :true\n",
- __func__);
- return true;
- }
-
- if (old_conn_state->content_protection == new_conn_state->content_protection) {
- if (new_conn_state->content_protection >= DRM_MODE_CONTENT_PROTECTION_DESIRED) {
- if (new_crtc_state && new_crtc_state->mode_changed) {
- pr_debug("[HDCP_DM] DESIRED->DESIRED or ENABLE->ENABLE mode_change %s :true\n",
- __func__);
- return true;
- }
- pr_debug("[HDCP_DM] DESIRED->DESIRED & ENABLE->ENABLE %s :false\n",
- __func__);
- return false;
- }
-
- pr_debug("[HDCP_DM] UNDESIRED->UNDESIRED %s :false\n", __func__);
- return false;
- }
-
- if (new_conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_ENABLED) {
- pr_debug("[HDCP_DM] UNDESIRED->DESIRED or DESIRED->UNDESIRED or ENABLED->UNDESIRED %s :true\n",
- __func__);
- return true;
- }
-
- pr_debug("[HDCP_DM] DESIRED->ENABLED %s :false\n", __func__);
- return false;
-}
-
-static void remove_stream(struct amdgpu_device *adev,
- struct amdgpu_crtc *acrtc,
- struct dc_stream_state *stream)
-{
- /* this is the update mode case */
-
- acrtc->otg_inst = -1;
- acrtc->enabled = false;
-}
-
-static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
-{
-
- assert_spin_locked(&acrtc->base.dev->event_lock);
- WARN_ON(acrtc->event);
-
- acrtc->event = acrtc->base.state->event;
-
- /* Set the flip status */
- acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
-
- /* Mark this event as consumed */
- acrtc->base.state->event = NULL;
-
- drm_dbg_state(acrtc->base.dev,
- "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
- acrtc->crtc_id);
-}
-
-static void update_freesync_state_on_stream(
- struct amdgpu_display_manager *dm,
- struct dm_crtc_state *new_crtc_state,
- struct dc_stream_state *new_stream,
- struct dc_plane_state *surface,
- u32 flip_timestamp_in_us)
-{
- struct mod_vrr_params vrr_params;
- struct dc_info_packet vrr_infopacket = {0};
- struct amdgpu_device *adev = dm->adev;
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
- unsigned long flags;
- bool pack_sdp_v1_3 = false;
- struct amdgpu_dm_connector *aconn;
- enum vrr_packet_type packet_type = PACKET_TYPE_VRR;
-
- if (!new_stream)
- return;
-
- /*
- * TODO: Determine why min/max totals and vrefresh can be 0 here.
- * For now it's sufficient to just guard against these conditions.
- */
-
- if (!new_stream->timing.h_total || !new_stream->timing.v_total)
- return;
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- vrr_params = acrtc->dm_irq_params.vrr_params;
-
- if (surface) {
- mod_freesync_handle_preflip(
- dm->freesync_module,
- surface,
- new_stream,
- flip_timestamp_in_us,
- &vrr_params);
-
- if (adev->family < AMDGPU_FAMILY_AI &&
- amdgpu_dm_crtc_vrr_active(new_crtc_state)) {
- mod_freesync_handle_v_update(dm->freesync_module,
- new_stream, &vrr_params);
-
- /* Need to call this before the frame ends. */
- dc_stream_adjust_vmin_vmax(dm->dc,
- new_crtc_state->stream,
- &vrr_params.adjust);
- }
- }
-
- aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context;
-
- if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) {
- pack_sdp_v1_3 = aconn->pack_sdp_v1_3;
-
- if (aconn->vsdb_info.amd_vsdb_version == 1)
- packet_type = PACKET_TYPE_FS_V1;
- else if (aconn->vsdb_info.amd_vsdb_version == 2)
- packet_type = PACKET_TYPE_FS_V2;
- else if (aconn->vsdb_info.amd_vsdb_version == 3)
- packet_type = PACKET_TYPE_FS_V3;
-
- mod_build_adaptive_sync_infopacket(new_stream, aconn->as_type, NULL,
- &new_stream->adaptive_sync_infopacket);
- }
-
- mod_freesync_build_vrr_infopacket(
- dm->freesync_module,
- new_stream,
- &vrr_params,
- packet_type,
- TRANSFER_FUNC_UNKNOWN,
- &vrr_infopacket,
- pack_sdp_v1_3);
-
- new_crtc_state->freesync_vrr_info_changed |=
- (memcmp(&new_crtc_state->vrr_infopacket,
- &vrr_infopacket,
- sizeof(vrr_infopacket)) != 0);
-
- acrtc->dm_irq_params.vrr_params = vrr_params;
- new_crtc_state->vrr_infopacket = vrr_infopacket;
-
- new_stream->vrr_infopacket = vrr_infopacket;
- new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params);
-
- if (new_crtc_state->freesync_vrr_info_changed)
- DRM_DEBUG_KMS("VRR packet update: crtc=%u enabled=%d state=%d",
- new_crtc_state->base.crtc->base.id,
- (int)new_crtc_state->base.vrr_enabled,
- (int)vrr_params.state);
-
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-}
-
-static void update_stream_irq_parameters(
- struct amdgpu_display_manager *dm,
- struct dm_crtc_state *new_crtc_state)
-{
- struct dc_stream_state *new_stream = new_crtc_state->stream;
- struct mod_vrr_params vrr_params;
- struct mod_freesync_config config = new_crtc_state->freesync_config;
- struct amdgpu_device *adev = dm->adev;
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
- unsigned long flags;
-
- if (!new_stream)
- return;
-
- /*
- * TODO: Determine why min/max totals and vrefresh can be 0 here.
- * For now it's sufficient to just guard against these conditions.
- */
- if (!new_stream->timing.h_total || !new_stream->timing.v_total)
- return;
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- vrr_params = acrtc->dm_irq_params.vrr_params;
-
- if (new_crtc_state->vrr_supported &&
- config.min_refresh_in_uhz &&
- config.max_refresh_in_uhz) {
- /*
- * if freesync compatible mode was set, config.state will be set
- * in atomic check
- */
- if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz &&
- (!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) ||
- new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) {
- vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz;
- vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz;
- vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz;
- vrr_params.state = VRR_STATE_ACTIVE_FIXED;
- } else {
- config.state = new_crtc_state->base.vrr_enabled ?
- VRR_STATE_ACTIVE_VARIABLE :
- VRR_STATE_INACTIVE;
- }
- } else {
- config.state = VRR_STATE_UNSUPPORTED;
- }
-
- mod_freesync_build_vrr_params(dm->freesync_module,
- new_stream,
- &config, &vrr_params);
-
- new_crtc_state->freesync_config = config;
- /* Copy state for access from DM IRQ handler */
- acrtc->dm_irq_params.freesync_config = config;
- acrtc->dm_irq_params.active_planes = new_crtc_state->active_planes;
- acrtc->dm_irq_params.vrr_params = vrr_params;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-}
-
-static void amdgpu_dm_handle_vrr_transition(struct dm_crtc_state *old_state,
- struct dm_crtc_state *new_state)
-{
- bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
- bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
-
- if (!old_vrr_active && new_vrr_active) {
- /* Transition VRR inactive -> active:
- * While VRR is active, we must not disable vblank irq, as a
- * reenable after disable would compute bogus vblank/pflip
- * timestamps if it likely happened inside display front-porch.
- *
- * We also need vupdate irq for the actual core vblank handling
- * at end of vblank.
- */
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
- WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
- DRM_DEBUG_DRIVER("%s: crtc=%u VRR off->on: Get vblank ref\n",
- __func__, new_state->base.crtc->base.id);
- } else if (old_vrr_active && !new_vrr_active) {
- /* Transition VRR active -> inactive:
- * Allow vblank irq disable again for fixed refresh rate.
- */
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
- drm_crtc_vblank_put(new_state->base.crtc);
- DRM_DEBUG_DRIVER("%s: crtc=%u VRR on->off: Drop vblank ref\n",
- __func__, new_state->base.crtc->base.id);
- }
-}
-
-static void amdgpu_dm_commit_cursors(struct drm_atomic_state *state)
-{
- struct drm_plane *plane;
- struct drm_plane_state *old_plane_state;
- int i;
-
- /*
- * TODO: Make this per-stream so we don't issue redundant updates for
- * commits with multiple streams.
- */
- for_each_old_plane_in_state(state, plane, old_plane_state, i)
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- amdgpu_dm_plane_handle_cursor_update(plane, old_plane_state);
-}
-
-static inline uint32_t get_mem_type(struct drm_framebuffer *fb)
-{
- struct amdgpu_bo *abo = gem_to_amdgpu_bo(fb->obj[0]);
-
- return abo->tbo.resource ? abo->tbo.resource->mem_type : 0;
-}
-
-static void amdgpu_dm_update_cursor(struct drm_plane *plane,
- struct drm_plane_state *old_plane_state,
- struct dc_stream_update *update)
-{
- struct amdgpu_device *adev = drm_to_adev(plane->dev);
- struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(plane->state->fb);
- struct drm_crtc *crtc = afb ? plane->state->crtc : old_plane_state->crtc;
- struct dm_crtc_state *crtc_state = crtc ? to_dm_crtc_state(crtc->state) : NULL;
- struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
- uint64_t address = afb ? afb->address : 0;
- struct dc_cursor_position position = {0};
- struct dc_cursor_attributes attributes;
- int ret;
-
- if (!plane->state->fb && !old_plane_state->fb)
- return;
-
- drm_dbg_atomic(plane->dev, "crtc_id=%d with size %d to %d\n",
- amdgpu_crtc->crtc_id, plane->state->crtc_w,
- plane->state->crtc_h);
-
- ret = amdgpu_dm_plane_get_cursor_position(plane, crtc, &position);
- if (ret)
- return;
-
- if (!position.enable) {
- /* turn off cursor */
- if (crtc_state && crtc_state->stream) {
- dc_stream_set_cursor_position(crtc_state->stream,
- &position);
- update->cursor_position = &crtc_state->stream->cursor_position;
- }
- return;
- }
-
- amdgpu_crtc->cursor_width = plane->state->crtc_w;
- amdgpu_crtc->cursor_height = plane->state->crtc_h;
-
- memset(&attributes, 0, sizeof(attributes));
- attributes.address.high_part = upper_32_bits(address);
- attributes.address.low_part = lower_32_bits(address);
- attributes.width = plane->state->crtc_w;
- attributes.height = plane->state->crtc_h;
- attributes.color_format = CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA;
- attributes.rotation_angle = 0;
- attributes.attribute_flags.value = 0;
-
- /* Enable cursor degamma ROM on DCN3+ for implicit sRGB degamma in DRM
- * legacy gamma setup.
- */
- if (crtc_state->cm_is_degamma_srgb &&
- adev->dm.dc->caps.color.dpp.gamma_corr)
- attributes.attribute_flags.bits.ENABLE_CURSOR_DEGAMMA = 1;
-
- if (afb)
- attributes.pitch = afb->base.pitches[0] / afb->base.format->cpp[0];
-
- if (crtc_state->stream) {
- if (!dc_stream_set_cursor_attributes(crtc_state->stream,
- &attributes))
- DRM_ERROR("DC failed to set cursor attributes\n");
-
- update->cursor_attributes = &crtc_state->stream->cursor_attributes;
-
- if (!dc_stream_set_cursor_position(crtc_state->stream,
- &position))
- DRM_ERROR("DC failed to set cursor position\n");
-
- update->cursor_position = &crtc_state->stream->cursor_position;
- }
-}
-
-static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
- struct drm_device *dev,
- struct amdgpu_display_manager *dm,
- struct drm_crtc *pcrtc,
- bool wait_for_vblank)
-{
- u32 i;
- u64 timestamp_ns = ktime_get_ns();
- struct drm_plane *plane;
- struct drm_plane_state *old_plane_state, *new_plane_state;
- struct amdgpu_crtc *acrtc_attach = to_amdgpu_crtc(pcrtc);
- struct drm_crtc_state *new_pcrtc_state =
- drm_atomic_get_new_crtc_state(state, pcrtc);
- struct dm_crtc_state *acrtc_state = to_dm_crtc_state(new_pcrtc_state);
- struct dm_crtc_state *dm_old_crtc_state =
- to_dm_crtc_state(drm_atomic_get_old_crtc_state(state, pcrtc));
- int planes_count = 0, vpos, hpos;
- unsigned long flags;
- u32 target_vblank, last_flip_vblank;
- bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state);
- bool cursor_update = false;
- bool pflip_present = false;
- bool dirty_rects_changed = false;
- bool updated_planes_and_streams = false;
- struct {
- struct dc_surface_update surface_updates[MAX_SURFACES];
- struct dc_plane_info plane_infos[MAX_SURFACES];
- struct dc_scaling_info scaling_infos[MAX_SURFACES];
- struct dc_flip_addrs flip_addrs[MAX_SURFACES];
- struct dc_stream_update stream_update;
- } *bundle;
-
- bundle = kzalloc(sizeof(*bundle), GFP_KERNEL);
-
- if (!bundle) {
- drm_err(dev, "Failed to allocate update bundle\n");
- goto cleanup;
- }
-
- /*
- * Disable the cursor first if we're disabling all the planes.
- * It'll remain on the screen after the planes are re-enabled
- * if we don't.
- *
- * If the cursor is transitioning from native to overlay mode, the
- * native cursor needs to be disabled first.
- */
- if (acrtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE &&
- dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) {
- struct dc_cursor_position cursor_position = {0};
-
- if (!dc_stream_set_cursor_position(acrtc_state->stream,
- &cursor_position))
- drm_err(dev, "DC failed to disable native cursor\n");
-
- bundle->stream_update.cursor_position =
- &acrtc_state->stream->cursor_position;
- }
-
- if (acrtc_state->active_planes == 0 &&
- dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
- amdgpu_dm_commit_cursors(state);
-
- /* update planes when needed */
- for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
- struct drm_crtc *crtc = new_plane_state->crtc;
- struct drm_crtc_state *new_crtc_state;
- struct drm_framebuffer *fb = new_plane_state->fb;
- struct amdgpu_framebuffer *afb = (struct amdgpu_framebuffer *)fb;
- bool plane_needs_flip;
- struct dc_plane_state *dc_plane;
- struct dm_plane_state *dm_new_plane_state = to_dm_plane_state(new_plane_state);
-
- /* Cursor plane is handled after stream updates */
- if (plane->type == DRM_PLANE_TYPE_CURSOR &&
- acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE) {
- if ((fb && crtc == pcrtc) ||
- (old_plane_state->fb && old_plane_state->crtc == pcrtc)) {
- cursor_update = true;
- if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0)
- amdgpu_dm_update_cursor(plane, old_plane_state, &bundle->stream_update);
- }
-
- continue;
- }
-
- if (!fb || !crtc || pcrtc != crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
- if (!new_crtc_state->active)
- continue;
-
- dc_plane = dm_new_plane_state->dc_state;
- if (!dc_plane)
- continue;
-
- bundle->surface_updates[planes_count].surface = dc_plane;
- 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;
- }
-
- amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state,
- &bundle->scaling_infos[planes_count]);
-
- bundle->surface_updates[planes_count].scaling_info =
- &bundle->scaling_infos[planes_count];
-
- plane_needs_flip = old_plane_state->fb && new_plane_state->fb;
-
- pflip_present = pflip_present || plane_needs_flip;
-
- if (!plane_needs_flip) {
- planes_count += 1;
- continue;
- }
-
- fill_dc_plane_info_and_addr(
- dm->adev, new_plane_state,
- afb->tiling_flags,
- &bundle->plane_infos[planes_count],
- &bundle->flip_addrs[planes_count].address,
- afb->tmz_surface, false);
-
- drm_dbg_state(state->dev, "plane: id=%d dcc_en=%d\n",
- new_plane_state->plane->index,
- bundle->plane_infos[planes_count].dcc.enable);
-
- bundle->surface_updates[planes_count].plane_info =
- &bundle->plane_infos[planes_count];
-
- if (acrtc_state->stream->link->psr_settings.psr_feature_enabled ||
- acrtc_state->stream->link->replay_settings.replay_feature_enabled) {
- fill_dc_dirty_rects(plane, old_plane_state,
- new_plane_state, new_crtc_state,
- &bundle->flip_addrs[planes_count],
- acrtc_state->stream->link->psr_settings.psr_version ==
- DC_PSR_VERSION_SU_1,
- &dirty_rects_changed);
-
- /*
- * If the dirty regions changed, PSR-SU need to be disabled temporarily
- * and enabled it again after dirty regions are stable to avoid video glitch.
- * PSR-SU will be enabled in vblank_control_worker() if user pause the video
- * during the PSR-SU was disabled.
- */
- if (acrtc_state->stream->link->psr_settings.psr_version >= DC_PSR_VERSION_SU_1 &&
- acrtc_attach->dm_irq_params.allow_psr_entry &&
-#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
- !amdgpu_dm_crc_window_is_activated(acrtc_state->base.crtc) &&
-#endif
- dirty_rects_changed) {
- mutex_lock(&dm->dc_lock);
- acrtc_state->stream->link->psr_settings.psr_dirty_rects_change_timestamp_ns =
- timestamp_ns;
- if (acrtc_state->stream->link->psr_settings.psr_allow_active)
- amdgpu_dm_psr_disable(acrtc_state->stream);
- mutex_unlock(&dm->dc_lock);
- }
- }
-
- /*
- * Only allow immediate flips for fast updates that don't
- * change memory domain, FB pitch, DCC state, rotation or
- * mirroring.
- *
- * dm_crtc_helper_atomic_check() only accepts async flips with
- * fast updates.
- */
- if (crtc->state->async_flip &&
- (acrtc_state->update_type != UPDATE_TYPE_FAST ||
- get_mem_type(old_plane_state->fb) != get_mem_type(fb)))
- drm_warn_once(state->dev,
- "[PLANE:%d:%s] async flip with non-fast update\n",
- plane->base.id, plane->name);
-
- bundle->flip_addrs[planes_count].flip_immediate =
- crtc->state->async_flip &&
- acrtc_state->update_type == UPDATE_TYPE_FAST &&
- get_mem_type(old_plane_state->fb) == get_mem_type(fb);
-
- timestamp_ns = ktime_get_ns();
- bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000);
- bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count];
- bundle->surface_updates[planes_count].surface = dc_plane;
-
- if (!bundle->surface_updates[planes_count].surface) {
- DRM_ERROR("No surface for CRTC: id=%d\n",
- acrtc_attach->crtc_id);
- continue;
- }
-
- if (plane == pcrtc->primary)
- update_freesync_state_on_stream(
- dm,
- acrtc_state,
- acrtc_state->stream,
- dc_plane,
- bundle->flip_addrs[planes_count].flip_timestamp_in_us);
-
- drm_dbg_state(state->dev, "%s Flipping to hi: 0x%x, low: 0x%x\n",
- __func__,
- bundle->flip_addrs[planes_count].address.grph.addr.high_part,
- bundle->flip_addrs[planes_count].address.grph.addr.low_part);
-
- planes_count += 1;
-
- }
-
- if (pflip_present) {
- if (!vrr_active) {
- /* Use old throttling in non-vrr fixed refresh rate mode
- * to keep flip scheduling based on target vblank counts
- * working in a backwards compatible way, e.g., for
- * clients using the GLX_OML_sync_control extension or
- * DRI3/Present extension with defined target_msc.
- */
- last_flip_vblank = amdgpu_get_vblank_counter_kms(pcrtc);
- } else {
- /* For variable refresh rate mode only:
- * Get vblank of last completed flip to avoid > 1 vrr
- * flips per video frame by use of throttling, but allow
- * flip programming anywhere in the possibly large
- * variable vrr vblank interval for fine-grained flip
- * timing control and more opportunity to avoid stutter
- * on late submission of flips.
- */
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- last_flip_vblank = acrtc_attach->dm_irq_params.last_flip_vblank;
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
-
- target_vblank = last_flip_vblank + wait_for_vblank;
-
- /*
- * Wait until we're out of the vertical blank period before the one
- * targeted by the flip
- */
- while ((acrtc_attach->enabled &&
- (amdgpu_display_get_crtc_scanoutpos(dm->ddev, acrtc_attach->crtc_id,
- 0, &vpos, &hpos, NULL,
- NULL, &pcrtc->hwmode)
- & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
- (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
- (int)(target_vblank -
- amdgpu_get_vblank_counter_kms(pcrtc)) > 0)) {
- usleep_range(1000, 1100);
- }
-
- /**
- * Prepare the flip event for the pageflip interrupt to handle.
- *
- * This only works in the case where we've already turned on the
- * appropriate hardware blocks (eg. HUBP) so in the transition case
- * from 0 -> n planes we have to skip a hardware generated event
- * and rely on sending it from software.
- */
- if (acrtc_attach->base.state->event &&
- acrtc_state->active_planes > 0) {
- drm_crtc_vblank_get(pcrtc);
-
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
-
- WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE);
- prepare_flip_isr(acrtc_attach);
-
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
-
- if (acrtc_state->stream) {
- if (acrtc_state->freesync_vrr_info_changed)
- bundle->stream_update.vrr_infopacket =
- &acrtc_state->stream->vrr_infopacket;
- }
- } else if (cursor_update && acrtc_state->active_planes > 0) {
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- if (acrtc_attach->base.state->event) {
- drm_crtc_vblank_get(pcrtc);
- acrtc_attach->event = acrtc_attach->base.state->event;
- acrtc_attach->base.state->event = NULL;
- }
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
-
- /* Update the planes if changed or disable if we don't have any. */
- if ((planes_count || acrtc_state->active_planes == 0) &&
- acrtc_state->stream) {
- /*
- * If PSR or idle optimizations are enabled then flush out
- * any pending work before hardware programming.
- */
- if (dm->vblank_control_workqueue)
- flush_workqueue(dm->vblank_control_workqueue);
-
- bundle->stream_update.stream = acrtc_state->stream;
- if (new_pcrtc_state->mode_changed) {
- bundle->stream_update.src = acrtc_state->stream->src;
- bundle->stream_update.dst = acrtc_state->stream->dst;
- }
-
- if (new_pcrtc_state->color_mgmt_changed) {
- /*
- * TODO: This isn't fully correct since we've actually
- * already modified the stream in place.
- */
- bundle->stream_update.gamut_remap =
- &acrtc_state->stream->gamut_remap_matrix;
- bundle->stream_update.output_csc_transform =
- &acrtc_state->stream->csc_color_matrix;
- bundle->stream_update.out_transfer_func =
- &acrtc_state->stream->out_transfer_func;
- bundle->stream_update.lut3d_func =
- (struct dc_3dlut *) acrtc_state->stream->lut3d_func;
- bundle->stream_update.func_shaper =
- (struct dc_transfer_func *) acrtc_state->stream->func_shaper;
- }
-
- acrtc_state->stream->abm_level = acrtc_state->abm_level;
- if (acrtc_state->abm_level != dm_old_crtc_state->abm_level)
- bundle->stream_update.abm_level = &acrtc_state->abm_level;
-
- mutex_lock(&dm->dc_lock);
- if ((acrtc_state->update_type > UPDATE_TYPE_FAST) &&
- acrtc_state->stream->link->psr_settings.psr_allow_active)
- amdgpu_dm_psr_disable(acrtc_state->stream);
- mutex_unlock(&dm->dc_lock);
-
- /*
- * If FreeSync state on the stream has changed then we need to
- * re-adjust the min/max bounds now that DC doesn't handle this
- * as part of commit.
- */
- if (is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) {
- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
- dc_stream_adjust_vmin_vmax(
- dm->dc, acrtc_state->stream,
- &acrtc_attach->dm_irq_params.vrr_params.adjust);
- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
- }
- mutex_lock(&dm->dc_lock);
- update_planes_and_stream_adapter(dm->dc,
- acrtc_state->update_type,
- planes_count,
- acrtc_state->stream,
- &bundle->stream_update,
- bundle->surface_updates);
- updated_planes_and_streams = true;
-
- /**
- * Enable or disable the interrupts on the backend.
- *
- * Most pipes are put into power gating when unused.
- *
- * When power gating is enabled on a pipe we lose the
- * interrupt enablement state when power gating is disabled.
- *
- * So we need to update the IRQ control state in hardware
- * whenever the pipe turns on (since it could be previously
- * power gated) or off (since some pipes can't be power gated
- * on some ASICs).
- */
- if (dm_old_crtc_state->active_planes != acrtc_state->active_planes)
- dm_update_pflip_irq_state(drm_to_adev(dev),
- acrtc_attach);
-
- if (acrtc_state->update_type > UPDATE_TYPE_FAST) {
- if (acrtc_state->stream->link->replay_settings.config.replay_supported &&
- !acrtc_state->stream->link->replay_settings.replay_feature_enabled) {
- struct amdgpu_dm_connector *aconn =
- (struct amdgpu_dm_connector *)acrtc_state->stream->dm_stream_context;
- amdgpu_dm_link_setup_replay(acrtc_state->stream->link, aconn);
- } else if (acrtc_state->stream->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED &&
- !acrtc_state->stream->link->psr_settings.psr_feature_enabled) {
-
- struct amdgpu_dm_connector *aconn = (struct amdgpu_dm_connector *)
- acrtc_state->stream->dm_stream_context;
-
- if (!aconn->disallow_edp_enter_psr)
- amdgpu_dm_link_setup_psr(acrtc_state->stream);
- }
- }
-
- /* Decrement skip count when PSR is enabled and we're doing fast updates. */
- if (acrtc_state->update_type == UPDATE_TYPE_FAST &&
- acrtc_state->stream->link->psr_settings.psr_feature_enabled) {
- struct amdgpu_dm_connector *aconn =
- (struct amdgpu_dm_connector *)acrtc_state->stream->dm_stream_context;
-
- if (aconn->psr_skip_count > 0)
- aconn->psr_skip_count--;
-
- /* Allow PSR when skip count is 0. */
- acrtc_attach->dm_irq_params.allow_psr_entry = !aconn->psr_skip_count;
-
- /*
- * If sink supports PSR SU, there is no need to rely on
- * a vblank event disable request to enable PSR. PSR SU
- * can be enabled immediately once OS demonstrates an
- * adequate number of fast atomic commits to notify KMD
- * of update events. See `vblank_control_worker()`.
- */
- if (acrtc_state->stream->link->psr_settings.psr_version >= DC_PSR_VERSION_SU_1 &&
- acrtc_attach->dm_irq_params.allow_psr_entry &&
-#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
- !amdgpu_dm_crc_window_is_activated(acrtc_state->base.crtc) &&
-#endif
- !acrtc_state->stream->link->psr_settings.psr_allow_active &&
- !aconn->disallow_edp_enter_psr &&
- (timestamp_ns -
- acrtc_state->stream->link->psr_settings.psr_dirty_rects_change_timestamp_ns) >
- 500000000)
- amdgpu_dm_psr_enable(acrtc_state->stream);
- } else {
- acrtc_attach->dm_irq_params.allow_psr_entry = false;
- }
-
- mutex_unlock(&dm->dc_lock);
- }
-
- /*
- * Update cursor state *after* programming all the planes.
- * This avoids redundant programming in the case where we're going
- * to be disabling a single plane - those pipes are being disabled.
- */
- if (acrtc_state->active_planes &&
- (!updated_planes_and_streams || amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) &&
- acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
- amdgpu_dm_commit_cursors(state);
-
-cleanup:
- kfree(bundle);
-}
-
-static void amdgpu_dm_commit_audio(struct drm_device *dev,
- struct drm_atomic_state *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
- * @stream_state: the DC stream state.
- *
- * Copy the mirrored transient state flags from DRM, to DC. It is used to bring
- * a dc_stream_state's flags in sync with a drm_crtc_state's flags.
- */
-static void amdgpu_dm_crtc_copy_transient_flags(struct drm_crtc_state *crtc_state,
- struct dc_stream_state *stream_state)
-{
- stream_state->mode_changed = drm_atomic_crtc_needs_modeset(crtc_state);
-}
-
-static void dm_clear_writeback(struct amdgpu_display_manager *dm,
- struct dm_crtc_state *crtc_state)
-{
- dc_stream_remove_writeback(dm->dc, crtc_state->stream, 0);
-}
-
-static void amdgpu_dm_commit_streams(struct drm_atomic_state *state,
- struct dc_state *dc_state)
-{
- struct drm_device *dev = state->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct drm_crtc *crtc;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
- struct drm_connector_state *old_con_state;
- struct drm_connector *connector;
- bool mode_set_reset_required = false;
- u32 i;
- struct dc_commit_streams_params params = {dc_state->streams, dc_state->stream_count};
-
- /* Disable writeback */
- for_each_old_connector_in_state(state, connector, old_con_state, i) {
- struct dm_connector_state *dm_old_con_state;
- struct amdgpu_crtc *acrtc;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- old_crtc_state = NULL;
-
- dm_old_con_state = to_dm_connector_state(old_con_state);
- if (!dm_old_con_state->base.crtc)
- continue;
-
- acrtc = to_amdgpu_crtc(dm_old_con_state->base.crtc);
- if (acrtc)
- old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base);
-
- if (!acrtc || !acrtc->wb_enabled)
- continue;
-
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- dm_clear_writeback(dm, dm_old_crtc_state);
- acrtc->wb_enabled = false;
- }
-
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state,
- new_crtc_state, i) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- if (old_crtc_state->active &&
- (!new_crtc_state->active ||
- drm_atomic_crtc_needs_modeset(new_crtc_state))) {
- manage_dm_interrupts(adev, acrtc, NULL);
- dc_stream_release(dm_old_crtc_state->stream);
- }
- }
-
- drm_atomic_helper_calc_timestamping_constants(state);
-
- /* update changed items */
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- drm_dbg_state(state->dev,
- "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, planes_changed:%d, mode_changed:%d,active_changed:%d,connectors_changed:%d\n",
- acrtc->crtc_id,
- new_crtc_state->enable,
- new_crtc_state->active,
- new_crtc_state->planes_changed,
- new_crtc_state->mode_changed,
- new_crtc_state->active_changed,
- new_crtc_state->connectors_changed);
-
- /* Disable cursor if disabling crtc */
- if (old_crtc_state->active && !new_crtc_state->active) {
- struct dc_cursor_position position;
-
- memset(&position, 0, sizeof(position));
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- dc_stream_program_cursor_position(dm_old_crtc_state->stream, &position);
- mutex_unlock(&dm->dc_lock);
- }
-
- /* Copy all transient state flags into dc state */
- if (dm_new_crtc_state->stream) {
- amdgpu_dm_crtc_copy_transient_flags(&dm_new_crtc_state->base,
- dm_new_crtc_state->stream);
- }
-
- /* handles headless hotplug case, updating new_state and
- * aconnector as needed
- */
-
- if (amdgpu_dm_crtc_modeset_required(new_crtc_state, dm_new_crtc_state->stream, dm_old_crtc_state->stream)) {
-
- drm_dbg_atomic(dev,
- "Atomic commit: SET crtc id %d: [%p]\n",
- acrtc->crtc_id, acrtc);
-
- if (!dm_new_crtc_state->stream) {
- /*
- * this could happen because of issues with
- * userspace notifications delivery.
- * In this case userspace tries to set mode on
- * display which is disconnected in fact.
- * dc_sink is NULL in this case on aconnector.
- * We expect reset mode will come soon.
- *
- * This can also happen when unplug is done
- * during resume sequence ended
- *
- * In this case, we want to pretend we still
- * have a sink to keep the pipe running so that
- * hw state is consistent with the sw state
- */
- drm_dbg_atomic(dev,
- "Failed to create new stream for crtc %d\n",
- acrtc->base.base.id);
- continue;
- }
-
- if (dm_old_crtc_state->stream)
- remove_stream(adev, acrtc, dm_old_crtc_state->stream);
-
- pm_runtime_get_noresume(dev->dev);
-
- acrtc->enabled = true;
- acrtc->hw_mode = new_crtc_state->mode;
- crtc->hwmode = new_crtc_state->mode;
- mode_set_reset_required = true;
- } else if (modereset_required(new_crtc_state)) {
- drm_dbg_atomic(dev,
- "Atomic commit: RESET. crtc id %d:[%p]\n",
- acrtc->crtc_id, acrtc);
- /* i.e. reset mode */
- if (dm_old_crtc_state->stream)
- remove_stream(adev, acrtc, dm_old_crtc_state->stream);
-
- mode_set_reset_required = true;
- }
- } /* for_each_crtc_in_state() */
-
- /* if there mode set or reset, disable eDP PSR, Replay */
- if (mode_set_reset_required) {
- if (dm->vblank_control_workqueue)
- flush_workqueue(dm->vblank_control_workqueue);
-
- amdgpu_dm_replay_disable_all(dm);
- amdgpu_dm_psr_disable_all(dm);
- }
-
- dm_enable_per_frame_crtc_master_sync(dc_state);
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- WARN_ON(!dc_commit_streams(dm->dc, &params));
-
- /* Allow idle optimization when vblank count is 0 for display off */
- if (dm->active_vblank_irq_count == 0)
- dc_allow_idle_optimizations(dm->dc, true);
- mutex_unlock(&dm->dc_lock);
-
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- if (dm_new_crtc_state->stream != NULL) {
- const struct dc_stream_status *status =
- dc_stream_get_status(dm_new_crtc_state->stream);
-
- if (!status)
- status = dc_state_get_stream_status(dc_state,
- dm_new_crtc_state->stream);
- if (!status)
- drm_err(dev,
- "got no status for stream %p on acrtc%p\n",
- dm_new_crtc_state->stream, acrtc);
- else
- acrtc->otg_inst = status->primary_otg_inst;
- }
- }
-}
-
-static void dm_set_writeback(struct amdgpu_display_manager *dm,
- struct dm_crtc_state *crtc_state,
- struct drm_connector *connector,
- struct drm_connector_state *new_con_state)
-{
- struct drm_writeback_connector *wb_conn = drm_connector_to_writeback(connector);
- struct amdgpu_device *adev = dm->adev;
- struct amdgpu_crtc *acrtc;
- struct dc_writeback_info *wb_info;
- struct pipe_ctx *pipe = NULL;
- struct amdgpu_framebuffer *afb;
- int i = 0;
-
- wb_info = kzalloc(sizeof(*wb_info), GFP_KERNEL);
- if (!wb_info) {
- DRM_ERROR("Failed to allocate wb_info\n");
- return;
- }
-
- acrtc = to_amdgpu_crtc(wb_conn->encoder.crtc);
- if (!acrtc) {
- DRM_ERROR("no amdgpu_crtc found\n");
- kfree(wb_info);
- return;
- }
-
- afb = to_amdgpu_framebuffer(new_con_state->writeback_job->fb);
- if (!afb) {
- DRM_ERROR("No amdgpu_framebuffer found\n");
- kfree(wb_info);
- return;
- }
-
- for (i = 0; i < MAX_PIPES; i++) {
- if (dm->dc->current_state->res_ctx.pipe_ctx[i].stream == crtc_state->stream) {
- pipe = &dm->dc->current_state->res_ctx.pipe_ctx[i];
- break;
- }
- }
-
- /* fill in wb_info */
- wb_info->wb_enabled = true;
-
- wb_info->dwb_pipe_inst = 0;
- wb_info->dwb_params.dwbscl_black_color = 0;
- wb_info->dwb_params.hdr_mult = 0x1F000;
- wb_info->dwb_params.csc_params.gamut_adjust_type = CM_GAMUT_ADJUST_TYPE_BYPASS;
- wb_info->dwb_params.csc_params.gamut_coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S2_13;
- wb_info->dwb_params.output_depth = DWB_OUTPUT_PIXEL_DEPTH_10BPC;
- wb_info->dwb_params.cnv_params.cnv_out_bpc = DWB_CNV_OUT_BPC_10BPC;
-
- /* width & height from crtc */
- wb_info->dwb_params.cnv_params.src_width = acrtc->base.mode.crtc_hdisplay;
- wb_info->dwb_params.cnv_params.src_height = acrtc->base.mode.crtc_vdisplay;
- wb_info->dwb_params.dest_width = acrtc->base.mode.crtc_hdisplay;
- wb_info->dwb_params.dest_height = acrtc->base.mode.crtc_vdisplay;
-
- wb_info->dwb_params.cnv_params.crop_en = false;
- wb_info->dwb_params.stereo_params.stereo_enabled = false;
-
- wb_info->dwb_params.cnv_params.out_max_pix_val = 0x3ff; // 10 bits
- wb_info->dwb_params.cnv_params.out_min_pix_val = 0;
- wb_info->dwb_params.cnv_params.fc_out_format = DWB_OUT_FORMAT_32BPP_ARGB;
- wb_info->dwb_params.cnv_params.out_denorm_mode = DWB_OUT_DENORM_BYPASS;
-
- wb_info->dwb_params.out_format = dwb_scaler_mode_bypass444;
-
- wb_info->dwb_params.capture_rate = dwb_capture_rate_0;
-
- wb_info->dwb_params.scaler_taps.h_taps = 4;
- wb_info->dwb_params.scaler_taps.v_taps = 4;
- wb_info->dwb_params.scaler_taps.h_taps_c = 2;
- wb_info->dwb_params.scaler_taps.v_taps_c = 2;
- wb_info->dwb_params.subsample_position = DWB_INTERSTITIAL_SUBSAMPLING;
-
- wb_info->mcif_buf_params.luma_pitch = afb->base.pitches[0];
- wb_info->mcif_buf_params.chroma_pitch = afb->base.pitches[1];
-
- for (i = 0; i < DWB_MCIF_BUF_COUNT; i++) {
- wb_info->mcif_buf_params.luma_address[i] = afb->address;
- wb_info->mcif_buf_params.chroma_address[i] = 0;
- }
-
- wb_info->mcif_buf_params.p_vmid = 1;
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 0, 0)) {
- wb_info->mcif_warmup_params.start_address.quad_part = afb->address;
- wb_info->mcif_warmup_params.region_size =
- wb_info->mcif_buf_params.luma_pitch * wb_info->dwb_params.dest_height;
- }
- wb_info->mcif_warmup_params.p_vmid = 1;
- wb_info->writeback_source_plane = pipe->plane_state;
-
- 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);
-}
-
-/**
- * amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation.
- * @state: The atomic state to commit
- *
- * This will tell DC to commit the constructed DC state from atomic_check,
- * programming the hardware. Any failures here implies a hardware failure, since
- * atomic check should have filtered anything non-kosher.
- */
-static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
-{
- struct drm_device *dev = state->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_display_manager *dm = &adev->dm;
- struct dm_atomic_state *dm_state;
- struct dc_state *dc_state = NULL;
- u32 i, j;
- struct drm_crtc *crtc;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- unsigned long flags;
- bool wait_for_vblank = true;
- struct drm_connector *connector;
- struct drm_connector_state *old_con_state, *new_con_state;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
- int crtc_disable_count = 0;
-
- trace_amdgpu_dm_atomic_commit_tail_begin(state);
-
- drm_atomic_helper_update_legacy_modeset_state(dev, state);
- drm_dp_mst_atomic_wait_for_dependencies(state);
-
- dm_state = dm_atomic_get_new_state(state);
- if (dm_state && dm_state->context) {
- dc_state = dm_state->context;
- amdgpu_dm_commit_streams(state, dc_state);
- }
-
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
- struct amdgpu_dm_connector *aconnector;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (!adev->dm.hdcp_workqueue)
- continue;
-
- pr_debug("[HDCP_DM] -------------- i : %x ----------\n", i);
-
- if (!connector)
- continue;
-
- pr_debug("[HDCP_DM] connector->index: %x connect_status: %x dpms: %x\n",
- connector->index, connector->status, connector->dpms);
- pr_debug("[HDCP_DM] state protection old: %x new: %x\n",
- old_con_state->content_protection, new_con_state->content_protection);
-
- if (aconnector->dc_sink) {
- if (aconnector->dc_sink->sink_signal != SIGNAL_TYPE_VIRTUAL &&
- aconnector->dc_sink->sink_signal != SIGNAL_TYPE_NONE) {
- pr_debug("[HDCP_DM] pipe_ctx dispname=%s\n",
- aconnector->dc_sink->edid_caps.display_name);
- }
- }
-
- new_crtc_state = NULL;
- old_crtc_state = NULL;
-
- if (acrtc) {
- new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base);
- old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base);
- }
-
- if (old_crtc_state)
- pr_debug("old crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n",
- old_crtc_state->enable,
- old_crtc_state->active,
- old_crtc_state->mode_changed,
- old_crtc_state->active_changed,
- old_crtc_state->connectors_changed);
-
- if (new_crtc_state)
- pr_debug("NEW crtc en: %x a: %x m: %x a-chg: %x c-chg: %x\n",
- new_crtc_state->enable,
- new_crtc_state->active,
- new_crtc_state->mode_changed,
- new_crtc_state->active_changed,
- new_crtc_state->connectors_changed);
- }
-
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- if (!adev->dm.hdcp_workqueue)
- continue;
-
- new_crtc_state = NULL;
- old_crtc_state = NULL;
-
- if (acrtc) {
- new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base);
- old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base);
- }
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- if (dm_new_crtc_state && dm_new_crtc_state->stream == NULL &&
- connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
- hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index);
- new_con_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- dm_new_con_state->update_hdcp = true;
- continue;
- }
-
- if (is_content_protection_different(new_crtc_state, old_crtc_state, new_con_state,
- old_con_state, connector, adev->dm.hdcp_workqueue)) {
- /* when display is unplugged from mst hub, connctor will
- * be destroyed within dm_dp_mst_connector_destroy. connector
- * hdcp perperties, like type, undesired, desired, enabled,
- * will be lost. So, save hdcp properties into hdcp_work within
- * amdgpu_dm_atomic_commit_tail. if the same display is
- * plugged back with same display index, its hdcp properties
- * will be retrieved from hdcp_work within dm_dp_mst_get_modes
- */
-
- bool enable_encryption = false;
-
- if (new_con_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED)
- enable_encryption = true;
-
- if (aconnector->dc_link && aconnector->dc_sink &&
- aconnector->dc_link->type == dc_connection_mst_branch) {
- struct hdcp_workqueue *hdcp_work = adev->dm.hdcp_workqueue;
- struct hdcp_workqueue *hdcp_w =
- &hdcp_work[aconnector->dc_link->link_index];
-
- hdcp_w->hdcp_content_type[connector->index] =
- new_con_state->hdcp_content_type;
- hdcp_w->content_protection[connector->index] =
- new_con_state->content_protection;
- }
-
- if (new_crtc_state && new_crtc_state->mode_changed &&
- new_con_state->content_protection >= DRM_MODE_CONTENT_PROTECTION_DESIRED)
- enable_encryption = true;
-
- DRM_INFO("[HDCP_DM] hdcp_update_display enable_encryption = %x\n", enable_encryption);
-
- if (aconnector->dc_link)
- hdcp_update_display(
- adev->dm.hdcp_workqueue, aconnector->dc_link->link_index, aconnector,
- new_con_state->hdcp_content_type, enable_encryption);
- }
- }
-
- /* Handle connector state changes */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
- struct dc_surface_update *dummy_updates;
- struct dc_stream_update stream_update;
- struct dc_info_packet hdr_packet;
- struct dc_stream_status *status = NULL;
- bool abm_changed, hdr_changed, scaling_changed;
-
- memset(&stream_update, 0, sizeof(stream_update));
-
- if (acrtc) {
- new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base);
- old_crtc_state = drm_atomic_get_old_crtc_state(state, &acrtc->base);
- }
-
- /* Skip any modesets/resets */
- if (!acrtc || drm_atomic_crtc_needs_modeset(new_crtc_state))
- continue;
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- scaling_changed = is_scaling_state_different(dm_new_con_state,
- dm_old_con_state);
-
- abm_changed = dm_new_crtc_state->abm_level !=
- dm_old_crtc_state->abm_level;
-
- hdr_changed =
- !drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state);
-
- if (!scaling_changed && !abm_changed && !hdr_changed)
- continue;
-
- stream_update.stream = dm_new_crtc_state->stream;
- if (scaling_changed) {
- update_stream_scaling_settings(&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;
- stream_update.dst = dm_new_crtc_state->stream->dst;
- }
-
- if (abm_changed) {
- dm_new_crtc_state->stream->abm_level = dm_new_crtc_state->abm_level;
-
- stream_update.abm_level = &dm_new_crtc_state->abm_level;
- }
-
- if (hdr_changed) {
- fill_hdr_info_packet(new_con_state, &hdr_packet);
- stream_update.hdr_static_metadata = &hdr_packet;
- }
-
- status = dc_stream_get_status(dm_new_crtc_state->stream);
-
- if (WARN_ON(!status))
- continue;
-
- WARN_ON(!status->plane_count);
-
- /*
- * TODO: DC refuses to perform stream updates without a dc_surface_update.
- * Here we create an empty update on each plane.
- * To fix this, DC should permit updating only stream properties.
- */
- dummy_updates = kzalloc(sizeof(struct dc_surface_update) * MAX_SURFACES, GFP_ATOMIC);
- if (!dummy_updates) {
- DRM_ERROR("Failed to allocate memory for dummy_updates.\n");
- continue;
- }
- for (j = 0; j < status->plane_count; j++)
- dummy_updates[j].surface = status->plane_states[0];
-
- sort(dummy_updates, status->plane_count,
- sizeof(*dummy_updates), dm_plane_layer_index_cmp, NULL);
-
- mutex_lock(&dm->dc_lock);
- dc_exit_ips_for_hw_access(dm->dc);
- dc_update_planes_and_stream(dm->dc,
- dummy_updates,
- status->plane_count,
- dm_new_crtc_state->stream,
- &stream_update);
- mutex_unlock(&dm->dc_lock);
- kfree(dummy_updates);
- }
-
- /**
- * Enable interrupts for CRTCs that are newly enabled or went through
- * a modeset. It was intentionally deferred until after the front end
- * state was modified to wait until the OTG was on and so the IRQ
- * handlers didn't access stale or invalid state.
- */
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-#ifdef CONFIG_DEBUG_FS
- enum amdgpu_dm_pipe_crc_source cur_crc_src;
-#endif
- /* Count number of newly disabled CRTCs for dropping PM refs later. */
- if (old_crtc_state->active && !new_crtc_state->active)
- crtc_disable_count++;
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- /* For freesync config update on crtc state and params for irq */
- update_stream_irq_parameters(dm, dm_new_crtc_state);
-
-#ifdef CONFIG_DEBUG_FS
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- cur_crc_src = acrtc->dm_irq_params.crc_src;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-#endif
-
- if (new_crtc_state->active &&
- (!old_crtc_state->active ||
- drm_atomic_crtc_needs_modeset(new_crtc_state))) {
- dc_stream_retain(dm_new_crtc_state->stream);
- acrtc->dm_irq_params.stream = dm_new_crtc_state->stream;
- manage_dm_interrupts(adev, acrtc, dm_new_crtc_state);
- }
- /* Handle vrr on->off / off->on transitions */
- amdgpu_dm_handle_vrr_transition(dm_old_crtc_state, dm_new_crtc_state);
-
-#ifdef CONFIG_DEBUG_FS
- if (new_crtc_state->active &&
- (!old_crtc_state->active ||
- drm_atomic_crtc_needs_modeset(new_crtc_state))) {
- /**
- * Frontend may have changed so reapply the CRC capture
- * settings for the stream.
- */
- if (amdgpu_dm_is_valid_crc_source(cur_crc_src)) {
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- if (amdgpu_dm_crc_window_is_activated(crtc)) {
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- acrtc->dm_irq_params.window_param.update_win = true;
-
- /**
- * It takes 2 frames for HW to stably generate CRC when
- * resuming from suspend, so we set skip_frame_cnt 2.
- */
- acrtc->dm_irq_params.window_param.skip_frame_cnt = 2;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
-#endif
- if (amdgpu_dm_crtc_configure_crc_source(
- crtc, dm_new_crtc_state, cur_crc_src))
- drm_dbg_atomic(dev, "Failed to configure crc source");
- }
- }
-#endif
- }
-
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, j)
- if (new_crtc_state->async_flip)
- wait_for_vblank = false;
-
- /* update planes when needed per crtc*/
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, j) {
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- if (dm_new_crtc_state->stream)
- amdgpu_dm_commit_planes(state, dev, dm, crtc, wait_for_vblank);
- }
-
- /* Enable writeback */
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- if (!new_con_state->writeback_job)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(state, &acrtc->base);
-
- if (!new_crtc_state)
- continue;
-
- if (acrtc->wb_enabled)
- continue;
-
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- dm_set_writeback(dm, dm_new_crtc_state, connector, new_con_state);
- acrtc->wb_enabled = true;
- }
-
- /* Update audio instances for each connector. */
- amdgpu_dm_commit_audio(dev, state);
-
- /* restore the backlight level */
- for (i = 0; i < dm->num_of_edps; i++) {
- if (dm->backlight_dev[i] &&
- (dm->actual_brightness[i] != dm->brightness[i]))
- amdgpu_dm_backlight_set_level(dm, i, dm->brightness[i]);
- }
-
- /*
- * send vblank event on all events not handled in flip and
- * mark consumed event for drm_atomic_helper_commit_hw_done
- */
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
-
- if (new_crtc_state->event)
- drm_send_event_locked(dev, &new_crtc_state->event->base);
-
- new_crtc_state->event = NULL;
- }
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-
- /* Signal HW programming completion */
- drm_atomic_helper_commit_hw_done(state);
-
- if (wait_for_vblank)
- drm_atomic_helper_wait_for_flip_done(dev, state);
-
- drm_atomic_helper_cleanup_planes(dev, state);
-
- /* Don't free the memory if we are hitting this as part of suspend.
- * This way we don't free any memory during suspend; see
- * amdgpu_bo_free_kernel(). The memory will be freed in the first
- * non-suspend modeset or when the driver is torn down.
- */
- if (!adev->in_suspend) {
- /* return the stolen vga memory back to VRAM */
- if (!adev->mman.keep_stolen_vga_memory)
- amdgpu_bo_free_kernel(&adev->mman.stolen_vga_memory, NULL, NULL);
- amdgpu_bo_free_kernel(&adev->mman.stolen_extended_memory, NULL, NULL);
- }
-
- /*
- * Finally, drop a runtime PM reference for each newly disabled CRTC,
- * so we can put the GPU into runtime suspend if we're not driving any
- * displays anymore
- */
- for (i = 0; i < crtc_disable_count; i++)
- pm_runtime_put_autosuspend(dev->dev);
- pm_runtime_mark_last_busy(dev->dev);
-}
-
-static int dm_force_atomic_commit(struct drm_connector *connector)
-{
- int ret = 0;
- struct drm_device *ddev = connector->dev;
- struct drm_atomic_state *state = drm_atomic_state_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_state
- */
- conn_state = drm_atomic_get_connector_state(state, connector);
-
- ret = PTR_ERR_OR_ZERO(conn_state);
- if (ret)
- goto out;
-
- /* Attach crtc to drm_atomic_state*/
- crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base);
-
- ret = PTR_ERR_OR_ZERO(crtc_state);
- if (ret)
- goto out;
-
- /* force a restore */
- crtc_state->mode_changed = true;
-
- /* Attach plane to drm_atomic_state */
- plane_state = drm_atomic_get_plane_state(state, plane);
-
- ret = PTR_ERR_OR_ZERO(plane_state);
- if (ret)
- goto out;
-
- /* Call commit internally with the state we just constructed */
- ret = drm_atomic_commit(state);
-
-out:
- drm_atomic_state_put(state);
- if (ret)
- DRM_ERROR("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.
- */
-static int do_aquire_global_lock(struct drm_device *dev,
- struct drm_atomic_state *state)
-{
- struct drm_crtc *crtc;
- struct drm_crtc_commit *commit;
- long ret;
-
- /*
- * Adding all modeset locks to aquire_ctx will
- * ensure that when the framework release it the
- * extra locks we are locking here will get released to
- */
- ret = drm_modeset_lock_all_ctx(dev, state->acquire_ctx);
- if (ret)
- return ret;
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- spin_lock(&crtc->commit_lock);
- commit = list_first_entry_or_null(&crtc->commit_list,
- struct drm_crtc_commit, commit_entry);
- if (commit)
- drm_crtc_commit_get(commit);
- spin_unlock(&crtc->commit_lock);
-
- if (!commit)
- continue;
-
- /*
- * Make sure all pending HW programming completed and
- * page flips done
- */
- ret = wait_for_completion_interruptible_timeout(&commit->hw_done, 10*HZ);
-
- if (ret > 0)
- ret = wait_for_completion_interruptible_timeout(
- &commit->flip_done, 10*HZ);
-
- if (ret == 0)
- DRM_ERROR("[CRTC:%d:%s] hw_done or flip_done timed out\n",
- crtc->base.id, crtc->name);
-
- drm_crtc_commit_put(commit);
- }
-
- return ret < 0 ? ret : 0;
-}
-
-static void get_freesync_config_for_crtc(
- struct dm_crtc_state *new_crtc_state,
- struct dm_connector_state *new_con_state)
-{
- struct mod_freesync_config config = {0};
- struct amdgpu_dm_connector *aconnector;
- struct drm_display_mode *mode = &new_crtc_state->base.mode;
- int vrefresh = drm_mode_vrefresh(mode);
- bool fs_vid_mode = false;
-
- if (new_con_state->base.connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return;
-
- aconnector = to_amdgpu_dm_connector(new_con_state->base.connector);
-
- new_crtc_state->vrr_supported = new_con_state->freesync_capable &&
- vrefresh >= aconnector->min_vfreq &&
- vrefresh <= aconnector->max_vfreq;
-
- if (new_crtc_state->vrr_supported) {
- new_crtc_state->stream->ignore_msa_timing_param = true;
- fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED;
-
- config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000;
- config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000;
- config.vsif_supported = true;
- config.btr = true;
-
- if (fs_vid_mode) {
- config.state = VRR_STATE_ACTIVE_FIXED;
- config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz;
- goto out;
- } else if (new_crtc_state->base.vrr_enabled) {
- config.state = VRR_STATE_ACTIVE_VARIABLE;
- } else {
- config.state = VRR_STATE_INACTIVE;
- }
- }
-out:
- new_crtc_state->freesync_config = config;
-}
-
-static void reset_freesync_config_for_crtc(
- struct dm_crtc_state *new_crtc_state)
-{
- new_crtc_state->vrr_supported = false;
-
- memset(&new_crtc_state->vrr_infopacket, 0,
- sizeof(new_crtc_state->vrr_infopacket));
-}
-
-static bool
-is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
- struct drm_crtc_state *new_crtc_state)
-{
- const struct drm_display_mode *old_mode, *new_mode;
-
- if (!old_crtc_state || !new_crtc_state)
- return false;
-
- old_mode = &old_crtc_state->mode;
- new_mode = &new_crtc_state->mode;
-
- if (old_mode->clock == new_mode->clock &&
- old_mode->hdisplay == new_mode->hdisplay &&
- old_mode->vdisplay == new_mode->vdisplay &&
- old_mode->htotal == new_mode->htotal &&
- old_mode->vtotal != new_mode->vtotal &&
- old_mode->hsync_start == new_mode->hsync_start &&
- old_mode->vsync_start != new_mode->vsync_start &&
- old_mode->hsync_end == new_mode->hsync_end &&
- old_mode->vsync_end != new_mode->vsync_end &&
- old_mode->hskew == new_mode->hskew &&
- old_mode->vscan == new_mode->vscan &&
- (old_mode->vsync_end - old_mode->vsync_start) ==
- (new_mode->vsync_end - new_mode->vsync_start))
- return true;
-
- return false;
-}
-
-static 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;
-
- dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
-
- num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000;
- den = (unsigned long long)new_crtc_state->mode.htotal *
- (unsigned long long)new_crtc_state->mode.vtotal;
-
- res = div_u64(num, den);
- dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
-}
-
-static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
- struct drm_atomic_state *state,
- struct drm_crtc *crtc,
- struct drm_crtc_state *old_crtc_state,
- struct drm_crtc_state *new_crtc_state,
- bool enable,
- bool *lock_and_validation_needed)
-{
- struct dm_atomic_state *dm_state = NULL;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
- struct dc_stream_state *new_stream;
- int ret = 0;
-
- /*
- * TODO Move this code into dm_crtc_atomic_check once we get rid of dc_validation_set
- * update changed items
- */
- struct amdgpu_crtc *acrtc = NULL;
- struct drm_connector *connector = NULL;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct drm_connector_state *drm_new_conn_state = NULL, *drm_old_conn_state = NULL;
- struct dm_connector_state *dm_new_conn_state = NULL, *dm_old_conn_state = NULL;
-
- new_stream = NULL;
-
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- acrtc = to_amdgpu_crtc(crtc);
- connector = amdgpu_dm_find_first_crtc_matching_connector(state, crtc);
- if (connector)
- aconnector = to_amdgpu_dm_connector(connector);
-
- /* TODO This hack should go away */
- if (connector && enable) {
- /* Make sure fake sink is created in plug-in scenario */
- drm_new_conn_state = drm_atomic_get_new_connector_state(state,
- connector);
- drm_old_conn_state = drm_atomic_get_old_connector_state(state,
- connector);
-
- if (IS_ERR(drm_new_conn_state)) {
- ret = PTR_ERR_OR_ZERO(drm_new_conn_state);
- goto fail;
- }
-
- dm_new_conn_state = to_dm_connector_state(drm_new_conn_state);
- dm_old_conn_state = to_dm_connector_state(drm_old_conn_state);
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- goto skip_modeset;
-
- new_stream = create_validate_stream_for_sink(aconnector,
- &new_crtc_state->mode,
- dm_new_conn_state,
- dm_old_crtc_state->stream);
-
- /*
- * we can have no stream on ACTION_SET if a display
- * was disconnected during S3, in this case it is not an
- * error, the OS will be updated after detection, and
- * will do the right thing on next atomic commit
- */
-
- if (!new_stream) {
- DRM_DEBUG_DRIVER("%s: Failed to create new stream for crtc %d\n",
- __func__, acrtc->base.base.id);
- ret = -ENOMEM;
- goto fail;
- }
-
- /*
- * TODO: Check VSDB bits to decide whether this should
- * be enabled or not.
- */
- new_stream->triggered_crtc_reset.enabled =
- dm->force_timing_sync;
-
- dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level;
-
- ret = fill_hdr_info_packet(drm_new_conn_state,
- &new_stream->hdr_static_metadata);
- if (ret)
- goto fail;
-
- /*
- * If we already removed the old stream from the context
- * (and set the new stream to NULL) then we can't reuse
- * the old stream even if the stream and scaling are unchanged.
- * We'll hit the BUG_ON and black screen.
- *
- * TODO: Refactor this function to allow this check to work
- * in all conditions.
- */
- if (amdgpu_freesync_vid_mode &&
- dm_new_crtc_state->stream &&
- is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state))
- goto skip_modeset;
-
- if (dm_new_crtc_state->stream &&
- dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) &&
- dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream)) {
- new_crtc_state->mode_changed = false;
- DRM_DEBUG_DRIVER("Mode change not required, setting mode_changed to %d",
- new_crtc_state->mode_changed);
- }
- }
-
- /* mode_changed flag may get updated above, need to check again */
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- goto skip_modeset;
-
- drm_dbg_state(state->dev,
- "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, planes_changed:%d, mode_changed:%d,active_changed:%d,connectors_changed:%d\n",
- acrtc->crtc_id,
- new_crtc_state->enable,
- new_crtc_state->active,
- new_crtc_state->planes_changed,
- new_crtc_state->mode_changed,
- new_crtc_state->active_changed,
- new_crtc_state->connectors_changed);
-
- /* Remove stream for any changed/disabled CRTC */
- if (!enable) {
-
- if (!dm_old_crtc_state->stream)
- goto skip_modeset;
-
- /* Unset freesync video if it was active before */
- if (dm_old_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED) {
- dm_new_crtc_state->freesync_config.state = VRR_STATE_INACTIVE;
- dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = 0;
- }
-
- /* Now check if we should set freesync video mode */
- if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream &&
- dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) &&
- dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream) &&
- is_timing_unchanged_for_freesync(new_crtc_state,
- old_crtc_state)) {
- new_crtc_state->mode_changed = false;
- DRM_DEBUG_DRIVER(
- "Mode change not required for front porch change, setting mode_changed to %d",
- new_crtc_state->mode_changed);
-
- set_freesync_fixed_config(dm_new_crtc_state);
-
- goto skip_modeset;
- } else if (amdgpu_freesync_vid_mode && aconnector &&
- is_freesync_video_mode(&new_crtc_state->mode,
- aconnector)) {
- struct drm_display_mode *high_mode;
-
- high_mode = 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);
- }
-
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret)
- goto fail;
-
- DRM_DEBUG_DRIVER("Disabling DRM crtc: %d\n",
- crtc->base.id);
-
- /* i.e. reset mode */
- if (dc_state_remove_stream(
- dm->dc,
- dm_state->context,
- dm_old_crtc_state->stream) != DC_OK) {
- ret = -EINVAL;
- goto fail;
- }
-
- dc_stream_release(dm_old_crtc_state->stream);
- dm_new_crtc_state->stream = NULL;
-
- reset_freesync_config_for_crtc(dm_new_crtc_state);
-
- *lock_and_validation_needed = true;
-
- } else {/* Add stream for any updated/enabled CRTC */
- /*
- * Quick fix to prevent NULL pointer on new_stream when
- * added MST connectors not found in existing crtc_state in the chained mode
- * TODO: need to dig out the root cause of that
- */
- if (!connector)
- goto skip_modeset;
-
- if (modereset_required(new_crtc_state))
- goto skip_modeset;
-
- if (amdgpu_dm_crtc_modeset_required(new_crtc_state, new_stream,
- dm_old_crtc_state->stream)) {
-
- WARN_ON(dm_new_crtc_state->stream);
-
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret)
- goto fail;
-
- dm_new_crtc_state->stream = new_stream;
-
- dc_stream_retain(new_stream);
-
- DRM_DEBUG_ATOMIC("Enabling DRM crtc: %d\n",
- crtc->base.id);
-
- if (dc_state_add_stream(
- dm->dc,
- dm_state->context,
- dm_new_crtc_state->stream) != DC_OK) {
- ret = -EINVAL;
- goto fail;
- }
-
- *lock_and_validation_needed = true;
- }
- }
-
-skip_modeset:
- /* Release extra reference */
- if (new_stream)
- dc_stream_release(new_stream);
-
- /*
- * We want to do dc stream updates that do not require a
- * full modeset below.
- */
- if (!(enable && connector && new_crtc_state->active))
- return 0;
- /*
- * Given above conditions, the dc state cannot be NULL because:
- * 1. We're in the process of enabling CRTCs (just been added
- * to the dc context, or already is on the context)
- * 2. Has a valid connector attached, and
- * 3. Is currently active and enabled.
- * => The dc stream state currently exists.
- */
- BUG_ON(dm_new_crtc_state->stream == NULL);
-
- /* 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(
- &new_crtc_state->mode, dm_new_conn_state, dm_new_crtc_state->stream);
-
- /* ABM settings */
- dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level;
-
- /*
- * Color management settings. We also update color properties
- * when a modeset is needed, to ensure it gets reprogrammed.
- */
- if (dm_new_crtc_state->base.color_mgmt_changed ||
- dm_old_crtc_state->regamma_tf != dm_new_crtc_state->regamma_tf ||
- drm_atomic_crtc_needs_modeset(new_crtc_state)) {
- ret = amdgpu_dm_update_crtc_color_mgmt(dm_new_crtc_state);
- if (ret)
- goto fail;
- }
-
- /* Update Freesync settings. */
- get_freesync_config_for_crtc(dm_new_crtc_state,
- dm_new_conn_state);
-
- return ret;
-
-fail:
- if (new_stream)
- dc_stream_release(new_stream);
- return ret;
-}
-
-static bool should_reset_plane(struct drm_atomic_state *state,
- struct drm_plane *plane,
- struct drm_plane_state *old_plane_state,
- struct drm_plane_state *new_plane_state)
-{
- struct drm_plane *other;
- struct drm_plane_state *old_other_state, *new_other_state;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *old_dm_crtc_state, *new_dm_crtc_state;
- struct amdgpu_device *adev = drm_to_adev(plane->dev);
- int i;
-
- /*
- * TODO: Remove this hack for all asics once it proves that the
- * fast updates works fine on DCN3.2+.
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 2, 0) &&
- state->allow_modeset)
- return true;
-
- /* Exit early if we know that we're adding or removing the plane. */
- if (old_plane_state->crtc != new_plane_state->crtc)
- return true;
-
- /* old crtc == new_crtc == NULL, plane not in context. */
- if (!new_plane_state->crtc)
- return false;
-
- new_crtc_state =
- drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
- old_crtc_state =
- drm_atomic_get_old_crtc_state(state, old_plane_state->crtc);
-
- if (!new_crtc_state)
- return true;
-
- /*
- * A change in cursor mode means a new dc pipe needs to be acquired or
- * released from the state
- */
- old_dm_crtc_state = to_dm_crtc_state(old_crtc_state);
- new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (plane->type == DRM_PLANE_TYPE_CURSOR &&
- old_dm_crtc_state != NULL &&
- old_dm_crtc_state->cursor_mode != new_dm_crtc_state->cursor_mode) {
- return true;
- }
-
- /* CRTC Degamma changes currently require us to recreate planes. */
- if (new_crtc_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.
- *
- * TODO: We can likely skip bandwidth validation if the only thing that
- * changed about the plane was it'z z-ordering.
- */
- if (new_crtc_state->zpos_changed)
- return true;
-
- if (drm_atomic_crtc_needs_modeset(new_crtc_state))
- return true;
-
- /*
- * If there are any new primary or overlay planes being added or
- * removed then the z-order can potentially change. To ensure
- * correct z-order and pipe acquisition the current DC architecture
- * requires us to remove and recreate all existing planes.
- *
- * TODO: Come up with a more elegant solution for this.
- */
- for_each_oldnew_plane_in_state(state, other, old_other_state, new_other_state, i) {
- struct amdgpu_framebuffer *old_afb, *new_afb;
- struct dm_plane_state *dm_new_other_state, *dm_old_other_state;
-
- dm_new_other_state = to_dm_plane_state(new_other_state);
- dm_old_other_state = to_dm_plane_state(old_other_state);
-
- if (other->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- if (old_other_state->crtc != new_plane_state->crtc &&
- new_other_state->crtc != new_plane_state->crtc)
- continue;
-
- if (old_other_state->crtc != new_other_state->crtc)
- return true;
-
- /* Src/dst size and scaling updates. */
- if (old_other_state->src_w != new_other_state->src_w ||
- old_other_state->src_h != new_other_state->src_h ||
- old_other_state->crtc_w != new_other_state->crtc_w ||
- old_other_state->crtc_h != new_other_state->crtc_h)
- return true;
-
- /* Rotation / mirroring updates. */
- if (old_other_state->rotation != new_other_state->rotation)
- return true;
-
- /* Blending updates. */
- if (old_other_state->pixel_blend_mode !=
- new_other_state->pixel_blend_mode)
- return true;
-
- /* Alpha updates. */
- if (old_other_state->alpha != new_other_state->alpha)
- return true;
-
- /* Colorspace changes. */
- if (old_other_state->color_range != new_other_state->color_range ||
- old_other_state->color_encoding != new_other_state->color_encoding)
- return true;
-
- /* HDR/Transfer Function changes. */
- if (dm_old_other_state->degamma_tf != dm_new_other_state->degamma_tf ||
- dm_old_other_state->degamma_lut != dm_new_other_state->degamma_lut ||
- dm_old_other_state->hdr_mult != dm_new_other_state->hdr_mult ||
- dm_old_other_state->ctm != dm_new_other_state->ctm ||
- dm_old_other_state->shaper_lut != dm_new_other_state->shaper_lut ||
- dm_old_other_state->shaper_tf != dm_new_other_state->shaper_tf ||
- dm_old_other_state->lut3d != dm_new_other_state->lut3d ||
- dm_old_other_state->blend_lut != dm_new_other_state->blend_lut ||
- dm_old_other_state->blend_tf != dm_new_other_state->blend_tf)
- return true;
-
- /* Framebuffer checks fall at the end. */
- if (!old_other_state->fb || !new_other_state->fb)
- continue;
-
- /* Pixel format changes can require bandwidth updates. */
- if (old_other_state->fb->format != new_other_state->fb->format)
- return true;
-
- old_afb = (struct amdgpu_framebuffer *)old_other_state->fb;
- new_afb = (struct amdgpu_framebuffer *)new_other_state->fb;
-
- /* Tiling and DCC changes also require bandwidth updates. */
- if (old_afb->tiling_flags != new_afb->tiling_flags ||
- old_afb->base.modifier != new_afb->base.modifier)
- return true;
- }
-
- return false;
-}
-
-static int dm_check_cursor_fb(struct amdgpu_crtc *new_acrtc,
- struct drm_plane_state *new_plane_state,
- struct drm_framebuffer *fb)
-{
- struct amdgpu_device *adev = drm_to_adev(new_acrtc->base.dev);
- struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(fb);
- unsigned int pitch;
- bool linear;
-
- if (fb->width > new_acrtc->max_cursor_width ||
- fb->height > new_acrtc->max_cursor_height) {
- DRM_DEBUG_ATOMIC("Bad cursor FB size %dx%d\n",
- new_plane_state->fb->width,
- new_plane_state->fb->height);
- return -EINVAL;
- }
- if (new_plane_state->src_w != fb->width << 16 ||
- new_plane_state->src_h != fb->height << 16) {
- DRM_DEBUG_ATOMIC("Cropping not supported for cursor plane\n");
- return -EINVAL;
- }
-
- /* Pitch in pixels */
- pitch = fb->pitches[0] / fb->format->cpp[0];
-
- if (fb->width != pitch) {
- DRM_DEBUG_ATOMIC("Cursor FB width %d doesn't match pitch %d",
- fb->width, pitch);
- return -EINVAL;
- }
-
- switch (pitch) {
- case 64:
- case 128:
- case 256:
- /* FB pitch is supported by cursor plane */
- break;
- default:
- DRM_DEBUG_ATOMIC("Bad cursor FB pitch %d px\n", pitch);
- return -EINVAL;
- }
-
- /* Core DRM takes care of checking FB modifiers, so we only need to
- * check tiling flags when the FB doesn't have a modifier.
- */
- if (!(fb->flags & DRM_MODE_FB_MODIFIERS)) {
- if (adev->family >= AMDGPU_FAMILY_GC_12_0_0) {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, GFX12_SWIZZLE_MODE) == 0;
- } else if (adev->family >= AMDGPU_FAMILY_AI) {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE) == 0;
- } else {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_2D_TILED_THIN1 &&
- AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_1D_TILED_THIN1 &&
- AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE) == 0;
- }
- if (!linear) {
- DRM_DEBUG_ATOMIC("Cursor FB not linear");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-/*
- * Helper function for checking the cursor in native mode
- */
-static int dm_check_native_cursor_state(struct drm_crtc *new_plane_crtc,
- struct drm_plane *plane,
- struct drm_plane_state *new_plane_state,
- bool enable)
-{
-
- struct amdgpu_crtc *new_acrtc;
- int ret;
-
- if (!enable || !new_plane_crtc ||
- drm_atomic_plane_disabling(plane->state, new_plane_state))
- return 0;
-
- new_acrtc = to_amdgpu_crtc(new_plane_crtc);
-
- if (new_plane_state->src_x != 0 || new_plane_state->src_y != 0) {
- DRM_DEBUG_ATOMIC("Cropping not supported for cursor plane\n");
- return -EINVAL;
- }
-
- if (new_plane_state->fb) {
- ret = dm_check_cursor_fb(new_acrtc, new_plane_state,
- new_plane_state->fb);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static bool dm_should_update_native_cursor(struct drm_atomic_state *state,
- struct drm_crtc *old_plane_crtc,
- struct drm_crtc *new_plane_crtc,
- bool enable)
-{
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
-
- if (!enable) {
- if (old_plane_crtc == NULL)
- return true;
-
- old_crtc_state = drm_atomic_get_old_crtc_state(
- state, old_plane_crtc);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- return dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE;
- } else {
- if (new_plane_crtc == NULL)
- return true;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_plane_crtc);
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- return dm_new_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE;
- }
-}
-
-static int dm_update_plane_state(struct dc *dc,
- struct drm_atomic_state *state,
- struct drm_plane *plane,
- struct drm_plane_state *old_plane_state,
- struct drm_plane_state *new_plane_state,
- bool enable,
- bool *lock_and_validation_needed,
- bool *is_top_most_overlay)
-{
-
- struct dm_atomic_state *dm_state = NULL;
- struct drm_crtc *new_plane_crtc, *old_plane_crtc;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *dm_new_crtc_state, *dm_old_crtc_state;
- struct dm_plane_state *dm_new_plane_state, *dm_old_plane_state;
- bool needs_reset, update_native_cursor;
- int ret = 0;
-
-
- new_plane_crtc = new_plane_state->crtc;
- old_plane_crtc = old_plane_state->crtc;
- dm_new_plane_state = to_dm_plane_state(new_plane_state);
- dm_old_plane_state = to_dm_plane_state(old_plane_state);
-
- update_native_cursor = dm_should_update_native_cursor(state,
- old_plane_crtc,
- new_plane_crtc,
- enable);
-
- if (plane->type == DRM_PLANE_TYPE_CURSOR && update_native_cursor) {
- ret = dm_check_native_cursor_state(new_plane_crtc, plane,
- new_plane_state, enable);
- if (ret)
- return ret;
-
- return 0;
- }
-
- needs_reset = should_reset_plane(state, plane, old_plane_state,
- new_plane_state);
-
- /* Remove any changed/removed planes */
- if (!enable) {
- if (!needs_reset)
- return 0;
-
- if (!old_plane_crtc)
- return 0;
-
- old_crtc_state = drm_atomic_get_old_crtc_state(
- state, old_plane_crtc);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- if (!dm_old_crtc_state->stream)
- return 0;
-
- DRM_DEBUG_ATOMIC("Disabling DRM plane: %d on DRM crtc %d\n",
- plane->base.id, old_plane_crtc->base.id);
-
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret)
- return ret;
-
- if (!dc_state_remove_plane(
- dc,
- dm_old_crtc_state->stream,
- dm_old_plane_state->dc_state,
- dm_state->context)) {
-
- return -EINVAL;
- }
-
- if (dm_old_plane_state->dc_state)
- dc_plane_state_release(dm_old_plane_state->dc_state);
-
- dm_new_plane_state->dc_state = NULL;
-
- *lock_and_validation_needed = true;
-
- } else { /* Add new planes */
- struct dc_plane_state *dc_new_plane_state;
-
- if (drm_atomic_plane_disabling(plane->state, new_plane_state))
- return 0;
-
- if (!new_plane_crtc)
- return 0;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_crtc);
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- if (!dm_new_crtc_state->stream)
- return 0;
-
- if (!needs_reset)
- return 0;
-
- ret = amdgpu_dm_plane_helper_check_state(new_plane_state, new_crtc_state);
- if (ret)
- goto out;
-
- WARN_ON(dm_new_plane_state->dc_state);
-
- dc_new_plane_state = dc_create_plane_state(dc);
- if (!dc_new_plane_state) {
- ret = -ENOMEM;
- goto out;
- }
-
- DRM_DEBUG_ATOMIC("Enabling DRM plane: %d on DRM crtc %d\n",
- plane->base.id, new_plane_crtc->base.id);
-
- ret = fill_dc_plane_attributes(
- drm_to_adev(new_plane_crtc->dev),
- dc_new_plane_state,
- new_plane_state,
- new_crtc_state);
- if (ret) {
- dc_plane_state_release(dc_new_plane_state);
- goto out;
- }
-
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret) {
- dc_plane_state_release(dc_new_plane_state);
- goto out;
- }
-
- /*
- * Any atomic check errors that occur after this will
- * not need a release. The plane state will be attached
- * to the stream, and therefore part of the atomic
- * state. It'll be released when the atomic state is
- * cleaned.
- */
- if (!dc_state_add_plane(
- dc,
- dm_new_crtc_state->stream,
- dc_new_plane_state,
- dm_state->context)) {
-
- dc_plane_state_release(dc_new_plane_state);
- ret = -EINVAL;
- goto out;
- }
-
- dm_new_plane_state->dc_state = dc_new_plane_state;
-
- dm_new_crtc_state->mpo_requested |= (plane->type == DRM_PLANE_TYPE_OVERLAY);
-
- /* 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;
-
- *lock_and_validation_needed = true;
- }
-
-out:
- /* If enabling cursor overlay failed, attempt fallback to native mode */
- if (enable && ret == -EINVAL && plane->type == DRM_PLANE_TYPE_CURSOR) {
- ret = dm_check_native_cursor_state(new_plane_crtc, plane,
- new_plane_state, enable);
- if (ret)
- return ret;
-
- dm_new_crtc_state->cursor_mode = DM_CURSOR_NATIVE_MODE;
- }
-
- return ret;
-}
-
-static 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:
- case DRM_MODE_ROTATE_270:
- *src_w = plane_state->src_h >> 16;
- *src_h = plane_state->src_w >> 16;
- break;
- case DRM_MODE_ROTATE_0:
- case DRM_MODE_ROTATE_180:
- default:
- *src_w = plane_state->src_w >> 16;
- *src_h = plane_state->src_h >> 16;
- break;
- }
-}
-
-static void
-dm_get_plane_scale(struct drm_plane_state *plane_state,
- int *out_plane_scale_w, int *out_plane_scale_h)
-{
- int plane_src_w, plane_src_h;
-
- dm_get_oriented_plane_size(plane_state, &plane_src_w, &plane_src_h);
- *out_plane_scale_w = plane_state->crtc_w * 1000 / plane_src_w;
- *out_plane_scale_h = plane_state->crtc_h * 1000 / plane_src_h;
-}
-
-/*
- * The normalized_zpos value cannot be used by this iterator directly. It's only
- * calculated for enabled planes, potentially causing normalized_zpos collisions
- * between enabled/disabled planes in the atomic state. We need a unique value
- * so that the iterator will not generate the same object twice, or loop
- * indefinitely.
- */
-static inline struct __drm_planes_state *__get_next_zpos(
- struct drm_atomic_state *state,
- struct __drm_planes_state *prev)
-{
- unsigned int highest_zpos = 0, prev_zpos = 256;
- uint32_t highest_id = 0, prev_id = UINT_MAX;
- struct drm_plane_state *new_plane_state;
- struct drm_plane *plane;
- int i, highest_i = -1;
-
- if (prev != NULL) {
- prev_zpos = prev->new_state->zpos;
- prev_id = prev->ptr->base.id;
- }
-
- for_each_new_plane_in_state(state, plane, new_plane_state, i) {
- /* Skip planes with higher zpos than the previously returned */
- if (new_plane_state->zpos > prev_zpos ||
- (new_plane_state->zpos == prev_zpos &&
- plane->base.id >= prev_id))
- continue;
-
- /* Save the index of the plane with highest zpos */
- if (new_plane_state->zpos > highest_zpos ||
- (new_plane_state->zpos == highest_zpos &&
- plane->base.id > highest_id)) {
- highest_zpos = new_plane_state->zpos;
- highest_id = plane->base.id;
- highest_i = i;
- }
- }
-
- if (highest_i < 0)
- return NULL;
-
- return &state->planes[highest_i];
-}
-
-/*
- * Use the uniqueness of the plane's (zpos, drm obj ID) combination to iterate
- * by descending zpos, as read from the new plane state. This is the same
- * ordering as defined by drm_atomic_normalize_zpos().
- */
-#define for_each_oldnew_plane_in_descending_zpos(__state, plane, old_plane_state, new_plane_state) \
- for (struct __drm_planes_state *__i = __get_next_zpos((__state), NULL); \
- __i != NULL; __i = __get_next_zpos((__state), __i)) \
- for_each_if(((plane) = __i->ptr, \
- (void)(plane) /* Only to avoid unused-but-set-variable warning */, \
- (old_plane_state) = __i->old_state, \
- (new_plane_state) = __i->new_state, 1))
-
-static int add_affected_mst_dsc_crtcs(struct drm_atomic_state *state, struct drm_crtc *crtc)
-{
- struct drm_connector *connector;
- struct drm_connector_state *conn_state, *old_conn_state;
- struct amdgpu_dm_connector *aconnector = NULL;
- int i;
-
- for_each_oldnew_connector_in_state(state, connector, old_conn_state, conn_state, i) {
- if (!conn_state->crtc)
- conn_state = old_conn_state;
-
- if (conn_state->crtc != crtc)
- continue;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (!aconnector->mst_output_port || !aconnector->mst_root)
- aconnector = NULL;
- else
- break;
- }
-
- if (!aconnector)
- return 0;
-
- return drm_dp_mst_add_affected_dsc_crtcs(state, &aconnector->mst_root->mst_mgr);
-}
-
-/**
- * DOC: Cursor Modes - Native vs Overlay
- *
- * In native mode, the cursor uses a integrated cursor pipe within each DCN hw
- * plane. It does not require a dedicated hw plane to enable, but it is
- * subjected to the same z-order and scaling as the hw plane. It also has format
- * restrictions, a RGB cursor in native mode cannot be enabled within a non-RGB
- * hw plane.
- *
- * In overlay mode, the cursor uses a separate DCN hw plane, and thus has its
- * own scaling and z-pos. It also has no blending restrictions. It lends to a
- * cursor behavior more akin to a DRM client's expectations. However, it does
- * occupy an extra DCN plane, and therefore will only be used if a DCN plane is
- * available.
- */
-
-/**
- * dm_crtc_get_cursor_mode() - Determine the required cursor mode on crtc
- * @adev: amdgpu device
- * @state: DRM atomic state
- * @dm_crtc_state: amdgpu state for the CRTC containing the cursor
- * @cursor_mode: Returns the required cursor mode on dm_crtc_state
- *
- * Get whether the cursor should be enabled in native mode, or overlay mode, on
- * the dm_crtc_state.
- *
- * The cursor should be enabled in overlay mode if there exists an underlying
- * plane - on which the cursor may be blended - that is either YUV formatted, or
- * scaled differently from the cursor.
- *
- * Since zpos info is required, drm_atomic_normalize_zpos must be called before
- * calling this function.
- *
- * Return: 0 on success, or an error code if getting the cursor plane state
- * failed.
- */
-static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev,
- struct drm_atomic_state *state,
- struct dm_crtc_state *dm_crtc_state,
- enum amdgpu_dm_cursor_mode *cursor_mode)
-{
- struct drm_plane_state *old_plane_state, *plane_state, *cursor_state;
- struct drm_crtc_state *crtc_state = &dm_crtc_state->base;
- struct drm_plane *plane;
- bool consider_mode_change = false;
- bool entire_crtc_covered = false;
- bool cursor_changed = false;
- int underlying_scale_w, underlying_scale_h;
- int cursor_scale_w, cursor_scale_h;
- int i;
-
- /* Overlay cursor not supported on HW before DCN
- * DCN401 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions
- * as previous DCN generations, so enable native mode on DCN401 in addition to DCE
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0 ||
- amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1)) {
- *cursor_mode = DM_CURSOR_NATIVE_MODE;
- return 0;
- }
-
- /* Init cursor_mode to be the same as current */
- *cursor_mode = dm_crtc_state->cursor_mode;
-
- /*
- * Cursor mode can change if a plane's format changes, scale changes, is
- * enabled/disabled, or z-order changes.
- */
- for_each_oldnew_plane_in_state(state, plane, old_plane_state, plane_state, i) {
- int new_scale_w, new_scale_h, old_scale_w, old_scale_h;
-
- /* Only care about planes on this CRTC */
- if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0)
- continue;
-
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- cursor_changed = true;
-
- if (drm_atomic_plane_enabling(old_plane_state, plane_state) ||
- drm_atomic_plane_disabling(old_plane_state, plane_state) ||
- old_plane_state->fb->format != plane_state->fb->format) {
- consider_mode_change = true;
- break;
- }
-
- dm_get_plane_scale(plane_state, &new_scale_w, &new_scale_h);
- dm_get_plane_scale(old_plane_state, &old_scale_w, &old_scale_h);
- if (new_scale_w != old_scale_w || new_scale_h != old_scale_h) {
- consider_mode_change = true;
- break;
- }
- }
-
- if (!consider_mode_change && !crtc_state->zpos_changed)
- return 0;
-
- /*
- * If no cursor change on this CRTC, and not enabled on this CRTC, then
- * no need to set cursor mode. This avoids needlessly locking the cursor
- * state.
- */
- if (!cursor_changed &&
- !(drm_plane_mask(crtc_state->crtc->cursor) & crtc_state->plane_mask)) {
- return 0;
- }
-
- cursor_state = drm_atomic_get_plane_state(state,
- crtc_state->crtc->cursor);
- if (IS_ERR(cursor_state))
- return PTR_ERR(cursor_state);
-
- /* Cursor is disabled */
- if (!cursor_state->fb)
- return 0;
-
- /* For all planes in descending z-order (all of which are below cursor
- * as per zpos definitions), check their scaling and format
- */
- for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, plane_state) {
-
- /* Only care about non-cursor planes on this CRTC */
- if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0 ||
- plane->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- /* Underlying plane is YUV format - use overlay cursor */
- if (amdgpu_dm_plane_is_video_format(plane_state->fb->format->format)) {
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
- return 0;
- }
-
- dm_get_plane_scale(plane_state,
- &underlying_scale_w, &underlying_scale_h);
- dm_get_plane_scale(cursor_state,
- &cursor_scale_w, &cursor_scale_h);
-
- /* Underlying plane has different scale - use overlay cursor */
- if (cursor_scale_w != underlying_scale_w &&
- cursor_scale_h != underlying_scale_h) {
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
- return 0;
- }
-
- /* If this plane covers the whole CRTC, no need to check planes underneath */
- if (plane_state->crtc_x <= 0 && plane_state->crtc_y <= 0 &&
- plane_state->crtc_x + plane_state->crtc_w >= crtc_state->mode.hdisplay &&
- plane_state->crtc_y + plane_state->crtc_h >= crtc_state->mode.vdisplay) {
- entire_crtc_covered = true;
- break;
- }
- }
-
- /* If planes do not cover the entire CRTC, use overlay mode to enable
- * cursor over holes
- */
- if (entire_crtc_covered)
- *cursor_mode = DM_CURSOR_NATIVE_MODE;
- else
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
-
- return 0;
-}
-
-/**
- * amdgpu_dm_atomic_check() - Atomic check implementation for AMDgpu DM.
- *
- * @dev: The DRM device
- * @state: The atomic state to commit
- *
- * Validate that the given atomic state is programmable by DC into hardware.
- * This involves constructing a &struct dc_state reflecting the new hardware
- * state we wish to commit, then querying DC to see if it is programmable. It's
- * important not to modify the existing DC state. Otherwise, atomic_check
- * may unexpectedly commit hardware changes.
- *
- * When validating the DC state, it's important that the right locks are
- * acquired. For full updates case which removes/adds/updates streams on one
- * CRTC while flipping on another CRTC, acquiring global lock will guarantee
- * that any such full update commit will wait for completion of any outstanding
- * flip using DRMs synchronization events.
- *
- * Note that DM adds the affected connectors for all CRTCs in state, when that
- * might not seem necessary. This is because DC stream creation requires the
- * DC sink, which is tied to the DRM connector state. Cleaning this up should
- * be possible but non-trivial - a possible TODO item.
- *
- * Return: -Error code if validation failed.
- */
-static int amdgpu_dm_atomic_check(struct drm_device *dev,
- struct drm_atomic_state *state)
-{
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_atomic_state *dm_state = NULL;
- struct dc *dc = adev->dm.dc;
- struct drm_connector *connector;
- struct drm_connector_state *old_con_state, *new_con_state;
- struct drm_crtc *crtc;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct drm_plane *plane;
- struct drm_plane_state *old_plane_state, *new_plane_state, *new_cursor_state;
- enum dc_status status;
- int ret, i;
- bool lock_and_validation_needed = false;
- bool is_top_most_overlay = true;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
- struct drm_dp_mst_topology_mgr *mgr;
- struct drm_dp_mst_topology_state *mst_state;
- struct dsc_mst_fairness_vars vars[MAX_PIPES] = {0};
-
- trace_amdgpu_dm_atomic_check_begin(state);
-
- ret = drm_atomic_helper_check_modeset(dev, state);
- if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_helper_check_modeset() failed\n");
- goto fail;
- }
-
- /* Check connector changes */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state);
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
-
- /* Skip connectors that are disabled or part of modeset already. */
- if (!new_con_state->crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_crtc_state(state, new_con_state->crtc);
- if (IS_ERR(new_crtc_state)) {
- drm_dbg_atomic(dev, "drm_atomic_get_crtc_state() failed\n");
- ret = PTR_ERR(new_crtc_state);
- goto fail;
- }
-
- if (dm_old_con_state->abm_level != dm_new_con_state->abm_level ||
- dm_old_con_state->scaling != dm_new_con_state->scaling)
- new_crtc_state->connectors_changed = true;
- }
-
- if (dc_resource_is_dsc_encoding_supported(dc)) {
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- if (drm_atomic_crtc_needs_modeset(new_crtc_state)) {
- ret = add_affected_mst_dsc_crtcs(state, crtc);
- if (ret) {
- drm_dbg_atomic(dev, "add_affected_mst_dsc_crtcs() failed\n");
- goto fail;
- }
- }
- }
- }
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state) &&
- !new_crtc_state->color_mgmt_changed &&
- old_crtc_state->vrr_enabled == new_crtc_state->vrr_enabled &&
- dm_old_crtc_state->dsc_force_changed == false)
- continue;
-
- ret = amdgpu_dm_verify_lut_sizes(new_crtc_state);
- if (ret) {
- drm_dbg_atomic(dev, "amdgpu_dm_verify_lut_sizes() failed\n");
- goto fail;
- }
-
- if (!new_crtc_state->enable)
- continue;
-
- ret = drm_atomic_add_affected_connectors(state, crtc);
- if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_add_affected_connectors() failed\n");
- goto fail;
- }
-
- ret = drm_atomic_add_affected_planes(state, crtc);
- if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_add_affected_planes() failed\n");
- goto fail;
- }
-
- if (dm_old_crtc_state->dsc_force_changed)
- new_crtc_state->mode_changed = true;
- }
-
- /*
- * Add all primary and overlay planes on the CRTC to the state
- * whenever a plane is enabled to maintain correct z-ordering
- * and to enable fast surface updates.
- */
- drm_for_each_crtc(crtc, dev) {
- bool modified = false;
-
- for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- if (new_plane_state->crtc == crtc ||
- old_plane_state->crtc == crtc) {
- modified = true;
- break;
- }
- }
-
- if (!modified)
- continue;
-
- drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) {
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- new_plane_state =
- drm_atomic_get_plane_state(state, plane);
-
- if (IS_ERR(new_plane_state)) {
- ret = PTR_ERR(new_plane_state);
- drm_dbg_atomic(dev, "new_plane_state is BAD\n");
- goto fail;
- }
- }
- }
-
- /*
- * DC consults the zpos (layer_index in DC terminology) to determine the
- * hw plane on which to enable the hw cursor (see
- * `dcn10_can_pipe_disable_cursor`). By now, all modified planes are in
- * atomic state, so call drm helper to normalize zpos.
- */
- ret = drm_atomic_normalize_zpos(dev, state);
- if (ret) {
- drm_dbg(dev, "drm_atomic_normalize_zpos() failed\n");
- goto fail;
- }
-
- /*
- * Determine whether cursors on each CRTC should be enabled in native or
- * overlay mode.
- */
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
- &dm_new_crtc_state->cursor_mode);
- if (ret) {
- drm_dbg(dev, "Failed to determine cursor mode\n");
- goto fail;
- }
- }
-
- /* Remove exiting planes if they are modified */
- for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, new_plane_state) {
- if (old_plane_state->fb && new_plane_state->fb &&
- get_mem_type(old_plane_state->fb) !=
- get_mem_type(new_plane_state->fb))
- lock_and_validation_needed = true;
-
- ret = dm_update_plane_state(dc, state, plane,
- old_plane_state,
- new_plane_state,
- false,
- &lock_and_validation_needed,
- &is_top_most_overlay);
- if (ret) {
- drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
- goto fail;
- }
- }
-
- /* Disable all crtcs which require disable */
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- ret = dm_update_crtc_state(&adev->dm, state, crtc,
- old_crtc_state,
- new_crtc_state,
- false,
- &lock_and_validation_needed);
- if (ret) {
- drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed\n");
- goto fail;
- }
- }
-
- /* Enable all crtcs which require enable */
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- ret = dm_update_crtc_state(&adev->dm, state, crtc,
- old_crtc_state,
- new_crtc_state,
- true,
- &lock_and_validation_needed);
- if (ret) {
- drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed\n");
- goto fail;
- }
- }
-
- /* Add new/modified planes */
- for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, new_plane_state) {
- ret = dm_update_plane_state(dc, state, plane,
- old_plane_state,
- new_plane_state,
- true,
- &lock_and_validation_needed,
- &is_top_most_overlay);
- if (ret) {
- drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
- goto fail;
- }
- }
-
-#if defined(CONFIG_DRM_AMD_DC_FP)
- if (dc_resource_is_dsc_encoding_supported(dc)) {
- ret = pre_validate_dsc(state, &dm_state, vars);
- if (ret != 0)
- goto fail;
- }
-#endif
-
- /* Run this here since we want to validate the streams we created */
- ret = drm_atomic_helper_check_planes(dev, state);
- if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_helper_check_planes() failed\n");
- goto fail;
- }
-
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (dm_new_crtc_state->mpo_requested)
- drm_dbg_atomic(dev, "MPO enablement requested on crtc:[%p]\n", crtc);
- }
-
- /* Check cursor restrictions */
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- enum amdgpu_dm_cursor_mode required_cursor_mode;
- int is_rotated, is_scaled;
-
- /* Overlay cusor not subject to native cursor restrictions */
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (dm_new_crtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE)
- continue;
-
- /* Check if rotation or scaling is enabled on DCN401 */
- if ((drm_plane_mask(crtc->cursor) & new_crtc_state->plane_mask) &&
- amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1)) {
- new_cursor_state = drm_atomic_get_new_plane_state(state, crtc->cursor);
-
- is_rotated = new_cursor_state &&
- ((new_cursor_state->rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_0);
- is_scaled = new_cursor_state && ((new_cursor_state->src_w >> 16 != new_cursor_state->crtc_w) ||
- (new_cursor_state->src_h >> 16 != new_cursor_state->crtc_h));
-
- if (is_rotated || is_scaled) {
- drm_dbg_driver(
- crtc->dev,
- "[CRTC:%d:%s] cannot enable hardware cursor due to rotation/scaling\n",
- crtc->base.id, crtc->name);
- ret = -EINVAL;
- goto fail;
- }
- }
-
- /* If HW can only do native cursor, check restrictions again */
- ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
- &required_cursor_mode);
- if (ret) {
- drm_dbg_driver(crtc->dev,
- "[CRTC:%d:%s] Checking cursor mode failed\n",
- crtc->base.id, crtc->name);
- goto fail;
- } else if (required_cursor_mode == DM_CURSOR_OVERLAY_MODE) {
- drm_dbg_driver(crtc->dev,
- "[CRTC:%d:%s] Cannot enable native cursor due to scaling or YUV restrictions\n",
- crtc->base.id, crtc->name);
- ret = -EINVAL;
- goto fail;
- }
- }
-
- if (state->legacy_cursor_update) {
- /*
- * This is a fast cursor update coming from the plane update
- * helper, check if it can be done asynchronously for better
- * performance.
- */
- state->async_update =
- !drm_atomic_helper_async_check(dev, state);
-
- /*
- * Skip the remaining global validation if this is an async
- * update. Cursor updates can be done without affecting
- * state or bandwidth calcs and this avoids the performance
- * penalty of locking the private state object and
- * allocating a new dc_state.
- */
- if (state->async_update)
- return 0;
- }
-
- /* Check scaling and underscan changes*/
- /* TODO Removed scaling changes validation due to inability to commit
- * new stream into context w\o causing full reset. Need to
- * decide how to handle.
- */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- struct dm_connector_state *dm_old_con_state = to_dm_connector_state(old_con_state);
- struct dm_connector_state *dm_new_con_state = to_dm_connector_state(new_con_state);
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(dm_new_con_state->base.crtc);
-
- /* Skip any modesets/resets */
- if (!acrtc || drm_atomic_crtc_needs_modeset(
- drm_atomic_get_new_crtc_state(state, &acrtc->base)))
- continue;
-
- /* Skip any thing not scale or underscan changes */
- if (!is_scaling_state_different(dm_new_con_state, dm_old_con_state))
- continue;
-
- lock_and_validation_needed = true;
- }
-
- /* set the slot info for each mst_state based on the link encoding format */
- for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- u8 link_coding_cap;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
- if (connector->index == mst_state->mgr->conn_base_id) {
- aconnector = to_amdgpu_dm_connector(connector);
- link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(aconnector->dc_link);
- drm_dp_mst_update_slots(mst_state, link_coding_cap);
-
- break;
- }
- }
- drm_connector_list_iter_end(&iter);
- }
-
- /**
- * Streams and planes are reset when there are changes that affect
- * bandwidth. Anything that affects bandwidth needs to go through
- * DC global validation to ensure that the configuration can be applied
- * to hardware.
- *
- * We have to currently stall out here in atomic_check for outstanding
- * commits to finish in this case because our IRQ handlers reference
- * DRM state directly - we can end up disabling interrupts too early
- * if we don't.
- *
- * TODO: Remove this stall and drop DM state private objects.
- */
- if (lock_and_validation_needed) {
- ret = dm_atomic_get_state(state, &dm_state);
- if (ret) {
- drm_dbg_atomic(dev, "dm_atomic_get_state() failed\n");
- goto fail;
- }
-
- ret = do_aquire_global_lock(dev, state);
- if (ret) {
- drm_dbg_atomic(dev, "do_aquire_global_lock() failed\n");
- goto fail;
- }
-
-#if defined(CONFIG_DRM_AMD_DC_FP)
- if (dc_resource_is_dsc_encoding_supported(dc)) {
- ret = compute_mst_dsc_configs_for_state(state, dm_state->context, vars);
- if (ret) {
- drm_dbg_atomic(dev, "compute_mst_dsc_configs_for_state() failed\n");
- ret = -EINVAL;
- goto fail;
- }
- }
-#endif
-
- ret = dm_update_mst_vcpi_slots_for_dsc(state, dm_state->context, vars);
- if (ret) {
- drm_dbg_atomic(dev, "dm_update_mst_vcpi_slots_for_dsc() failed\n");
- goto fail;
- }
-
- /*
- * Perform validation of MST topology in the state:
- * We need to perform MST atomic check before calling
- * dc_validate_global_state(), or there is a chance
- * to get stuck in an infinite loop and hang eventually.
- */
- ret = drm_dp_mst_atomic_check(state);
- if (ret) {
- drm_dbg_atomic(dev, "drm_dp_mst_atomic_check() failed\n");
- goto fail;
- }
- status = dc_validate_global_state(dc, dm_state->context, true);
- if (status != DC_OK) {
- drm_dbg_atomic(dev, "DC global validation failure: %s (%d)",
- dc_status_to_str(status), status);
- ret = -EINVAL;
- goto fail;
- }
- } else {
- /*
- * The commit is a fast update. Fast updates shouldn't change
- * the DC context, affect global validation, and can have their
- * commit work done in parallel with other commits not touching
- * the same resource. If we have a new DC context as part of
- * the DM atomic state from validation we need to free it and
- * retain the existing one instead.
- *
- * Furthermore, since the DM atomic state only contains the DC
- * context and can safely be annulled, we can free the state
- * and clear the associated private object now to free
- * some memory and avoid a possible use-after-free later.
- */
-
- for (i = 0; i < state->num_private_objs; i++) {
- struct drm_private_obj *obj = state->private_objs[i].ptr;
-
- if (obj->funcs == adev->dm.atomic_obj.funcs) {
- int j = state->num_private_objs-1;
-
- dm_atomic_destroy_state(obj,
- state->private_objs[i].state);
-
- /* If i is not at the end of the array then the
- * last element needs to be moved to where i was
- * before the array can safely be truncated.
- */
- if (i != j)
- state->private_objs[i] =
- state->private_objs[j];
-
- state->private_objs[j].ptr = NULL;
- state->private_objs[j].state = NULL;
- state->private_objs[j].old_state = NULL;
- state->private_objs[j].new_state = NULL;
-
- state->num_private_objs = j;
- break;
- }
- }
- }
-
- /* Store the overall update type for use later in atomic check. */
- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
- struct dm_crtc_state *dm_new_crtc_state =
- to_dm_crtc_state(new_crtc_state);
-
- /*
- * Only allow async flips for fast updates that don't change
- * the FB pitch, the DCC state, rotation, etc.
- */
- if (new_crtc_state->async_flip && lock_and_validation_needed) {
- drm_dbg_atomic(crtc->dev,
- "[CRTC:%d:%s] async flips are only supported for fast updates\n",
- crtc->base.id, crtc->name);
- ret = -EINVAL;
- goto fail;
- }
-
- dm_new_crtc_state->update_type = lock_and_validation_needed ?
- UPDATE_TYPE_FULL : UPDATE_TYPE_FAST;
- }
-
- /* Must be success */
- WARN_ON(ret);
-
- trace_amdgpu_dm_atomic_check_finish(state, ret);
-
- return ret;
-
-fail:
- if (ret == -EDEADLK)
- drm_dbg_atomic(dev, "Atomic check stopped to avoid deadlock.\n");
- else if (ret == -EINTR || ret == -EAGAIN || ret == -ERESTARTSYS)
- drm_dbg_atomic(dev, "Atomic check stopped due to signal.\n");
- else
- drm_dbg_atomic(dev, "Atomic check failed with err: %d\n", ret);
-
- trace_amdgpu_dm_atomic_check_finish(state, ret);
-
- 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_ERROR("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_ERROR("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;
- } else {
- DRM_WARN("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;
- 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,
- struct edid *edid)
-{
- u8 *edid_ext = NULL;
- int i;
- int j = 0;
- u16 min_vfreq;
- u16 max_vfreq;
-
- if (edid == NULL || edid->extensions == 0)
- 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 (edid_ext == NULL)
- 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 parse_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- u8 *edid_ext = NULL;
- int i;
- int j = 0;
-
- if (edid == NULL || edid->extensions == 0)
- return -ENODEV;
-
- /* Find DisplayID extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (void *)(edid + (i + 1));
- if (edid_ext[0] == DISPLAYID_EXT)
- break;
- }
-
- while (j < EDID_LENGTH) {
- struct amd_vsdb_block *amd_vsdb = (struct amd_vsdb_block *)&edid_ext[j];
- unsigned int ieeeId = (amd_vsdb->ieee_id[2] << 16) | (amd_vsdb->ieee_id[1] << 8) | (amd_vsdb->ieee_id[0]);
-
- if (ieeeId == HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID &&
- amd_vsdb->version == HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3) {
- vsdb_info->replay_mode = (amd_vsdb->feature_caps & AMD_VSDB_VERSION_3_FEATURECAP_REPLAYMODE) ? true : false;
- vsdb_info->amd_vsdb_version = HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3;
- DRM_DEBUG_KMS("Panel supports Replay Mode: %d\n", vsdb_info->replay_mode);
-
- return true;
- }
- j++;
- }
-
- return false;
-}
-
-static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- 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.
- * @edid: EDID from monitor
- *
- * 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,
- struct edid *edid)
-{
- int i = 0;
- struct detailed_timing *timing;
- struct detailed_non_pixel *data;
- struct detailed_data_monitor_range *range;
- 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};
- bool freesync_capable = false;
- enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE;
-
- if (!connector->state) {
- DRM_ERROR("%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;
-
- if (!edid || !sink) {
- dm_con_state = to_dm_connector_state(connector->state);
-
- amdgpu_dm_connector->min_vfreq = 0;
- amdgpu_dm_connector->max_vfreq = 0;
- connector->display_info.monitor_range.min_vfreq = 0;
- connector->display_info.monitor_range.max_vfreq = 0;
- freesync_capable = false;
-
- goto update;
- }
-
- dm_con_state = to_dm_connector_state(connector->state);
-
- if (!adev->dm.freesync_module)
- goto update;
-
- /* 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)) {
- bool edid_check_required = false;
-
- if (amdgpu_dm_connector->dc_link &&
- amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
- if (edid->features & DRM_EDID_FEATURE_CONTINUOUS_FREQ) {
- 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;
- } else {
- edid_check_required = edid->version > 1 ||
- (edid->version == 1 &&
- edid->revision > 1);
- }
- }
-
- if (edid_check_required) {
- for (i = 0; i < 4; i++) {
-
- timing = &edid->detailed_timings[i];
- data = &timing->data.other_data;
- range = &data->data.range;
- /*
- * Check if monitor has continuous frequency mode
- */
- if (data->type != EDID_DETAIL_MONITOR_RANGE)
- continue;
- /*
- * Check for flag range limits only. If flag == 1 then
- * no additional timing information provided.
- * Default GTF, GTF Secondary curve and CVT are not
- * supported
- */
- if (range->flags != 1)
- continue;
-
- connector->display_info.monitor_range.min_vfreq = range->min_vfreq;
- connector->display_info.monitor_range.max_vfreq = range->max_vfreq;
-
- if (edid->revision >= 4) {
- if (data->pad2 & DRM_EDID_RANGE_OFFSET_MIN_VFREQ)
- connector->display_info.monitor_range.min_vfreq += 255;
- if (data->pad2 & DRM_EDID_RANGE_OFFSET_MAX_VFREQ)
- connector->display_info.monitor_range.max_vfreq += 255;
- }
-
- amdgpu_dm_connector->min_vfreq =
- connector->display_info.monitor_range.min_vfreq;
- amdgpu_dm_connector->max_vfreq =
- connector->display_info.monitor_range.max_vfreq;
-
- break;
- }
-
- if (amdgpu_dm_connector->max_vfreq -
- amdgpu_dm_connector->min_vfreq > 10) {
-
- freesync_capable = true;
- }
- }
- parse_amd_vsdb(amdgpu_dm_connector, edid, &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 (edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0 && vsdb_info.freesync_supported) {
- timing = &edid->detailed_timings[i];
- data = &timing->data.other_data;
-
- 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 && 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->vsdb_info = vsdb_info;
-
- 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;
- }
- }
-
-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);
- struct dc *dc = adev->dm.dc;
- int i;
-
- mutex_lock(&adev->dm.dc_lock);
- if (dc->current_state) {
- for (i = 0; i < dc->current_state->stream_count; ++i)
- dc->current_state->streams[i]
- ->triggered_crtc_reset.enabled =
- adev->dm.force_timing_sync;
-
- dm_enable_per_frame_crtc_master_sync(dc->current_state);
- dc_trigger_sync(dc, dc->current_state);
- }
- 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)
-{
-#ifdef DM_CHECK_ADDR_0
- if (address == 0) {
- drm_err(adev_to_drm(ctx->driver_context),
- "invalid register write. address = 0");
- return;
- }
-#endif
-
- amdgpu_dm_exit_ips_for_hw_access(ctx->dc);
- cgs_write_register(ctx->cgs_device, address, value);
- trace_amdgpu_dc_wreg(&ctx->perf_trace->write_count, address, value);
-}
-
-uint32_t dm_read_reg_func(const struct dc_context *ctx, uint32_t address,
- const char *func_name)
-{
- u32 value;
-#ifdef DM_CHECK_ADDR_0
- if (address == 0) {
- drm_err(adev_to_drm(ctx->driver_context),
- "invalid register read; address = 0\n");
- return 0;
- }
-#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);
-
- trace_amdgpu_dc_rreg(&ctx->perf_trace->read_count, address, value);
-
- 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_ERROR("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("DPIA AUX failed on 0x%x(%d), error %d\n",
- payload->address, payload->length,
- p_notify->result);
- }
- *operation_result = AUX_RET_ERROR_INVALID_REPLY;
- goto out;
- }
-
-
- payload->reply[0] = adev->dm.dmub_notify->aux_reply.command;
- if (!payload->write && p_notify->aux_reply.length &&
- (payload->reply[0] == AUX_TRANSACTION_REPLY_AUX_ACK)) {
-
- if (payload->length != p_notify->aux_reply.length) {
- DRM_WARN("invalid read length %d from DPIA AUX 0x%x(%d)!\n",
- p_notify->aux_reply.length,
- payload->address, payload->length);
- *operation_result = AUX_RET_ERROR_INVALID_REPLY;
- goto out;
- }
-
- 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;
-}
-
-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_ERROR("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)
-{
- 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)
-{
- return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type);
-}
diff --git a/rr-cache/3b5fa8b49e38141113421fdc2503fb1e1928096c/preimage b/rr-cache/3b5fa8b49e38141113421fdc2503fb1e1928096c/preimage
deleted file mode 100644
index 454f2a4c2cbe..000000000000
--- a/rr-cache/3b5fa8b49e38141113421fdc2503fb1e1928096c/preimage
+++ /dev/null
@@ -1,36 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2019 Intel Corporation
- */
-
-#ifndef __INTEL_DP_MST_H__
-#define __INTEL_DP_MST_H__
-
-#include <linux/types.h>
-
-struct intel_atomic_state;
-struct intel_crtc;
-struct intel_crtc_state;
-struct intel_digital_port;
-struct intel_dp;
-struct intel_link_bw_limits;
-
-int intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_id);
-void intel_dp_mst_encoder_cleanup(struct intel_digital_port *dig_port);
-int intel_dp_mst_encoder_active_links(struct intel_digital_port *dig_port);
-bool intel_dp_mst_is_master_trans(const struct intel_crtc_state *crtc_state);
-bool intel_dp_mst_is_slave_trans(const struct intel_crtc_state *crtc_state);
-bool intel_dp_mst_source_support(struct intel_dp *intel_dp);
-int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state *state,
- struct intel_crtc *crtc);
-int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
- struct intel_link_bw_limits *limits);
-bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
- struct intel_crtc *crtc);
-<<<<<<<
-=======
-void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp);
-bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp);
->>>>>>>
-
-#endif /* __INTEL_DP_MST_H__ */
diff --git a/rr-cache/41549dd6cc337627199acbe6749c5685c7e927d3/preimage b/rr-cache/41549dd6cc337627199acbe6749c5685c7e927d3/preimage
deleted file mode 100644
index 232d29e9a561..000000000000
--- a/rr-cache/41549dd6cc337627199acbe6749c5685c7e927d3/preimage
+++ /dev/null
@@ -1,2234 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2022 Intel Corporation
- */
-
-#include <linux/dma-fence-chain.h>
-
-#include "xe_pt.h"
-
-#include "regs/xe_gtt_defs.h"
-#include "xe_bo.h"
-#include "xe_device.h"
-#include "xe_drm_client.h"
-#include "xe_gt.h"
-#include "xe_gt_tlb_invalidation.h"
-#include "xe_migrate.h"
-#include "xe_pt_types.h"
-#include "xe_pt_walk.h"
-#include "xe_res_cursor.h"
-#include "xe_trace.h"
-#include "xe_ttm_stolen_mgr.h"
-#include "xe_vm.h"
-
-struct xe_pt_dir {
- struct xe_pt pt;
- /** @children: Array of page-table child nodes */
- struct xe_ptw *children[XE_PDES];
-};
-
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)
-#define xe_pt_set_addr(__xe_pt, __addr) ((__xe_pt)->addr = (__addr))
-#define xe_pt_addr(__xe_pt) ((__xe_pt)->addr)
-#else
-#define xe_pt_set_addr(__xe_pt, __addr)
-#define xe_pt_addr(__xe_pt) 0ull
-#endif
-
-static const u64 xe_normal_pt_shifts[] = {12, 21, 30, 39, 48};
-static const u64 xe_compact_pt_shifts[] = {16, 21, 30, 39, 48};
-
-#define XE_PT_HIGHEST_LEVEL (ARRAY_SIZE(xe_normal_pt_shifts) - 1)
-
-static struct xe_pt_dir *as_xe_pt_dir(struct xe_pt *pt)
-{
- return container_of(pt, struct xe_pt_dir, pt);
-}
-
-static struct xe_pt *xe_pt_entry(struct xe_pt_dir *pt_dir, unsigned int index)
-{
- return container_of(pt_dir->children[index], struct xe_pt, base);
-}
-
-static u64 __xe_pt_empty_pte(struct xe_tile *tile, struct xe_vm *vm,
- unsigned int level)
-{
- struct xe_device *xe = tile_to_xe(tile);
- u16 pat_index = xe->pat.idx[XE_CACHE_WB];
- u8 id = tile->id;
-
- if (!xe_vm_has_scratch(vm))
- return 0;
-
- if (level > MAX_HUGEPTE_LEVEL)
- return vm->pt_ops->pde_encode_bo(vm->scratch_pt[id][level - 1]->bo,
- 0, pat_index);
-
- return vm->pt_ops->pte_encode_addr(xe, 0, pat_index, level, IS_DGFX(xe), 0) |
- XE_PTE_NULL;
-}
-
-static void xe_pt_free(struct xe_pt *pt)
-{
- if (pt->level)
- kfree(as_xe_pt_dir(pt));
- else
- kfree(pt);
-}
-
-/**
- * xe_pt_create() - Create a page-table.
- * @vm: The vm to create for.
- * @tile: The tile to create for.
- * @level: The page-table level.
- *
- * Allocate and initialize a single struct xe_pt metadata structure. Also
- * create the corresponding page-table bo, but don't initialize it. If the
- * level is grater than zero, then it's assumed to be a directory page-
- * table and the directory structure is also allocated and initialized to
- * NULL pointers.
- *
- * Return: A valid struct xe_pt pointer on success, Pointer error code on
- * error.
- */
-struct xe_pt *xe_pt_create(struct xe_vm *vm, struct xe_tile *tile,
- unsigned int level)
-{
- struct xe_pt *pt;
- struct xe_bo *bo;
- int err;
-
- if (level) {
- struct xe_pt_dir *dir = kzalloc(sizeof(*dir), GFP_KERNEL);
-
- pt = (dir) ? &dir->pt : NULL;
- } else {
- pt = kzalloc(sizeof(*pt), GFP_KERNEL);
- }
- if (!pt)
- return ERR_PTR(-ENOMEM);
-
- pt->level = level;
- bo = xe_bo_create_pin_map(vm->xe, tile, vm, SZ_4K,
- ttm_bo_type_kernel,
- XE_BO_FLAG_VRAM_IF_DGFX(tile) |
- XE_BO_FLAG_IGNORE_MIN_PAGE_SIZE |
- XE_BO_FLAG_PINNED |
- XE_BO_FLAG_NO_RESV_EVICT |
- XE_BO_FLAG_PAGETABLE);
- if (IS_ERR(bo)) {
- err = PTR_ERR(bo);
- goto err_kfree;
- }
- pt->bo = bo;
- pt->base.children = level ? as_xe_pt_dir(pt)->children : NULL;
-
- if (vm->xef)
- xe_drm_client_add_bo(vm->xef->client, pt->bo);
- xe_tile_assert(tile, level <= XE_VM_MAX_LEVEL);
-
- return pt;
-
-err_kfree:
- xe_pt_free(pt);
- return ERR_PTR(err);
-}
-
-/**
- * xe_pt_populate_empty() - Populate a page-table bo with scratch- or zero
- * entries.
- * @tile: The tile the scratch pagetable of which to use.
- * @vm: The vm we populate for.
- * @pt: The pagetable the bo of which to initialize.
- *
- * Populate the page-table bo of @pt with entries pointing into the tile's
- * scratch page-table tree if any. Otherwise populate with zeros.
- */
-void xe_pt_populate_empty(struct xe_tile *tile, struct xe_vm *vm,
- struct xe_pt *pt)
-{
- struct iosys_map *map = &pt->bo->vmap;
- u64 empty;
- int i;
-
- if (!xe_vm_has_scratch(vm)) {
- /*
- * FIXME: Some memory is allocated already allocated to zero?
- * Find out which memory that is and avoid this memset...
- */
- xe_map_memset(vm->xe, map, 0, 0, SZ_4K);
- } else {
- empty = __xe_pt_empty_pte(tile, vm, pt->level);
- for (i = 0; i < XE_PDES; i++)
- xe_pt_write(vm->xe, map, i, empty);
- }
-}
-
-/**
- * xe_pt_shift() - Return the ilog2 value of the size of the address range of
- * a page-table at a certain level.
- * @level: The level.
- *
- * Return: The ilog2 value of the size of the address range of a page-table
- * at level @level.
- */
-unsigned int xe_pt_shift(unsigned int level)
-{
- return XE_PTE_SHIFT + XE_PDE_SHIFT * level;
-}
-
-/**
- * xe_pt_destroy() - Destroy a page-table tree.
- * @pt: The root of the page-table tree to destroy.
- * @flags: vm flags. Currently unused.
- * @deferred: List head of lockless list for deferred putting. NULL for
- * immediate putting.
- *
- * Puts the page-table bo, recursively calls xe_pt_destroy on all children
- * and finally frees @pt. TODO: Can we remove the @flags argument?
- */
-void xe_pt_destroy(struct xe_pt *pt, u32 flags, struct llist_head *deferred)
-{
- int i;
-
- if (!pt)
- return;
-
- XE_WARN_ON(!list_empty(&pt->bo->ttm.base.gpuva.list));
- xe_bo_unpin(pt->bo);
- xe_bo_put_deferred(pt->bo, deferred);
-
- if (pt->level > 0 && pt->num_live) {
- struct xe_pt_dir *pt_dir = as_xe_pt_dir(pt);
-
- for (i = 0; i < XE_PDES; i++) {
- if (xe_pt_entry(pt_dir, i))
- xe_pt_destroy(xe_pt_entry(pt_dir, i), flags,
- deferred);
- }
- }
- xe_pt_free(pt);
-}
-
-/**
- * DOC: Pagetable building
- *
- * Below we use the term "page-table" for both page-directories, containing
- * pointers to lower level page-directories or page-tables, and level 0
- * page-tables that contain only page-table-entries pointing to memory pages.
- *
- * When inserting an address range in an already existing page-table tree
- * there will typically be a set of page-tables that are shared with other
- * address ranges, and a set that are private to this address range.
- * The set of shared page-tables can be at most two per level,
- * and those can't be updated immediately because the entries of those
- * page-tables may still be in use by the gpu for other mappings. Therefore
- * when inserting entries into those, we instead stage those insertions by
- * adding insertion data into struct xe_vm_pgtable_update structures. This
- * data, (subtrees for the cpu and page-table-entries for the gpu) is then
- * added in a separate commit step. CPU-data is committed while still under the
- * vm lock, the object lock and for userptr, the notifier lock in read mode.
- * The GPU async data is committed either by the GPU or CPU after fulfilling
- * relevant dependencies.
- * For non-shared page-tables (and, in fact, for shared ones that aren't
- * existing at the time of staging), we add the data in-place without the
- * special update structures. This private part of the page-table tree will
- * remain disconnected from the vm page-table tree until data is committed to
- * the shared page tables of the vm tree in the commit phase.
- */
-
-struct xe_pt_update {
- /** @update: The update structure we're building for this parent. */
- struct xe_vm_pgtable_update *update;
- /** @parent: The parent. Used to detect a parent change. */
- struct xe_pt *parent;
- /** @preexisting: Whether the parent was pre-existing or allocated */
- bool preexisting;
-};
-
-struct xe_pt_stage_bind_walk {
- /** base: The base class. */
- struct xe_pt_walk base;
-
- /* Input parameters for the walk */
- /** @vm: The vm we're building for. */
- struct xe_vm *vm;
- /** @tile: The tile we're building for. */
- struct xe_tile *tile;
- /** @default_pte: PTE flag only template. No address is associated */
- u64 default_pte;
- /** @dma_offset: DMA offset to add to the PTE. */
- u64 dma_offset;
- /**
- * @needs_64k: This address range enforces 64K alignment and
- * granularity.
- */
- bool needs_64K;
- /**
- * @vma: VMA being mapped
- */
- struct xe_vma *vma;
-
- /* Also input, but is updated during the walk*/
- /** @curs: The DMA address cursor. */
- struct xe_res_cursor *curs;
- /** @va_curs_start: The Virtual address coresponding to @curs->start */
- u64 va_curs_start;
-
- /* Output */
- struct xe_walk_update {
- /** @wupd.entries: Caller provided storage. */
- struct xe_vm_pgtable_update *entries;
- /** @wupd.num_used_entries: Number of update @entries used. */
- unsigned int num_used_entries;
- /** @wupd.updates: Tracks the update entry at a given level */
- struct xe_pt_update updates[XE_VM_MAX_LEVEL + 1];
- } wupd;
-
- /* Walk state */
- /**
- * @l0_end_addr: The end address of the current l0 leaf. Used for
- * 64K granularity detection.
- */
- u64 l0_end_addr;
- /** @addr_64K: The start address of the current 64K chunk. */
- u64 addr_64K;
- /** @found_64: Whether @add_64K actually points to a 64K chunk. */
- bool found_64K;
-};
-
-static int
-xe_pt_new_shared(struct xe_walk_update *wupd, struct xe_pt *parent,
- pgoff_t offset, bool alloc_entries)
-{
- struct xe_pt_update *upd = &wupd->updates[parent->level];
- struct xe_vm_pgtable_update *entry;
-
- /*
- * For *each level*, we could only have one active
- * struct xt_pt_update at any one time. Once we move on to a
- * new parent and page-directory, the old one is complete, and
- * updates are either already stored in the build tree or in
- * @wupd->entries
- */
- if (likely(upd->parent == parent))
- return 0;
-
- upd->parent = parent;
- upd->preexisting = true;
-
- if (wupd->num_used_entries == XE_VM_MAX_LEVEL * 2 + 1)
- return -EINVAL;
-
- entry = wupd->entries + wupd->num_used_entries++;
- upd->update = entry;
- entry->ofs = offset;
- entry->pt_bo = parent->bo;
- entry->pt = parent;
- entry->flags = 0;
- entry->qwords = 0;
-
- if (alloc_entries) {
- entry->pt_entries = kmalloc_array(XE_PDES,
- sizeof(*entry->pt_entries),
- GFP_KERNEL);
- if (!entry->pt_entries)
- return -ENOMEM;
- }
-
- return 0;
-}
-
-/*
- * NOTE: This is a very frequently called function so we allow ourselves
- * to annotate (using branch prediction hints) the fastpath of updating a
- * non-pre-existing pagetable with leaf ptes.
- */
-static int
-xe_pt_insert_entry(struct xe_pt_stage_bind_walk *xe_walk, struct xe_pt *parent,
- pgoff_t offset, struct xe_pt *xe_child, u64 pte)
-{
- struct xe_pt_update *upd = &xe_walk->wupd.updates[parent->level];
- struct xe_pt_update *child_upd = xe_child ?
- &xe_walk->wupd.updates[xe_child->level] : NULL;
- int ret;
-
- ret = xe_pt_new_shared(&xe_walk->wupd, parent, offset, true);
- if (unlikely(ret))
- return ret;
-
- /*
- * Register this new pagetable so that it won't be recognized as
- * a shared pagetable by a subsequent insertion.
- */
- if (unlikely(child_upd)) {
- child_upd->update = NULL;
- child_upd->parent = xe_child;
- child_upd->preexisting = false;
- }
-
- if (likely(!upd->preexisting)) {
- /* Continue building a non-connected subtree. */
- struct iosys_map *map = &parent->bo->vmap;
-
- if (unlikely(xe_child))
- parent->base.children[offset] = &xe_child->base;
-
- xe_pt_write(xe_walk->vm->xe, map, offset, pte);
- parent->num_live++;
- } else {
- /* Shared pt. Stage update. */
- unsigned int idx;
- struct xe_vm_pgtable_update *entry = upd->update;
-
- idx = offset - entry->ofs;
- entry->pt_entries[idx].pt = xe_child;
- entry->pt_entries[idx].pte = pte;
- entry->qwords++;
- }
-
- return 0;
-}
-
-static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
- struct xe_pt_stage_bind_walk *xe_walk)
-{
- u64 size, dma;
-
- if (level > MAX_HUGEPTE_LEVEL)
- return false;
-
- /* Does the virtual range requested cover a huge pte? */
- if (!xe_pt_covers(addr, next, level, &xe_walk->base))
- return false;
-
- /* Does the DMA segment cover the whole pte? */
- 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))
- return true;
-
- /* Is the DMA address huge PTE size aligned? */
- size = next - addr;
- dma = addr - xe_walk->va_curs_start + xe_res_dma(xe_walk->curs);
-
- return IS_ALIGNED(dma, size);
-}
-
-/*
- * Scan the requested mapping to check whether it can be done entirely
- * with 64K PTEs.
- */
-static bool
-xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk)
-{
- struct xe_res_cursor curs = *xe_walk->curs;
-
- if (!IS_ALIGNED(addr, SZ_64K))
- return false;
-
- 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))
- return true;
-
- xe_res_next(&curs, addr - xe_walk->va_curs_start);
- for (; addr < next; addr += SZ_64K) {
- if (!IS_ALIGNED(xe_res_dma(&curs), SZ_64K) || curs.size < SZ_64K)
- return false;
-
- xe_res_next(&curs, SZ_64K);
- }
-
- return addr == next;
-}
-
-/*
- * For non-compact "normal" 4K level-0 pagetables, we want to try to group
- * addresses together in 64K-contigous regions to add a 64K TLB hint for the
- * device to the PTE.
- * This function determines whether the address is part of such a
- * segment. For VRAM in normal pagetables, this is strictly necessary on
- * some devices.
- */
-static bool
-xe_pt_is_pte_ps64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk)
-{
- /* Address is within an already found 64k region */
- if (xe_walk->found_64K && addr - xe_walk->addr_64K < SZ_64K)
- return true;
-
- xe_walk->found_64K = xe_pt_scan_64K(addr, addr + SZ_64K, xe_walk);
- xe_walk->addr_64K = addr;
-
- return xe_walk->found_64K;
-}
-
-static int
-xe_pt_stage_bind_entry(struct xe_ptw *parent, pgoff_t offset,
- unsigned int level, u64 addr, u64 next,
- struct xe_ptw **child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- struct xe_pt_stage_bind_walk *xe_walk =
- container_of(walk, typeof(*xe_walk), base);
- u16 pat_index = xe_walk->vma->pat_index;
- struct xe_pt *xe_parent = container_of(parent, typeof(*xe_parent), base);
- struct xe_vm *vm = xe_walk->vm;
- struct xe_pt *xe_child;
- bool covers;
- int ret = 0;
- u64 pte;
-
- /* Is this a leaf entry ?*/
- if (level == 0 || xe_pt_hugepte_possible(addr, next, level, xe_walk)) {
- struct xe_res_cursor *curs = xe_walk->curs;
- bool is_null = xe_vma_is_null(xe_walk->vma);
-
- XE_WARN_ON(xe_walk->va_curs_start != addr);
-
- pte = vm->pt_ops->pte_encode_vma(is_null ? 0 :
- xe_res_dma(curs) + xe_walk->dma_offset,
- xe_walk->vma, pat_index, level);
- pte |= xe_walk->default_pte;
-
- /*
- * Set the XE_PTE_PS64 hint if possible, otherwise if
- * this device *requires* 64K PTE size for VRAM, fail.
- */
- if (level == 0 && !xe_parent->is_compact) {
- if (xe_pt_is_pte_ps64K(addr, next, xe_walk)) {
- xe_walk->vma->gpuva.flags |= XE_VMA_PTE_64K;
- pte |= XE_PTE_PS64;
- } else if (XE_WARN_ON(xe_walk->needs_64K)) {
- return -EINVAL;
- }
- }
-
- ret = xe_pt_insert_entry(xe_walk, xe_parent, offset, NULL, pte);
- if (unlikely(ret))
- return ret;
-
- if (!is_null)
- xe_res_next(curs, next - addr);
- xe_walk->va_curs_start = next;
- xe_walk->vma->gpuva.flags |= (XE_VMA_PTE_4K << level);
- *action = ACTION_CONTINUE;
-
- return ret;
- }
-
- /*
- * Descending to lower level. Determine if we need to allocate a
- * new page table or -directory, which we do if there is no
- * previous one or there is one we can completely replace.
- */
- if (level == 1) {
- walk->shifts = xe_normal_pt_shifts;
- xe_walk->l0_end_addr = next;
- }
-
- covers = xe_pt_covers(addr, next, level, &xe_walk->base);
- if (covers || !*child) {
- u64 flags = 0;
-
- xe_child = xe_pt_create(xe_walk->vm, xe_walk->tile, level - 1);
- if (IS_ERR(xe_child))
- return PTR_ERR(xe_child);
-
- xe_pt_set_addr(xe_child,
- round_down(addr, 1ull << walk->shifts[level]));
-
- if (!covers)
- xe_pt_populate_empty(xe_walk->tile, xe_walk->vm, xe_child);
-
- *child = &xe_child->base;
-
- /*
- * Prefer the compact pagetable layout for L0 if possible. Only
- * possible if VMA covers entire 2MB region as compact 64k and
- * 4k pages cannot be mixed within a 2MB region.
- * TODO: Suballocate the pt bo to avoid wasting a lot of
- * memory.
- */
- if (GRAPHICS_VERx100(tile_to_xe(xe_walk->tile)) >= 1250 && level == 1 &&
- covers && xe_pt_scan_64K(addr, next, xe_walk)) {
- walk->shifts = xe_compact_pt_shifts;
- xe_walk->vma->gpuva.flags |= XE_VMA_PTE_COMPACT;
- flags |= XE_PDE_64K;
- xe_child->is_compact = true;
- }
-
- pte = vm->pt_ops->pde_encode_bo(xe_child->bo, 0, pat_index) | flags;
- ret = xe_pt_insert_entry(xe_walk, xe_parent, offset, xe_child,
- pte);
- }
-
- *action = ACTION_SUBTREE;
- return ret;
-}
-
-static const struct xe_pt_walk_ops xe_pt_stage_bind_ops = {
- .pt_entry = xe_pt_stage_bind_entry,
-};
-
-/**
- * xe_pt_stage_bind() - Build a disconnected page-table tree for a given address
- * range.
- * @tile: The tile we're building for.
- * @vma: The vma indicating the address range.
- * @entries: Storage for the update entries used for connecting the tree to
- * the main tree at commit time.
- * @num_entries: On output contains the number of @entries used.
- *
- * This function builds a disconnected page-table tree for a given address
- * range. The tree is connected to the main vm tree for the gpu using
- * xe_migrate_update_pgtables() and for the cpu using xe_pt_commit_bind().
- * The function builds xe_vm_pgtable_update structures for already existing
- * shared page-tables, and non-existing shared and non-shared page-tables
- * are built and populated directly.
- *
- * Return 0 on success, negative error code on error.
- */
-static int
-xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries, u32 *num_entries)
-{
- struct xe_device *xe = tile_to_xe(tile);
- struct xe_bo *bo = xe_vma_bo(vma);
- bool is_devmem = !xe_vma_is_userptr(vma) && bo &&
- (xe_bo_is_vram(bo) || xe_bo_is_stolen_devmem(bo));
- struct xe_res_cursor curs;
- struct xe_pt_stage_bind_walk xe_walk = {
- .base = {
- .ops = &xe_pt_stage_bind_ops,
- .shifts = xe_normal_pt_shifts,
- .max_level = XE_PT_HIGHEST_LEVEL,
- },
- .vm = xe_vma_vm(vma),
- .tile = tile,
- .curs = &curs,
- .va_curs_start = xe_vma_start(vma),
- .vma = vma,
- .wupd.entries = entries,
- .needs_64K = (xe_vma_vm(vma)->flags & XE_VM_FLAG_64K) && is_devmem,
- };
- struct xe_pt *pt = xe_vma_vm(vma)->pt_root[tile->id];
- int ret;
-
- /**
- * Default atomic expectations for different allocation scenarios are as follows:
- *
- * 1. Traditional API: When the VM is not in LR mode:
- * - Device atomics are expected to function with all allocations.
- *
- * 2. Compute/SVM API: When the VM is in LR mode:
- * - Device atomics are the default behavior when the bo is placed in a single region.
- * - In all other cases device atomics will be disabled with AE=0 until an application
- * request differently using a ioctl like madvise.
- */
- if (vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) {
- if (xe_vm_in_lr_mode(xe_vma_vm(vma))) {
- if (bo && xe_bo_has_single_placement(bo))
- xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
- /**
- * If a SMEM+LMEM allocation is backed by SMEM, a device
- * atomics will cause a gpu page fault and which then
- * gets migrated to LMEM, bind such allocations with
- * device atomics enabled.
- */
- else if (is_devmem && !xe_bo_has_single_placement(bo))
- xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
- } else {
- xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
- }
-
- /**
- * Unset AE if the platform(PVC) doesn't support it on an
- * allocation
- */
- if (!xe->info.has_device_atomics_on_smem && !is_devmem)
- xe_walk.default_pte &= ~XE_USM_PPGTT_PTE_AE;
- }
-
- if (is_devmem) {
- xe_walk.default_pte |= XE_PPGTT_PTE_DM;
- xe_walk.dma_offset = vram_region_gpu_offset(bo->ttm.resource);
- }
-
- if (!xe_vma_has_no_bo(vma) && xe_bo_is_stolen(bo))
- xe_walk.dma_offset = xe_ttm_stolen_gpu_offset(xe_bo_device(bo));
-
- xe_bo_assert_held(bo);
-
- if (!xe_vma_is_null(vma)) {
- if (xe_vma_is_userptr(vma))
- xe_res_first_sg(to_userptr_vma(vma)->userptr.sg, 0,
- xe_vma_size(vma), &curs);
- else if (xe_bo_is_vram(bo) || xe_bo_is_stolen(bo))
- xe_res_first(bo->ttm.resource, xe_vma_bo_offset(vma),
- xe_vma_size(vma), &curs);
- else
- xe_res_first_sg(xe_bo_sg(bo), xe_vma_bo_offset(vma),
- xe_vma_size(vma), &curs);
- } else {
- curs.size = xe_vma_size(vma);
- }
-
- ret = xe_pt_walk_range(&pt->base, pt->level, xe_vma_start(vma),
- xe_vma_end(vma), &xe_walk.base);
-
- *num_entries = xe_walk.wupd.num_used_entries;
- return ret;
-}
-
-/**
- * xe_pt_nonshared_offsets() - Determine the non-shared entry offsets of a
- * shared pagetable.
- * @addr: The start address within the non-shared pagetable.
- * @end: The end address within the non-shared pagetable.
- * @level: The level of the non-shared pagetable.
- * @walk: Walk info. The function adjusts the walk action.
- * @action: next action to perform (see enum page_walk_action)
- * @offset: Ignored on input, First non-shared entry on output.
- * @end_offset: Ignored on input, Last non-shared entry + 1 on output.
- *
- * A non-shared page-table has some entries that belong to the address range
- * and others that don't. This function determines the entries that belong
- * fully to the address range. Depending on level, some entries may
- * partially belong to the address range (that can't happen at level 0).
- * The function detects that and adjust those offsets to not include those
- * partial entries. Iff it does detect partial entries, we know that there must
- * be shared page tables also at lower levels, so it adjusts the walk action
- * accordingly.
- *
- * Return: true if there were non-shared entries, false otherwise.
- */
-static bool xe_pt_nonshared_offsets(u64 addr, u64 end, unsigned int level,
- struct xe_pt_walk *walk,
- enum page_walk_action *action,
- pgoff_t *offset, pgoff_t *end_offset)
-{
- u64 size = 1ull << walk->shifts[level];
-
- *offset = xe_pt_offset(addr, level, walk);
- *end_offset = xe_pt_num_entries(addr, end, level, walk) + *offset;
-
- if (!level)
- return true;
-
- /*
- * If addr or next are not size aligned, there are shared pts at lower
- * level, so in that case traverse down the subtree
- */
- *action = ACTION_CONTINUE;
- if (!IS_ALIGNED(addr, size)) {
- *action = ACTION_SUBTREE;
- (*offset)++;
- }
-
- if (!IS_ALIGNED(end, size)) {
- *action = ACTION_SUBTREE;
- (*end_offset)--;
- }
-
- return *end_offset > *offset;
-}
-
-struct xe_pt_zap_ptes_walk {
- /** @base: The walk base-class */
- struct xe_pt_walk base;
-
- /* Input parameters for the walk */
- /** @tile: The tile we're building for */
- struct xe_tile *tile;
-
- /* Output */
- /** @needs_invalidate: Whether we need to invalidate TLB*/
- bool needs_invalidate;
-};
-
-static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset,
- unsigned int level, u64 addr, u64 next,
- struct xe_ptw **child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- 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);
- pgoff_t end_offset;
-
- XE_WARN_ON(!*child);
- XE_WARN_ON(!level);
-
- /*
- * 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.
- */
- if (xe_pt_nonshared_offsets(addr, next, --level, walk, action, &offset,
- &end_offset)) {
- xe_map_memset(tile_to_xe(xe_walk->tile), &xe_child->bo->vmap,
- offset * sizeof(u64), 0,
- (end_offset - offset) * sizeof(u64));
- xe_walk->needs_invalidate = true;
- }
-
- return 0;
-}
-
-static const struct xe_pt_walk_ops xe_pt_zap_ptes_ops = {
- .pt_entry = xe_pt_zap_ptes_entry,
-};
-
-/**
- * xe_pt_zap_ptes() - Zap (zero) gpu ptes of an address range
- * @tile: The tile we're zapping for.
- * @vma: GPU VMA detailing address range.
- *
- * Eviction and Userptr invalidation needs to be able to zap the
- * gpu ptes of a given address range in pagefaulting mode.
- * In order to be able to do that, that function needs access to the shared
- * page-table entrieaso it can either clear the leaf PTEs or
- * clear the pointers to lower-level page-tables. The caller is required
- * to hold the necessary locks to ensure neither the page-table connectivity
- * nor the page-table entries of the range is updated from under us.
- *
- * Return: Whether ptes were actually updated and a TLB invalidation is
- * required.
- */
-bool xe_pt_zap_ptes(struct xe_tile *tile, struct xe_vma *vma)
-{
- struct xe_pt_zap_ptes_walk xe_walk = {
- .base = {
- .ops = &xe_pt_zap_ptes_ops,
- .shifts = xe_normal_pt_shifts,
- .max_level = XE_PT_HIGHEST_LEVEL,
- },
- .tile = tile,
- };
- struct xe_pt *pt = xe_vma_vm(vma)->pt_root[tile->id];
- u8 pt_mask = (vma->tile_present & ~vma->tile_invalidated);
-
- if (!(pt_mask & BIT(tile->id)))
- return false;
-
- (void)xe_pt_walk_shared(&pt->base, pt->level, xe_vma_start(vma),
- xe_vma_end(vma), &xe_walk.base);
-
- return xe_walk.needs_invalidate;
-}
-
-static void
-xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *tile,
- struct iosys_map *map, void *data,
- u32 qword_ofs, u32 num_qwords,
- const struct xe_vm_pgtable_update *update)
-{
- struct xe_pt_entry *ptes = update->pt_entries;
- u64 *ptr = data;
- u32 i;
-
- for (i = 0; i < num_qwords; i++) {
- if (map)
- xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
- sizeof(u64), u64, ptes[i].pte);
- else
- ptr[i] = ptes[i].pte;
- }
-}
-
-static void xe_pt_abort_bind(struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries,
- u32 num_entries)
-{
- u32 i, j;
-
- for (i = 0; i < num_entries; i++) {
- if (!entries[i].pt_entries)
- continue;
-
- for (j = 0; j < entries[i].qwords; j++)
- xe_pt_destroy(entries[i].pt_entries[j].pt, xe_vma_vm(vma)->flags, NULL);
- kfree(entries[i].pt_entries);
- }
-}
-
-static void xe_pt_commit_locks_assert(struct xe_vma *vma)
-{
- struct xe_vm *vm = xe_vma_vm(vma);
-
- lockdep_assert_held(&vm->lock);
-
- if (xe_vma_is_userptr(vma))
- lockdep_assert_held_read(&vm->userptr.notifier_lock);
- else if (!xe_vma_is_null(vma))
- dma_resv_assert_held(xe_vma_bo(vma)->ttm.base.resv);
-
- xe_vm_assert_held(vm);
-}
-
-static void xe_pt_commit_bind(struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries,
- u32 num_entries, bool rebind,
- struct llist_head *deferred)
-{
- u32 i, j;
-
- xe_pt_commit_locks_assert(vma);
-
- for (i = 0; i < num_entries; i++) {
- struct xe_pt *pt = entries[i].pt;
- struct xe_pt_dir *pt_dir;
-
- if (!rebind)
- pt->num_live += entries[i].qwords;
-
- if (!pt->level) {
- kfree(entries[i].pt_entries);
- continue;
- }
-
- pt_dir = as_xe_pt_dir(pt);
- for (j = 0; j < entries[i].qwords; j++) {
- u32 j_ = j + entries[i].ofs;
- struct xe_pt *newpte = entries[i].pt_entries[j].pt;
-
- if (xe_pt_entry(pt_dir, j_))
- xe_pt_destroy(xe_pt_entry(pt_dir, j_),
- xe_vma_vm(vma)->flags, deferred);
-
- pt_dir->children[j_] = &newpte->base;
- }
- kfree(entries[i].pt_entries);
- }
-}
-
-static int
-xe_pt_prepare_bind(struct xe_tile *tile, struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries, u32 *num_entries)
-{
- int err;
-
- *num_entries = 0;
- err = xe_pt_stage_bind(tile, vma, entries, num_entries);
- if (!err)
- xe_tile_assert(tile, *num_entries);
- else /* abort! */
- xe_pt_abort_bind(vma, entries, *num_entries);
-
- return err;
-}
-
-static void xe_vm_dbg_print_entries(struct xe_device *xe,
- const struct xe_vm_pgtable_update *entries,
- unsigned int num_entries)
-#if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM))
-{
- unsigned int i;
-
- vm_dbg(&xe->drm, "%u entries to update\n", num_entries);
- for (i = 0; i < num_entries; i++) {
- const struct xe_vm_pgtable_update *entry = &entries[i];
- struct xe_pt *xe_pt = entry->pt;
- u64 page_size = 1ull << xe_pt_shift(xe_pt->level);
- u64 end;
- u64 start;
-
- xe_assert(xe, !entry->pt->is_compact);
- start = entry->ofs * page_size;
- end = start + page_size * entry->qwords;
- vm_dbg(&xe->drm,
- "\t%u: Update level %u at (%u + %u) [%llx...%llx) f:%x\n",
- i, xe_pt->level, entry->ofs, entry->qwords,
- xe_pt_addr(xe_pt) + start, xe_pt_addr(xe_pt) + end, 0);
- }
-}
-#else
-{}
-#endif
-
-#ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT
-
-static int xe_pt_userptr_inject_eagain(struct xe_userptr_vma *uvma)
-{
- u32 divisor = uvma->userptr.divisor ? uvma->userptr.divisor : 2;
- static u32 count;
-
- if (count++ % divisor == divisor - 1) {
- struct xe_vm *vm = xe_vma_vm(&uvma->vma);
-
- uvma->userptr.divisor = divisor << 1;
- spin_lock(&vm->userptr.invalidated_lock);
- list_move_tail(&uvma->userptr.invalidate_link,
- &vm->userptr.invalidated);
- spin_unlock(&vm->userptr.invalidated_lock);
- return true;
- }
-
- return false;
-}
-
-#else
-
-static bool xe_pt_userptr_inject_eagain(struct xe_userptr_vma *uvma)
-{
- return false;
-}
-
-#endif
-
-/**
- * struct xe_pt_migrate_pt_update - Callback argument for pre-commit callbacks
- * @base: Base we derive from.
- * @bind: Whether this is a bind or an unbind operation. A bind operation
- * makes the pre-commit callback error with -EAGAIN if it detects a
- * pending invalidation.
- * @locked: Whether the pre-commit callback locked the userptr notifier lock
- * and it needs unlocking.
- */
-struct xe_pt_migrate_pt_update {
- struct xe_migrate_pt_update base;
- bool bind;
- bool locked;
-};
-
-/*
- * This function adds the needed dependencies to a page-table update job
- * to make sure racing jobs for separate bind engines don't race writing
- * to the same page-table range, wreaking havoc. Initially use a single
- * fence for the entire VM. An optimization would use smaller granularity.
- */
-static int xe_pt_vm_dependencies(struct xe_sched_job *job,
- struct xe_range_fence_tree *rftree,
- u64 start, u64 last)
-{
- struct xe_range_fence *rtfence;
- struct dma_fence *fence;
- int err;
-
- rtfence = xe_range_fence_tree_first(rftree, start, last);
- while (rtfence) {
- fence = rtfence->fence;
-
- if (!dma_fence_is_signaled(fence)) {
- /*
- * Is this a CPU update? GPU is busy updating, so return
- * an error
- */
- if (!job)
- return -ETIME;
-
- dma_fence_get(fence);
- err = drm_sched_job_add_dependency(&job->drm, fence);
- if (err)
- return err;
- }
-
- rtfence = xe_range_fence_tree_next(rtfence, start, last);
- }
-
- return 0;
-}
-
-static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update)
-{
- struct xe_range_fence_tree *rftree =
- &xe_vma_vm(pt_update->vma)->rftree[pt_update->tile_id];
-
- return xe_pt_vm_dependencies(pt_update->job, rftree,
- pt_update->start, pt_update->last);
-}
-
-static int xe_pt_userptr_pre_commit(struct xe_migrate_pt_update *pt_update)
-{
- struct xe_pt_migrate_pt_update *userptr_update =
- container_of(pt_update, typeof(*userptr_update), base);
- struct xe_userptr_vma *uvma = to_userptr_vma(pt_update->vma);
- unsigned long notifier_seq = uvma->userptr.notifier_seq;
- struct xe_vm *vm = xe_vma_vm(&uvma->vma);
- int err = xe_pt_vm_dependencies(pt_update->job,
- &vm->rftree[pt_update->tile_id],
- pt_update->start,
- pt_update->last);
-
- if (err)
- return err;
-
- userptr_update->locked = false;
-
- /*
- * Wait until nobody is running the invalidation notifier, and
- * since we're exiting the loop holding the notifier lock,
- * nobody can proceed invalidating either.
- *
- * Note that we don't update the vma->userptr.notifier_seq since
- * we don't update the userptr pages.
- */
- do {
- down_read(&vm->userptr.notifier_lock);
- if (!mmu_interval_read_retry(&uvma->userptr.notifier,
- notifier_seq))
- break;
-
- up_read(&vm->userptr.notifier_lock);
-
- if (userptr_update->bind)
- return -EAGAIN;
-
- notifier_seq = mmu_interval_read_begin(&uvma->userptr.notifier);
- } while (true);
-
- /* Inject errors to test_whether they are handled correctly */
- if (userptr_update->bind && xe_pt_userptr_inject_eagain(uvma)) {
- up_read(&vm->userptr.notifier_lock);
- return -EAGAIN;
- }
-
- userptr_update->locked = true;
-
- return 0;
-}
-
-static const struct xe_migrate_pt_update_ops bind_ops = {
- .populate = xe_vm_populate_pgtable,
- .pre_commit = xe_pt_pre_commit,
-};
-
-static const struct xe_migrate_pt_update_ops userptr_bind_ops = {
- .populate = xe_vm_populate_pgtable,
- .pre_commit = xe_pt_userptr_pre_commit,
-};
-
-struct invalidation_fence {
- struct xe_gt_tlb_invalidation_fence base;
- struct xe_gt *gt;
- struct dma_fence *fence;
- struct dma_fence_cb cb;
- struct work_struct work;
- u64 start;
- u64 end;
- u32 asid;
-};
-
-static void invalidation_fence_cb(struct dma_fence *fence,
- struct dma_fence_cb *cb)
-{
- struct invalidation_fence *ifence =
- container_of(cb, struct invalidation_fence, cb);
- struct xe_device *xe = gt_to_xe(ifence->gt);
-
- trace_xe_gt_tlb_invalidation_fence_cb(xe, &ifence->base);
- if (!ifence->fence->error) {
- queue_work(system_wq, &ifence->work);
- } else {
- ifence->base.base.error = ifence->fence->error;
- dma_fence_signal(&ifence->base.base);
- dma_fence_put(&ifence->base.base);
- }
- dma_fence_put(ifence->fence);
-}
-
-static void invalidation_fence_work_func(struct work_struct *w)
-{
- struct invalidation_fence *ifence =
- container_of(w, struct invalidation_fence, work);
- struct xe_device *xe = gt_to_xe(ifence->gt);
-
- trace_xe_gt_tlb_invalidation_fence_work_func(xe, &ifence->base);
- xe_gt_tlb_invalidation_range(ifence->gt, &ifence->base, ifence->start,
- ifence->end, ifence->asid);
-}
-
-static int invalidation_fence_init(struct xe_gt *gt,
- struct invalidation_fence *ifence,
- struct dma_fence *fence,
- u64 start, u64 end, u32 asid)
-{
- int ret;
-
- trace_xe_gt_tlb_invalidation_fence_create(gt_to_xe(gt), &ifence->base);
-
- xe_gt_tlb_invalidation_fence_init(gt, &ifence->base, false);
-
- ifence->fence = fence;
- ifence->gt = gt;
- ifence->start = start;
- ifence->end = end;
- ifence->asid = asid;
-
- INIT_WORK(&ifence->work, invalidation_fence_work_func);
- ret = dma_fence_add_callback(fence, &ifence->cb, invalidation_fence_cb);
- if (ret == -ENOENT) {
- dma_fence_put(ifence->fence); /* Usually dropped in CB */
- invalidation_fence_work_func(&ifence->work);
- } else if (ret) {
- dma_fence_put(&ifence->base.base); /* Caller ref */
- dma_fence_put(&ifence->base.base); /* Creation ref */
- }
-
- xe_gt_assert(gt, !ret || ret == -ENOENT);
-
- return ret && ret != -ENOENT ? ret : 0;
-}
-
-static void xe_pt_calc_rfence_interval(struct xe_vma *vma,
- struct xe_pt_migrate_pt_update *update,
- struct xe_vm_pgtable_update *entries,
- u32 num_entries)
-{
- int i, level = 0;
-
- for (i = 0; i < num_entries; i++) {
- const struct xe_vm_pgtable_update *entry = &entries[i];
-
- if (entry->pt->level > level)
- level = entry->pt->level;
- }
-
- /* Greedy (non-optimal) calculation but simple */
- update->base.start = ALIGN_DOWN(xe_vma_start(vma),
- 0x1ull << xe_pt_shift(level));
- update->base.last = ALIGN(xe_vma_end(vma),
- 0x1ull << xe_pt_shift(level)) - 1;
-}
-
-/**
- * __xe_pt_bind_vma() - Build and connect a page-table tree for the vma
- * address range.
- * @tile: The tile to bind for.
- * @vma: The vma to bind.
- * @q: The exec_queue with which to do pipelined page-table updates.
- * @syncs: Entries to sync on before binding the built tree to the live vm tree.
- * @num_syncs: Number of @sync entries.
- * @rebind: Whether we're rebinding this vma to the same address range without
- * an unbind in-between.
- *
- * This function builds a page-table tree (see xe_pt_stage_bind() for more
- * information on page-table building), and the xe_vm_pgtable_update entries
- * abstracting the operations needed to attach it to the main vm tree. It
- * then takes the relevant locks and updates the metadata side of the main
- * vm tree and submits the operations for pipelined attachment of the
- * gpu page-table to the vm main tree, (which can be done either by the
- * cpu and the GPU).
- *
- * Return: A valid dma-fence representing the pipelined attachment operation
- * on success, an error pointer on error.
- */
-struct dma_fence *
-__xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_exec_queue *q,
- struct xe_sync_entry *syncs, u32 num_syncs,
- bool rebind)
-{
- struct xe_vm_pgtable_update entries[XE_VM_MAX_LEVEL * 2 + 1];
- struct xe_pt_migrate_pt_update bind_pt_update = {
- .base = {
- .ops = xe_vma_is_userptr(vma) ? &userptr_bind_ops : &bind_ops,
- .vma = vma,
- .tile_id = tile->id,
- },
- .bind = true,
- };
- struct xe_vm *vm = xe_vma_vm(vma);
- u32 num_entries;
- struct dma_fence *fence;
- struct invalidation_fence *ifence = NULL;
- struct xe_range_fence *rfence;
- int err;
-
- bind_pt_update.locked = false;
- xe_bo_assert_held(xe_vma_bo(vma));
- xe_vm_assert_held(vm);
-
- vm_dbg(&xe_vma_vm(vma)->xe->drm,
- "Preparing bind, with range [%llx...%llx) engine %p.\n",
- xe_vma_start(vma), xe_vma_end(vma), q);
-
- err = xe_pt_prepare_bind(tile, vma, entries, &num_entries);
- if (err)
- goto err;
-
- err = dma_resv_reserve_fences(xe_vm_resv(vm), 1);
- if (!err && !xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- err = dma_resv_reserve_fences(xe_vma_bo(vma)->ttm.base.resv, 1);
- if (err)
- goto err;
-
- xe_tile_assert(tile, num_entries <= ARRAY_SIZE(entries));
-
- xe_vm_dbg_print_entries(tile_to_xe(tile), entries, num_entries);
- xe_pt_calc_rfence_interval(vma, &bind_pt_update, entries,
- num_entries);
-
- /*
- * If rebind, we have to invalidate TLB on !LR vms to invalidate
- * cached PTEs point to freed memory. on LR vms this is done
- * automatically when the context is re-enabled by the rebind worker,
- * or in fault mode it was invalidated on PTE zapping.
- *
- * 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
- * non-faulting LR, in particular on user-space batch buffer chaining,
- * it needs to be done here.
- */
- if ((!rebind && xe_vm_has_scratch(vm) && xe_vm_in_preempt_fence_mode(vm))) {
- ifence = kzalloc(sizeof(*ifence), GFP_KERNEL);
- if (!ifence)
- return ERR_PTR(-ENOMEM);
- } else if (rebind && !xe_vm_in_lr_mode(vm)) {
- /* We bump also if batch_invalidate_tlb is true */
- vm->tlb_flush_seqno++;
- }
-
- rfence = kzalloc(sizeof(*rfence), GFP_KERNEL);
- if (!rfence) {
- kfree(ifence);
- return ERR_PTR(-ENOMEM);
- }
-
- fence = xe_migrate_update_pgtables(tile->migrate,
- vm, xe_vma_bo(vma), q,
- entries, num_entries,
- syncs, num_syncs,
- &bind_pt_update.base);
- if (!IS_ERR(fence)) {
- bool last_munmap_rebind = vma->gpuva.flags & XE_VMA_LAST_REBIND;
- LLIST_HEAD(deferred);
- int err;
-
- err = xe_range_fence_insert(&vm->rftree[tile->id], rfence,
- &xe_range_fence_kfree_ops,
- bind_pt_update.base.start,
- bind_pt_update.base.last, fence);
- if (err)
- dma_fence_wait(fence, false);
-
- /* TLB invalidation must be done before signaling rebind */
- if (ifence) {
- int err = invalidation_fence_init(tile->primary_gt,
- ifence, fence,
- xe_vma_start(vma),
- xe_vma_end(vma),
- xe_vma_vm(vma)->usm.asid);
- if (err) {
- dma_fence_put(fence);
- kfree(ifence);
- return ERR_PTR(err);
- }
- fence = &ifence->base.base;
- }
-
- /* add shared fence now for pagetable delayed destroy */
- dma_resv_add_fence(xe_vm_resv(vm), fence, rebind ||
- last_munmap_rebind ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- DMA_RESV_USAGE_BOOKKEEP);
- xe_pt_commit_bind(vma, entries, num_entries, rebind,
- bind_pt_update.locked ? &deferred : NULL);
-
- /* This vma is live (again?) now */
- vma->tile_present |= BIT(tile->id);
-
- if (bind_pt_update.locked) {
- to_userptr_vma(vma)->userptr.initial_bind = true;
- up_read(&vm->userptr.notifier_lock);
- xe_bo_put_commit(&deferred);
- }
- if (!rebind && last_munmap_rebind &&
- xe_vm_in_preempt_fence_mode(vm))
- xe_vm_queue_rebind_worker(vm);
- } else {
- kfree(rfence);
- kfree(ifence);
- if (bind_pt_update.locked)
- up_read(&vm->userptr.notifier_lock);
- xe_pt_abort_bind(vma, entries, num_entries);
- }
-
- return fence;
-
-err:
- return ERR_PTR(err);
-}
-
-struct xe_pt_stage_unbind_walk {
- /** @base: The pagewalk base-class. */
- struct xe_pt_walk base;
-
- /* Input parameters for the walk */
- /** @tile: The tile we're unbinding from. */
- struct xe_tile *tile;
-
- /**
- * @modified_start: Walk range start, modified to include any
- * shared pagetables that we're the only user of and can thus
- * treat as private.
- */
- u64 modified_start;
- /** @modified_end: Walk range start, modified like @modified_start. */
- u64 modified_end;
-
- /* Output */
- /* @wupd: Structure to track the page-table updates we're building */
- struct xe_walk_update wupd;
-};
-
-/*
- * Check whether this range is the only one populating this pagetable,
- * and in that case, update the walk range checks so that higher levels don't
- * view us as a shared pagetable.
- */
-static bool xe_pt_check_kill(u64 addr, u64 next, unsigned int level,
- const struct xe_pt *child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- struct xe_pt_stage_unbind_walk *xe_walk =
- container_of(walk, typeof(*xe_walk), base);
- unsigned int shift = walk->shifts[level];
- u64 size = 1ull << shift;
-
- if (IS_ALIGNED(addr, size) && IS_ALIGNED(next, size) &&
- ((next - addr) >> shift) == child->num_live) {
- u64 size = 1ull << walk->shifts[level + 1];
-
- *action = ACTION_CONTINUE;
-
- if (xe_walk->modified_start >= addr)
- xe_walk->modified_start = round_down(addr, size);
- if (xe_walk->modified_end <= next)
- xe_walk->modified_end = round_up(next, size);
-
- return true;
- }
-
- return false;
-}
-
-static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
- unsigned int level, u64 addr, u64 next,
- struct xe_ptw **child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base);
-
- XE_WARN_ON(!*child);
- XE_WARN_ON(!level);
-
- xe_pt_check_kill(addr, next, level - 1, xe_child, action, walk);
-
- return 0;
-}
-
-static int
-xe_pt_stage_unbind_post_descend(struct xe_ptw *parent, pgoff_t offset,
- unsigned int level, u64 addr, u64 next,
- struct xe_ptw **child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- struct xe_pt_stage_unbind_walk *xe_walk =
- container_of(walk, typeof(*xe_walk), base);
- struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base);
- pgoff_t end_offset;
- u64 size = 1ull << walk->shifts[--level];
-
- if (!IS_ALIGNED(addr, size))
- addr = xe_walk->modified_start;
- if (!IS_ALIGNED(next, size))
- next = xe_walk->modified_end;
-
- /* Parent == *child is the root pt. Don't kill it. */
- if (parent != *child &&
- xe_pt_check_kill(addr, next, level, xe_child, action, walk))
- return 0;
-
- if (!xe_pt_nonshared_offsets(addr, next, level, walk, action, &offset,
- &end_offset))
- return 0;
-
- (void)xe_pt_new_shared(&xe_walk->wupd, xe_child, offset, false);
- xe_walk->wupd.updates[level].update->qwords = end_offset - offset;
-
- return 0;
-}
-
-static const struct xe_pt_walk_ops xe_pt_stage_unbind_ops = {
- .pt_entry = xe_pt_stage_unbind_entry,
- .pt_post_descend = xe_pt_stage_unbind_post_descend,
-};
-
-/**
- * xe_pt_stage_unbind() - Build page-table update structures for an unbind
- * operation
- * @tile: The tile we're unbinding for.
- * @vma: The vma we're unbinding.
- * @entries: Caller-provided storage for the update structures.
- *
- * Builds page-table update structures for an unbind operation. The function
- * will attempt to remove all page-tables that we're the only user
- * of, and for that to work, the unbind operation must be committed in the
- * same critical section that blocks racing binds to the same page-table tree.
- *
- * Return: The number of entries used.
- */
-static unsigned int xe_pt_stage_unbind(struct xe_tile *tile, struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries)
-{
- struct xe_pt_stage_unbind_walk xe_walk = {
- .base = {
- .ops = &xe_pt_stage_unbind_ops,
- .shifts = xe_normal_pt_shifts,
- .max_level = XE_PT_HIGHEST_LEVEL,
- },
- .tile = tile,
- .modified_start = xe_vma_start(vma),
- .modified_end = xe_vma_end(vma),
- .wupd.entries = entries,
- };
- struct xe_pt *pt = xe_vma_vm(vma)->pt_root[tile->id];
-
- (void)xe_pt_walk_shared(&pt->base, pt->level, xe_vma_start(vma),
- xe_vma_end(vma), &xe_walk.base);
-
- return xe_walk.wupd.num_used_entries;
-}
-
-static void
-xe_migrate_clear_pgtable_callback(struct xe_migrate_pt_update *pt_update,
- struct xe_tile *tile, struct iosys_map *map,
- void *ptr, u32 qword_ofs, u32 num_qwords,
- const struct xe_vm_pgtable_update *update)
-{
- struct xe_vma *vma = pt_update->vma;
- u64 empty = __xe_pt_empty_pte(tile, xe_vma_vm(vma), update->pt->level);
- int i;
-
- if (map && map->is_iomem)
- for (i = 0; i < num_qwords; ++i)
- xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
- sizeof(u64), u64, empty);
- else if (map)
- memset64(map->vaddr + qword_ofs * sizeof(u64), empty,
- num_qwords);
- else
- memset64(ptr, empty, num_qwords);
-}
-
-static void
-xe_pt_commit_unbind(struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries, u32 num_entries,
- struct llist_head *deferred)
-{
- u32 j;
-
- xe_pt_commit_locks_assert(vma);
-
- for (j = 0; j < num_entries; ++j) {
- struct xe_vm_pgtable_update *entry = &entries[j];
- struct xe_pt *pt = entry->pt;
-
- pt->num_live -= entry->qwords;
- if (pt->level) {
- struct xe_pt_dir *pt_dir = as_xe_pt_dir(pt);
- u32 i;
-
- for (i = entry->ofs; i < entry->ofs + entry->qwords;
- i++) {
- if (xe_pt_entry(pt_dir, i))
- xe_pt_destroy(xe_pt_entry(pt_dir, i),
- xe_vma_vm(vma)->flags, deferred);
-
- pt_dir->children[i] = NULL;
- }
- }
- }
-}
-
-<<<<<<<
-static const struct xe_migrate_pt_update_ops unbind_ops = {
- .populate = xe_migrate_clear_pgtable_callback,
-=======
-static void
-xe_pt_update_ops_rfence_interval(struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma)
-{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
- int i, level = 0;
- u64 start, last;
-
- for (i = 0; i < pt_op->num_entries; i++) {
- const struct xe_vm_pgtable_update *entry = &pt_op->entries[i];
-
- if (entry->pt->level > level)
- level = entry->pt->level;
- }
-
- /* Greedy (non-optimal) calculation but simple */
- start = ALIGN_DOWN(xe_vma_start(vma), 0x1ull << xe_pt_shift(level));
- last = ALIGN(xe_vma_end(vma), 0x1ull << xe_pt_shift(level)) - 1;
-
- if (start < pt_update_ops->start)
- pt_update_ops->start = start;
- if (last > pt_update_ops->last)
- pt_update_ops->last = last;
-}
-
-static int vma_reserve_fences(struct xe_device *xe, struct xe_vma *vma)
-{
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- return dma_resv_reserve_fences(xe_vma_bo(vma)->ttm.base.resv,
- xe->info.tile_count);
-
- return 0;
-}
-
-static int bind_op_prepare(struct xe_vm *vm, struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma)
-{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
- int err;
-
- xe_bo_assert_held(xe_vma_bo(vma));
-
- vm_dbg(&xe_vma_vm(vma)->xe->drm,
- "Preparing bind, with range [%llx...%llx)\n",
- xe_vma_start(vma), xe_vma_end(vma) - 1);
-
- pt_op->vma = NULL;
- pt_op->bind = true;
- pt_op->rebind = BIT(tile->id) & vma->tile_present;
-
- err = vma_reserve_fences(tile_to_xe(tile), vma);
- if (err)
- return err;
-
- err = xe_pt_prepare_bind(tile, vma, pt_op->entries,
- &pt_op->num_entries);
- if (!err) {
- xe_tile_assert(tile, pt_op->num_entries <=
- ARRAY_SIZE(pt_op->entries));
- xe_vm_dbg_print_entries(tile_to_xe(tile), pt_op->entries,
- pt_op->num_entries, true);
-
- xe_pt_update_ops_rfence_interval(pt_update_ops, vma);
- ++pt_update_ops->current_op;
- pt_update_ops->needs_userptr_lock |= xe_vma_is_userptr(vma);
-
- /*
- * If rebind, we have to invalidate TLB on !LR vms to invalidate
- * cached PTEs point to freed memory. On LR vms this is done
- * automatically when the context is re-enabled by the rebind worker,
- * or in fault mode it was invalidated on PTE zapping.
- *
- * 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
- * non-faulting LR, in particular on user-space batch buffer chaining,
- * it needs to be done here.
- */
- if ((!pt_op->rebind && xe_vm_has_scratch(vm) &&
- xe_vm_in_preempt_fence_mode(vm)))
- 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++;
-
- vma->tile_staged |= BIT(tile->id);
- pt_op->vma = vma;
- xe_pt_commit_prepare_bind(vma, pt_op->entries,
- pt_op->num_entries, pt_op->rebind);
- } else {
- xe_pt_cancel_bind(vma, pt_op->entries, pt_op->num_entries);
- }
-
- return err;
-}
-
-static int unbind_op_prepare(struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma)
-{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
- int err;
-
- if (!((vma->tile_present | vma->tile_staged) & BIT(tile->id)))
- return 0;
-
- xe_bo_assert_held(xe_vma_bo(vma));
-
- vm_dbg(&xe_vma_vm(vma)->xe->drm,
- "Preparing unbind, with range [%llx...%llx)\n",
- xe_vma_start(vma), xe_vma_end(vma) - 1);
-
- /*
- * Wait for invalidation to complete. Can corrupt internal page table
- * state if an invalidation is running while preparing an unbind.
- */
- if (xe_vma_is_userptr(vma) && xe_vm_in_fault_mode(xe_vma_vm(vma)))
- mmu_interval_read_begin(&to_userptr_vma(vma)->userptr.notifier);
-
- pt_op->vma = vma;
- pt_op->bind = false;
- pt_op->rebind = false;
-
- err = vma_reserve_fences(tile_to_xe(tile), vma);
- if (err)
- return err;
-
- pt_op->num_entries = xe_pt_stage_unbind(tile, vma, pt_op->entries);
-
- xe_vm_dbg_print_entries(tile_to_xe(tile), pt_op->entries,
- pt_op->num_entries, false);
- xe_pt_update_ops_rfence_interval(pt_update_ops, vma);
- ++pt_update_ops->current_op;
- pt_update_ops->needs_userptr_lock |= xe_vma_is_userptr(vma);
- pt_update_ops->needs_invalidation = true;
-
- xe_pt_commit_prepare_unbind(vma, pt_op->entries, pt_op->num_entries);
-
- return 0;
-}
-
-static int op_prepare(struct xe_vm *vm,
- struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma_op *op)
-{
- int err = 0;
-
- xe_vm_assert_held(vm);
-
- switch (op->base.op) {
- case DRM_GPUVA_OP_MAP:
- if (!op->map.immediate && xe_vm_in_fault_mode(vm))
- break;
-
- err = bind_op_prepare(vm, tile, pt_update_ops, op->map.vma);
- pt_update_ops->wait_vm_kernel = true;
- break;
- case DRM_GPUVA_OP_REMAP:
- err = unbind_op_prepare(tile, pt_update_ops,
- gpuva_to_vma(op->base.remap.unmap->va));
-
- if (!err && op->remap.prev) {
- err = bind_op_prepare(vm, tile, pt_update_ops,
- op->remap.prev);
- pt_update_ops->wait_vm_bookkeep = true;
- }
- if (!err && op->remap.next) {
- err = bind_op_prepare(vm, tile, pt_update_ops,
- op->remap.next);
- pt_update_ops->wait_vm_bookkeep = true;
- }
- break;
- case DRM_GPUVA_OP_UNMAP:
- err = unbind_op_prepare(tile, pt_update_ops,
- gpuva_to_vma(op->base.unmap.va));
- break;
- case DRM_GPUVA_OP_PREFETCH:
- err = bind_op_prepare(vm, tile, pt_update_ops,
- gpuva_to_vma(op->base.prefetch.va));
- pt_update_ops->wait_vm_kernel = true;
- break;
- default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
- }
-
- return err;
-}
-
-static void
-xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops)
-{
- init_llist_head(&pt_update_ops->deferred);
- pt_update_ops->start = ~0x0ull;
- pt_update_ops->last = 0x0ull;
-}
-
-/**
- * xe_pt_update_ops_prepare() - Prepare PT update operations
- * @tile: Tile of PT update operations
- * @vops: VMA operationa
- *
- * Prepare PT update operations which includes updating internal PT state,
- * allocate memory for page tables, populate page table being pruned in, and
- * create PT update operations for leaf insertion / removal.
- *
- * Return: 0 on success, negative error code on error.
- */
-int xe_pt_update_ops_prepare(struct xe_tile *tile, struct xe_vma_ops *vops)
-{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- struct xe_vma_op *op;
- int err;
-
- lockdep_assert_held(&vops->vm->lock);
- xe_vm_assert_held(vops->vm);
-
- xe_pt_update_ops_init(pt_update_ops);
-
- err = dma_resv_reserve_fences(xe_vm_resv(vops->vm),
- tile_to_xe(tile)->info.tile_count);
- if (err)
- return err;
-
- list_for_each_entry(op, &vops->list, link) {
- err = op_prepare(vops->vm, tile, pt_update_ops, op);
-
- if (err)
- return err;
- }
-
- xe_tile_assert(tile, pt_update_ops->current_op <=
- pt_update_ops->num_ops);
-
-#ifdef TEST_VM_OPS_ERROR
- if (vops->inject_error &&
- vops->vm->xe->vm_inject_error_position == FORCE_OP_ERROR_PREPARE)
- return -ENOSPC;
-#endif
-
- return 0;
-}
-
-static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma, struct dma_fence *fence,
- struct dma_fence *fence2)
-{
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm) {
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- if (fence2)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence2,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- }
- vma->tile_present |= BIT(tile->id);
- vma->tile_staged &= ~BIT(tile->id);
- if (xe_vma_is_userptr(vma)) {
- lockdep_assert_held_read(&vm->userptr.notifier_lock);
- to_userptr_vma(vma)->userptr.initial_bind = true;
- }
-
- /*
- * Kick rebind worker if this bind triggers preempt fences and not in
- * the rebind worker
- */
- if (pt_update_ops->wait_vm_bookkeep &&
- xe_vm_in_preempt_fence_mode(vm) &&
- !current->mm)
- xe_vm_queue_rebind_worker(vm);
-}
-
-static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma, struct dma_fence *fence,
- struct dma_fence *fence2)
-{
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm) {
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- if (fence2)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence2,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- }
- vma->tile_present &= ~BIT(tile->id);
- if (!vma->tile_present) {
- list_del_init(&vma->combined_links.rebind);
- if (xe_vma_is_userptr(vma)) {
- lockdep_assert_held_read(&vm->userptr.notifier_lock);
-
- spin_lock(&vm->userptr.invalidated_lock);
- list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link);
- spin_unlock(&vm->userptr.invalidated_lock);
- }
- }
-}
-
-static void op_commit(struct xe_vm *vm,
- struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma_op *op, struct dma_fence *fence,
- struct dma_fence *fence2)
-{
- xe_vm_assert_held(vm);
-
- switch (op->base.op) {
- case DRM_GPUVA_OP_MAP:
- if (!op->map.immediate && xe_vm_in_fault_mode(vm))
- break;
-
- bind_op_commit(vm, tile, pt_update_ops, op->map.vma, fence,
- fence2);
- break;
- case DRM_GPUVA_OP_REMAP:
- unbind_op_commit(vm, tile, pt_update_ops,
- gpuva_to_vma(op->base.remap.unmap->va), fence,
- fence2);
-
- if (op->remap.prev)
- bind_op_commit(vm, tile, pt_update_ops, op->remap.prev,
- fence, fence2);
- if (op->remap.next)
- bind_op_commit(vm, tile, pt_update_ops, op->remap.next,
- fence, fence2);
- break;
- case DRM_GPUVA_OP_UNMAP:
- unbind_op_commit(vm, tile, pt_update_ops,
- gpuva_to_vma(op->base.unmap.va), fence, fence2);
- break;
- case DRM_GPUVA_OP_PREFETCH:
- bind_op_commit(vm, tile, pt_update_ops,
- gpuva_to_vma(op->base.prefetch.va), fence, fence2);
- break;
- default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
- }
-}
-
-static const struct xe_migrate_pt_update_ops migrate_ops = {
- .populate = xe_vm_populate_pgtable,
- .clear = xe_migrate_clear_pgtable_callback,
->>>>>>>
- .pre_commit = xe_pt_pre_commit,
-};
-
-static const struct xe_migrate_pt_update_ops userptr_unbind_ops = {
- .populate = xe_migrate_clear_pgtable_callback,
- .pre_commit = xe_pt_userptr_pre_commit,
-};
-
-/**
- * __xe_pt_unbind_vma() - Disconnect and free a page-table tree for the vma
- * address range.
- * @tile: The tile to unbind for.
- * @vma: The vma to unbind.
- * @q: The exec_queue with which to do pipelined page-table updates.
- * @syncs: Entries to sync on before disconnecting the tree to be destroyed.
- * @num_syncs: Number of @sync entries.
- *
- * This function builds a the xe_vm_pgtable_update entries abstracting the
- * operations needed to detach the page-table tree to be destroyed from the
- * man vm tree.
- * It then takes the relevant locks and submits the operations for
- * pipelined detachment of the gpu page-table from the vm main tree,
- * (which can be done either by the cpu and the GPU), Finally it frees the
- * detached page-table tree.
- *
- * Return: A valid dma-fence representing the pipelined detachment operation
- * on success, an error pointer on error.
- */
-struct dma_fence *
-__xe_pt_unbind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_exec_queue *q,
- struct xe_sync_entry *syncs, u32 num_syncs)
-{
-<<<<<<<
- struct xe_vm *vm = vops->vm;
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- struct dma_fence *fence;
- struct invalidation_fence *ifence = NULL, *mfence = NULL;
- struct dma_fence_chain *chain_fence = NULL;
- struct xe_range_fence *rfence;
- struct xe_vma_op *op;
- int err = 0, i;
- struct xe_migrate_pt_update update = {
- .ops = pt_update_ops->needs_userptr_lock ?
- &userptr_migrate_ops :
- &migrate_ops,
- .vops = vops,
- .tile_id = tile->id,
-=======
- struct xe_vm_pgtable_update entries[XE_VM_MAX_LEVEL * 2 + 1];
- struct xe_pt_migrate_pt_update unbind_pt_update = {
- .base = {
- .ops = xe_vma_is_userptr(vma) ? &userptr_unbind_ops :
- &unbind_ops,
- .vma = vma,
- .tile_id = tile->id,
- },
->>>>>>>
- };
- struct xe_vm *vm = xe_vma_vm(vma);
- u32 num_entries;
- struct dma_fence *fence = NULL;
- struct invalidation_fence *ifence;
- struct xe_range_fence *rfence;
- int err;
-
- LLIST_HEAD(deferred);
-
- xe_bo_assert_held(xe_vma_bo(vma));
- xe_vm_assert_held(vm);
-
- vm_dbg(&xe_vma_vm(vma)->xe->drm,
- "Preparing unbind, with range [%llx...%llx) engine %p.\n",
- xe_vma_start(vma), xe_vma_end(vma), q);
-
- num_entries = xe_pt_stage_unbind(tile, vma, entries);
- xe_tile_assert(tile, num_entries <= ARRAY_SIZE(entries));
-
- xe_vm_dbg_print_entries(tile_to_xe(tile), entries, num_entries);
- xe_pt_calc_rfence_interval(vma, &unbind_pt_update, entries,
- num_entries);
-
-<<<<<<<
- err = dma_resv_reserve_fences(xe_vm_resv(vm), 1);
- if (!err && !xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- err = dma_resv_reserve_fences(xe_vma_bo(vma)->ttm.base.resv, 1);
- if (err)
- return ERR_PTR(err);
-
- ifence = kzalloc(sizeof(*ifence), GFP_KERNEL);
- if (!ifence)
- return ERR_PTR(-ENOMEM);
-=======
- if (pt_update_ops->needs_invalidation) {
- ifence = kzalloc(sizeof(*ifence), GFP_KERNEL);
- if (!ifence) {
- err = -ENOMEM;
- goto kill_vm_tile1;
- }
- if (tile->media_gt) {
- mfence = kzalloc(sizeof(*ifence), GFP_KERNEL);
- if (!mfence) {
- err = -ENOMEM;
- goto free_ifence;
- }
- chain_fence = dma_fence_chain_alloc();
- if (!chain_fence) {
- err = -ENOMEM;
- goto free_ifence;
- }
- }
- }
->>>>>>>
-
- rfence = kzalloc(sizeof(*rfence), GFP_KERNEL);
- if (!rfence) {
- kfree(ifence);
- return ERR_PTR(-ENOMEM);
- }
-
- /*
- * Even if we were already evicted and unbind to destroy, we need to
- * clear again here. The eviction may have updated pagetables at a
- * lower level, because it needs to be more conservative.
- */
- fence = xe_migrate_update_pgtables(tile->migrate,
- vm, NULL, q ? q :
- vm->q[tile->id],
- entries, num_entries,
- syncs, num_syncs,
- &unbind_pt_update.base);
- if (!IS_ERR(fence)) {
- int err;
-
- err = xe_range_fence_insert(&vm->rftree[tile->id], rfence,
- &xe_range_fence_kfree_ops,
- unbind_pt_update.base.start,
- unbind_pt_update.base.last, fence);
- if (err)
- dma_fence_wait(fence, false);
-
-<<<<<<<
- /* TLB invalidation must be done before signaling unbind */
- err = invalidation_fence_init(tile->primary_gt, ifence, fence,
- xe_vma_start(vma),
- xe_vma_end(vma),
- xe_vma_vm(vma)->usm.asid);
- if (err) {
- dma_fence_put(fence);
- kfree(ifence);
- return ERR_PTR(err);
- }
- fence = &ifence->base.base;
-
- /* add shared fence now for pagetable delayed destroy */
- dma_resv_add_fence(xe_vm_resv(vm), fence,
- DMA_RESV_USAGE_BOOKKEEP);
-
- /* This fence will be installed by caller when doing eviction */
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- DMA_RESV_USAGE_BOOKKEEP);
- xe_pt_commit_unbind(vma, entries, num_entries,
- unbind_pt_update.locked ? &deferred : NULL);
- vma->tile_present &= ~BIT(tile->id);
- } else {
- kfree(rfence);
- kfree(ifence);
- }
-
- if (!vma->tile_present)
- list_del_init(&vma->combined_links.rebind);
-
- if (unbind_pt_update.locked) {
- xe_tile_assert(tile, xe_vma_is_userptr(vma));
-=======
- xe_pt_commit(pt_op->vma, pt_op->entries,
- pt_op->num_entries, &pt_update_ops->deferred);
- pt_op->vma = NULL; /* skip in xe_pt_update_ops_abort */
- }
-
- if (xe_range_fence_insert(&vm->rftree[tile->id], rfence,
- &xe_range_fence_kfree_ops,
- pt_update_ops->start,
- pt_update_ops->last, fence))
- dma_fence_wait(fence, false);
-
- /* tlb invalidation must be done before signaling rebind */
- if (ifence) {
- if (mfence)
- dma_fence_get(fence);
- invalidation_fence_init(tile->primary_gt, ifence, fence,
- pt_update_ops->start,
- pt_update_ops->last, vm->usm.asid);
- if (mfence) {
- invalidation_fence_init(tile->media_gt, mfence, fence,
- pt_update_ops->start,
- pt_update_ops->last, vm->usm.asid);
- dma_fence_chain_init(chain_fence, &ifence->base.base,
- &mfence->base.base, 0);
- fence = &chain_fence->base;
- } else {
- fence = &ifence->base.base;
- }
- }
-
- if (!mfence) {
- dma_resv_add_fence(xe_vm_resv(vm), fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- list_for_each_entry(op, &vops->list, link)
- op_commit(vops->vm, tile, pt_update_ops, op, fence, NULL);
- } else {
- dma_resv_add_fence(xe_vm_resv(vm), &ifence->base.base,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- dma_resv_add_fence(xe_vm_resv(vm), &mfence->base.base,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- list_for_each_entry(op, &vops->list, link)
- op_commit(vops->vm, tile, pt_update_ops, op,
- &ifence->base.base, &mfence->base.base);
- }
->>>>>>>
-
- if (!vma->tile_present) {
- spin_lock(&vm->userptr.invalidated_lock);
- list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link);
- spin_unlock(&vm->userptr.invalidated_lock);
- }
- up_read(&vm->userptr.notifier_lock);
- xe_bo_put_commit(&deferred);
- }
-
- return fence;
-<<<<<<<
-=======
-
-free_rfence:
- kfree(rfence);
-free_ifence:
- dma_fence_chain_free(chain_fence);
- kfree(mfence);
- kfree(ifence);
-kill_vm_tile1:
- if (err != -EAGAIN && tile->id)
- xe_vm_kill(vops->vm, false);
-
- return ERR_PTR(err);
-}
-
-/**
- * xe_pt_update_ops_fini() - Finish PT update operations
- * @tile: Tile of PT update operations
- * @vops: VMA operations
- *
- * Finish PT update operations by committing to destroy page table memory
- */
-void xe_pt_update_ops_fini(struct xe_tile *tile, struct xe_vma_ops *vops)
-{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- int i;
-
- lockdep_assert_held(&vops->vm->lock);
- xe_vm_assert_held(vops->vm);
-
- for (i = 0; i < pt_update_ops->current_op; ++i) {
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[i];
-
- xe_pt_free_bind(pt_op->entries, pt_op->num_entries);
- }
- xe_bo_put_commit(&vops->pt_update_ops[tile->id].deferred);
-}
-
-/**
- * xe_pt_update_ops_abort() - Abort PT update operations
- * @tile: Tile of PT update operations
- * @vops: VMA operationa
- *
- * Abort PT update operations by unwinding internal PT state
- */
-void xe_pt_update_ops_abort(struct xe_tile *tile, struct xe_vma_ops *vops)
-{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- int i;
-
- lockdep_assert_held(&vops->vm->lock);
- xe_vm_assert_held(vops->vm);
-
- for (i = pt_update_ops->num_ops - 1; i >= 0; --i) {
- struct xe_vm_pgtable_update_op *pt_op =
- &pt_update_ops->ops[i];
-
- if (!pt_op->vma || i >= pt_update_ops->current_op)
- continue;
-
- if (pt_op->bind)
- xe_pt_abort_bind(pt_op->vma, pt_op->entries,
- pt_op->num_entries,
- pt_op->rebind);
- else
- xe_pt_abort_unbind(pt_op->vma, pt_op->entries,
- pt_op->num_entries);
- }
-
- xe_bo_put_commit(&vops->pt_update_ops[tile->id].deferred);
->>>>>>>
-}
diff --git a/rr-cache/41549dd6cc337627199acbe6749c5685c7e927d3/preimage.1 b/rr-cache/41549dd6cc337627199acbe6749c5685c7e927d3/preimage.1
deleted file mode 100644
index 232d29e9a561..000000000000
--- a/rr-cache/41549dd6cc337627199acbe6749c5685c7e927d3/preimage.1
+++ /dev/null
@@ -1,2234 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2022 Intel Corporation
- */
-
-#include <linux/dma-fence-chain.h>
-
-#include "xe_pt.h"
-
-#include "regs/xe_gtt_defs.h"
-#include "xe_bo.h"
-#include "xe_device.h"
-#include "xe_drm_client.h"
-#include "xe_gt.h"
-#include "xe_gt_tlb_invalidation.h"
-#include "xe_migrate.h"
-#include "xe_pt_types.h"
-#include "xe_pt_walk.h"
-#include "xe_res_cursor.h"
-#include "xe_trace.h"
-#include "xe_ttm_stolen_mgr.h"
-#include "xe_vm.h"
-
-struct xe_pt_dir {
- struct xe_pt pt;
- /** @children: Array of page-table child nodes */
- struct xe_ptw *children[XE_PDES];
-};
-
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM)
-#define xe_pt_set_addr(__xe_pt, __addr) ((__xe_pt)->addr = (__addr))
-#define xe_pt_addr(__xe_pt) ((__xe_pt)->addr)
-#else
-#define xe_pt_set_addr(__xe_pt, __addr)
-#define xe_pt_addr(__xe_pt) 0ull
-#endif
-
-static const u64 xe_normal_pt_shifts[] = {12, 21, 30, 39, 48};
-static const u64 xe_compact_pt_shifts[] = {16, 21, 30, 39, 48};
-
-#define XE_PT_HIGHEST_LEVEL (ARRAY_SIZE(xe_normal_pt_shifts) - 1)
-
-static struct xe_pt_dir *as_xe_pt_dir(struct xe_pt *pt)
-{
- return container_of(pt, struct xe_pt_dir, pt);
-}
-
-static struct xe_pt *xe_pt_entry(struct xe_pt_dir *pt_dir, unsigned int index)
-{
- return container_of(pt_dir->children[index], struct xe_pt, base);
-}
-
-static u64 __xe_pt_empty_pte(struct xe_tile *tile, struct xe_vm *vm,
- unsigned int level)
-{
- struct xe_device *xe = tile_to_xe(tile);
- u16 pat_index = xe->pat.idx[XE_CACHE_WB];
- u8 id = tile->id;
-
- if (!xe_vm_has_scratch(vm))
- return 0;
-
- if (level > MAX_HUGEPTE_LEVEL)
- return vm->pt_ops->pde_encode_bo(vm->scratch_pt[id][level - 1]->bo,
- 0, pat_index);
-
- return vm->pt_ops->pte_encode_addr(xe, 0, pat_index, level, IS_DGFX(xe), 0) |
- XE_PTE_NULL;
-}
-
-static void xe_pt_free(struct xe_pt *pt)
-{
- if (pt->level)
- kfree(as_xe_pt_dir(pt));
- else
- kfree(pt);
-}
-
-/**
- * xe_pt_create() - Create a page-table.
- * @vm: The vm to create for.
- * @tile: The tile to create for.
- * @level: The page-table level.
- *
- * Allocate and initialize a single struct xe_pt metadata structure. Also
- * create the corresponding page-table bo, but don't initialize it. If the
- * level is grater than zero, then it's assumed to be a directory page-
- * table and the directory structure is also allocated and initialized to
- * NULL pointers.
- *
- * Return: A valid struct xe_pt pointer on success, Pointer error code on
- * error.
- */
-struct xe_pt *xe_pt_create(struct xe_vm *vm, struct xe_tile *tile,
- unsigned int level)
-{
- struct xe_pt *pt;
- struct xe_bo *bo;
- int err;
-
- if (level) {
- struct xe_pt_dir *dir = kzalloc(sizeof(*dir), GFP_KERNEL);
-
- pt = (dir) ? &dir->pt : NULL;
- } else {
- pt = kzalloc(sizeof(*pt), GFP_KERNEL);
- }
- if (!pt)
- return ERR_PTR(-ENOMEM);
-
- pt->level = level;
- bo = xe_bo_create_pin_map(vm->xe, tile, vm, SZ_4K,
- ttm_bo_type_kernel,
- XE_BO_FLAG_VRAM_IF_DGFX(tile) |
- XE_BO_FLAG_IGNORE_MIN_PAGE_SIZE |
- XE_BO_FLAG_PINNED |
- XE_BO_FLAG_NO_RESV_EVICT |
- XE_BO_FLAG_PAGETABLE);
- if (IS_ERR(bo)) {
- err = PTR_ERR(bo);
- goto err_kfree;
- }
- pt->bo = bo;
- pt->base.children = level ? as_xe_pt_dir(pt)->children : NULL;
-
- if (vm->xef)
- xe_drm_client_add_bo(vm->xef->client, pt->bo);
- xe_tile_assert(tile, level <= XE_VM_MAX_LEVEL);
-
- return pt;
-
-err_kfree:
- xe_pt_free(pt);
- return ERR_PTR(err);
-}
-
-/**
- * xe_pt_populate_empty() - Populate a page-table bo with scratch- or zero
- * entries.
- * @tile: The tile the scratch pagetable of which to use.
- * @vm: The vm we populate for.
- * @pt: The pagetable the bo of which to initialize.
- *
- * Populate the page-table bo of @pt with entries pointing into the tile's
- * scratch page-table tree if any. Otherwise populate with zeros.
- */
-void xe_pt_populate_empty(struct xe_tile *tile, struct xe_vm *vm,
- struct xe_pt *pt)
-{
- struct iosys_map *map = &pt->bo->vmap;
- u64 empty;
- int i;
-
- if (!xe_vm_has_scratch(vm)) {
- /*
- * FIXME: Some memory is allocated already allocated to zero?
- * Find out which memory that is and avoid this memset...
- */
- xe_map_memset(vm->xe, map, 0, 0, SZ_4K);
- } else {
- empty = __xe_pt_empty_pte(tile, vm, pt->level);
- for (i = 0; i < XE_PDES; i++)
- xe_pt_write(vm->xe, map, i, empty);
- }
-}
-
-/**
- * xe_pt_shift() - Return the ilog2 value of the size of the address range of
- * a page-table at a certain level.
- * @level: The level.
- *
- * Return: The ilog2 value of the size of the address range of a page-table
- * at level @level.
- */
-unsigned int xe_pt_shift(unsigned int level)
-{
- return XE_PTE_SHIFT + XE_PDE_SHIFT * level;
-}
-
-/**
- * xe_pt_destroy() - Destroy a page-table tree.
- * @pt: The root of the page-table tree to destroy.
- * @flags: vm flags. Currently unused.
- * @deferred: List head of lockless list for deferred putting. NULL for
- * immediate putting.
- *
- * Puts the page-table bo, recursively calls xe_pt_destroy on all children
- * and finally frees @pt. TODO: Can we remove the @flags argument?
- */
-void xe_pt_destroy(struct xe_pt *pt, u32 flags, struct llist_head *deferred)
-{
- int i;
-
- if (!pt)
- return;
-
- XE_WARN_ON(!list_empty(&pt->bo->ttm.base.gpuva.list));
- xe_bo_unpin(pt->bo);
- xe_bo_put_deferred(pt->bo, deferred);
-
- if (pt->level > 0 && pt->num_live) {
- struct xe_pt_dir *pt_dir = as_xe_pt_dir(pt);
-
- for (i = 0; i < XE_PDES; i++) {
- if (xe_pt_entry(pt_dir, i))
- xe_pt_destroy(xe_pt_entry(pt_dir, i), flags,
- deferred);
- }
- }
- xe_pt_free(pt);
-}
-
-/**
- * DOC: Pagetable building
- *
- * Below we use the term "page-table" for both page-directories, containing
- * pointers to lower level page-directories or page-tables, and level 0
- * page-tables that contain only page-table-entries pointing to memory pages.
- *
- * When inserting an address range in an already existing page-table tree
- * there will typically be a set of page-tables that are shared with other
- * address ranges, and a set that are private to this address range.
- * The set of shared page-tables can be at most two per level,
- * and those can't be updated immediately because the entries of those
- * page-tables may still be in use by the gpu for other mappings. Therefore
- * when inserting entries into those, we instead stage those insertions by
- * adding insertion data into struct xe_vm_pgtable_update structures. This
- * data, (subtrees for the cpu and page-table-entries for the gpu) is then
- * added in a separate commit step. CPU-data is committed while still under the
- * vm lock, the object lock and for userptr, the notifier lock in read mode.
- * The GPU async data is committed either by the GPU or CPU after fulfilling
- * relevant dependencies.
- * For non-shared page-tables (and, in fact, for shared ones that aren't
- * existing at the time of staging), we add the data in-place without the
- * special update structures. This private part of the page-table tree will
- * remain disconnected from the vm page-table tree until data is committed to
- * the shared page tables of the vm tree in the commit phase.
- */
-
-struct xe_pt_update {
- /** @update: The update structure we're building for this parent. */
- struct xe_vm_pgtable_update *update;
- /** @parent: The parent. Used to detect a parent change. */
- struct xe_pt *parent;
- /** @preexisting: Whether the parent was pre-existing or allocated */
- bool preexisting;
-};
-
-struct xe_pt_stage_bind_walk {
- /** base: The base class. */
- struct xe_pt_walk base;
-
- /* Input parameters for the walk */
- /** @vm: The vm we're building for. */
- struct xe_vm *vm;
- /** @tile: The tile we're building for. */
- struct xe_tile *tile;
- /** @default_pte: PTE flag only template. No address is associated */
- u64 default_pte;
- /** @dma_offset: DMA offset to add to the PTE. */
- u64 dma_offset;
- /**
- * @needs_64k: This address range enforces 64K alignment and
- * granularity.
- */
- bool needs_64K;
- /**
- * @vma: VMA being mapped
- */
- struct xe_vma *vma;
-
- /* Also input, but is updated during the walk*/
- /** @curs: The DMA address cursor. */
- struct xe_res_cursor *curs;
- /** @va_curs_start: The Virtual address coresponding to @curs->start */
- u64 va_curs_start;
-
- /* Output */
- struct xe_walk_update {
- /** @wupd.entries: Caller provided storage. */
- struct xe_vm_pgtable_update *entries;
- /** @wupd.num_used_entries: Number of update @entries used. */
- unsigned int num_used_entries;
- /** @wupd.updates: Tracks the update entry at a given level */
- struct xe_pt_update updates[XE_VM_MAX_LEVEL + 1];
- } wupd;
-
- /* Walk state */
- /**
- * @l0_end_addr: The end address of the current l0 leaf. Used for
- * 64K granularity detection.
- */
- u64 l0_end_addr;
- /** @addr_64K: The start address of the current 64K chunk. */
- u64 addr_64K;
- /** @found_64: Whether @add_64K actually points to a 64K chunk. */
- bool found_64K;
-};
-
-static int
-xe_pt_new_shared(struct xe_walk_update *wupd, struct xe_pt *parent,
- pgoff_t offset, bool alloc_entries)
-{
- struct xe_pt_update *upd = &wupd->updates[parent->level];
- struct xe_vm_pgtable_update *entry;
-
- /*
- * For *each level*, we could only have one active
- * struct xt_pt_update at any one time. Once we move on to a
- * new parent and page-directory, the old one is complete, and
- * updates are either already stored in the build tree or in
- * @wupd->entries
- */
- if (likely(upd->parent == parent))
- return 0;
-
- upd->parent = parent;
- upd->preexisting = true;
-
- if (wupd->num_used_entries == XE_VM_MAX_LEVEL * 2 + 1)
- return -EINVAL;
-
- entry = wupd->entries + wupd->num_used_entries++;
- upd->update = entry;
- entry->ofs = offset;
- entry->pt_bo = parent->bo;
- entry->pt = parent;
- entry->flags = 0;
- entry->qwords = 0;
-
- if (alloc_entries) {
- entry->pt_entries = kmalloc_array(XE_PDES,
- sizeof(*entry->pt_entries),
- GFP_KERNEL);
- if (!entry->pt_entries)
- return -ENOMEM;
- }
-
- return 0;
-}
-
-/*
- * NOTE: This is a very frequently called function so we allow ourselves
- * to annotate (using branch prediction hints) the fastpath of updating a
- * non-pre-existing pagetable with leaf ptes.
- */
-static int
-xe_pt_insert_entry(struct xe_pt_stage_bind_walk *xe_walk, struct xe_pt *parent,
- pgoff_t offset, struct xe_pt *xe_child, u64 pte)
-{
- struct xe_pt_update *upd = &xe_walk->wupd.updates[parent->level];
- struct xe_pt_update *child_upd = xe_child ?
- &xe_walk->wupd.updates[xe_child->level] : NULL;
- int ret;
-
- ret = xe_pt_new_shared(&xe_walk->wupd, parent, offset, true);
- if (unlikely(ret))
- return ret;
-
- /*
- * Register this new pagetable so that it won't be recognized as
- * a shared pagetable by a subsequent insertion.
- */
- if (unlikely(child_upd)) {
- child_upd->update = NULL;
- child_upd->parent = xe_child;
- child_upd->preexisting = false;
- }
-
- if (likely(!upd->preexisting)) {
- /* Continue building a non-connected subtree. */
- struct iosys_map *map = &parent->bo->vmap;
-
- if (unlikely(xe_child))
- parent->base.children[offset] = &xe_child->base;
-
- xe_pt_write(xe_walk->vm->xe, map, offset, pte);
- parent->num_live++;
- } else {
- /* Shared pt. Stage update. */
- unsigned int idx;
- struct xe_vm_pgtable_update *entry = upd->update;
-
- idx = offset - entry->ofs;
- entry->pt_entries[idx].pt = xe_child;
- entry->pt_entries[idx].pte = pte;
- entry->qwords++;
- }
-
- return 0;
-}
-
-static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
- struct xe_pt_stage_bind_walk *xe_walk)
-{
- u64 size, dma;
-
- if (level > MAX_HUGEPTE_LEVEL)
- return false;
-
- /* Does the virtual range requested cover a huge pte? */
- if (!xe_pt_covers(addr, next, level, &xe_walk->base))
- return false;
-
- /* Does the DMA segment cover the whole pte? */
- 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))
- return true;
-
- /* Is the DMA address huge PTE size aligned? */
- size = next - addr;
- dma = addr - xe_walk->va_curs_start + xe_res_dma(xe_walk->curs);
-
- return IS_ALIGNED(dma, size);
-}
-
-/*
- * Scan the requested mapping to check whether it can be done entirely
- * with 64K PTEs.
- */
-static bool
-xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk)
-{
- struct xe_res_cursor curs = *xe_walk->curs;
-
- if (!IS_ALIGNED(addr, SZ_64K))
- return false;
-
- 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))
- return true;
-
- xe_res_next(&curs, addr - xe_walk->va_curs_start);
- for (; addr < next; addr += SZ_64K) {
- if (!IS_ALIGNED(xe_res_dma(&curs), SZ_64K) || curs.size < SZ_64K)
- return false;
-
- xe_res_next(&curs, SZ_64K);
- }
-
- return addr == next;
-}
-
-/*
- * For non-compact "normal" 4K level-0 pagetables, we want to try to group
- * addresses together in 64K-contigous regions to add a 64K TLB hint for the
- * device to the PTE.
- * This function determines whether the address is part of such a
- * segment. For VRAM in normal pagetables, this is strictly necessary on
- * some devices.
- */
-static bool
-xe_pt_is_pte_ps64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk)
-{
- /* Address is within an already found 64k region */
- if (xe_walk->found_64K && addr - xe_walk->addr_64K < SZ_64K)
- return true;
-
- xe_walk->found_64K = xe_pt_scan_64K(addr, addr + SZ_64K, xe_walk);
- xe_walk->addr_64K = addr;
-
- return xe_walk->found_64K;
-}
-
-static int
-xe_pt_stage_bind_entry(struct xe_ptw *parent, pgoff_t offset,
- unsigned int level, u64 addr, u64 next,
- struct xe_ptw **child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- struct xe_pt_stage_bind_walk *xe_walk =
- container_of(walk, typeof(*xe_walk), base);
- u16 pat_index = xe_walk->vma->pat_index;
- struct xe_pt *xe_parent = container_of(parent, typeof(*xe_parent), base);
- struct xe_vm *vm = xe_walk->vm;
- struct xe_pt *xe_child;
- bool covers;
- int ret = 0;
- u64 pte;
-
- /* Is this a leaf entry ?*/
- if (level == 0 || xe_pt_hugepte_possible(addr, next, level, xe_walk)) {
- struct xe_res_cursor *curs = xe_walk->curs;
- bool is_null = xe_vma_is_null(xe_walk->vma);
-
- XE_WARN_ON(xe_walk->va_curs_start != addr);
-
- pte = vm->pt_ops->pte_encode_vma(is_null ? 0 :
- xe_res_dma(curs) + xe_walk->dma_offset,
- xe_walk->vma, pat_index, level);
- pte |= xe_walk->default_pte;
-
- /*
- * Set the XE_PTE_PS64 hint if possible, otherwise if
- * this device *requires* 64K PTE size for VRAM, fail.
- */
- if (level == 0 && !xe_parent->is_compact) {
- if (xe_pt_is_pte_ps64K(addr, next, xe_walk)) {
- xe_walk->vma->gpuva.flags |= XE_VMA_PTE_64K;
- pte |= XE_PTE_PS64;
- } else if (XE_WARN_ON(xe_walk->needs_64K)) {
- return -EINVAL;
- }
- }
-
- ret = xe_pt_insert_entry(xe_walk, xe_parent, offset, NULL, pte);
- if (unlikely(ret))
- return ret;
-
- if (!is_null)
- xe_res_next(curs, next - addr);
- xe_walk->va_curs_start = next;
- xe_walk->vma->gpuva.flags |= (XE_VMA_PTE_4K << level);
- *action = ACTION_CONTINUE;
-
- return ret;
- }
-
- /*
- * Descending to lower level. Determine if we need to allocate a
- * new page table or -directory, which we do if there is no
- * previous one or there is one we can completely replace.
- */
- if (level == 1) {
- walk->shifts = xe_normal_pt_shifts;
- xe_walk->l0_end_addr = next;
- }
-
- covers = xe_pt_covers(addr, next, level, &xe_walk->base);
- if (covers || !*child) {
- u64 flags = 0;
-
- xe_child = xe_pt_create(xe_walk->vm, xe_walk->tile, level - 1);
- if (IS_ERR(xe_child))
- return PTR_ERR(xe_child);
-
- xe_pt_set_addr(xe_child,
- round_down(addr, 1ull << walk->shifts[level]));
-
- if (!covers)
- xe_pt_populate_empty(xe_walk->tile, xe_walk->vm, xe_child);
-
- *child = &xe_child->base;
-
- /*
- * Prefer the compact pagetable layout for L0 if possible. Only
- * possible if VMA covers entire 2MB region as compact 64k and
- * 4k pages cannot be mixed within a 2MB region.
- * TODO: Suballocate the pt bo to avoid wasting a lot of
- * memory.
- */
- if (GRAPHICS_VERx100(tile_to_xe(xe_walk->tile)) >= 1250 && level == 1 &&
- covers && xe_pt_scan_64K(addr, next, xe_walk)) {
- walk->shifts = xe_compact_pt_shifts;
- xe_walk->vma->gpuva.flags |= XE_VMA_PTE_COMPACT;
- flags |= XE_PDE_64K;
- xe_child->is_compact = true;
- }
-
- pte = vm->pt_ops->pde_encode_bo(xe_child->bo, 0, pat_index) | flags;
- ret = xe_pt_insert_entry(xe_walk, xe_parent, offset, xe_child,
- pte);
- }
-
- *action = ACTION_SUBTREE;
- return ret;
-}
-
-static const struct xe_pt_walk_ops xe_pt_stage_bind_ops = {
- .pt_entry = xe_pt_stage_bind_entry,
-};
-
-/**
- * xe_pt_stage_bind() - Build a disconnected page-table tree for a given address
- * range.
- * @tile: The tile we're building for.
- * @vma: The vma indicating the address range.
- * @entries: Storage for the update entries used for connecting the tree to
- * the main tree at commit time.
- * @num_entries: On output contains the number of @entries used.
- *
- * This function builds a disconnected page-table tree for a given address
- * range. The tree is connected to the main vm tree for the gpu using
- * xe_migrate_update_pgtables() and for the cpu using xe_pt_commit_bind().
- * The function builds xe_vm_pgtable_update structures for already existing
- * shared page-tables, and non-existing shared and non-shared page-tables
- * are built and populated directly.
- *
- * Return 0 on success, negative error code on error.
- */
-static int
-xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries, u32 *num_entries)
-{
- struct xe_device *xe = tile_to_xe(tile);
- struct xe_bo *bo = xe_vma_bo(vma);
- bool is_devmem = !xe_vma_is_userptr(vma) && bo &&
- (xe_bo_is_vram(bo) || xe_bo_is_stolen_devmem(bo));
- struct xe_res_cursor curs;
- struct xe_pt_stage_bind_walk xe_walk = {
- .base = {
- .ops = &xe_pt_stage_bind_ops,
- .shifts = xe_normal_pt_shifts,
- .max_level = XE_PT_HIGHEST_LEVEL,
- },
- .vm = xe_vma_vm(vma),
- .tile = tile,
- .curs = &curs,
- .va_curs_start = xe_vma_start(vma),
- .vma = vma,
- .wupd.entries = entries,
- .needs_64K = (xe_vma_vm(vma)->flags & XE_VM_FLAG_64K) && is_devmem,
- };
- struct xe_pt *pt = xe_vma_vm(vma)->pt_root[tile->id];
- int ret;
-
- /**
- * Default atomic expectations for different allocation scenarios are as follows:
- *
- * 1. Traditional API: When the VM is not in LR mode:
- * - Device atomics are expected to function with all allocations.
- *
- * 2. Compute/SVM API: When the VM is in LR mode:
- * - Device atomics are the default behavior when the bo is placed in a single region.
- * - In all other cases device atomics will be disabled with AE=0 until an application
- * request differently using a ioctl like madvise.
- */
- if (vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) {
- if (xe_vm_in_lr_mode(xe_vma_vm(vma))) {
- if (bo && xe_bo_has_single_placement(bo))
- xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
- /**
- * If a SMEM+LMEM allocation is backed by SMEM, a device
- * atomics will cause a gpu page fault and which then
- * gets migrated to LMEM, bind such allocations with
- * device atomics enabled.
- */
- else if (is_devmem && !xe_bo_has_single_placement(bo))
- xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
- } else {
- xe_walk.default_pte |= XE_USM_PPGTT_PTE_AE;
- }
-
- /**
- * Unset AE if the platform(PVC) doesn't support it on an
- * allocation
- */
- if (!xe->info.has_device_atomics_on_smem && !is_devmem)
- xe_walk.default_pte &= ~XE_USM_PPGTT_PTE_AE;
- }
-
- if (is_devmem) {
- xe_walk.default_pte |= XE_PPGTT_PTE_DM;
- xe_walk.dma_offset = vram_region_gpu_offset(bo->ttm.resource);
- }
-
- if (!xe_vma_has_no_bo(vma) && xe_bo_is_stolen(bo))
- xe_walk.dma_offset = xe_ttm_stolen_gpu_offset(xe_bo_device(bo));
-
- xe_bo_assert_held(bo);
-
- if (!xe_vma_is_null(vma)) {
- if (xe_vma_is_userptr(vma))
- xe_res_first_sg(to_userptr_vma(vma)->userptr.sg, 0,
- xe_vma_size(vma), &curs);
- else if (xe_bo_is_vram(bo) || xe_bo_is_stolen(bo))
- xe_res_first(bo->ttm.resource, xe_vma_bo_offset(vma),
- xe_vma_size(vma), &curs);
- else
- xe_res_first_sg(xe_bo_sg(bo), xe_vma_bo_offset(vma),
- xe_vma_size(vma), &curs);
- } else {
- curs.size = xe_vma_size(vma);
- }
-
- ret = xe_pt_walk_range(&pt->base, pt->level, xe_vma_start(vma),
- xe_vma_end(vma), &xe_walk.base);
-
- *num_entries = xe_walk.wupd.num_used_entries;
- return ret;
-}
-
-/**
- * xe_pt_nonshared_offsets() - Determine the non-shared entry offsets of a
- * shared pagetable.
- * @addr: The start address within the non-shared pagetable.
- * @end: The end address within the non-shared pagetable.
- * @level: The level of the non-shared pagetable.
- * @walk: Walk info. The function adjusts the walk action.
- * @action: next action to perform (see enum page_walk_action)
- * @offset: Ignored on input, First non-shared entry on output.
- * @end_offset: Ignored on input, Last non-shared entry + 1 on output.
- *
- * A non-shared page-table has some entries that belong to the address range
- * and others that don't. This function determines the entries that belong
- * fully to the address range. Depending on level, some entries may
- * partially belong to the address range (that can't happen at level 0).
- * The function detects that and adjust those offsets to not include those
- * partial entries. Iff it does detect partial entries, we know that there must
- * be shared page tables also at lower levels, so it adjusts the walk action
- * accordingly.
- *
- * Return: true if there were non-shared entries, false otherwise.
- */
-static bool xe_pt_nonshared_offsets(u64 addr, u64 end, unsigned int level,
- struct xe_pt_walk *walk,
- enum page_walk_action *action,
- pgoff_t *offset, pgoff_t *end_offset)
-{
- u64 size = 1ull << walk->shifts[level];
-
- *offset = xe_pt_offset(addr, level, walk);
- *end_offset = xe_pt_num_entries(addr, end, level, walk) + *offset;
-
- if (!level)
- return true;
-
- /*
- * If addr or next are not size aligned, there are shared pts at lower
- * level, so in that case traverse down the subtree
- */
- *action = ACTION_CONTINUE;
- if (!IS_ALIGNED(addr, size)) {
- *action = ACTION_SUBTREE;
- (*offset)++;
- }
-
- if (!IS_ALIGNED(end, size)) {
- *action = ACTION_SUBTREE;
- (*end_offset)--;
- }
-
- return *end_offset > *offset;
-}
-
-struct xe_pt_zap_ptes_walk {
- /** @base: The walk base-class */
- struct xe_pt_walk base;
-
- /* Input parameters for the walk */
- /** @tile: The tile we're building for */
- struct xe_tile *tile;
-
- /* Output */
- /** @needs_invalidate: Whether we need to invalidate TLB*/
- bool needs_invalidate;
-};
-
-static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset,
- unsigned int level, u64 addr, u64 next,
- struct xe_ptw **child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- 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);
- pgoff_t end_offset;
-
- XE_WARN_ON(!*child);
- XE_WARN_ON(!level);
-
- /*
- * 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.
- */
- if (xe_pt_nonshared_offsets(addr, next, --level, walk, action, &offset,
- &end_offset)) {
- xe_map_memset(tile_to_xe(xe_walk->tile), &xe_child->bo->vmap,
- offset * sizeof(u64), 0,
- (end_offset - offset) * sizeof(u64));
- xe_walk->needs_invalidate = true;
- }
-
- return 0;
-}
-
-static const struct xe_pt_walk_ops xe_pt_zap_ptes_ops = {
- .pt_entry = xe_pt_zap_ptes_entry,
-};
-
-/**
- * xe_pt_zap_ptes() - Zap (zero) gpu ptes of an address range
- * @tile: The tile we're zapping for.
- * @vma: GPU VMA detailing address range.
- *
- * Eviction and Userptr invalidation needs to be able to zap the
- * gpu ptes of a given address range in pagefaulting mode.
- * In order to be able to do that, that function needs access to the shared
- * page-table entrieaso it can either clear the leaf PTEs or
- * clear the pointers to lower-level page-tables. The caller is required
- * to hold the necessary locks to ensure neither the page-table connectivity
- * nor the page-table entries of the range is updated from under us.
- *
- * Return: Whether ptes were actually updated and a TLB invalidation is
- * required.
- */
-bool xe_pt_zap_ptes(struct xe_tile *tile, struct xe_vma *vma)
-{
- struct xe_pt_zap_ptes_walk xe_walk = {
- .base = {
- .ops = &xe_pt_zap_ptes_ops,
- .shifts = xe_normal_pt_shifts,
- .max_level = XE_PT_HIGHEST_LEVEL,
- },
- .tile = tile,
- };
- struct xe_pt *pt = xe_vma_vm(vma)->pt_root[tile->id];
- u8 pt_mask = (vma->tile_present & ~vma->tile_invalidated);
-
- if (!(pt_mask & BIT(tile->id)))
- return false;
-
- (void)xe_pt_walk_shared(&pt->base, pt->level, xe_vma_start(vma),
- xe_vma_end(vma), &xe_walk.base);
-
- return xe_walk.needs_invalidate;
-}
-
-static void
-xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *tile,
- struct iosys_map *map, void *data,
- u32 qword_ofs, u32 num_qwords,
- const struct xe_vm_pgtable_update *update)
-{
- struct xe_pt_entry *ptes = update->pt_entries;
- u64 *ptr = data;
- u32 i;
-
- for (i = 0; i < num_qwords; i++) {
- if (map)
- xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
- sizeof(u64), u64, ptes[i].pte);
- else
- ptr[i] = ptes[i].pte;
- }
-}
-
-static void xe_pt_abort_bind(struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries,
- u32 num_entries)
-{
- u32 i, j;
-
- for (i = 0; i < num_entries; i++) {
- if (!entries[i].pt_entries)
- continue;
-
- for (j = 0; j < entries[i].qwords; j++)
- xe_pt_destroy(entries[i].pt_entries[j].pt, xe_vma_vm(vma)->flags, NULL);
- kfree(entries[i].pt_entries);
- }
-}
-
-static void xe_pt_commit_locks_assert(struct xe_vma *vma)
-{
- struct xe_vm *vm = xe_vma_vm(vma);
-
- lockdep_assert_held(&vm->lock);
-
- if (xe_vma_is_userptr(vma))
- lockdep_assert_held_read(&vm->userptr.notifier_lock);
- else if (!xe_vma_is_null(vma))
- dma_resv_assert_held(xe_vma_bo(vma)->ttm.base.resv);
-
- xe_vm_assert_held(vm);
-}
-
-static void xe_pt_commit_bind(struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries,
- u32 num_entries, bool rebind,
- struct llist_head *deferred)
-{
- u32 i, j;
-
- xe_pt_commit_locks_assert(vma);
-
- for (i = 0; i < num_entries; i++) {
- struct xe_pt *pt = entries[i].pt;
- struct xe_pt_dir *pt_dir;
-
- if (!rebind)
- pt->num_live += entries[i].qwords;
-
- if (!pt->level) {
- kfree(entries[i].pt_entries);
- continue;
- }
-
- pt_dir = as_xe_pt_dir(pt);
- for (j = 0; j < entries[i].qwords; j++) {
- u32 j_ = j + entries[i].ofs;
- struct xe_pt *newpte = entries[i].pt_entries[j].pt;
-
- if (xe_pt_entry(pt_dir, j_))
- xe_pt_destroy(xe_pt_entry(pt_dir, j_),
- xe_vma_vm(vma)->flags, deferred);
-
- pt_dir->children[j_] = &newpte->base;
- }
- kfree(entries[i].pt_entries);
- }
-}
-
-static int
-xe_pt_prepare_bind(struct xe_tile *tile, struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries, u32 *num_entries)
-{
- int err;
-
- *num_entries = 0;
- err = xe_pt_stage_bind(tile, vma, entries, num_entries);
- if (!err)
- xe_tile_assert(tile, *num_entries);
- else /* abort! */
- xe_pt_abort_bind(vma, entries, *num_entries);
-
- return err;
-}
-
-static void xe_vm_dbg_print_entries(struct xe_device *xe,
- const struct xe_vm_pgtable_update *entries,
- unsigned int num_entries)
-#if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_VM))
-{
- unsigned int i;
-
- vm_dbg(&xe->drm, "%u entries to update\n", num_entries);
- for (i = 0; i < num_entries; i++) {
- const struct xe_vm_pgtable_update *entry = &entries[i];
- struct xe_pt *xe_pt = entry->pt;
- u64 page_size = 1ull << xe_pt_shift(xe_pt->level);
- u64 end;
- u64 start;
-
- xe_assert(xe, !entry->pt->is_compact);
- start = entry->ofs * page_size;
- end = start + page_size * entry->qwords;
- vm_dbg(&xe->drm,
- "\t%u: Update level %u at (%u + %u) [%llx...%llx) f:%x\n",
- i, xe_pt->level, entry->ofs, entry->qwords,
- xe_pt_addr(xe_pt) + start, xe_pt_addr(xe_pt) + end, 0);
- }
-}
-#else
-{}
-#endif
-
-#ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT
-
-static int xe_pt_userptr_inject_eagain(struct xe_userptr_vma *uvma)
-{
- u32 divisor = uvma->userptr.divisor ? uvma->userptr.divisor : 2;
- static u32 count;
-
- if (count++ % divisor == divisor - 1) {
- struct xe_vm *vm = xe_vma_vm(&uvma->vma);
-
- uvma->userptr.divisor = divisor << 1;
- spin_lock(&vm->userptr.invalidated_lock);
- list_move_tail(&uvma->userptr.invalidate_link,
- &vm->userptr.invalidated);
- spin_unlock(&vm->userptr.invalidated_lock);
- return true;
- }
-
- return false;
-}
-
-#else
-
-static bool xe_pt_userptr_inject_eagain(struct xe_userptr_vma *uvma)
-{
- return false;
-}
-
-#endif
-
-/**
- * struct xe_pt_migrate_pt_update - Callback argument for pre-commit callbacks
- * @base: Base we derive from.
- * @bind: Whether this is a bind or an unbind operation. A bind operation
- * makes the pre-commit callback error with -EAGAIN if it detects a
- * pending invalidation.
- * @locked: Whether the pre-commit callback locked the userptr notifier lock
- * and it needs unlocking.
- */
-struct xe_pt_migrate_pt_update {
- struct xe_migrate_pt_update base;
- bool bind;
- bool locked;
-};
-
-/*
- * This function adds the needed dependencies to a page-table update job
- * to make sure racing jobs for separate bind engines don't race writing
- * to the same page-table range, wreaking havoc. Initially use a single
- * fence for the entire VM. An optimization would use smaller granularity.
- */
-static int xe_pt_vm_dependencies(struct xe_sched_job *job,
- struct xe_range_fence_tree *rftree,
- u64 start, u64 last)
-{
- struct xe_range_fence *rtfence;
- struct dma_fence *fence;
- int err;
-
- rtfence = xe_range_fence_tree_first(rftree, start, last);
- while (rtfence) {
- fence = rtfence->fence;
-
- if (!dma_fence_is_signaled(fence)) {
- /*
- * Is this a CPU update? GPU is busy updating, so return
- * an error
- */
- if (!job)
- return -ETIME;
-
- dma_fence_get(fence);
- err = drm_sched_job_add_dependency(&job->drm, fence);
- if (err)
- return err;
- }
-
- rtfence = xe_range_fence_tree_next(rtfence, start, last);
- }
-
- return 0;
-}
-
-static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update)
-{
- struct xe_range_fence_tree *rftree =
- &xe_vma_vm(pt_update->vma)->rftree[pt_update->tile_id];
-
- return xe_pt_vm_dependencies(pt_update->job, rftree,
- pt_update->start, pt_update->last);
-}
-
-static int xe_pt_userptr_pre_commit(struct xe_migrate_pt_update *pt_update)
-{
- struct xe_pt_migrate_pt_update *userptr_update =
- container_of(pt_update, typeof(*userptr_update), base);
- struct xe_userptr_vma *uvma = to_userptr_vma(pt_update->vma);
- unsigned long notifier_seq = uvma->userptr.notifier_seq;
- struct xe_vm *vm = xe_vma_vm(&uvma->vma);
- int err = xe_pt_vm_dependencies(pt_update->job,
- &vm->rftree[pt_update->tile_id],
- pt_update->start,
- pt_update->last);
-
- if (err)
- return err;
-
- userptr_update->locked = false;
-
- /*
- * Wait until nobody is running the invalidation notifier, and
- * since we're exiting the loop holding the notifier lock,
- * nobody can proceed invalidating either.
- *
- * Note that we don't update the vma->userptr.notifier_seq since
- * we don't update the userptr pages.
- */
- do {
- down_read(&vm->userptr.notifier_lock);
- if (!mmu_interval_read_retry(&uvma->userptr.notifier,
- notifier_seq))
- break;
-
- up_read(&vm->userptr.notifier_lock);
-
- if (userptr_update->bind)
- return -EAGAIN;
-
- notifier_seq = mmu_interval_read_begin(&uvma->userptr.notifier);
- } while (true);
-
- /* Inject errors to test_whether they are handled correctly */
- if (userptr_update->bind && xe_pt_userptr_inject_eagain(uvma)) {
- up_read(&vm->userptr.notifier_lock);
- return -EAGAIN;
- }
-
- userptr_update->locked = true;
-
- return 0;
-}
-
-static const struct xe_migrate_pt_update_ops bind_ops = {
- .populate = xe_vm_populate_pgtable,
- .pre_commit = xe_pt_pre_commit,
-};
-
-static const struct xe_migrate_pt_update_ops userptr_bind_ops = {
- .populate = xe_vm_populate_pgtable,
- .pre_commit = xe_pt_userptr_pre_commit,
-};
-
-struct invalidation_fence {
- struct xe_gt_tlb_invalidation_fence base;
- struct xe_gt *gt;
- struct dma_fence *fence;
- struct dma_fence_cb cb;
- struct work_struct work;
- u64 start;
- u64 end;
- u32 asid;
-};
-
-static void invalidation_fence_cb(struct dma_fence *fence,
- struct dma_fence_cb *cb)
-{
- struct invalidation_fence *ifence =
- container_of(cb, struct invalidation_fence, cb);
- struct xe_device *xe = gt_to_xe(ifence->gt);
-
- trace_xe_gt_tlb_invalidation_fence_cb(xe, &ifence->base);
- if (!ifence->fence->error) {
- queue_work(system_wq, &ifence->work);
- } else {
- ifence->base.base.error = ifence->fence->error;
- dma_fence_signal(&ifence->base.base);
- dma_fence_put(&ifence->base.base);
- }
- dma_fence_put(ifence->fence);
-}
-
-static void invalidation_fence_work_func(struct work_struct *w)
-{
- struct invalidation_fence *ifence =
- container_of(w, struct invalidation_fence, work);
- struct xe_device *xe = gt_to_xe(ifence->gt);
-
- trace_xe_gt_tlb_invalidation_fence_work_func(xe, &ifence->base);
- xe_gt_tlb_invalidation_range(ifence->gt, &ifence->base, ifence->start,
- ifence->end, ifence->asid);
-}
-
-static int invalidation_fence_init(struct xe_gt *gt,
- struct invalidation_fence *ifence,
- struct dma_fence *fence,
- u64 start, u64 end, u32 asid)
-{
- int ret;
-
- trace_xe_gt_tlb_invalidation_fence_create(gt_to_xe(gt), &ifence->base);
-
- xe_gt_tlb_invalidation_fence_init(gt, &ifence->base, false);
-
- ifence->fence = fence;
- ifence->gt = gt;
- ifence->start = start;
- ifence->end = end;
- ifence->asid = asid;
-
- INIT_WORK(&ifence->work, invalidation_fence_work_func);
- ret = dma_fence_add_callback(fence, &ifence->cb, invalidation_fence_cb);
- if (ret == -ENOENT) {
- dma_fence_put(ifence->fence); /* Usually dropped in CB */
- invalidation_fence_work_func(&ifence->work);
- } else if (ret) {
- dma_fence_put(&ifence->base.base); /* Caller ref */
- dma_fence_put(&ifence->base.base); /* Creation ref */
- }
-
- xe_gt_assert(gt, !ret || ret == -ENOENT);
-
- return ret && ret != -ENOENT ? ret : 0;
-}
-
-static void xe_pt_calc_rfence_interval(struct xe_vma *vma,
- struct xe_pt_migrate_pt_update *update,
- struct xe_vm_pgtable_update *entries,
- u32 num_entries)
-{
- int i, level = 0;
-
- for (i = 0; i < num_entries; i++) {
- const struct xe_vm_pgtable_update *entry = &entries[i];
-
- if (entry->pt->level > level)
- level = entry->pt->level;
- }
-
- /* Greedy (non-optimal) calculation but simple */
- update->base.start = ALIGN_DOWN(xe_vma_start(vma),
- 0x1ull << xe_pt_shift(level));
- update->base.last = ALIGN(xe_vma_end(vma),
- 0x1ull << xe_pt_shift(level)) - 1;
-}
-
-/**
- * __xe_pt_bind_vma() - Build and connect a page-table tree for the vma
- * address range.
- * @tile: The tile to bind for.
- * @vma: The vma to bind.
- * @q: The exec_queue with which to do pipelined page-table updates.
- * @syncs: Entries to sync on before binding the built tree to the live vm tree.
- * @num_syncs: Number of @sync entries.
- * @rebind: Whether we're rebinding this vma to the same address range without
- * an unbind in-between.
- *
- * This function builds a page-table tree (see xe_pt_stage_bind() for more
- * information on page-table building), and the xe_vm_pgtable_update entries
- * abstracting the operations needed to attach it to the main vm tree. It
- * then takes the relevant locks and updates the metadata side of the main
- * vm tree and submits the operations for pipelined attachment of the
- * gpu page-table to the vm main tree, (which can be done either by the
- * cpu and the GPU).
- *
- * Return: A valid dma-fence representing the pipelined attachment operation
- * on success, an error pointer on error.
- */
-struct dma_fence *
-__xe_pt_bind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_exec_queue *q,
- struct xe_sync_entry *syncs, u32 num_syncs,
- bool rebind)
-{
- struct xe_vm_pgtable_update entries[XE_VM_MAX_LEVEL * 2 + 1];
- struct xe_pt_migrate_pt_update bind_pt_update = {
- .base = {
- .ops = xe_vma_is_userptr(vma) ? &userptr_bind_ops : &bind_ops,
- .vma = vma,
- .tile_id = tile->id,
- },
- .bind = true,
- };
- struct xe_vm *vm = xe_vma_vm(vma);
- u32 num_entries;
- struct dma_fence *fence;
- struct invalidation_fence *ifence = NULL;
- struct xe_range_fence *rfence;
- int err;
-
- bind_pt_update.locked = false;
- xe_bo_assert_held(xe_vma_bo(vma));
- xe_vm_assert_held(vm);
-
- vm_dbg(&xe_vma_vm(vma)->xe->drm,
- "Preparing bind, with range [%llx...%llx) engine %p.\n",
- xe_vma_start(vma), xe_vma_end(vma), q);
-
- err = xe_pt_prepare_bind(tile, vma, entries, &num_entries);
- if (err)
- goto err;
-
- err = dma_resv_reserve_fences(xe_vm_resv(vm), 1);
- if (!err && !xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- err = dma_resv_reserve_fences(xe_vma_bo(vma)->ttm.base.resv, 1);
- if (err)
- goto err;
-
- xe_tile_assert(tile, num_entries <= ARRAY_SIZE(entries));
-
- xe_vm_dbg_print_entries(tile_to_xe(tile), entries, num_entries);
- xe_pt_calc_rfence_interval(vma, &bind_pt_update, entries,
- num_entries);
-
- /*
- * If rebind, we have to invalidate TLB on !LR vms to invalidate
- * cached PTEs point to freed memory. on LR vms this is done
- * automatically when the context is re-enabled by the rebind worker,
- * or in fault mode it was invalidated on PTE zapping.
- *
- * 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
- * non-faulting LR, in particular on user-space batch buffer chaining,
- * it needs to be done here.
- */
- if ((!rebind && xe_vm_has_scratch(vm) && xe_vm_in_preempt_fence_mode(vm))) {
- ifence = kzalloc(sizeof(*ifence), GFP_KERNEL);
- if (!ifence)
- return ERR_PTR(-ENOMEM);
- } else if (rebind && !xe_vm_in_lr_mode(vm)) {
- /* We bump also if batch_invalidate_tlb is true */
- vm->tlb_flush_seqno++;
- }
-
- rfence = kzalloc(sizeof(*rfence), GFP_KERNEL);
- if (!rfence) {
- kfree(ifence);
- return ERR_PTR(-ENOMEM);
- }
-
- fence = xe_migrate_update_pgtables(tile->migrate,
- vm, xe_vma_bo(vma), q,
- entries, num_entries,
- syncs, num_syncs,
- &bind_pt_update.base);
- if (!IS_ERR(fence)) {
- bool last_munmap_rebind = vma->gpuva.flags & XE_VMA_LAST_REBIND;
- LLIST_HEAD(deferred);
- int err;
-
- err = xe_range_fence_insert(&vm->rftree[tile->id], rfence,
- &xe_range_fence_kfree_ops,
- bind_pt_update.base.start,
- bind_pt_update.base.last, fence);
- if (err)
- dma_fence_wait(fence, false);
-
- /* TLB invalidation must be done before signaling rebind */
- if (ifence) {
- int err = invalidation_fence_init(tile->primary_gt,
- ifence, fence,
- xe_vma_start(vma),
- xe_vma_end(vma),
- xe_vma_vm(vma)->usm.asid);
- if (err) {
- dma_fence_put(fence);
- kfree(ifence);
- return ERR_PTR(err);
- }
- fence = &ifence->base.base;
- }
-
- /* add shared fence now for pagetable delayed destroy */
- dma_resv_add_fence(xe_vm_resv(vm), fence, rebind ||
- last_munmap_rebind ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- DMA_RESV_USAGE_BOOKKEEP);
- xe_pt_commit_bind(vma, entries, num_entries, rebind,
- bind_pt_update.locked ? &deferred : NULL);
-
- /* This vma is live (again?) now */
- vma->tile_present |= BIT(tile->id);
-
- if (bind_pt_update.locked) {
- to_userptr_vma(vma)->userptr.initial_bind = true;
- up_read(&vm->userptr.notifier_lock);
- xe_bo_put_commit(&deferred);
- }
- if (!rebind && last_munmap_rebind &&
- xe_vm_in_preempt_fence_mode(vm))
- xe_vm_queue_rebind_worker(vm);
- } else {
- kfree(rfence);
- kfree(ifence);
- if (bind_pt_update.locked)
- up_read(&vm->userptr.notifier_lock);
- xe_pt_abort_bind(vma, entries, num_entries);
- }
-
- return fence;
-
-err:
- return ERR_PTR(err);
-}
-
-struct xe_pt_stage_unbind_walk {
- /** @base: The pagewalk base-class. */
- struct xe_pt_walk base;
-
- /* Input parameters for the walk */
- /** @tile: The tile we're unbinding from. */
- struct xe_tile *tile;
-
- /**
- * @modified_start: Walk range start, modified to include any
- * shared pagetables that we're the only user of and can thus
- * treat as private.
- */
- u64 modified_start;
- /** @modified_end: Walk range start, modified like @modified_start. */
- u64 modified_end;
-
- /* Output */
- /* @wupd: Structure to track the page-table updates we're building */
- struct xe_walk_update wupd;
-};
-
-/*
- * Check whether this range is the only one populating this pagetable,
- * and in that case, update the walk range checks so that higher levels don't
- * view us as a shared pagetable.
- */
-static bool xe_pt_check_kill(u64 addr, u64 next, unsigned int level,
- const struct xe_pt *child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- struct xe_pt_stage_unbind_walk *xe_walk =
- container_of(walk, typeof(*xe_walk), base);
- unsigned int shift = walk->shifts[level];
- u64 size = 1ull << shift;
-
- if (IS_ALIGNED(addr, size) && IS_ALIGNED(next, size) &&
- ((next - addr) >> shift) == child->num_live) {
- u64 size = 1ull << walk->shifts[level + 1];
-
- *action = ACTION_CONTINUE;
-
- if (xe_walk->modified_start >= addr)
- xe_walk->modified_start = round_down(addr, size);
- if (xe_walk->modified_end <= next)
- xe_walk->modified_end = round_up(next, size);
-
- return true;
- }
-
- return false;
-}
-
-static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset,
- unsigned int level, u64 addr, u64 next,
- struct xe_ptw **child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base);
-
- XE_WARN_ON(!*child);
- XE_WARN_ON(!level);
-
- xe_pt_check_kill(addr, next, level - 1, xe_child, action, walk);
-
- return 0;
-}
-
-static int
-xe_pt_stage_unbind_post_descend(struct xe_ptw *parent, pgoff_t offset,
- unsigned int level, u64 addr, u64 next,
- struct xe_ptw **child,
- enum page_walk_action *action,
- struct xe_pt_walk *walk)
-{
- struct xe_pt_stage_unbind_walk *xe_walk =
- container_of(walk, typeof(*xe_walk), base);
- struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base);
- pgoff_t end_offset;
- u64 size = 1ull << walk->shifts[--level];
-
- if (!IS_ALIGNED(addr, size))
- addr = xe_walk->modified_start;
- if (!IS_ALIGNED(next, size))
- next = xe_walk->modified_end;
-
- /* Parent == *child is the root pt. Don't kill it. */
- if (parent != *child &&
- xe_pt_check_kill(addr, next, level, xe_child, action, walk))
- return 0;
-
- if (!xe_pt_nonshared_offsets(addr, next, level, walk, action, &offset,
- &end_offset))
- return 0;
-
- (void)xe_pt_new_shared(&xe_walk->wupd, xe_child, offset, false);
- xe_walk->wupd.updates[level].update->qwords = end_offset - offset;
-
- return 0;
-}
-
-static const struct xe_pt_walk_ops xe_pt_stage_unbind_ops = {
- .pt_entry = xe_pt_stage_unbind_entry,
- .pt_post_descend = xe_pt_stage_unbind_post_descend,
-};
-
-/**
- * xe_pt_stage_unbind() - Build page-table update structures for an unbind
- * operation
- * @tile: The tile we're unbinding for.
- * @vma: The vma we're unbinding.
- * @entries: Caller-provided storage for the update structures.
- *
- * Builds page-table update structures for an unbind operation. The function
- * will attempt to remove all page-tables that we're the only user
- * of, and for that to work, the unbind operation must be committed in the
- * same critical section that blocks racing binds to the same page-table tree.
- *
- * Return: The number of entries used.
- */
-static unsigned int xe_pt_stage_unbind(struct xe_tile *tile, struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries)
-{
- struct xe_pt_stage_unbind_walk xe_walk = {
- .base = {
- .ops = &xe_pt_stage_unbind_ops,
- .shifts = xe_normal_pt_shifts,
- .max_level = XE_PT_HIGHEST_LEVEL,
- },
- .tile = tile,
- .modified_start = xe_vma_start(vma),
- .modified_end = xe_vma_end(vma),
- .wupd.entries = entries,
- };
- struct xe_pt *pt = xe_vma_vm(vma)->pt_root[tile->id];
-
- (void)xe_pt_walk_shared(&pt->base, pt->level, xe_vma_start(vma),
- xe_vma_end(vma), &xe_walk.base);
-
- return xe_walk.wupd.num_used_entries;
-}
-
-static void
-xe_migrate_clear_pgtable_callback(struct xe_migrate_pt_update *pt_update,
- struct xe_tile *tile, struct iosys_map *map,
- void *ptr, u32 qword_ofs, u32 num_qwords,
- const struct xe_vm_pgtable_update *update)
-{
- struct xe_vma *vma = pt_update->vma;
- u64 empty = __xe_pt_empty_pte(tile, xe_vma_vm(vma), update->pt->level);
- int i;
-
- if (map && map->is_iomem)
- for (i = 0; i < num_qwords; ++i)
- xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) *
- sizeof(u64), u64, empty);
- else if (map)
- memset64(map->vaddr + qword_ofs * sizeof(u64), empty,
- num_qwords);
- else
- memset64(ptr, empty, num_qwords);
-}
-
-static void
-xe_pt_commit_unbind(struct xe_vma *vma,
- struct xe_vm_pgtable_update *entries, u32 num_entries,
- struct llist_head *deferred)
-{
- u32 j;
-
- xe_pt_commit_locks_assert(vma);
-
- for (j = 0; j < num_entries; ++j) {
- struct xe_vm_pgtable_update *entry = &entries[j];
- struct xe_pt *pt = entry->pt;
-
- pt->num_live -= entry->qwords;
- if (pt->level) {
- struct xe_pt_dir *pt_dir = as_xe_pt_dir(pt);
- u32 i;
-
- for (i = entry->ofs; i < entry->ofs + entry->qwords;
- i++) {
- if (xe_pt_entry(pt_dir, i))
- xe_pt_destroy(xe_pt_entry(pt_dir, i),
- xe_vma_vm(vma)->flags, deferred);
-
- pt_dir->children[i] = NULL;
- }
- }
- }
-}
-
-<<<<<<<
-static const struct xe_migrate_pt_update_ops unbind_ops = {
- .populate = xe_migrate_clear_pgtable_callback,
-=======
-static void
-xe_pt_update_ops_rfence_interval(struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma)
-{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
- int i, level = 0;
- u64 start, last;
-
- for (i = 0; i < pt_op->num_entries; i++) {
- const struct xe_vm_pgtable_update *entry = &pt_op->entries[i];
-
- if (entry->pt->level > level)
- level = entry->pt->level;
- }
-
- /* Greedy (non-optimal) calculation but simple */
- start = ALIGN_DOWN(xe_vma_start(vma), 0x1ull << xe_pt_shift(level));
- last = ALIGN(xe_vma_end(vma), 0x1ull << xe_pt_shift(level)) - 1;
-
- if (start < pt_update_ops->start)
- pt_update_ops->start = start;
- if (last > pt_update_ops->last)
- pt_update_ops->last = last;
-}
-
-static int vma_reserve_fences(struct xe_device *xe, struct xe_vma *vma)
-{
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- return dma_resv_reserve_fences(xe_vma_bo(vma)->ttm.base.resv,
- xe->info.tile_count);
-
- return 0;
-}
-
-static int bind_op_prepare(struct xe_vm *vm, struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma)
-{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
- int err;
-
- xe_bo_assert_held(xe_vma_bo(vma));
-
- vm_dbg(&xe_vma_vm(vma)->xe->drm,
- "Preparing bind, with range [%llx...%llx)\n",
- xe_vma_start(vma), xe_vma_end(vma) - 1);
-
- pt_op->vma = NULL;
- pt_op->bind = true;
- pt_op->rebind = BIT(tile->id) & vma->tile_present;
-
- err = vma_reserve_fences(tile_to_xe(tile), vma);
- if (err)
- return err;
-
- err = xe_pt_prepare_bind(tile, vma, pt_op->entries,
- &pt_op->num_entries);
- if (!err) {
- xe_tile_assert(tile, pt_op->num_entries <=
- ARRAY_SIZE(pt_op->entries));
- xe_vm_dbg_print_entries(tile_to_xe(tile), pt_op->entries,
- pt_op->num_entries, true);
-
- xe_pt_update_ops_rfence_interval(pt_update_ops, vma);
- ++pt_update_ops->current_op;
- pt_update_ops->needs_userptr_lock |= xe_vma_is_userptr(vma);
-
- /*
- * If rebind, we have to invalidate TLB on !LR vms to invalidate
- * cached PTEs point to freed memory. On LR vms this is done
- * automatically when the context is re-enabled by the rebind worker,
- * or in fault mode it was invalidated on PTE zapping.
- *
- * 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
- * non-faulting LR, in particular on user-space batch buffer chaining,
- * it needs to be done here.
- */
- if ((!pt_op->rebind && xe_vm_has_scratch(vm) &&
- xe_vm_in_preempt_fence_mode(vm)))
- 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++;
-
- vma->tile_staged |= BIT(tile->id);
- pt_op->vma = vma;
- xe_pt_commit_prepare_bind(vma, pt_op->entries,
- pt_op->num_entries, pt_op->rebind);
- } else {
- xe_pt_cancel_bind(vma, pt_op->entries, pt_op->num_entries);
- }
-
- return err;
-}
-
-static int unbind_op_prepare(struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma)
-{
- u32 current_op = pt_update_ops->current_op;
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[current_op];
- int err;
-
- if (!((vma->tile_present | vma->tile_staged) & BIT(tile->id)))
- return 0;
-
- xe_bo_assert_held(xe_vma_bo(vma));
-
- vm_dbg(&xe_vma_vm(vma)->xe->drm,
- "Preparing unbind, with range [%llx...%llx)\n",
- xe_vma_start(vma), xe_vma_end(vma) - 1);
-
- /*
- * Wait for invalidation to complete. Can corrupt internal page table
- * state if an invalidation is running while preparing an unbind.
- */
- if (xe_vma_is_userptr(vma) && xe_vm_in_fault_mode(xe_vma_vm(vma)))
- mmu_interval_read_begin(&to_userptr_vma(vma)->userptr.notifier);
-
- pt_op->vma = vma;
- pt_op->bind = false;
- pt_op->rebind = false;
-
- err = vma_reserve_fences(tile_to_xe(tile), vma);
- if (err)
- return err;
-
- pt_op->num_entries = xe_pt_stage_unbind(tile, vma, pt_op->entries);
-
- xe_vm_dbg_print_entries(tile_to_xe(tile), pt_op->entries,
- pt_op->num_entries, false);
- xe_pt_update_ops_rfence_interval(pt_update_ops, vma);
- ++pt_update_ops->current_op;
- pt_update_ops->needs_userptr_lock |= xe_vma_is_userptr(vma);
- pt_update_ops->needs_invalidation = true;
-
- xe_pt_commit_prepare_unbind(vma, pt_op->entries, pt_op->num_entries);
-
- return 0;
-}
-
-static int op_prepare(struct xe_vm *vm,
- struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma_op *op)
-{
- int err = 0;
-
- xe_vm_assert_held(vm);
-
- switch (op->base.op) {
- case DRM_GPUVA_OP_MAP:
- if (!op->map.immediate && xe_vm_in_fault_mode(vm))
- break;
-
- err = bind_op_prepare(vm, tile, pt_update_ops, op->map.vma);
- pt_update_ops->wait_vm_kernel = true;
- break;
- case DRM_GPUVA_OP_REMAP:
- err = unbind_op_prepare(tile, pt_update_ops,
- gpuva_to_vma(op->base.remap.unmap->va));
-
- if (!err && op->remap.prev) {
- err = bind_op_prepare(vm, tile, pt_update_ops,
- op->remap.prev);
- pt_update_ops->wait_vm_bookkeep = true;
- }
- if (!err && op->remap.next) {
- err = bind_op_prepare(vm, tile, pt_update_ops,
- op->remap.next);
- pt_update_ops->wait_vm_bookkeep = true;
- }
- break;
- case DRM_GPUVA_OP_UNMAP:
- err = unbind_op_prepare(tile, pt_update_ops,
- gpuva_to_vma(op->base.unmap.va));
- break;
- case DRM_GPUVA_OP_PREFETCH:
- err = bind_op_prepare(vm, tile, pt_update_ops,
- gpuva_to_vma(op->base.prefetch.va));
- pt_update_ops->wait_vm_kernel = true;
- break;
- default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
- }
-
- return err;
-}
-
-static void
-xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops)
-{
- init_llist_head(&pt_update_ops->deferred);
- pt_update_ops->start = ~0x0ull;
- pt_update_ops->last = 0x0ull;
-}
-
-/**
- * xe_pt_update_ops_prepare() - Prepare PT update operations
- * @tile: Tile of PT update operations
- * @vops: VMA operationa
- *
- * Prepare PT update operations which includes updating internal PT state,
- * allocate memory for page tables, populate page table being pruned in, and
- * create PT update operations for leaf insertion / removal.
- *
- * Return: 0 on success, negative error code on error.
- */
-int xe_pt_update_ops_prepare(struct xe_tile *tile, struct xe_vma_ops *vops)
-{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- struct xe_vma_op *op;
- int err;
-
- lockdep_assert_held(&vops->vm->lock);
- xe_vm_assert_held(vops->vm);
-
- xe_pt_update_ops_init(pt_update_ops);
-
- err = dma_resv_reserve_fences(xe_vm_resv(vops->vm),
- tile_to_xe(tile)->info.tile_count);
- if (err)
- return err;
-
- list_for_each_entry(op, &vops->list, link) {
- err = op_prepare(vops->vm, tile, pt_update_ops, op);
-
- if (err)
- return err;
- }
-
- xe_tile_assert(tile, pt_update_ops->current_op <=
- pt_update_ops->num_ops);
-
-#ifdef TEST_VM_OPS_ERROR
- if (vops->inject_error &&
- vops->vm->xe->vm_inject_error_position == FORCE_OP_ERROR_PREPARE)
- return -ENOSPC;
-#endif
-
- return 0;
-}
-
-static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma, struct dma_fence *fence,
- struct dma_fence *fence2)
-{
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm) {
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- if (fence2)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence2,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- }
- vma->tile_present |= BIT(tile->id);
- vma->tile_staged &= ~BIT(tile->id);
- if (xe_vma_is_userptr(vma)) {
- lockdep_assert_held_read(&vm->userptr.notifier_lock);
- to_userptr_vma(vma)->userptr.initial_bind = true;
- }
-
- /*
- * Kick rebind worker if this bind triggers preempt fences and not in
- * the rebind worker
- */
- if (pt_update_ops->wait_vm_bookkeep &&
- xe_vm_in_preempt_fence_mode(vm) &&
- !current->mm)
- xe_vm_queue_rebind_worker(vm);
-}
-
-static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma *vma, struct dma_fence *fence,
- struct dma_fence *fence2)
-{
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm) {
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- if (fence2)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence2,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
- }
- vma->tile_present &= ~BIT(tile->id);
- if (!vma->tile_present) {
- list_del_init(&vma->combined_links.rebind);
- if (xe_vma_is_userptr(vma)) {
- lockdep_assert_held_read(&vm->userptr.notifier_lock);
-
- spin_lock(&vm->userptr.invalidated_lock);
- list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link);
- spin_unlock(&vm->userptr.invalidated_lock);
- }
- }
-}
-
-static void op_commit(struct xe_vm *vm,
- struct xe_tile *tile,
- struct xe_vm_pgtable_update_ops *pt_update_ops,
- struct xe_vma_op *op, struct dma_fence *fence,
- struct dma_fence *fence2)
-{
- xe_vm_assert_held(vm);
-
- switch (op->base.op) {
- case DRM_GPUVA_OP_MAP:
- if (!op->map.immediate && xe_vm_in_fault_mode(vm))
- break;
-
- bind_op_commit(vm, tile, pt_update_ops, op->map.vma, fence,
- fence2);
- break;
- case DRM_GPUVA_OP_REMAP:
- unbind_op_commit(vm, tile, pt_update_ops,
- gpuva_to_vma(op->base.remap.unmap->va), fence,
- fence2);
-
- if (op->remap.prev)
- bind_op_commit(vm, tile, pt_update_ops, op->remap.prev,
- fence, fence2);
- if (op->remap.next)
- bind_op_commit(vm, tile, pt_update_ops, op->remap.next,
- fence, fence2);
- break;
- case DRM_GPUVA_OP_UNMAP:
- unbind_op_commit(vm, tile, pt_update_ops,
- gpuva_to_vma(op->base.unmap.va), fence, fence2);
- break;
- case DRM_GPUVA_OP_PREFETCH:
- bind_op_commit(vm, tile, pt_update_ops,
- gpuva_to_vma(op->base.prefetch.va), fence, fence2);
- break;
- default:
- drm_warn(&vm->xe->drm, "NOT POSSIBLE");
- }
-}
-
-static const struct xe_migrate_pt_update_ops migrate_ops = {
- .populate = xe_vm_populate_pgtable,
- .clear = xe_migrate_clear_pgtable_callback,
->>>>>>>
- .pre_commit = xe_pt_pre_commit,
-};
-
-static const struct xe_migrate_pt_update_ops userptr_unbind_ops = {
- .populate = xe_migrate_clear_pgtable_callback,
- .pre_commit = xe_pt_userptr_pre_commit,
-};
-
-/**
- * __xe_pt_unbind_vma() - Disconnect and free a page-table tree for the vma
- * address range.
- * @tile: The tile to unbind for.
- * @vma: The vma to unbind.
- * @q: The exec_queue with which to do pipelined page-table updates.
- * @syncs: Entries to sync on before disconnecting the tree to be destroyed.
- * @num_syncs: Number of @sync entries.
- *
- * This function builds a the xe_vm_pgtable_update entries abstracting the
- * operations needed to detach the page-table tree to be destroyed from the
- * man vm tree.
- * It then takes the relevant locks and submits the operations for
- * pipelined detachment of the gpu page-table from the vm main tree,
- * (which can be done either by the cpu and the GPU), Finally it frees the
- * detached page-table tree.
- *
- * Return: A valid dma-fence representing the pipelined detachment operation
- * on success, an error pointer on error.
- */
-struct dma_fence *
-__xe_pt_unbind_vma(struct xe_tile *tile, struct xe_vma *vma, struct xe_exec_queue *q,
- struct xe_sync_entry *syncs, u32 num_syncs)
-{
-<<<<<<<
- struct xe_vm *vm = vops->vm;
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- struct dma_fence *fence;
- struct invalidation_fence *ifence = NULL, *mfence = NULL;
- struct dma_fence_chain *chain_fence = NULL;
- struct xe_range_fence *rfence;
- struct xe_vma_op *op;
- int err = 0, i;
- struct xe_migrate_pt_update update = {
- .ops = pt_update_ops->needs_userptr_lock ?
- &userptr_migrate_ops :
- &migrate_ops,
- .vops = vops,
- .tile_id = tile->id,
-=======
- struct xe_vm_pgtable_update entries[XE_VM_MAX_LEVEL * 2 + 1];
- struct xe_pt_migrate_pt_update unbind_pt_update = {
- .base = {
- .ops = xe_vma_is_userptr(vma) ? &userptr_unbind_ops :
- &unbind_ops,
- .vma = vma,
- .tile_id = tile->id,
- },
->>>>>>>
- };
- struct xe_vm *vm = xe_vma_vm(vma);
- u32 num_entries;
- struct dma_fence *fence = NULL;
- struct invalidation_fence *ifence;
- struct xe_range_fence *rfence;
- int err;
-
- LLIST_HEAD(deferred);
-
- xe_bo_assert_held(xe_vma_bo(vma));
- xe_vm_assert_held(vm);
-
- vm_dbg(&xe_vma_vm(vma)->xe->drm,
- "Preparing unbind, with range [%llx...%llx) engine %p.\n",
- xe_vma_start(vma), xe_vma_end(vma), q);
-
- num_entries = xe_pt_stage_unbind(tile, vma, entries);
- xe_tile_assert(tile, num_entries <= ARRAY_SIZE(entries));
-
- xe_vm_dbg_print_entries(tile_to_xe(tile), entries, num_entries);
- xe_pt_calc_rfence_interval(vma, &unbind_pt_update, entries,
- num_entries);
-
-<<<<<<<
- err = dma_resv_reserve_fences(xe_vm_resv(vm), 1);
- if (!err && !xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- err = dma_resv_reserve_fences(xe_vma_bo(vma)->ttm.base.resv, 1);
- if (err)
- return ERR_PTR(err);
-
- ifence = kzalloc(sizeof(*ifence), GFP_KERNEL);
- if (!ifence)
- return ERR_PTR(-ENOMEM);
-=======
- if (pt_update_ops->needs_invalidation) {
- ifence = kzalloc(sizeof(*ifence), GFP_KERNEL);
- if (!ifence) {
- err = -ENOMEM;
- goto kill_vm_tile1;
- }
- if (tile->media_gt) {
- mfence = kzalloc(sizeof(*ifence), GFP_KERNEL);
- if (!mfence) {
- err = -ENOMEM;
- goto free_ifence;
- }
- chain_fence = dma_fence_chain_alloc();
- if (!chain_fence) {
- err = -ENOMEM;
- goto free_ifence;
- }
- }
- }
->>>>>>>
-
- rfence = kzalloc(sizeof(*rfence), GFP_KERNEL);
- if (!rfence) {
- kfree(ifence);
- return ERR_PTR(-ENOMEM);
- }
-
- /*
- * Even if we were already evicted and unbind to destroy, we need to
- * clear again here. The eviction may have updated pagetables at a
- * lower level, because it needs to be more conservative.
- */
- fence = xe_migrate_update_pgtables(tile->migrate,
- vm, NULL, q ? q :
- vm->q[tile->id],
- entries, num_entries,
- syncs, num_syncs,
- &unbind_pt_update.base);
- if (!IS_ERR(fence)) {
- int err;
-
- err = xe_range_fence_insert(&vm->rftree[tile->id], rfence,
- &xe_range_fence_kfree_ops,
- unbind_pt_update.base.start,
- unbind_pt_update.base.last, fence);
- if (err)
- dma_fence_wait(fence, false);
-
-<<<<<<<
- /* TLB invalidation must be done before signaling unbind */
- err = invalidation_fence_init(tile->primary_gt, ifence, fence,
- xe_vma_start(vma),
- xe_vma_end(vma),
- xe_vma_vm(vma)->usm.asid);
- if (err) {
- dma_fence_put(fence);
- kfree(ifence);
- return ERR_PTR(err);
- }
- fence = &ifence->base.base;
-
- /* add shared fence now for pagetable delayed destroy */
- dma_resv_add_fence(xe_vm_resv(vm), fence,
- DMA_RESV_USAGE_BOOKKEEP);
-
- /* This fence will be installed by caller when doing eviction */
- if (!xe_vma_has_no_bo(vma) && !xe_vma_bo(vma)->vm)
- dma_resv_add_fence(xe_vma_bo(vma)->ttm.base.resv, fence,
- DMA_RESV_USAGE_BOOKKEEP);
- xe_pt_commit_unbind(vma, entries, num_entries,
- unbind_pt_update.locked ? &deferred : NULL);
- vma->tile_present &= ~BIT(tile->id);
- } else {
- kfree(rfence);
- kfree(ifence);
- }
-
- if (!vma->tile_present)
- list_del_init(&vma->combined_links.rebind);
-
- if (unbind_pt_update.locked) {
- xe_tile_assert(tile, xe_vma_is_userptr(vma));
-=======
- xe_pt_commit(pt_op->vma, pt_op->entries,
- pt_op->num_entries, &pt_update_ops->deferred);
- pt_op->vma = NULL; /* skip in xe_pt_update_ops_abort */
- }
-
- if (xe_range_fence_insert(&vm->rftree[tile->id], rfence,
- &xe_range_fence_kfree_ops,
- pt_update_ops->start,
- pt_update_ops->last, fence))
- dma_fence_wait(fence, false);
-
- /* tlb invalidation must be done before signaling rebind */
- if (ifence) {
- if (mfence)
- dma_fence_get(fence);
- invalidation_fence_init(tile->primary_gt, ifence, fence,
- pt_update_ops->start,
- pt_update_ops->last, vm->usm.asid);
- if (mfence) {
- invalidation_fence_init(tile->media_gt, mfence, fence,
- pt_update_ops->start,
- pt_update_ops->last, vm->usm.asid);
- dma_fence_chain_init(chain_fence, &ifence->base.base,
- &mfence->base.base, 0);
- fence = &chain_fence->base;
- } else {
- fence = &ifence->base.base;
- }
- }
-
- if (!mfence) {
- dma_resv_add_fence(xe_vm_resv(vm), fence,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- list_for_each_entry(op, &vops->list, link)
- op_commit(vops->vm, tile, pt_update_ops, op, fence, NULL);
- } else {
- dma_resv_add_fence(xe_vm_resv(vm), &ifence->base.base,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- dma_resv_add_fence(xe_vm_resv(vm), &mfence->base.base,
- pt_update_ops->wait_vm_bookkeep ?
- DMA_RESV_USAGE_KERNEL :
- DMA_RESV_USAGE_BOOKKEEP);
-
- list_for_each_entry(op, &vops->list, link)
- op_commit(vops->vm, tile, pt_update_ops, op,
- &ifence->base.base, &mfence->base.base);
- }
->>>>>>>
-
- if (!vma->tile_present) {
- spin_lock(&vm->userptr.invalidated_lock);
- list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link);
- spin_unlock(&vm->userptr.invalidated_lock);
- }
- up_read(&vm->userptr.notifier_lock);
- xe_bo_put_commit(&deferred);
- }
-
- return fence;
-<<<<<<<
-=======
-
-free_rfence:
- kfree(rfence);
-free_ifence:
- dma_fence_chain_free(chain_fence);
- kfree(mfence);
- kfree(ifence);
-kill_vm_tile1:
- if (err != -EAGAIN && tile->id)
- xe_vm_kill(vops->vm, false);
-
- return ERR_PTR(err);
-}
-
-/**
- * xe_pt_update_ops_fini() - Finish PT update operations
- * @tile: Tile of PT update operations
- * @vops: VMA operations
- *
- * Finish PT update operations by committing to destroy page table memory
- */
-void xe_pt_update_ops_fini(struct xe_tile *tile, struct xe_vma_ops *vops)
-{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- int i;
-
- lockdep_assert_held(&vops->vm->lock);
- xe_vm_assert_held(vops->vm);
-
- for (i = 0; i < pt_update_ops->current_op; ++i) {
- struct xe_vm_pgtable_update_op *pt_op = &pt_update_ops->ops[i];
-
- xe_pt_free_bind(pt_op->entries, pt_op->num_entries);
- }
- xe_bo_put_commit(&vops->pt_update_ops[tile->id].deferred);
-}
-
-/**
- * xe_pt_update_ops_abort() - Abort PT update operations
- * @tile: Tile of PT update operations
- * @vops: VMA operationa
- *
- * Abort PT update operations by unwinding internal PT state
- */
-void xe_pt_update_ops_abort(struct xe_tile *tile, struct xe_vma_ops *vops)
-{
- struct xe_vm_pgtable_update_ops *pt_update_ops =
- &vops->pt_update_ops[tile->id];
- int i;
-
- lockdep_assert_held(&vops->vm->lock);
- xe_vm_assert_held(vops->vm);
-
- for (i = pt_update_ops->num_ops - 1; i >= 0; --i) {
- struct xe_vm_pgtable_update_op *pt_op =
- &pt_update_ops->ops[i];
-
- if (!pt_op->vma || i >= pt_update_ops->current_op)
- continue;
-
- if (pt_op->bind)
- xe_pt_abort_bind(pt_op->vma, pt_op->entries,
- pt_op->num_entries,
- pt_op->rebind);
- else
- xe_pt_abort_unbind(pt_op->vma, pt_op->entries,
- pt_op->num_entries);
- }
-
- xe_bo_put_commit(&vops->pt_update_ops[tile->id].deferred);
->>>>>>>
-}
diff --git a/rr-cache/44bc583546c40f3144ba90b705854f1ee09d13ea/preimage b/rr-cache/44bc583546c40f3144ba90b705854f1ee09d13ea/preimage
new file mode 100644
index 000000000000..ef95bec8d180
--- /dev/null
+++ b/rr-cache/44bc583546c40f3144ba90b705854f1ee09d13ea/preimage
@@ -0,0 +1,1231 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * 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 (including the next
+ * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+ */
+
+/**
+ * DOC: atomic plane helpers
+ *
+ * The functions here are used by the atomic plane helper functions to
+ * implement legacy plane updates (i.e., drm_plane->update_plane() and
+ * drm_plane->disable_plane()). This allows plane updates to use the
+ * atomic state infrastructure and perform plane updates as separate
+ * prepare/check/commit/cleanup steps.
+ */
+
+#include <linux/dma-fence-chain.h>
+#include <linux/dma-resv.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_atomic_helper.h>
+
+#include "i915_config.h"
+#include "i9xx_plane_regs.h"
+#include "intel_atomic_plane.h"
+#include "intel_cdclk.h"
+#include "intel_cursor.h"
+#include "intel_display_rps.h"
+#include "intel_display_trace.h"
+#include "intel_display_types.h"
+#include "intel_fb.h"
+#include "intel_fb_pin.h"
+#include "skl_scaler.h"
+#include "skl_watermark.h"
+
+static void intel_plane_state_reset(struct intel_plane_state *plane_state,
+ struct intel_plane *plane)
+{
+ memset(plane_state, 0, sizeof(*plane_state));
+
+ __drm_atomic_helper_plane_state_reset(&plane_state->uapi, &plane->base);
+
+ plane_state->scaler_id = -1;
+}
+
+struct intel_plane *intel_plane_alloc(void)
+{
+ struct intel_plane_state *plane_state;
+ struct intel_plane *plane;
+
+ plane = kzalloc(sizeof(*plane), GFP_KERNEL);
+ if (!plane)
+ return ERR_PTR(-ENOMEM);
+
+ plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL);
+ if (!plane_state) {
+ kfree(plane);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ intel_plane_state_reset(plane_state, plane);
+
+ plane->base.state = &plane_state->uapi;
+
+ return plane;
+}
+
+void intel_plane_free(struct intel_plane *plane)
+{
+ intel_plane_destroy_state(&plane->base, plane->base.state);
+ kfree(plane);
+}
+
+/**
+ * intel_plane_duplicate_state - duplicate plane state
+ * @plane: drm plane
+ *
+ * Allocates and returns a copy of the plane state (both common and
+ * Intel-specific) for the specified plane.
+ *
+ * Returns: The newly allocated plane state, or NULL on failure.
+ */
+struct drm_plane_state *
+intel_plane_duplicate_state(struct drm_plane *plane)
+{
+ struct intel_plane_state *intel_state;
+
+ intel_state = to_intel_plane_state(plane->state);
+ intel_state = kmemdup(intel_state, sizeof(*intel_state), GFP_KERNEL);
+
+ if (!intel_state)
+ return NULL;
+
+ __drm_atomic_helper_plane_duplicate_state(plane, &intel_state->uapi);
+
+ intel_state->ggtt_vma = NULL;
+ intel_state->dpt_vma = NULL;
+ intel_state->flags = 0;
+
+ /* add reference to fb */
+ if (intel_state->hw.fb)
+ drm_framebuffer_get(intel_state->hw.fb);
+
+ return &intel_state->uapi;
+}
+
+/**
+ * intel_plane_destroy_state - destroy plane state
+ * @plane: drm plane
+ * @state: state object to destroy
+ *
+ * Destroys the plane state (both common and Intel-specific) for the
+ * specified plane.
+ */
+void
+intel_plane_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct intel_plane_state *plane_state = to_intel_plane_state(state);
+
+ drm_WARN_ON(plane->dev, plane_state->ggtt_vma);
+ drm_WARN_ON(plane->dev, plane_state->dpt_vma);
+
+ __drm_atomic_helper_plane_destroy_state(&plane_state->uapi);
+ if (plane_state->hw.fb)
+ drm_framebuffer_put(plane_state->hw.fb);
+ kfree(plane_state);
+}
+
+bool intel_plane_needs_physical(struct intel_plane *plane)
+{
+ struct drm_i915_private *i915 = to_i915(plane->base.dev);
+
+ return plane->id == PLANE_CURSOR &&
+ DISPLAY_INFO(i915)->cursor_needs_physical;
+}
+
+unsigned int intel_adjusted_rate(const struct drm_rect *src,
+ const struct drm_rect *dst,
+ unsigned int rate)
+{
+ unsigned int src_w, src_h, dst_w, dst_h;
+
+ src_w = drm_rect_width(src) >> 16;
+ src_h = drm_rect_height(src) >> 16;
+ dst_w = drm_rect_width(dst);
+ dst_h = drm_rect_height(dst);
+
+ /* Downscaling limits the maximum pixel rate */
+ dst_w = min(src_w, dst_w);
+ dst_h = min(src_h, dst_h);
+
+ return DIV_ROUND_UP_ULL(mul_u32_u32(rate, src_w * src_h),
+ dst_w * dst_h);
+}
+
+unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+{
+ /*
+ * Note we don't check for plane visibility here as
+ * we want to use this when calculating the cursor
+ * watermarks even if the cursor is fully offscreen.
+ * That depends on the src/dst rectangles being
+ * correctly populated whenever the watermark code
+ * considers the cursor to be visible, whether or not
+ * it is actually visible.
+ *
+ * See: intel_wm_plane_visible() and intel_check_cursor()
+ */
+
+ return intel_adjusted_rate(&plane_state->uapi.src,
+ &plane_state->uapi.dst,
+ crtc_state->pixel_rate);
+}
+
+unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ int color_plane)
+{
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+
+ if (!plane_state->uapi.visible)
+ return 0;
+
+ return intel_plane_pixel_rate(crtc_state, plane_state) *
+ fb->format->cpp[color_plane];
+}
+
+static bool
+use_min_ddb(const struct intel_crtc_state *crtc_state,
+ struct intel_plane *plane)
+{
+ struct drm_i915_private *i915 = to_i915(plane->base.dev);
+
+ return DISPLAY_VER(i915) >= 13 &&
+ crtc_state->uapi.async_flip &&
+ plane->async_flip;
+}
+
+static unsigned int
+intel_plane_relative_data_rate(const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ int color_plane)
+{
+ struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+ int width, height;
+ unsigned int rel_data_rate;
+
+ if (plane->id == PLANE_CURSOR)
+ return 0;
+
+ if (!plane_state->uapi.visible)
+ return 0;
+
+ /*
+ * We calculate extra ddb based on ratio plane rate/total data rate
+ * in case, in some cases we should not allocate extra ddb for the plane,
+ * so do not count its data rate, if this is the case.
+ */
+ if (use_min_ddb(crtc_state, plane))
+ return 0;
+
+ /*
+ * Src coordinates are already rotated by 270 degrees for
+ * the 90/270 degree plane rotation cases (to match the
+ * GTT mapping), hence no need to account for rotation here.
+ */
+ width = drm_rect_width(&plane_state->uapi.src) >> 16;
+ height = drm_rect_height(&plane_state->uapi.src) >> 16;
+
+ /* UV plane does 1/2 pixel sub-sampling */
+ if (color_plane == 1) {
+ width /= 2;
+ height /= 2;
+ }
+
+ rel_data_rate = width * height * fb->format->cpp[color_plane];
+
+ return intel_adjusted_rate(&plane_state->uapi.src,
+ &plane_state->uapi.dst,
+ rel_data_rate);
+}
+
+int intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
+ struct intel_plane *plane,
+ bool *need_cdclk_calc)
+{
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ const struct intel_plane_state *plane_state =
+ intel_atomic_get_new_plane_state(state, plane);
+ struct intel_crtc *crtc = to_intel_crtc(plane_state->hw.crtc);
+ const struct intel_cdclk_state *cdclk_state;
+ const struct intel_crtc_state *old_crtc_state;
+ struct intel_crtc_state *new_crtc_state;
+
+ if (!plane_state->uapi.visible || !plane->min_cdclk)
+ return 0;
+
+ old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
+ new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
+
+ new_crtc_state->min_cdclk[plane->id] =
+ plane->min_cdclk(new_crtc_state, plane_state);
+
+ /*
+ * No need to check against the cdclk state if
+ * the min cdclk for the plane doesn't increase.
+ *
+ * Ie. we only ever increase the cdclk due to plane
+ * requirements. This can reduce back and forth
+ * display blinking due to constant cdclk changes.
+ */
+ if (new_crtc_state->min_cdclk[plane->id] <=
+ old_crtc_state->min_cdclk[plane->id])
+ return 0;
+
+ cdclk_state = intel_atomic_get_cdclk_state(state);
+ if (IS_ERR(cdclk_state))
+ return PTR_ERR(cdclk_state);
+
+ /*
+ * No need to recalculate the cdclk state if
+ * the min cdclk for the pipe doesn't increase.
+ *
+ * Ie. we only ever increase the cdclk due to plane
+ * requirements. This can reduce back and forth
+ * display blinking due to constant cdclk changes.
+ */
+ if (new_crtc_state->min_cdclk[plane->id] <=
+ cdclk_state->min_cdclk[crtc->pipe])
+ return 0;
+
+ drm_dbg_kms(&dev_priv->drm,
+ "[PLANE:%d:%s] min cdclk (%d kHz) > [CRTC:%d:%s] min cdclk (%d kHz)\n",
+ plane->base.base.id, plane->base.name,
+ new_crtc_state->min_cdclk[plane->id],
+ crtc->base.base.id, crtc->base.name,
+ cdclk_state->min_cdclk[crtc->pipe]);
+ *need_cdclk_calc = true;
+
+ return 0;
+}
+
+static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state)
+{
+ if (plane_state->hw.fb)
+ drm_framebuffer_put(plane_state->hw.fb);
+
+ memset(&plane_state->hw, 0, sizeof(plane_state->hw));
+}
+
+void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
+ const struct intel_plane_state *from_plane_state,
+ struct intel_crtc *crtc)
+{
+ intel_plane_clear_hw_state(plane_state);
+
+ /*
+ * For the joiner secondary uapi.crtc will point at
+ * the primary crtc. So we explicitly assign the right
+ * secondary crtc to hw.crtc. uapi.crtc!=NULL simply
+ * indicates the plane is logically enabled on the uapi level.
+ */
+ plane_state->hw.crtc = from_plane_state->uapi.crtc ? &crtc->base : NULL;
+
+ plane_state->hw.fb = from_plane_state->uapi.fb;
+ if (plane_state->hw.fb)
+ drm_framebuffer_get(plane_state->hw.fb);
+
+ plane_state->hw.alpha = from_plane_state->uapi.alpha;
+ plane_state->hw.pixel_blend_mode =
+ from_plane_state->uapi.pixel_blend_mode;
+ plane_state->hw.rotation = from_plane_state->uapi.rotation;
+ plane_state->hw.color_encoding = from_plane_state->uapi.color_encoding;
+ plane_state->hw.color_range = from_plane_state->uapi.color_range;
+ plane_state->hw.scaling_filter = from_plane_state->uapi.scaling_filter;
+
+ plane_state->uapi.src = drm_plane_state_src(&from_plane_state->uapi);
+ plane_state->uapi.dst = drm_plane_state_dest(&from_plane_state->uapi);
+}
+
+void intel_plane_copy_hw_state(struct intel_plane_state *plane_state,
+ const struct intel_plane_state *from_plane_state)
+{
+ intel_plane_clear_hw_state(plane_state);
+
+ memcpy(&plane_state->hw, &from_plane_state->hw,
+ sizeof(plane_state->hw));
+
+ if (plane_state->hw.fb)
+ drm_framebuffer_get(plane_state->hw.fb);
+}
+
+void intel_plane_set_invisible(struct intel_crtc_state *crtc_state,
+ struct intel_plane_state *plane_state)
+{
+ struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
+
+ crtc_state->active_planes &= ~BIT(plane->id);
+ crtc_state->scaled_planes &= ~BIT(plane->id);
+ crtc_state->nv12_planes &= ~BIT(plane->id);
+ crtc_state->c8_planes &= ~BIT(plane->id);
+ crtc_state->async_flip_planes &= ~BIT(plane->id);
+ crtc_state->data_rate[plane->id] = 0;
+ crtc_state->data_rate_y[plane->id] = 0;
+ crtc_state->rel_data_rate[plane->id] = 0;
+ crtc_state->rel_data_rate_y[plane->id] = 0;
+ crtc_state->min_cdclk[plane->id] = 0;
+
+ plane_state->uapi.visible = false;
+}
+
+static bool intel_plane_is_scaled(const struct intel_plane_state *plane_state)
+{
+ int src_w = drm_rect_width(&plane_state->uapi.src) >> 16;
+ int src_h = drm_rect_height(&plane_state->uapi.src) >> 16;
+ int dst_w = drm_rect_width(&plane_state->uapi.dst);
+ int dst_h = drm_rect_height(&plane_state->uapi.dst);
+
+ return src_w != dst_w || src_h != dst_h;
+}
+
+static bool intel_plane_do_async_flip(struct intel_plane *plane,
+ const struct intel_crtc_state *old_crtc_state,
+ const struct intel_crtc_state *new_crtc_state)
+{
+ struct drm_i915_private *i915 = to_i915(plane->base.dev);
+
+ if (!plane->async_flip)
+ return false;
+
+ if (!new_crtc_state->uapi.async_flip)
+ return false;
+
+ /*
+ * In platforms after DISPLAY13, we might need to override
+ * first async flip in order to change watermark levels
+ * as part of optimization.
+ *
+ * And let's do this for all skl+ so that we can eg. change the
+ * modifier as well.
+ *
+ * TODO: For older platforms there is less reason to do this as
+ * only X-tile is supported with async flips, though we could
+ * extend this so other scanout parameters (stride/etc) could
+ * be changed as well...
+ */
+ return DISPLAY_VER(i915) < 9 || old_crtc_state->uapi.async_flip;
+}
+
+static bool i9xx_must_disable_cxsr(const struct intel_crtc_state *new_crtc_state,
+ const struct intel_plane_state *old_plane_state,
+ const struct intel_plane_state *new_plane_state)
+{
+ struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
+ bool old_visible = old_plane_state->uapi.visible;
+ bool new_visible = new_plane_state->uapi.visible;
+ u32 old_ctl = old_plane_state->ctl;
+ u32 new_ctl = new_plane_state->ctl;
+ bool modeset, turn_on, turn_off;
+
+ if (plane->id == PLANE_CURSOR)
+ return false;
+
+ modeset = intel_crtc_needs_modeset(new_crtc_state);
+ turn_off = old_visible && (!new_visible || modeset);
+ turn_on = new_visible && (!old_visible || modeset);
+
+ /* Must disable CxSR around plane enable/disable */
+ if (turn_on || turn_off)
+ return true;
+
+ if (!old_visible || !new_visible)
+ return false;
+
+ /*
+ * Most plane control register updates are blocked while in CxSR.
+ *
+ * Tiling mode is one exception where the primary plane can
+ * apparently handle it, whereas the sprites can not (the
+ * sprite issue being only relevant on VLV/CHV where CxSR
+ * is actually possible with a sprite enabled).
+ */
+ if (plane->id == PLANE_PRIMARY) {
+ old_ctl &= ~DISP_TILED;
+ new_ctl &= ~DISP_TILED;
+ }
+
+ return old_ctl != new_ctl;
+}
+
+static bool ilk_must_disable_cxsr(const struct intel_crtc_state *new_crtc_state,
+ const struct intel_plane_state *old_plane_state,
+ const struct intel_plane_state *new_plane_state)
+{
+ struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
+ bool old_visible = old_plane_state->uapi.visible;
+ bool new_visible = new_plane_state->uapi.visible;
+ bool modeset, turn_on;
+
+ if (plane->id == PLANE_CURSOR)
+ return false;
+
+ modeset = intel_crtc_needs_modeset(new_crtc_state);
+ turn_on = new_visible && (!old_visible || modeset);
+
+ /*
+ * ILK/SNB DVSACNTR/Sprite Enable
+ * IVB SPR_CTL/Sprite Enable
+ * "When in Self Refresh Big FIFO mode, a write to enable the
+ * plane will be internally buffered and delayed while Big FIFO
+ * mode is exiting."
+ *
+ * Which means that enabling the sprite can take an extra frame
+ * when we start in big FIFO mode (LP1+). Thus we need to drop
+ * down to LP0 and wait for vblank in order to make sure the
+ * sprite gets enabled on the next vblank after the register write.
+ * Doing otherwise would risk enabling the sprite one frame after
+ * we've already signalled flip completion. We can resume LP1+
+ * once the sprite has been enabled.
+ *
+ * With experimental results seems this is needed also for primary
+ * plane, not only sprite plane.
+ */
+ if (turn_on)
+ return true;
+
+ /*
+ * WaCxSRDisabledForSpriteScaling:ivb
+ * IVB SPR_SCALE/Scaling Enable
+ * "Low Power watermarks must be disabled for at least one
+ * frame before enabling sprite scaling, and kept disabled
+ * until sprite scaling is disabled."
+ *
+ * ILK/SNB DVSASCALE/Scaling Enable
+ * "When in Self Refresh Big FIFO mode, scaling enable will be
+ * masked off while Big FIFO mode is exiting."
+ *
+ * Despite the w/a only being listed for IVB we assume that
+ * the ILK/SNB note has similar ramifications, hence we apply
+ * the w/a on all three platforms.
+ */
+ return !intel_plane_is_scaled(old_plane_state) &&
+ intel_plane_is_scaled(new_plane_state);
+}
+
+static int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_state,
+ struct intel_crtc_state *new_crtc_state,
+ const struct intel_plane_state *old_plane_state,
+ struct intel_plane_state *new_plane_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc);
+ struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+ bool mode_changed = intel_crtc_needs_modeset(new_crtc_state);
+ bool was_crtc_enabled = old_crtc_state->hw.active;
+ bool is_crtc_enabled = new_crtc_state->hw.active;
+ bool turn_off, turn_on, visible, was_visible;
+ int ret;
+
+ if (DISPLAY_VER(dev_priv) >= 9 && plane->id != PLANE_CURSOR) {
+ ret = skl_update_scaler_plane(new_crtc_state, new_plane_state);
+ if (ret)
+ return ret;
+ }
+
+ was_visible = old_plane_state->uapi.visible;
+ visible = new_plane_state->uapi.visible;
+
+ if (!was_crtc_enabled && drm_WARN_ON(&dev_priv->drm, was_visible))
+ was_visible = false;
+
+ /*
+ * Visibility is calculated as if the crtc was on, but
+ * after scaler setup everything depends on it being off
+ * when the crtc isn't active.
+ *
+ * FIXME this is wrong for watermarks. Watermarks should also
+ * be computed as if the pipe would be active. Perhaps move
+ * per-plane wm computation to the .check_plane() hook, and
+ * only combine the results from all planes in the current place?
+ */
+ if (!is_crtc_enabled) {
+ intel_plane_set_invisible(new_crtc_state, new_plane_state);
+ visible = false;
+ }
+
+ if (!was_visible && !visible)
+ return 0;
+
+ turn_off = was_visible && (!visible || mode_changed);
+ turn_on = visible && (!was_visible || mode_changed);
+
+ drm_dbg_atomic(&dev_priv->drm,
+ "[CRTC:%d:%s] with [PLANE:%d:%s] visible %i -> %i, off %i, on %i, ms %i\n",
+ crtc->base.base.id, crtc->base.name,
+ plane->base.base.id, plane->base.name,
+ was_visible, visible,
+ turn_off, turn_on, mode_changed);
+
+ if (visible || was_visible)
+ new_crtc_state->fb_bits |= plane->frontbuffer_bit;
+
+ if (HAS_GMCH(dev_priv) &&
+ i9xx_must_disable_cxsr(new_crtc_state, old_plane_state, new_plane_state))
+ new_crtc_state->disable_cxsr = true;
+
+ if ((IS_IRONLAKE(dev_priv) || IS_SANDYBRIDGE(dev_priv) || IS_IVYBRIDGE(dev_priv)) &&
+ ilk_must_disable_cxsr(new_crtc_state, old_plane_state, new_plane_state))
+ new_crtc_state->disable_cxsr = true;
+
+ if (intel_plane_do_async_flip(plane, old_crtc_state, new_crtc_state)) {
+ new_crtc_state->do_async_flip = true;
+ new_crtc_state->async_flip_planes |= BIT(plane->id);
+ } else if (plane->need_async_flip_toggle_wa &&
+ new_crtc_state->uapi.async_flip) {
+ /*
+ * On platforms with double buffered async flip bit we
+ * set the bit already one frame early during the sync
+ * flip (see {i9xx,skl}_plane_update_arm()). The
+ * hardware will therefore be ready to perform a real
+ * async flip during the next commit, without having
+ * to wait yet another frame for the bit to latch.
+ */
+ new_crtc_state->async_flip_planes |= BIT(plane->id);
+ }
+
+ return 0;
+}
+
+int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_state,
+ struct intel_crtc_state *new_crtc_state,
+ const struct intel_plane_state *old_plane_state,
+ struct intel_plane_state *new_plane_state)
+{
+ struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
+ const struct drm_framebuffer *fb = new_plane_state->hw.fb;
+ int ret;
+
+ intel_plane_set_invisible(new_crtc_state, new_plane_state);
+ new_crtc_state->enabled_planes &= ~BIT(plane->id);
+
+ if (!new_plane_state->hw.crtc && !old_plane_state->hw.crtc)
+ return 0;
+
+ ret = plane->check_plane(new_crtc_state, new_plane_state);
+ if (ret)
+ return ret;
+
+ if (fb)
+ new_crtc_state->enabled_planes |= BIT(plane->id);
+
+ /* FIXME pre-g4x don't work like this */
+ if (new_plane_state->uapi.visible)
+ new_crtc_state->active_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible &&
+ intel_plane_is_scaled(new_plane_state))
+ new_crtc_state->scaled_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible &&
+ intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier))
+ new_crtc_state->nv12_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible &&
+ fb->format->format == DRM_FORMAT_C8)
+ new_crtc_state->c8_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible || old_plane_state->uapi.visible)
+ new_crtc_state->update_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible &&
+ intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) {
+ new_crtc_state->data_rate_y[plane->id] =
+ intel_plane_data_rate(new_crtc_state, new_plane_state, 0);
+ new_crtc_state->data_rate[plane->id] =
+ intel_plane_data_rate(new_crtc_state, new_plane_state, 1);
+
+ new_crtc_state->rel_data_rate_y[plane->id] =
+ intel_plane_relative_data_rate(new_crtc_state,
+ new_plane_state, 0);
+ new_crtc_state->rel_data_rate[plane->id] =
+ intel_plane_relative_data_rate(new_crtc_state,
+ new_plane_state, 1);
+ } else if (new_plane_state->uapi.visible) {
+ new_crtc_state->data_rate[plane->id] =
+ intel_plane_data_rate(new_crtc_state, new_plane_state, 0);
+
+ new_crtc_state->rel_data_rate[plane->id] =
+ intel_plane_relative_data_rate(new_crtc_state,
+ new_plane_state, 0);
+ }
+
+ return intel_plane_atomic_calc_changes(old_crtc_state, new_crtc_state,
+ old_plane_state, new_plane_state);
+}
+
+static struct intel_plane *
+intel_crtc_get_plane(struct intel_crtc *crtc, enum plane_id plane_id)
+{
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ struct intel_plane *plane;
+
+ for_each_intel_plane_on_crtc(&i915->drm, crtc, plane) {
+ if (plane->id == plane_id)
+ return plane;
+ }
+
+ return NULL;
+}
+
+int intel_plane_atomic_check(struct intel_atomic_state *state,
+ struct intel_plane *plane)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_plane_state *new_plane_state =
+ intel_atomic_get_new_plane_state(state, plane);
+ const struct intel_plane_state *old_plane_state =
+ intel_atomic_get_old_plane_state(state, plane);
+ const struct intel_plane_state *new_primary_crtc_plane_state;
+ struct intel_crtc *crtc = intel_crtc_for_pipe(display, plane->pipe);
+ const struct intel_crtc_state *old_crtc_state =
+ intel_atomic_get_old_crtc_state(state, crtc);
+ struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+
+ if (new_crtc_state && intel_crtc_is_joiner_secondary(new_crtc_state)) {
+ struct intel_crtc *primary_crtc =
+ intel_primary_crtc(new_crtc_state);
+ struct intel_plane *primary_crtc_plane =
+ intel_crtc_get_plane(primary_crtc, plane->id);
+
+ new_primary_crtc_plane_state =
+ intel_atomic_get_new_plane_state(state, primary_crtc_plane);
+ } else {
+ new_primary_crtc_plane_state = new_plane_state;
+ }
+
+ intel_plane_copy_uapi_to_hw_state(new_plane_state,
+ new_primary_crtc_plane_state,
+ crtc);
+
+ new_plane_state->uapi.visible = false;
+ if (!new_crtc_state)
+ return 0;
+
+ return intel_plane_atomic_check_with_state(old_crtc_state,
+ new_crtc_state,
+ old_plane_state,
+ new_plane_state);
+}
+
+static struct intel_plane *
+skl_next_plane_to_commit(struct intel_atomic_state *state,
+ struct intel_crtc *crtc,
+ struct skl_ddb_entry ddb[I915_MAX_PLANES],
+ struct skl_ddb_entry ddb_y[I915_MAX_PLANES],
+ unsigned int *update_mask)
+{
+ struct intel_crtc_state *crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ struct intel_plane_state __maybe_unused *plane_state;
+ struct intel_plane *plane;
+ int i;
+
+ if (*update_mask == 0)
+ return NULL;
+
+ for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
+ enum plane_id plane_id = plane->id;
+
+ if (crtc->pipe != plane->pipe ||
+ !(*update_mask & BIT(plane_id)))
+ continue;
+
+ if (skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb[plane_id],
+ ddb, I915_MAX_PLANES, plane_id) ||
+ skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb_y[plane_id],
+ ddb_y, I915_MAX_PLANES, plane_id))
+ continue;
+
+ *update_mask &= ~BIT(plane_id);
+ ddb[plane_id] = crtc_state->wm.skl.plane_ddb[plane_id];
+ ddb_y[plane_id] = crtc_state->wm.skl.plane_ddb_y[plane_id];
+
+ return plane;
+ }
+
+ /* should never happen */
+ drm_WARN_ON(state->base.dev, 1);
+
+ return NULL;
+}
+
+void intel_plane_update_noarm(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ trace_intel_plane_update_noarm(plane, crtc);
+
+ if (plane->update_noarm)
+ plane->update_noarm(dsb, plane, crtc_state, plane_state);
+}
+
+void intel_plane_async_flip(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ bool async_flip)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ trace_intel_plane_async_flip(plane, crtc, async_flip);
+ plane->async_flip(dsb, plane, crtc_state, plane_state, async_flip);
+}
+
+void intel_plane_update_arm(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ if (crtc_state->do_async_flip && plane->async_flip) {
+ intel_plane_async_flip(dsb, plane, crtc_state, plane_state, true);
+ return;
+ }
+
+ trace_intel_plane_update_arm(plane, crtc);
+ plane->update_arm(dsb, plane, crtc_state, plane_state);
+}
+
+void intel_plane_disable_arm(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ trace_intel_plane_disable_arm(plane, crtc);
+ plane->disable_arm(dsb, plane, crtc_state);
+}
+
+void intel_crtc_planes_update_noarm(struct intel_dsb *dsb,
+ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ u32 update_mask = new_crtc_state->update_planes;
+ struct intel_plane_state *new_plane_state;
+ struct intel_plane *plane;
+ int i;
+
+ if (new_crtc_state->do_async_flip)
+ return;
+
+ /*
+ * Since we only write non-arming registers here,
+ * the order does not matter even for skl+.
+ */
+ for_each_new_intel_plane_in_state(state, plane, new_plane_state, i) {
+ if (crtc->pipe != plane->pipe ||
+ !(update_mask & BIT(plane->id)))
+ continue;
+
+ /* TODO: for mailbox updates this should be skipped */
+ if (new_plane_state->uapi.visible ||
+ new_plane_state->planar_slave)
+ intel_plane_update_noarm(dsb, plane,
+ new_crtc_state, new_plane_state);
+ }
+}
+
+static void skl_crtc_planes_update_arm(struct intel_dsb *dsb,
+ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_crtc_state *old_crtc_state =
+ intel_atomic_get_old_crtc_state(state, crtc);
+ struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ struct skl_ddb_entry ddb[I915_MAX_PLANES];
+ struct skl_ddb_entry ddb_y[I915_MAX_PLANES];
+ u32 update_mask = new_crtc_state->update_planes;
+ struct intel_plane *plane;
+
+ memcpy(ddb, old_crtc_state->wm.skl.plane_ddb,
+ sizeof(old_crtc_state->wm.skl.plane_ddb));
+ memcpy(ddb_y, old_crtc_state->wm.skl.plane_ddb_y,
+ sizeof(old_crtc_state->wm.skl.plane_ddb_y));
+
+ while ((plane = skl_next_plane_to_commit(state, crtc, ddb, ddb_y, &update_mask))) {
+ struct intel_plane_state *new_plane_state =
+ intel_atomic_get_new_plane_state(state, plane);
+
+ /*
+ * TODO: for mailbox updates intel_plane_update_noarm()
+ * would have to be called here as well.
+ */
+ if (new_plane_state->uapi.visible ||
+ new_plane_state->planar_slave)
+ intel_plane_update_arm(dsb, plane, new_crtc_state, new_plane_state);
+ else
+ intel_plane_disable_arm(dsb, plane, new_crtc_state);
+ }
+}
+
+static void i9xx_crtc_planes_update_arm(struct intel_dsb *dsb,
+ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ u32 update_mask = new_crtc_state->update_planes;
+ struct intel_plane_state *new_plane_state;
+ struct intel_plane *plane;
+ int i;
+
+ for_each_new_intel_plane_in_state(state, plane, new_plane_state, i) {
+ if (crtc->pipe != plane->pipe ||
+ !(update_mask & BIT(plane->id)))
+ continue;
+
+ /*
+ * TODO: for mailbox updates intel_plane_update_noarm()
+ * would have to be called here as well.
+ */
+ if (new_plane_state->uapi.visible)
+ intel_plane_update_arm(dsb, plane, new_crtc_state, new_plane_state);
+ else
+ intel_plane_disable_arm(dsb, plane, new_crtc_state);
+ }
+}
+
+void intel_crtc_planes_update_arm(struct intel_dsb *dsb,
+ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct drm_i915_private *i915 = to_i915(state->base.dev);
+
+ if (DISPLAY_VER(i915) >= 9)
+ skl_crtc_planes_update_arm(dsb, state, crtc);
+ else
+ i9xx_crtc_planes_update_arm(dsb, state, crtc);
+}
+
+int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state,
+ struct intel_crtc_state *crtc_state,
+ int min_scale, int max_scale,
+ bool can_position)
+{
+ struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev);
+ struct drm_framebuffer *fb = plane_state->hw.fb;
+ struct drm_rect *src = &plane_state->uapi.src;
+ struct drm_rect *dst = &plane_state->uapi.dst;
+ const struct drm_rect *clip = &crtc_state->pipe_src;
+ unsigned int rotation = plane_state->hw.rotation;
+ int hscale, vscale;
+
+ if (!fb) {
+ plane_state->uapi.visible = false;
+ return 0;
+ }
+
+ drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation);
+
+ /* Check scaling */
+ hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale);
+ vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale);
+ if (hscale < 0 || vscale < 0) {
+ drm_dbg_kms(&i915->drm, "Invalid scaling of plane\n");
+ drm_rect_debug_print("src: ", src, true);
+ drm_rect_debug_print("dst: ", dst, false);
+ return -ERANGE;
+ }
+
+ /*
+ * FIXME: This might need further adjustment for seamless scaling
+ * with phase information, for the 2p2 and 2p1 scenarios.
+ */
+ plane_state->uapi.visible = drm_rect_clip_scaled(src, dst, clip);
+
+ drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation);
+
+ if (!can_position && plane_state->uapi.visible &&
+ !drm_rect_equals(dst, clip)) {
+ drm_dbg_kms(&i915->drm, "Plane must cover entire CRTC\n");
+ drm_rect_debug_print("dst: ", dst, false);
+ drm_rect_debug_print("clip: ", clip, false);
+ return -EINVAL;
+ }
+
+ /* final plane coordinates will be relative to the plane's pipe */
+ drm_rect_translate(dst, -clip->x1, -clip->y1);
+
+ return 0;
+}
+
+int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state)
+{
+ struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev);
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+ struct drm_rect *src = &plane_state->uapi.src;
+ u32 src_x, src_y, src_w, src_h, hsub, vsub;
+ bool rotated = drm_rotation_90_or_270(plane_state->hw.rotation);
+
+ /*
+ * FIXME hsub/vsub vs. block size is a mess. Pre-tgl CCS
+ * abuses hsub/vsub so we can't use them here. But as they
+ * are limited to 32bpp RGB formats we don't actually need
+ * to check anything.
+ */
+ if (fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
+ fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS)
+ return 0;
+
+ /*
+ * Hardware doesn't handle subpixel coordinates.
+ * Adjust to (macro)pixel boundary, but be careful not to
+ * increase the source viewport size, because that could
+ * push the downscaling factor out of bounds.
+ */
+ src_x = src->x1 >> 16;
+ src_w = drm_rect_width(src) >> 16;
+ src_y = src->y1 >> 16;
+ src_h = drm_rect_height(src) >> 16;
+
+ drm_rect_init(src, src_x << 16, src_y << 16,
+ src_w << 16, src_h << 16);
+
+ if (fb->format->format == DRM_FORMAT_RGB565 && rotated) {
+ hsub = 2;
+ vsub = 2;
+ } else if (DISPLAY_VER(i915) >= 20 &&
+ intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) {
+ /*
+ * This allows NV12 and P0xx formats to have odd size and/or odd
+ * source coordinates on DISPLAY_VER(i915) >= 20
+ */
+ hsub = 1;
+ vsub = 1;
+
+ /* Wa_16023981245 */
+ if ((DISPLAY_VER_FULL(i915) == IP_VER(20, 0) ||
+<<<<<<<
+ DISPLAY_VER_FULL(i915) == IP_VER(30, 0)) &&
+ src_x % 2 != 0)
+=======
+ DISPLAY_VER_FULL(i915) == IP_VER(30, 0)) &&
+ src_x % 2 != 0)
+>>>>>>>
+ hsub = 2;
+ } else {
+ hsub = fb->format->hsub;
+ vsub = fb->format->vsub;
+ }
+
+ if (rotated)
+ hsub = vsub = max(hsub, vsub);
+
+ if (src_x % hsub || src_w % hsub) {
+ drm_dbg_kms(&i915->drm, "src x/w (%u, %u) must be a multiple of %u (rotated: %s)\n",
+ src_x, src_w, hsub, str_yes_no(rotated));
+ return -EINVAL;
+ }
+
+ if (src_y % vsub || src_h % vsub) {
+ drm_dbg_kms(&i915->drm, "src y/h (%u, %u) must be a multiple of %u (rotated: %s)\n",
+ src_y, src_h, vsub, str_yes_no(rotated));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int add_dma_resv_fences(struct dma_resv *resv,
+ struct drm_plane_state *new_plane_state)
+{
+ struct dma_fence *fence = dma_fence_get(new_plane_state->fence);
+ struct dma_fence *new;
+ int ret;
+
+ ret = dma_resv_get_singleton(resv, dma_resv_usage_rw(false), &new);
+ if (ret)
+ goto error;
+
+ if (new && fence) {
+ struct dma_fence_chain *chain = dma_fence_chain_alloc();
+
+ if (!chain) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ dma_fence_chain_init(chain, fence, new, 1);
+ fence = &chain->base;
+
+ } else if (new) {
+ fence = new;
+ }
+
+ dma_fence_put(new_plane_state->fence);
+ new_plane_state->fence = fence;
+ return 0;
+
+error:
+ dma_fence_put(fence);
+ return ret;
+}
+
+/**
+ * intel_prepare_plane_fb - Prepare fb for usage on plane
+ * @_plane: drm plane to prepare for
+ * @_new_plane_state: the plane state being prepared
+ *
+ * Prepares a framebuffer for usage on a display plane. Generally this
+ * involves pinning the underlying object and updating the frontbuffer tracking
+ * bits. Some older platforms need special physical address handling for
+ * cursor planes.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+static int
+intel_prepare_plane_fb(struct drm_plane *_plane,
+ struct drm_plane_state *_new_plane_state)
+{
+ struct i915_sched_attr attr = { .priority = I915_PRIORITY_DISPLAY };
+ struct intel_plane *plane = to_intel_plane(_plane);
+ struct intel_plane_state *new_plane_state =
+ to_intel_plane_state(_new_plane_state);
+ struct intel_atomic_state *state =
+ to_intel_atomic_state(new_plane_state->uapi.state);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ struct intel_plane_state *old_plane_state =
+ intel_atomic_get_old_plane_state(state, plane);
+ struct drm_gem_object *obj = intel_fb_bo(new_plane_state->hw.fb);
+ struct drm_gem_object *old_obj = intel_fb_bo(old_plane_state->hw.fb);
+ int ret;
+
+ if (old_obj) {
+ const struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state,
+ to_intel_crtc(old_plane_state->hw.crtc));
+
+ /* Big Hammer, we also need to ensure that any pending
+ * MI_WAIT_FOR_EVENT inside a user batch buffer on the
+ * current scanout is retired before unpinning the old
+ * framebuffer. Note that we rely on userspace rendering
+ * into the buffer attached to the pipe they are waiting
+ * on. If not, userspace generates a GPU hang with IPEHR
+ * point to the MI_WAIT_FOR_EVENT.
+ *
+ * This should only fail upon a hung GPU, in which case we
+ * can safely continue.
+ */
+ if (new_crtc_state && intel_crtc_needs_modeset(new_crtc_state)) {
+ ret = add_dma_resv_fences(old_obj->resv,
+ &new_plane_state->uapi);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (!obj)
+ return 0;
+
+ ret = intel_plane_pin_fb(new_plane_state);
+ if (ret)
+ return ret;
+
+ ret = drm_gem_plane_helper_prepare_fb(&plane->base, &new_plane_state->uapi);
+ if (ret < 0)
+ goto unpin_fb;
+
+ if (new_plane_state->uapi.fence) {
+ i915_gem_fence_wait_priority(new_plane_state->uapi.fence,
+ &attr);
+
+ intel_display_rps_boost_after_vblank(new_plane_state->hw.crtc,
+ new_plane_state->uapi.fence);
+ }
+
+ /*
+ * We declare pageflips to be interactive and so merit a small bias
+ * towards upclocking to deliver the frame on time. By only changing
+ * the RPS thresholds to sample more regularly and aim for higher
+ * clocks we can hopefully deliver low power workloads (like kodi)
+ * that are not quite steady state without resorting to forcing
+ * maximum clocks following a vblank miss (see do_rps_boost()).
+ */
+ intel_display_rps_mark_interactive(dev_priv, state, true);
+
+ return 0;
+
+unpin_fb:
+ intel_plane_unpin_fb(new_plane_state);
+
+ return ret;
+}
+
+/**
+ * intel_cleanup_plane_fb - Cleans up an fb after plane use
+ * @plane: drm plane to clean up for
+ * @_old_plane_state: the state from the previous modeset
+ *
+ * Cleans up a framebuffer that has just been removed from a plane.
+ */
+static void
+intel_cleanup_plane_fb(struct drm_plane *plane,
+ struct drm_plane_state *_old_plane_state)
+{
+ struct intel_plane_state *old_plane_state =
+ to_intel_plane_state(_old_plane_state);
+ struct intel_atomic_state *state =
+ to_intel_atomic_state(old_plane_state->uapi.state);
+ struct drm_i915_private *dev_priv = to_i915(plane->dev);
+ struct drm_gem_object *obj = intel_fb_bo(old_plane_state->hw.fb);
+
+ if (!obj)
+ return;
+
+ intel_display_rps_mark_interactive(dev_priv, state, false);
+
+ intel_plane_unpin_fb(old_plane_state);
+}
+
+static const struct drm_plane_helper_funcs intel_plane_helper_funcs = {
+ .prepare_fb = intel_prepare_plane_fb,
+ .cleanup_fb = intel_cleanup_plane_fb,
+};
+
+void intel_plane_helper_add(struct intel_plane *plane)
+{
+ drm_plane_helper_add(&plane->base, &intel_plane_helper_funcs);
+}
+
+void intel_plane_init_cursor_vblank_work(struct intel_plane_state *old_plane_state,
+ struct intel_plane_state *new_plane_state)
+{
+ if (!old_plane_state->ggtt_vma ||
+ old_plane_state->ggtt_vma == new_plane_state->ggtt_vma)
+ return;
+
+ drm_vblank_work_init(&old_plane_state->unpin_work, old_plane_state->uapi.crtc,
+ intel_cursor_unpin_work);
+}
diff --git a/rr-cache/44bc583546c40f3144ba90b705854f1ee09d13ea/preimage.1 b/rr-cache/44bc583546c40f3144ba90b705854f1ee09d13ea/preimage.1
new file mode 100644
index 000000000000..ef95bec8d180
--- /dev/null
+++ b/rr-cache/44bc583546c40f3144ba90b705854f1ee09d13ea/preimage.1
@@ -0,0 +1,1231 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * 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 (including the next
+ * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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.
+ */
+
+/**
+ * DOC: atomic plane helpers
+ *
+ * The functions here are used by the atomic plane helper functions to
+ * implement legacy plane updates (i.e., drm_plane->update_plane() and
+ * drm_plane->disable_plane()). This allows plane updates to use the
+ * atomic state infrastructure and perform plane updates as separate
+ * prepare/check/commit/cleanup steps.
+ */
+
+#include <linux/dma-fence-chain.h>
+#include <linux/dma-resv.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_atomic_helper.h>
+
+#include "i915_config.h"
+#include "i9xx_plane_regs.h"
+#include "intel_atomic_plane.h"
+#include "intel_cdclk.h"
+#include "intel_cursor.h"
+#include "intel_display_rps.h"
+#include "intel_display_trace.h"
+#include "intel_display_types.h"
+#include "intel_fb.h"
+#include "intel_fb_pin.h"
+#include "skl_scaler.h"
+#include "skl_watermark.h"
+
+static void intel_plane_state_reset(struct intel_plane_state *plane_state,
+ struct intel_plane *plane)
+{
+ memset(plane_state, 0, sizeof(*plane_state));
+
+ __drm_atomic_helper_plane_state_reset(&plane_state->uapi, &plane->base);
+
+ plane_state->scaler_id = -1;
+}
+
+struct intel_plane *intel_plane_alloc(void)
+{
+ struct intel_plane_state *plane_state;
+ struct intel_plane *plane;
+
+ plane = kzalloc(sizeof(*plane), GFP_KERNEL);
+ if (!plane)
+ return ERR_PTR(-ENOMEM);
+
+ plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL);
+ if (!plane_state) {
+ kfree(plane);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ intel_plane_state_reset(plane_state, plane);
+
+ plane->base.state = &plane_state->uapi;
+
+ return plane;
+}
+
+void intel_plane_free(struct intel_plane *plane)
+{
+ intel_plane_destroy_state(&plane->base, plane->base.state);
+ kfree(plane);
+}
+
+/**
+ * intel_plane_duplicate_state - duplicate plane state
+ * @plane: drm plane
+ *
+ * Allocates and returns a copy of the plane state (both common and
+ * Intel-specific) for the specified plane.
+ *
+ * Returns: The newly allocated plane state, or NULL on failure.
+ */
+struct drm_plane_state *
+intel_plane_duplicate_state(struct drm_plane *plane)
+{
+ struct intel_plane_state *intel_state;
+
+ intel_state = to_intel_plane_state(plane->state);
+ intel_state = kmemdup(intel_state, sizeof(*intel_state), GFP_KERNEL);
+
+ if (!intel_state)
+ return NULL;
+
+ __drm_atomic_helper_plane_duplicate_state(plane, &intel_state->uapi);
+
+ intel_state->ggtt_vma = NULL;
+ intel_state->dpt_vma = NULL;
+ intel_state->flags = 0;
+
+ /* add reference to fb */
+ if (intel_state->hw.fb)
+ drm_framebuffer_get(intel_state->hw.fb);
+
+ return &intel_state->uapi;
+}
+
+/**
+ * intel_plane_destroy_state - destroy plane state
+ * @plane: drm plane
+ * @state: state object to destroy
+ *
+ * Destroys the plane state (both common and Intel-specific) for the
+ * specified plane.
+ */
+void
+intel_plane_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct intel_plane_state *plane_state = to_intel_plane_state(state);
+
+ drm_WARN_ON(plane->dev, plane_state->ggtt_vma);
+ drm_WARN_ON(plane->dev, plane_state->dpt_vma);
+
+ __drm_atomic_helper_plane_destroy_state(&plane_state->uapi);
+ if (plane_state->hw.fb)
+ drm_framebuffer_put(plane_state->hw.fb);
+ kfree(plane_state);
+}
+
+bool intel_plane_needs_physical(struct intel_plane *plane)
+{
+ struct drm_i915_private *i915 = to_i915(plane->base.dev);
+
+ return plane->id == PLANE_CURSOR &&
+ DISPLAY_INFO(i915)->cursor_needs_physical;
+}
+
+unsigned int intel_adjusted_rate(const struct drm_rect *src,
+ const struct drm_rect *dst,
+ unsigned int rate)
+{
+ unsigned int src_w, src_h, dst_w, dst_h;
+
+ src_w = drm_rect_width(src) >> 16;
+ src_h = drm_rect_height(src) >> 16;
+ dst_w = drm_rect_width(dst);
+ dst_h = drm_rect_height(dst);
+
+ /* Downscaling limits the maximum pixel rate */
+ dst_w = min(src_w, dst_w);
+ dst_h = min(src_h, dst_h);
+
+ return DIV_ROUND_UP_ULL(mul_u32_u32(rate, src_w * src_h),
+ dst_w * dst_h);
+}
+
+unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+{
+ /*
+ * Note we don't check for plane visibility here as
+ * we want to use this when calculating the cursor
+ * watermarks even if the cursor is fully offscreen.
+ * That depends on the src/dst rectangles being
+ * correctly populated whenever the watermark code
+ * considers the cursor to be visible, whether or not
+ * it is actually visible.
+ *
+ * See: intel_wm_plane_visible() and intel_check_cursor()
+ */
+
+ return intel_adjusted_rate(&plane_state->uapi.src,
+ &plane_state->uapi.dst,
+ crtc_state->pixel_rate);
+}
+
+unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ int color_plane)
+{
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+
+ if (!plane_state->uapi.visible)
+ return 0;
+
+ return intel_plane_pixel_rate(crtc_state, plane_state) *
+ fb->format->cpp[color_plane];
+}
+
+static bool
+use_min_ddb(const struct intel_crtc_state *crtc_state,
+ struct intel_plane *plane)
+{
+ struct drm_i915_private *i915 = to_i915(plane->base.dev);
+
+ return DISPLAY_VER(i915) >= 13 &&
+ crtc_state->uapi.async_flip &&
+ plane->async_flip;
+}
+
+static unsigned int
+intel_plane_relative_data_rate(const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ int color_plane)
+{
+ struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+ int width, height;
+ unsigned int rel_data_rate;
+
+ if (plane->id == PLANE_CURSOR)
+ return 0;
+
+ if (!plane_state->uapi.visible)
+ return 0;
+
+ /*
+ * We calculate extra ddb based on ratio plane rate/total data rate
+ * in case, in some cases we should not allocate extra ddb for the plane,
+ * so do not count its data rate, if this is the case.
+ */
+ if (use_min_ddb(crtc_state, plane))
+ return 0;
+
+ /*
+ * Src coordinates are already rotated by 270 degrees for
+ * the 90/270 degree plane rotation cases (to match the
+ * GTT mapping), hence no need to account for rotation here.
+ */
+ width = drm_rect_width(&plane_state->uapi.src) >> 16;
+ height = drm_rect_height(&plane_state->uapi.src) >> 16;
+
+ /* UV plane does 1/2 pixel sub-sampling */
+ if (color_plane == 1) {
+ width /= 2;
+ height /= 2;
+ }
+
+ rel_data_rate = width * height * fb->format->cpp[color_plane];
+
+ return intel_adjusted_rate(&plane_state->uapi.src,
+ &plane_state->uapi.dst,
+ rel_data_rate);
+}
+
+int intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
+ struct intel_plane *plane,
+ bool *need_cdclk_calc)
+{
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ const struct intel_plane_state *plane_state =
+ intel_atomic_get_new_plane_state(state, plane);
+ struct intel_crtc *crtc = to_intel_crtc(plane_state->hw.crtc);
+ const struct intel_cdclk_state *cdclk_state;
+ const struct intel_crtc_state *old_crtc_state;
+ struct intel_crtc_state *new_crtc_state;
+
+ if (!plane_state->uapi.visible || !plane->min_cdclk)
+ return 0;
+
+ old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
+ new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
+
+ new_crtc_state->min_cdclk[plane->id] =
+ plane->min_cdclk(new_crtc_state, plane_state);
+
+ /*
+ * No need to check against the cdclk state if
+ * the min cdclk for the plane doesn't increase.
+ *
+ * Ie. we only ever increase the cdclk due to plane
+ * requirements. This can reduce back and forth
+ * display blinking due to constant cdclk changes.
+ */
+ if (new_crtc_state->min_cdclk[plane->id] <=
+ old_crtc_state->min_cdclk[plane->id])
+ return 0;
+
+ cdclk_state = intel_atomic_get_cdclk_state(state);
+ if (IS_ERR(cdclk_state))
+ return PTR_ERR(cdclk_state);
+
+ /*
+ * No need to recalculate the cdclk state if
+ * the min cdclk for the pipe doesn't increase.
+ *
+ * Ie. we only ever increase the cdclk due to plane
+ * requirements. This can reduce back and forth
+ * display blinking due to constant cdclk changes.
+ */
+ if (new_crtc_state->min_cdclk[plane->id] <=
+ cdclk_state->min_cdclk[crtc->pipe])
+ return 0;
+
+ drm_dbg_kms(&dev_priv->drm,
+ "[PLANE:%d:%s] min cdclk (%d kHz) > [CRTC:%d:%s] min cdclk (%d kHz)\n",
+ plane->base.base.id, plane->base.name,
+ new_crtc_state->min_cdclk[plane->id],
+ crtc->base.base.id, crtc->base.name,
+ cdclk_state->min_cdclk[crtc->pipe]);
+ *need_cdclk_calc = true;
+
+ return 0;
+}
+
+static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state)
+{
+ if (plane_state->hw.fb)
+ drm_framebuffer_put(plane_state->hw.fb);
+
+ memset(&plane_state->hw, 0, sizeof(plane_state->hw));
+}
+
+void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
+ const struct intel_plane_state *from_plane_state,
+ struct intel_crtc *crtc)
+{
+ intel_plane_clear_hw_state(plane_state);
+
+ /*
+ * For the joiner secondary uapi.crtc will point at
+ * the primary crtc. So we explicitly assign the right
+ * secondary crtc to hw.crtc. uapi.crtc!=NULL simply
+ * indicates the plane is logically enabled on the uapi level.
+ */
+ plane_state->hw.crtc = from_plane_state->uapi.crtc ? &crtc->base : NULL;
+
+ plane_state->hw.fb = from_plane_state->uapi.fb;
+ if (plane_state->hw.fb)
+ drm_framebuffer_get(plane_state->hw.fb);
+
+ plane_state->hw.alpha = from_plane_state->uapi.alpha;
+ plane_state->hw.pixel_blend_mode =
+ from_plane_state->uapi.pixel_blend_mode;
+ plane_state->hw.rotation = from_plane_state->uapi.rotation;
+ plane_state->hw.color_encoding = from_plane_state->uapi.color_encoding;
+ plane_state->hw.color_range = from_plane_state->uapi.color_range;
+ plane_state->hw.scaling_filter = from_plane_state->uapi.scaling_filter;
+
+ plane_state->uapi.src = drm_plane_state_src(&from_plane_state->uapi);
+ plane_state->uapi.dst = drm_plane_state_dest(&from_plane_state->uapi);
+}
+
+void intel_plane_copy_hw_state(struct intel_plane_state *plane_state,
+ const struct intel_plane_state *from_plane_state)
+{
+ intel_plane_clear_hw_state(plane_state);
+
+ memcpy(&plane_state->hw, &from_plane_state->hw,
+ sizeof(plane_state->hw));
+
+ if (plane_state->hw.fb)
+ drm_framebuffer_get(plane_state->hw.fb);
+}
+
+void intel_plane_set_invisible(struct intel_crtc_state *crtc_state,
+ struct intel_plane_state *plane_state)
+{
+ struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
+
+ crtc_state->active_planes &= ~BIT(plane->id);
+ crtc_state->scaled_planes &= ~BIT(plane->id);
+ crtc_state->nv12_planes &= ~BIT(plane->id);
+ crtc_state->c8_planes &= ~BIT(plane->id);
+ crtc_state->async_flip_planes &= ~BIT(plane->id);
+ crtc_state->data_rate[plane->id] = 0;
+ crtc_state->data_rate_y[plane->id] = 0;
+ crtc_state->rel_data_rate[plane->id] = 0;
+ crtc_state->rel_data_rate_y[plane->id] = 0;
+ crtc_state->min_cdclk[plane->id] = 0;
+
+ plane_state->uapi.visible = false;
+}
+
+static bool intel_plane_is_scaled(const struct intel_plane_state *plane_state)
+{
+ int src_w = drm_rect_width(&plane_state->uapi.src) >> 16;
+ int src_h = drm_rect_height(&plane_state->uapi.src) >> 16;
+ int dst_w = drm_rect_width(&plane_state->uapi.dst);
+ int dst_h = drm_rect_height(&plane_state->uapi.dst);
+
+ return src_w != dst_w || src_h != dst_h;
+}
+
+static bool intel_plane_do_async_flip(struct intel_plane *plane,
+ const struct intel_crtc_state *old_crtc_state,
+ const struct intel_crtc_state *new_crtc_state)
+{
+ struct drm_i915_private *i915 = to_i915(plane->base.dev);
+
+ if (!plane->async_flip)
+ return false;
+
+ if (!new_crtc_state->uapi.async_flip)
+ return false;
+
+ /*
+ * In platforms after DISPLAY13, we might need to override
+ * first async flip in order to change watermark levels
+ * as part of optimization.
+ *
+ * And let's do this for all skl+ so that we can eg. change the
+ * modifier as well.
+ *
+ * TODO: For older platforms there is less reason to do this as
+ * only X-tile is supported with async flips, though we could
+ * extend this so other scanout parameters (stride/etc) could
+ * be changed as well...
+ */
+ return DISPLAY_VER(i915) < 9 || old_crtc_state->uapi.async_flip;
+}
+
+static bool i9xx_must_disable_cxsr(const struct intel_crtc_state *new_crtc_state,
+ const struct intel_plane_state *old_plane_state,
+ const struct intel_plane_state *new_plane_state)
+{
+ struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
+ bool old_visible = old_plane_state->uapi.visible;
+ bool new_visible = new_plane_state->uapi.visible;
+ u32 old_ctl = old_plane_state->ctl;
+ u32 new_ctl = new_plane_state->ctl;
+ bool modeset, turn_on, turn_off;
+
+ if (plane->id == PLANE_CURSOR)
+ return false;
+
+ modeset = intel_crtc_needs_modeset(new_crtc_state);
+ turn_off = old_visible && (!new_visible || modeset);
+ turn_on = new_visible && (!old_visible || modeset);
+
+ /* Must disable CxSR around plane enable/disable */
+ if (turn_on || turn_off)
+ return true;
+
+ if (!old_visible || !new_visible)
+ return false;
+
+ /*
+ * Most plane control register updates are blocked while in CxSR.
+ *
+ * Tiling mode is one exception where the primary plane can
+ * apparently handle it, whereas the sprites can not (the
+ * sprite issue being only relevant on VLV/CHV where CxSR
+ * is actually possible with a sprite enabled).
+ */
+ if (plane->id == PLANE_PRIMARY) {
+ old_ctl &= ~DISP_TILED;
+ new_ctl &= ~DISP_TILED;
+ }
+
+ return old_ctl != new_ctl;
+}
+
+static bool ilk_must_disable_cxsr(const struct intel_crtc_state *new_crtc_state,
+ const struct intel_plane_state *old_plane_state,
+ const struct intel_plane_state *new_plane_state)
+{
+ struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
+ bool old_visible = old_plane_state->uapi.visible;
+ bool new_visible = new_plane_state->uapi.visible;
+ bool modeset, turn_on;
+
+ if (plane->id == PLANE_CURSOR)
+ return false;
+
+ modeset = intel_crtc_needs_modeset(new_crtc_state);
+ turn_on = new_visible && (!old_visible || modeset);
+
+ /*
+ * ILK/SNB DVSACNTR/Sprite Enable
+ * IVB SPR_CTL/Sprite Enable
+ * "When in Self Refresh Big FIFO mode, a write to enable the
+ * plane will be internally buffered and delayed while Big FIFO
+ * mode is exiting."
+ *
+ * Which means that enabling the sprite can take an extra frame
+ * when we start in big FIFO mode (LP1+). Thus we need to drop
+ * down to LP0 and wait for vblank in order to make sure the
+ * sprite gets enabled on the next vblank after the register write.
+ * Doing otherwise would risk enabling the sprite one frame after
+ * we've already signalled flip completion. We can resume LP1+
+ * once the sprite has been enabled.
+ *
+ * With experimental results seems this is needed also for primary
+ * plane, not only sprite plane.
+ */
+ if (turn_on)
+ return true;
+
+ /*
+ * WaCxSRDisabledForSpriteScaling:ivb
+ * IVB SPR_SCALE/Scaling Enable
+ * "Low Power watermarks must be disabled for at least one
+ * frame before enabling sprite scaling, and kept disabled
+ * until sprite scaling is disabled."
+ *
+ * ILK/SNB DVSASCALE/Scaling Enable
+ * "When in Self Refresh Big FIFO mode, scaling enable will be
+ * masked off while Big FIFO mode is exiting."
+ *
+ * Despite the w/a only being listed for IVB we assume that
+ * the ILK/SNB note has similar ramifications, hence we apply
+ * the w/a on all three platforms.
+ */
+ return !intel_plane_is_scaled(old_plane_state) &&
+ intel_plane_is_scaled(new_plane_state);
+}
+
+static int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_state,
+ struct intel_crtc_state *new_crtc_state,
+ const struct intel_plane_state *old_plane_state,
+ struct intel_plane_state *new_plane_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc);
+ struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+ bool mode_changed = intel_crtc_needs_modeset(new_crtc_state);
+ bool was_crtc_enabled = old_crtc_state->hw.active;
+ bool is_crtc_enabled = new_crtc_state->hw.active;
+ bool turn_off, turn_on, visible, was_visible;
+ int ret;
+
+ if (DISPLAY_VER(dev_priv) >= 9 && plane->id != PLANE_CURSOR) {
+ ret = skl_update_scaler_plane(new_crtc_state, new_plane_state);
+ if (ret)
+ return ret;
+ }
+
+ was_visible = old_plane_state->uapi.visible;
+ visible = new_plane_state->uapi.visible;
+
+ if (!was_crtc_enabled && drm_WARN_ON(&dev_priv->drm, was_visible))
+ was_visible = false;
+
+ /*
+ * Visibility is calculated as if the crtc was on, but
+ * after scaler setup everything depends on it being off
+ * when the crtc isn't active.
+ *
+ * FIXME this is wrong for watermarks. Watermarks should also
+ * be computed as if the pipe would be active. Perhaps move
+ * per-plane wm computation to the .check_plane() hook, and
+ * only combine the results from all planes in the current place?
+ */
+ if (!is_crtc_enabled) {
+ intel_plane_set_invisible(new_crtc_state, new_plane_state);
+ visible = false;
+ }
+
+ if (!was_visible && !visible)
+ return 0;
+
+ turn_off = was_visible && (!visible || mode_changed);
+ turn_on = visible && (!was_visible || mode_changed);
+
+ drm_dbg_atomic(&dev_priv->drm,
+ "[CRTC:%d:%s] with [PLANE:%d:%s] visible %i -> %i, off %i, on %i, ms %i\n",
+ crtc->base.base.id, crtc->base.name,
+ plane->base.base.id, plane->base.name,
+ was_visible, visible,
+ turn_off, turn_on, mode_changed);
+
+ if (visible || was_visible)
+ new_crtc_state->fb_bits |= plane->frontbuffer_bit;
+
+ if (HAS_GMCH(dev_priv) &&
+ i9xx_must_disable_cxsr(new_crtc_state, old_plane_state, new_plane_state))
+ new_crtc_state->disable_cxsr = true;
+
+ if ((IS_IRONLAKE(dev_priv) || IS_SANDYBRIDGE(dev_priv) || IS_IVYBRIDGE(dev_priv)) &&
+ ilk_must_disable_cxsr(new_crtc_state, old_plane_state, new_plane_state))
+ new_crtc_state->disable_cxsr = true;
+
+ if (intel_plane_do_async_flip(plane, old_crtc_state, new_crtc_state)) {
+ new_crtc_state->do_async_flip = true;
+ new_crtc_state->async_flip_planes |= BIT(plane->id);
+ } else if (plane->need_async_flip_toggle_wa &&
+ new_crtc_state->uapi.async_flip) {
+ /*
+ * On platforms with double buffered async flip bit we
+ * set the bit already one frame early during the sync
+ * flip (see {i9xx,skl}_plane_update_arm()). The
+ * hardware will therefore be ready to perform a real
+ * async flip during the next commit, without having
+ * to wait yet another frame for the bit to latch.
+ */
+ new_crtc_state->async_flip_planes |= BIT(plane->id);
+ }
+
+ return 0;
+}
+
+int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_state,
+ struct intel_crtc_state *new_crtc_state,
+ const struct intel_plane_state *old_plane_state,
+ struct intel_plane_state *new_plane_state)
+{
+ struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
+ const struct drm_framebuffer *fb = new_plane_state->hw.fb;
+ int ret;
+
+ intel_plane_set_invisible(new_crtc_state, new_plane_state);
+ new_crtc_state->enabled_planes &= ~BIT(plane->id);
+
+ if (!new_plane_state->hw.crtc && !old_plane_state->hw.crtc)
+ return 0;
+
+ ret = plane->check_plane(new_crtc_state, new_plane_state);
+ if (ret)
+ return ret;
+
+ if (fb)
+ new_crtc_state->enabled_planes |= BIT(plane->id);
+
+ /* FIXME pre-g4x don't work like this */
+ if (new_plane_state->uapi.visible)
+ new_crtc_state->active_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible &&
+ intel_plane_is_scaled(new_plane_state))
+ new_crtc_state->scaled_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible &&
+ intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier))
+ new_crtc_state->nv12_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible &&
+ fb->format->format == DRM_FORMAT_C8)
+ new_crtc_state->c8_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible || old_plane_state->uapi.visible)
+ new_crtc_state->update_planes |= BIT(plane->id);
+
+ if (new_plane_state->uapi.visible &&
+ intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) {
+ new_crtc_state->data_rate_y[plane->id] =
+ intel_plane_data_rate(new_crtc_state, new_plane_state, 0);
+ new_crtc_state->data_rate[plane->id] =
+ intel_plane_data_rate(new_crtc_state, new_plane_state, 1);
+
+ new_crtc_state->rel_data_rate_y[plane->id] =
+ intel_plane_relative_data_rate(new_crtc_state,
+ new_plane_state, 0);
+ new_crtc_state->rel_data_rate[plane->id] =
+ intel_plane_relative_data_rate(new_crtc_state,
+ new_plane_state, 1);
+ } else if (new_plane_state->uapi.visible) {
+ new_crtc_state->data_rate[plane->id] =
+ intel_plane_data_rate(new_crtc_state, new_plane_state, 0);
+
+ new_crtc_state->rel_data_rate[plane->id] =
+ intel_plane_relative_data_rate(new_crtc_state,
+ new_plane_state, 0);
+ }
+
+ return intel_plane_atomic_calc_changes(old_crtc_state, new_crtc_state,
+ old_plane_state, new_plane_state);
+}
+
+static struct intel_plane *
+intel_crtc_get_plane(struct intel_crtc *crtc, enum plane_id plane_id)
+{
+ struct drm_i915_private *i915 = to_i915(crtc->base.dev);
+ struct intel_plane *plane;
+
+ for_each_intel_plane_on_crtc(&i915->drm, crtc, plane) {
+ if (plane->id == plane_id)
+ return plane;
+ }
+
+ return NULL;
+}
+
+int intel_plane_atomic_check(struct intel_atomic_state *state,
+ struct intel_plane *plane)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_plane_state *new_plane_state =
+ intel_atomic_get_new_plane_state(state, plane);
+ const struct intel_plane_state *old_plane_state =
+ intel_atomic_get_old_plane_state(state, plane);
+ const struct intel_plane_state *new_primary_crtc_plane_state;
+ struct intel_crtc *crtc = intel_crtc_for_pipe(display, plane->pipe);
+ const struct intel_crtc_state *old_crtc_state =
+ intel_atomic_get_old_crtc_state(state, crtc);
+ struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+
+ if (new_crtc_state && intel_crtc_is_joiner_secondary(new_crtc_state)) {
+ struct intel_crtc *primary_crtc =
+ intel_primary_crtc(new_crtc_state);
+ struct intel_plane *primary_crtc_plane =
+ intel_crtc_get_plane(primary_crtc, plane->id);
+
+ new_primary_crtc_plane_state =
+ intel_atomic_get_new_plane_state(state, primary_crtc_plane);
+ } else {
+ new_primary_crtc_plane_state = new_plane_state;
+ }
+
+ intel_plane_copy_uapi_to_hw_state(new_plane_state,
+ new_primary_crtc_plane_state,
+ crtc);
+
+ new_plane_state->uapi.visible = false;
+ if (!new_crtc_state)
+ return 0;
+
+ return intel_plane_atomic_check_with_state(old_crtc_state,
+ new_crtc_state,
+ old_plane_state,
+ new_plane_state);
+}
+
+static struct intel_plane *
+skl_next_plane_to_commit(struct intel_atomic_state *state,
+ struct intel_crtc *crtc,
+ struct skl_ddb_entry ddb[I915_MAX_PLANES],
+ struct skl_ddb_entry ddb_y[I915_MAX_PLANES],
+ unsigned int *update_mask)
+{
+ struct intel_crtc_state *crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ struct intel_plane_state __maybe_unused *plane_state;
+ struct intel_plane *plane;
+ int i;
+
+ if (*update_mask == 0)
+ return NULL;
+
+ for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
+ enum plane_id plane_id = plane->id;
+
+ if (crtc->pipe != plane->pipe ||
+ !(*update_mask & BIT(plane_id)))
+ continue;
+
+ if (skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb[plane_id],
+ ddb, I915_MAX_PLANES, plane_id) ||
+ skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb_y[plane_id],
+ ddb_y, I915_MAX_PLANES, plane_id))
+ continue;
+
+ *update_mask &= ~BIT(plane_id);
+ ddb[plane_id] = crtc_state->wm.skl.plane_ddb[plane_id];
+ ddb_y[plane_id] = crtc_state->wm.skl.plane_ddb_y[plane_id];
+
+ return plane;
+ }
+
+ /* should never happen */
+ drm_WARN_ON(state->base.dev, 1);
+
+ return NULL;
+}
+
+void intel_plane_update_noarm(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ trace_intel_plane_update_noarm(plane, crtc);
+
+ if (plane->update_noarm)
+ plane->update_noarm(dsb, plane, crtc_state, plane_state);
+}
+
+void intel_plane_async_flip(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state,
+ bool async_flip)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ trace_intel_plane_async_flip(plane, crtc, async_flip);
+ plane->async_flip(dsb, plane, crtc_state, plane_state, async_flip);
+}
+
+void intel_plane_update_arm(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ if (crtc_state->do_async_flip && plane->async_flip) {
+ intel_plane_async_flip(dsb, plane, crtc_state, plane_state, true);
+ return;
+ }
+
+ trace_intel_plane_update_arm(plane, crtc);
+ plane->update_arm(dsb, plane, crtc_state, plane_state);
+}
+
+void intel_plane_disable_arm(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ trace_intel_plane_disable_arm(plane, crtc);
+ plane->disable_arm(dsb, plane, crtc_state);
+}
+
+void intel_crtc_planes_update_noarm(struct intel_dsb *dsb,
+ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ u32 update_mask = new_crtc_state->update_planes;
+ struct intel_plane_state *new_plane_state;
+ struct intel_plane *plane;
+ int i;
+
+ if (new_crtc_state->do_async_flip)
+ return;
+
+ /*
+ * Since we only write non-arming registers here,
+ * the order does not matter even for skl+.
+ */
+ for_each_new_intel_plane_in_state(state, plane, new_plane_state, i) {
+ if (crtc->pipe != plane->pipe ||
+ !(update_mask & BIT(plane->id)))
+ continue;
+
+ /* TODO: for mailbox updates this should be skipped */
+ if (new_plane_state->uapi.visible ||
+ new_plane_state->planar_slave)
+ intel_plane_update_noarm(dsb, plane,
+ new_crtc_state, new_plane_state);
+ }
+}
+
+static void skl_crtc_planes_update_arm(struct intel_dsb *dsb,
+ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_crtc_state *old_crtc_state =
+ intel_atomic_get_old_crtc_state(state, crtc);
+ struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ struct skl_ddb_entry ddb[I915_MAX_PLANES];
+ struct skl_ddb_entry ddb_y[I915_MAX_PLANES];
+ u32 update_mask = new_crtc_state->update_planes;
+ struct intel_plane *plane;
+
+ memcpy(ddb, old_crtc_state->wm.skl.plane_ddb,
+ sizeof(old_crtc_state->wm.skl.plane_ddb));
+ memcpy(ddb_y, old_crtc_state->wm.skl.plane_ddb_y,
+ sizeof(old_crtc_state->wm.skl.plane_ddb_y));
+
+ while ((plane = skl_next_plane_to_commit(state, crtc, ddb, ddb_y, &update_mask))) {
+ struct intel_plane_state *new_plane_state =
+ intel_atomic_get_new_plane_state(state, plane);
+
+ /*
+ * TODO: for mailbox updates intel_plane_update_noarm()
+ * would have to be called here as well.
+ */
+ if (new_plane_state->uapi.visible ||
+ new_plane_state->planar_slave)
+ intel_plane_update_arm(dsb, plane, new_crtc_state, new_plane_state);
+ else
+ intel_plane_disable_arm(dsb, plane, new_crtc_state);
+ }
+}
+
+static void i9xx_crtc_planes_update_arm(struct intel_dsb *dsb,
+ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ u32 update_mask = new_crtc_state->update_planes;
+ struct intel_plane_state *new_plane_state;
+ struct intel_plane *plane;
+ int i;
+
+ for_each_new_intel_plane_in_state(state, plane, new_plane_state, i) {
+ if (crtc->pipe != plane->pipe ||
+ !(update_mask & BIT(plane->id)))
+ continue;
+
+ /*
+ * TODO: for mailbox updates intel_plane_update_noarm()
+ * would have to be called here as well.
+ */
+ if (new_plane_state->uapi.visible)
+ intel_plane_update_arm(dsb, plane, new_crtc_state, new_plane_state);
+ else
+ intel_plane_disable_arm(dsb, plane, new_crtc_state);
+ }
+}
+
+void intel_crtc_planes_update_arm(struct intel_dsb *dsb,
+ struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct drm_i915_private *i915 = to_i915(state->base.dev);
+
+ if (DISPLAY_VER(i915) >= 9)
+ skl_crtc_planes_update_arm(dsb, state, crtc);
+ else
+ i9xx_crtc_planes_update_arm(dsb, state, crtc);
+}
+
+int intel_atomic_plane_check_clipping(struct intel_plane_state *plane_state,
+ struct intel_crtc_state *crtc_state,
+ int min_scale, int max_scale,
+ bool can_position)
+{
+ struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev);
+ struct drm_framebuffer *fb = plane_state->hw.fb;
+ struct drm_rect *src = &plane_state->uapi.src;
+ struct drm_rect *dst = &plane_state->uapi.dst;
+ const struct drm_rect *clip = &crtc_state->pipe_src;
+ unsigned int rotation = plane_state->hw.rotation;
+ int hscale, vscale;
+
+ if (!fb) {
+ plane_state->uapi.visible = false;
+ return 0;
+ }
+
+ drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation);
+
+ /* Check scaling */
+ hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale);
+ vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale);
+ if (hscale < 0 || vscale < 0) {
+ drm_dbg_kms(&i915->drm, "Invalid scaling of plane\n");
+ drm_rect_debug_print("src: ", src, true);
+ drm_rect_debug_print("dst: ", dst, false);
+ return -ERANGE;
+ }
+
+ /*
+ * FIXME: This might need further adjustment for seamless scaling
+ * with phase information, for the 2p2 and 2p1 scenarios.
+ */
+ plane_state->uapi.visible = drm_rect_clip_scaled(src, dst, clip);
+
+ drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation);
+
+ if (!can_position && plane_state->uapi.visible &&
+ !drm_rect_equals(dst, clip)) {
+ drm_dbg_kms(&i915->drm, "Plane must cover entire CRTC\n");
+ drm_rect_debug_print("dst: ", dst, false);
+ drm_rect_debug_print("clip: ", clip, false);
+ return -EINVAL;
+ }
+
+ /* final plane coordinates will be relative to the plane's pipe */
+ drm_rect_translate(dst, -clip->x1, -clip->y1);
+
+ return 0;
+}
+
+int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state)
+{
+ struct drm_i915_private *i915 = to_i915(plane_state->uapi.plane->dev);
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+ struct drm_rect *src = &plane_state->uapi.src;
+ u32 src_x, src_y, src_w, src_h, hsub, vsub;
+ bool rotated = drm_rotation_90_or_270(plane_state->hw.rotation);
+
+ /*
+ * FIXME hsub/vsub vs. block size is a mess. Pre-tgl CCS
+ * abuses hsub/vsub so we can't use them here. But as they
+ * are limited to 32bpp RGB formats we don't actually need
+ * to check anything.
+ */
+ if (fb->modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
+ fb->modifier == I915_FORMAT_MOD_Yf_TILED_CCS)
+ return 0;
+
+ /*
+ * Hardware doesn't handle subpixel coordinates.
+ * Adjust to (macro)pixel boundary, but be careful not to
+ * increase the source viewport size, because that could
+ * push the downscaling factor out of bounds.
+ */
+ src_x = src->x1 >> 16;
+ src_w = drm_rect_width(src) >> 16;
+ src_y = src->y1 >> 16;
+ src_h = drm_rect_height(src) >> 16;
+
+ drm_rect_init(src, src_x << 16, src_y << 16,
+ src_w << 16, src_h << 16);
+
+ if (fb->format->format == DRM_FORMAT_RGB565 && rotated) {
+ hsub = 2;
+ vsub = 2;
+ } else if (DISPLAY_VER(i915) >= 20 &&
+ intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier)) {
+ /*
+ * This allows NV12 and P0xx formats to have odd size and/or odd
+ * source coordinates on DISPLAY_VER(i915) >= 20
+ */
+ hsub = 1;
+ vsub = 1;
+
+ /* Wa_16023981245 */
+ if ((DISPLAY_VER_FULL(i915) == IP_VER(20, 0) ||
+<<<<<<<
+ DISPLAY_VER_FULL(i915) == IP_VER(30, 0)) &&
+ src_x % 2 != 0)
+=======
+ DISPLAY_VER_FULL(i915) == IP_VER(30, 0)) &&
+ src_x % 2 != 0)
+>>>>>>>
+ hsub = 2;
+ } else {
+ hsub = fb->format->hsub;
+ vsub = fb->format->vsub;
+ }
+
+ if (rotated)
+ hsub = vsub = max(hsub, vsub);
+
+ if (src_x % hsub || src_w % hsub) {
+ drm_dbg_kms(&i915->drm, "src x/w (%u, %u) must be a multiple of %u (rotated: %s)\n",
+ src_x, src_w, hsub, str_yes_no(rotated));
+ return -EINVAL;
+ }
+
+ if (src_y % vsub || src_h % vsub) {
+ drm_dbg_kms(&i915->drm, "src y/h (%u, %u) must be a multiple of %u (rotated: %s)\n",
+ src_y, src_h, vsub, str_yes_no(rotated));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int add_dma_resv_fences(struct dma_resv *resv,
+ struct drm_plane_state *new_plane_state)
+{
+ struct dma_fence *fence = dma_fence_get(new_plane_state->fence);
+ struct dma_fence *new;
+ int ret;
+
+ ret = dma_resv_get_singleton(resv, dma_resv_usage_rw(false), &new);
+ if (ret)
+ goto error;
+
+ if (new && fence) {
+ struct dma_fence_chain *chain = dma_fence_chain_alloc();
+
+ if (!chain) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ dma_fence_chain_init(chain, fence, new, 1);
+ fence = &chain->base;
+
+ } else if (new) {
+ fence = new;
+ }
+
+ dma_fence_put(new_plane_state->fence);
+ new_plane_state->fence = fence;
+ return 0;
+
+error:
+ dma_fence_put(fence);
+ return ret;
+}
+
+/**
+ * intel_prepare_plane_fb - Prepare fb for usage on plane
+ * @_plane: drm plane to prepare for
+ * @_new_plane_state: the plane state being prepared
+ *
+ * Prepares a framebuffer for usage on a display plane. Generally this
+ * involves pinning the underlying object and updating the frontbuffer tracking
+ * bits. Some older platforms need special physical address handling for
+ * cursor planes.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+static int
+intel_prepare_plane_fb(struct drm_plane *_plane,
+ struct drm_plane_state *_new_plane_state)
+{
+ struct i915_sched_attr attr = { .priority = I915_PRIORITY_DISPLAY };
+ struct intel_plane *plane = to_intel_plane(_plane);
+ struct intel_plane_state *new_plane_state =
+ to_intel_plane_state(_new_plane_state);
+ struct intel_atomic_state *state =
+ to_intel_atomic_state(new_plane_state->uapi.state);
+ struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
+ struct intel_plane_state *old_plane_state =
+ intel_atomic_get_old_plane_state(state, plane);
+ struct drm_gem_object *obj = intel_fb_bo(new_plane_state->hw.fb);
+ struct drm_gem_object *old_obj = intel_fb_bo(old_plane_state->hw.fb);
+ int ret;
+
+ if (old_obj) {
+ const struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state,
+ to_intel_crtc(old_plane_state->hw.crtc));
+
+ /* Big Hammer, we also need to ensure that any pending
+ * MI_WAIT_FOR_EVENT inside a user batch buffer on the
+ * current scanout is retired before unpinning the old
+ * framebuffer. Note that we rely on userspace rendering
+ * into the buffer attached to the pipe they are waiting
+ * on. If not, userspace generates a GPU hang with IPEHR
+ * point to the MI_WAIT_FOR_EVENT.
+ *
+ * This should only fail upon a hung GPU, in which case we
+ * can safely continue.
+ */
+ if (new_crtc_state && intel_crtc_needs_modeset(new_crtc_state)) {
+ ret = add_dma_resv_fences(old_obj->resv,
+ &new_plane_state->uapi);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ if (!obj)
+ return 0;
+
+ ret = intel_plane_pin_fb(new_plane_state);
+ if (ret)
+ return ret;
+
+ ret = drm_gem_plane_helper_prepare_fb(&plane->base, &new_plane_state->uapi);
+ if (ret < 0)
+ goto unpin_fb;
+
+ if (new_plane_state->uapi.fence) {
+ i915_gem_fence_wait_priority(new_plane_state->uapi.fence,
+ &attr);
+
+ intel_display_rps_boost_after_vblank(new_plane_state->hw.crtc,
+ new_plane_state->uapi.fence);
+ }
+
+ /*
+ * We declare pageflips to be interactive and so merit a small bias
+ * towards upclocking to deliver the frame on time. By only changing
+ * the RPS thresholds to sample more regularly and aim for higher
+ * clocks we can hopefully deliver low power workloads (like kodi)
+ * that are not quite steady state without resorting to forcing
+ * maximum clocks following a vblank miss (see do_rps_boost()).
+ */
+ intel_display_rps_mark_interactive(dev_priv, state, true);
+
+ return 0;
+
+unpin_fb:
+ intel_plane_unpin_fb(new_plane_state);
+
+ return ret;
+}
+
+/**
+ * intel_cleanup_plane_fb - Cleans up an fb after plane use
+ * @plane: drm plane to clean up for
+ * @_old_plane_state: the state from the previous modeset
+ *
+ * Cleans up a framebuffer that has just been removed from a plane.
+ */
+static void
+intel_cleanup_plane_fb(struct drm_plane *plane,
+ struct drm_plane_state *_old_plane_state)
+{
+ struct intel_plane_state *old_plane_state =
+ to_intel_plane_state(_old_plane_state);
+ struct intel_atomic_state *state =
+ to_intel_atomic_state(old_plane_state->uapi.state);
+ struct drm_i915_private *dev_priv = to_i915(plane->dev);
+ struct drm_gem_object *obj = intel_fb_bo(old_plane_state->hw.fb);
+
+ if (!obj)
+ return;
+
+ intel_display_rps_mark_interactive(dev_priv, state, false);
+
+ intel_plane_unpin_fb(old_plane_state);
+}
+
+static const struct drm_plane_helper_funcs intel_plane_helper_funcs = {
+ .prepare_fb = intel_prepare_plane_fb,
+ .cleanup_fb = intel_cleanup_plane_fb,
+};
+
+void intel_plane_helper_add(struct intel_plane *plane)
+{
+ drm_plane_helper_add(&plane->base, &intel_plane_helper_funcs);
+}
+
+void intel_plane_init_cursor_vblank_work(struct intel_plane_state *old_plane_state,
+ struct intel_plane_state *new_plane_state)
+{
+ if (!old_plane_state->ggtt_vma ||
+ old_plane_state->ggtt_vma == new_plane_state->ggtt_vma)
+ return;
+
+ drm_vblank_work_init(&old_plane_state->unpin_work, old_plane_state->uapi.crtc,
+ intel_cursor_unpin_work);
+}
diff --git a/rr-cache/607de968990f3d5df977e832f76f7e44097ae59c/preimage b/rr-cache/607de968990f3d5df977e832f76f7e44097ae59c/preimage
deleted file mode 100644
index 5ddd36e5bc54..000000000000
--- a/rr-cache/607de968990f3d5df977e832f76f7e44097ae59c/preimage
+++ /dev/null
@@ -1,2073 +0,0 @@
-/*
- * Copyright © 2008 Intel Corporation
- * 2014 Red Hat 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 (including the next
- * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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 <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_fixed.h>
-#include <drm/drm_probe_helper.h>
-
-#include "i915_drv.h"
-#include "i915_reg.h"
-#include "intel_atomic.h"
-#include "intel_audio.h"
-#include "intel_connector.h"
-#include "intel_crtc.h"
-#include "intel_ddi.h"
-#include "intel_de.h"
-#include "intel_display_driver.h"
-#include "intel_display_types.h"
-#include "intel_dp.h"
-#include "intel_dp_hdcp.h"
-#include "intel_dp_mst.h"
-#include "intel_dp_tunnel.h"
-#include "intel_dpio_phy.h"
-#include "intel_hdcp.h"
-#include "intel_hotplug.h"
-#include "intel_link_bw.h"
-#include "intel_psr.h"
-#include "intel_vdsc.h"
-#include "skl_scaler.h"
-
-static int intel_dp_mst_max_dpt_bpp(const struct intel_crtc_state *crtc_state,
- bool dsc)
-{
- struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
-
- if (!intel_dp_is_uhbr(crtc_state) || DISPLAY_VER(i915) >= 20 || !dsc)
- return INT_MAX;
-
- /*
- * DSC->DPT interface width:
- * ICL-MTL: 72 bits (each branch has 72 bits, only left branch is used)
- * LNL+: 144 bits (not a bottleneck in any config)
- *
- * Bspec/49259 suggests that the FEC overhead needs to be
- * applied here, though HW people claim that neither this FEC
- * or any other overhead is applicable here (that is the actual
- * available_bw is just symbol_clock * 72). However based on
- * testing on MTL-P the
- * - DELL U3224KBA display
- * - Unigraf UCD-500 CTS test sink
- * devices the
- * - 5120x2880/995.59Mhz
- * - 6016x3384/1357.23Mhz
- * - 6144x3456/1413.39Mhz
- * modes (all the ones having a DPT limit on the above devices),
- * both the channel coding efficiency and an additional 3%
- * overhead needs to be accounted for.
- */
- return div64_u64(mul_u32_u32(intel_dp_link_symbol_clock(crtc_state->port_clock) * 72,
- drm_dp_bw_channel_coding_efficiency(true)),
- mul_u32_u32(adjusted_mode->crtc_clock, 1030000));
-}
-
-static int intel_dp_mst_bw_overhead(const struct intel_crtc_state *crtc_state,
- const struct intel_connector *connector,
- bool ssc, bool dsc, int bpp_x16)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- unsigned long flags = DRM_DP_BW_OVERHEAD_MST;
- int dsc_slice_count = 0;
- int overhead;
-
- flags |= intel_dp_is_uhbr(crtc_state) ? DRM_DP_BW_OVERHEAD_UHBR : 0;
- flags |= ssc ? DRM_DP_BW_OVERHEAD_SSC_REF_CLK : 0;
- flags |= crtc_state->fec_enable ? DRM_DP_BW_OVERHEAD_FEC : 0;
-
- if (dsc) {
- flags |= DRM_DP_BW_OVERHEAD_DSC;
- dsc_slice_count = intel_dp_dsc_get_slice_count(connector,
- adjusted_mode->clock,
- adjusted_mode->hdisplay,
- crtc_state->joiner_pipes);
- }
-
- overhead = drm_dp_bw_overhead(crtc_state->lane_count,
- adjusted_mode->hdisplay,
- dsc_slice_count,
- bpp_x16,
- flags);
-
- /*
- * TODO: clarify whether a minimum required by the fixed FEC overhead
- * in the bspec audio programming sequence is required here.
- */
- return max(overhead, intel_dp_bw_fec_overhead(crtc_state->fec_enable));
-}
-
-static void intel_dp_mst_compute_m_n(const struct intel_crtc_state *crtc_state,
- const struct intel_connector *connector,
- int overhead,
- int bpp_x16,
- struct intel_link_m_n *m_n)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
-
- /* TODO: Check WA 14013163432 to set data M/N for full BW utilization. */
- intel_link_compute_m_n(bpp_x16, crtc_state->lane_count,
- adjusted_mode->crtc_clock,
- crtc_state->port_clock,
- overhead,
- m_n);
-
- m_n->tu = DIV_ROUND_UP_ULL(mul_u32_u32(m_n->data_m, 64), m_n->data_n);
-}
-
-static int intel_dp_mst_calc_pbn(int pixel_clock, int bpp_x16, int bw_overhead)
-{
- int effective_data_rate =
- intel_dp_effective_data_rate(pixel_clock, bpp_x16, bw_overhead);
-
- /*
- * TODO: Use drm_dp_calc_pbn_mode() instead, once it's converted
- * to calculate PBN with the BW overhead passed to it.
- */
- return DIV_ROUND_UP(effective_data_rate * 64, 54 * 1000);
-}
-
-static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- int max_bpp,
- int min_bpp,
- struct link_config_limits *limits,
- struct drm_connector_state *conn_state,
- int step,
- bool dsc)
-{
- struct drm_atomic_state *state = crtc_state->uapi.state;
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct drm_dp_mst_topology_state *mst_state;
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- int bpp, slots = -EINVAL;
- int max_dpt_bpp;
- int ret = 0;
-
- mst_state = drm_atomic_get_mst_topology_state(state, &intel_dp->mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- crtc_state->lane_count = limits->max_lane_count;
- crtc_state->port_clock = limits->max_rate;
-
- if (dsc) {
- if (!intel_dp_supports_fec(intel_dp, connector, crtc_state))
- return -EINVAL;
-
- crtc_state->fec_enable = !intel_dp_is_uhbr(crtc_state);
- }
-
- mst_state->pbn_div = drm_dp_get_vc_payload_bw(&intel_dp->mst_mgr,
- crtc_state->port_clock,
- crtc_state->lane_count);
-
- max_dpt_bpp = intel_dp_mst_max_dpt_bpp(crtc_state, dsc);
- if (max_bpp > max_dpt_bpp) {
- drm_dbg_kms(&i915->drm, "Limiting bpp to max DPT bpp (%d -> %d)\n",
- max_bpp, max_dpt_bpp);
- max_bpp = max_dpt_bpp;
- }
-
- drm_dbg_kms(&i915->drm, "Looking for slots in range min bpp %d max bpp %d\n",
- min_bpp, max_bpp);
-
- for (bpp = max_bpp; bpp >= min_bpp; bpp -= step) {
- int local_bw_overhead;
- int remote_bw_overhead;
- int link_bpp_x16;
- int remote_tu;
- fixed20_12 pbn;
-
- drm_dbg_kms(&i915->drm, "Trying bpp %d\n", bpp);
-
- link_bpp_x16 = to_bpp_x16(dsc ? bpp :
- intel_dp_output_bpp(crtc_state->output_format, bpp));
-
- local_bw_overhead = intel_dp_mst_bw_overhead(crtc_state, connector,
- false, dsc, link_bpp_x16);
- remote_bw_overhead = intel_dp_mst_bw_overhead(crtc_state, connector,
- true, dsc, link_bpp_x16);
-
- intel_dp_mst_compute_m_n(crtc_state, connector,
- local_bw_overhead,
- link_bpp_x16,
- &crtc_state->dp_m_n);
-
- /*
- * The TU size programmed to the HW determines which slots in
- * an MTP frame are used for this stream, which needs to match
- * the payload size programmed to the first downstream branch
- * device's payload table.
- *
- * Note that atm the payload's PBN value DRM core sends via
- * the ALLOCATE_PAYLOAD side-band message matches the payload
- * size (which it calculates from the PBN value) it programs
- * to the first branch device's payload table. The allocation
- * in the payload table could be reduced though (to
- * crtc_state->dp_m_n.tu), provided that the driver doesn't
- * enable SSC on the corresponding link.
- */
- pbn.full = dfixed_const(intel_dp_mst_calc_pbn(adjusted_mode->crtc_clock,
- link_bpp_x16,
- remote_bw_overhead));
- remote_tu = DIV_ROUND_UP(pbn.full, mst_state->pbn_div.full);
-
- /*
- * Aligning the TUs ensures that symbols consisting of multiple
- * (4) symbol cycles don't get split between two consecutive
- * MTPs, as required by Bspec.
- * TODO: remove the alignment restriction for 128b/132b links
- * on some platforms, where Bspec allows this.
- */
- remote_tu = ALIGN(remote_tu, 4 / crtc_state->lane_count);
-
- /*
- * Also align PBNs accordingly, since MST core will derive its
- * own copy of TU from the PBN in drm_dp_atomic_find_time_slots().
- * The above comment about the difference between the PBN
- * allocated for the whole path and the TUs allocated for the
- * first branch device's link also applies here.
- */
- pbn.full = remote_tu * mst_state->pbn_div.full;
- crtc_state->pbn = dfixed_trunc(pbn);
-
- drm_WARN_ON(&i915->drm, remote_tu < crtc_state->dp_m_n.tu);
- crtc_state->dp_m_n.tu = remote_tu;
-
- slots = drm_dp_atomic_find_time_slots(state, &intel_dp->mst_mgr,
- connector->port,
- crtc_state->pbn);
- if (slots == -EDEADLK)
- return slots;
-
- if (slots >= 0) {
- drm_WARN_ON(&i915->drm, slots != crtc_state->dp_m_n.tu);
-
- break;
- }
- }
-
- /* We failed to find a proper bpp/timeslots, return error */
- if (ret)
- slots = ret;
-
- if (slots < 0) {
- drm_dbg_kms(&i915->drm, "failed finding vcpi slots:%d\n",
- slots);
- } else {
- if (!dsc)
- crtc_state->pipe_bpp = bpp;
- else
- crtc_state->dsc.compressed_bpp_x16 = to_bpp_x16(bpp);
- drm_dbg_kms(&i915->drm, "Got %d slots for pipe bpp %d dsc %d\n", slots, bpp, dsc);
- }
-
- return slots;
-}
-
-static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state,
- struct link_config_limits *limits)
-{
- int slots = -EINVAL;
-
- /*
- * FIXME: allocate the BW according to link_bpp, which in the case of
- * YUV420 is only half of the pipe bpp value.
- */
- slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state,
- to_bpp_int(limits->link.max_bpp_x16),
- to_bpp_int(limits->link.min_bpp_x16),
- limits,
- conn_state, 2 * 3, false);
-
- if (slots < 0)
- return slots;
-
- return 0;
-}
-
-static int intel_dp_dsc_mst_compute_link_config(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state,
- struct link_config_limits *limits)
-{
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- int slots = -EINVAL;
- int i, num_bpc;
- u8 dsc_bpc[3] = {};
- int min_bpp, max_bpp, sink_min_bpp, sink_max_bpp;
- u8 dsc_max_bpc;
- int min_compressed_bpp, max_compressed_bpp;
-
- /* Max DSC Input BPC for ICL is 10 and for TGL+ is 12 */
- if (DISPLAY_VER(i915) >= 12)
- dsc_max_bpc = min_t(u8, 12, conn_state->max_requested_bpc);
- else
- dsc_max_bpc = min_t(u8, 10, conn_state->max_requested_bpc);
-
- max_bpp = min_t(u8, dsc_max_bpc * 3, limits->pipe.max_bpp);
- min_bpp = limits->pipe.min_bpp;
-
- num_bpc = drm_dp_dsc_sink_supported_input_bpcs(connector->dp.dsc_dpcd,
- dsc_bpc);
-
- drm_dbg_kms(&i915->drm, "DSC Source supported min bpp %d max bpp %d\n",
- min_bpp, max_bpp);
-
- sink_max_bpp = dsc_bpc[0] * 3;
- sink_min_bpp = sink_max_bpp;
-
- for (i = 1; i < num_bpc; i++) {
- if (sink_min_bpp > dsc_bpc[i] * 3)
- sink_min_bpp = dsc_bpc[i] * 3;
- if (sink_max_bpp < dsc_bpc[i] * 3)
- sink_max_bpp = dsc_bpc[i] * 3;
- }
-
- drm_dbg_kms(&i915->drm, "DSC Sink supported min bpp %d max bpp %d\n",
- sink_min_bpp, sink_max_bpp);
-
- if (min_bpp < sink_min_bpp)
- min_bpp = sink_min_bpp;
-
- if (max_bpp > sink_max_bpp)
- max_bpp = sink_max_bpp;
-
- crtc_state->pipe_bpp = max_bpp;
-
- max_compressed_bpp = intel_dp_dsc_sink_max_compressed_bpp(connector,
- crtc_state,
- max_bpp / 3);
- max_compressed_bpp = min(max_compressed_bpp,
- to_bpp_int(limits->link.max_bpp_x16));
-
- min_compressed_bpp = intel_dp_dsc_sink_min_compressed_bpp(crtc_state);
- min_compressed_bpp = max(min_compressed_bpp,
- to_bpp_int_roundup(limits->link.min_bpp_x16));
-
- drm_dbg_kms(&i915->drm, "DSC Sink supported compressed min bpp %d compressed max bpp %d\n",
- min_compressed_bpp, max_compressed_bpp);
-
- /* Align compressed bpps according to our own constraints */
- max_compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, max_compressed_bpp,
- crtc_state->pipe_bpp);
- min_compressed_bpp = intel_dp_dsc_nearest_valid_bpp(i915, min_compressed_bpp,
- crtc_state->pipe_bpp);
-
- slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state, max_compressed_bpp,
- min_compressed_bpp, limits,
- conn_state, 1, true);
-
- if (slots < 0)
- return slots;
-
- return 0;
-}
-static int intel_dp_mst_update_slots(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct drm_dp_mst_topology_mgr *mgr = &intel_dp->mst_mgr;
- struct drm_dp_mst_topology_state *topology_state;
- u8 link_coding_cap = intel_dp_is_uhbr(crtc_state) ?
- DP_CAP_ANSI_128B132B : DP_CAP_ANSI_8B10B;
-
- topology_state = drm_atomic_get_mst_topology_state(conn_state->state, mgr);
- if (IS_ERR(topology_state)) {
- drm_dbg_kms(&i915->drm, "slot update failed\n");
- return PTR_ERR(topology_state);
- }
-
- drm_dp_mst_update_slots(topology_state, link_coding_cap);
-
- return 0;
-}
-
-static int mode_hblank_period_ns(const struct drm_display_mode *mode)
-{
- return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(mode->htotal - mode->hdisplay,
- NSEC_PER_SEC / 1000),
- mode->crtc_clock);
-}
-
-static bool
-hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector,
- const struct intel_crtc_state *crtc_state,
- const struct link_config_limits *limits)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
- bool is_uhbr_sink = connector->mst_port &&
- drm_dp_128b132b_supported(connector->mst_port->dpcd);
- int hblank_limit = is_uhbr_sink ? 500 : 300;
-
- if (!connector->dp.dsc_hblank_expansion_quirk)
- return false;
-
- if (is_uhbr_sink && !drm_dp_is_uhbr_rate(limits->max_rate))
- return false;
-
- if (mode_hblank_period_ns(adjusted_mode) > hblank_limit)
- return false;
-
- return true;
-}
-
-static bool
-adjust_limits_for_dsc_hblank_expansion_quirk(const struct intel_connector *connector,
- const struct intel_crtc_state *crtc_state,
- struct link_config_limits *limits,
- bool dsc)
-{
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- int min_bpp_x16 = limits->link.min_bpp_x16;
-
- if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state, limits))
- return true;
-
- if (!dsc) {
- if (intel_dp_supports_dsc(connector, crtc_state)) {
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] DSC needed by hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name);
- return false;
- }
-
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] Increasing link min bpp to 24 due to hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name);
-
- if (limits->link.max_bpp_x16 < to_bpp_x16(24))
- return false;
-
- limits->link.min_bpp_x16 = to_bpp_x16(24);
-
- return true;
- }
-
- drm_WARN_ON(&i915->drm, limits->min_rate != limits->max_rate);
-
- if (limits->max_rate < 540000)
- min_bpp_x16 = to_bpp_x16(13);
- else if (limits->max_rate < 810000)
- min_bpp_x16 = to_bpp_x16(10);
-
- if (limits->link.min_bpp_x16 >= min_bpp_x16)
- return true;
-
- drm_dbg_kms(&i915->drm,
- "[CRTC:%d:%s][CONNECTOR:%d:%s] Increasing link min bpp to " BPP_X16_FMT " in DSC mode due to hblank expansion quirk\n",
- crtc->base.base.id, crtc->base.name,
- connector->base.base.id, connector->base.name,
- BPP_X16_ARGS(min_bpp_x16));
-
- if (limits->link.max_bpp_x16 < min_bpp_x16)
- return false;
-
- limits->link.min_bpp_x16 = min_bpp_x16;
-
- return true;
-}
-
-static bool
-intel_dp_mst_compute_config_limits(struct intel_dp *intel_dp,
- const struct intel_connector *connector,
- struct intel_crtc_state *crtc_state,
- bool dsc,
- struct link_config_limits *limits)
-{
- /*
- * for MST we always configure max link bw - the spec doesn't
- * seem to suggest we should do otherwise.
- */
- limits->min_rate = limits->max_rate =
- intel_dp_max_link_rate(intel_dp);
-
- limits->min_lane_count = limits->max_lane_count =
- intel_dp_max_lane_count(intel_dp);
-
- limits->pipe.min_bpp = intel_dp_min_bpp(crtc_state->output_format);
- /*
- * FIXME: If all the streams can't fit into the link with
- * their current pipe_bpp we should reduce pipe_bpp across
- * the board until things start to fit. Until then we
- * limit to <= 8bpc since that's what was hardcoded for all
- * MST streams previously. This hack should be removed once
- * we have the proper retry logic in place.
- */
- limits->pipe.max_bpp = min(crtc_state->pipe_bpp, 24);
-
- intel_dp_adjust_compliance_config(intel_dp, crtc_state, limits);
-
- if (!intel_dp_compute_config_link_bpp_limits(intel_dp,
- crtc_state,
- dsc,
- limits))
- return false;
-
- return adjust_limits_for_dsc_hblank_expansion_quirk(connector,
- crtc_state,
- limits,
- dsc);
-}
-
-static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
- struct intel_crtc_state *pipe_config,
- struct drm_connector_state *conn_state)
-{
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- 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_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- const struct drm_display_mode *adjusted_mode =
- &pipe_config->hw.adjusted_mode;
- struct link_config_limits limits;
- bool dsc_needed, joiner_needs_dsc;
- int ret = 0;
-
- if (pipe_config->fec_enable &&
- !intel_dp_supports_fec(intel_dp, connector, pipe_config))
- return -EINVAL;
-
- if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
- return -EINVAL;
-
- if (intel_dp_need_joiner(intel_dp, connector,
- adjusted_mode->crtc_hdisplay,
- adjusted_mode->crtc_clock))
- pipe_config->joiner_pipes = GENMASK(crtc->pipe + 1, crtc->pipe);
-
- pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
- pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
- pipe_config->has_pch_encoder = false;
-
- joiner_needs_dsc = intel_dp_joiner_needs_dsc(dev_priv, pipe_config->joiner_pipes);
-
- dsc_needed = joiner_needs_dsc || intel_dp->force_dsc_en ||
- !intel_dp_mst_compute_config_limits(intel_dp,
- connector,
- pipe_config,
- false,
- &limits);
-
- if (!dsc_needed) {
- ret = intel_dp_mst_compute_link_config(encoder, pipe_config,
- conn_state, &limits);
-
- if (ret == -EDEADLK)
- return ret;
-
- if (ret)
- dsc_needed = true;
- }
-
- /* enable compression if the mode doesn't fit available BW */
- if (dsc_needed) {
- drm_dbg_kms(&dev_priv->drm, "Try DSC (fallback=%s, joiner=%s, force=%s)\n",
- str_yes_no(ret), str_yes_no(joiner_needs_dsc),
- str_yes_no(intel_dp->force_dsc_en));
-
- if (!intel_dp_supports_dsc(connector, pipe_config))
- return -EINVAL;
-
- if (!intel_dp_mst_compute_config_limits(intel_dp,
- connector,
- pipe_config,
- true,
- &limits))
- return -EINVAL;
-
- /*
- * FIXME: As bpc is hardcoded to 8, as mentioned above,
- * WARN and ignore the debug flag force_dsc_bpc for now.
- */
- drm_WARN(&dev_priv->drm, intel_dp->force_dsc_bpc, "Cannot Force BPC for MST\n");
- /*
- * Try to get at least some timeslots and then see, if
- * we can fit there with DSC.
- */
- drm_dbg_kms(&dev_priv->drm, "Trying to find VCPI slots in DSC mode\n");
-
- ret = intel_dp_dsc_mst_compute_link_config(encoder, pipe_config,
- conn_state, &limits);
- if (ret < 0)
- return ret;
-
- ret = intel_dp_dsc_compute_config(intel_dp, pipe_config,
- conn_state, &limits,
- pipe_config->dp_m_n.tu, false);
- }
-
- if (ret)
- return ret;
-
- ret = intel_dp_mst_update_slots(encoder, pipe_config, conn_state);
- if (ret)
- return ret;
-
- pipe_config->limited_color_range =
- intel_dp_limited_color_range(pipe_config, conn_state);
-
- if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv))
- pipe_config->lane_lat_optim_mask =
- bxt_dpio_phy_calc_lane_lat_optim_mask(pipe_config->lane_count);
-
- intel_dp_audio_compute_config(encoder, pipe_config, conn_state);
-
- intel_ddi_compute_min_voltage_level(pipe_config);
-
- intel_psr_compute_config(intel_dp, pipe_config, conn_state);
-
- return intel_dp_tunnel_atomic_compute_stream_bw(state, intel_dp, connector,
- pipe_config);
-}
-
-/*
- * Iterate over all connectors and return a mask of
- * all CPU transcoders streaming over the same DP link.
- */
-static unsigned int
-intel_dp_mst_transcoder_mask(struct intel_atomic_state *state,
- struct intel_dp *mst_port)
-{
- struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- const struct intel_digital_connector_state *conn_state;
- struct intel_connector *connector;
- u8 transcoders = 0;
- int i;
-
- if (DISPLAY_VER(dev_priv) < 12)
- return 0;
-
- for_each_new_intel_connector_in_state(state, connector, conn_state, i) {
- const struct intel_crtc_state *crtc_state;
- struct intel_crtc *crtc;
-
- if (connector->mst_port != mst_port || !conn_state->base.crtc)
- continue;
-
- crtc = to_intel_crtc(conn_state->base.crtc);
- crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
-
- if (!crtc_state->hw.active)
- continue;
-
- transcoders |= BIT(crtc_state->cpu_transcoder);
- }
-
- return transcoders;
-}
-
-static u8 get_pipes_downstream_of_mst_port(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct drm_dp_mst_port *parent_port)
-{
- const struct intel_digital_connector_state *conn_state;
- struct intel_connector *connector;
- u8 mask = 0;
- int i;
-
- for_each_new_intel_connector_in_state(state, connector, conn_state, i) {
- if (!conn_state->base.crtc)
- continue;
-
- if (&connector->mst_port->mst_mgr != mst_mgr)
- continue;
-
- if (connector->port != parent_port &&
- !drm_dp_mst_port_downstream_of_parent(mst_mgr,
- connector->port,
- parent_port))
- continue;
-
- mask |= BIT(to_intel_crtc(conn_state->base.crtc)->pipe);
- }
-
- return mask;
-}
-
-static int intel_dp_mst_check_fec_change(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct intel_link_bw_limits *limits)
-{
- struct drm_i915_private *i915 = to_i915(state->base.dev);
- struct intel_crtc *crtc;
- u8 mst_pipe_mask;
- u8 fec_pipe_mask = 0;
- int ret;
-
- mst_pipe_mask = get_pipes_downstream_of_mst_port(state, mst_mgr, NULL);
-
- for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, mst_pipe_mask) {
- struct intel_crtc_state *crtc_state =
- intel_atomic_get_new_crtc_state(state, crtc);
-
- /* Atomic connector check should've added all the MST CRTCs. */
- if (drm_WARN_ON(&i915->drm, !crtc_state))
- return -EINVAL;
-
- if (crtc_state->fec_enable)
- fec_pipe_mask |= BIT(crtc->pipe);
- }
-
- if (!fec_pipe_mask || mst_pipe_mask == fec_pipe_mask)
- return 0;
-
- limits->force_fec_pipes |= mst_pipe_mask;
-
- ret = intel_modeset_pipes_in_mask_early(state, "MST FEC",
- mst_pipe_mask);
-
- return ret ? : -EAGAIN;
-}
-
-static int intel_dp_mst_check_bw(struct intel_atomic_state *state,
- struct drm_dp_mst_topology_mgr *mst_mgr,
- struct drm_dp_mst_topology_state *mst_state,
- struct intel_link_bw_limits *limits)
-{
- struct drm_dp_mst_port *mst_port;
- u8 mst_port_pipes;
- int ret;
-
- ret = drm_dp_mst_atomic_check_mgr(&state->base, mst_mgr, mst_state, &mst_port);
- if (ret != -ENOSPC)
- return ret;
-
- mst_port_pipes = get_pipes_downstream_of_mst_port(state, mst_mgr, mst_port);
-
- ret = intel_link_bw_reduce_bpp(state, limits,
- mst_port_pipes, "MST link BW");
-
- return ret ? : -EAGAIN;
-}
-
-/**
- * intel_dp_mst_atomic_check_link - check all modeset MST link configuration
- * @state: intel atomic state
- * @limits: link BW limits
- *
- * Check the link configuration for all modeset MST outputs. If the
- * configuration is invalid @limits will be updated if possible to
- * reduce the total BW, after which the configuration for all CRTCs in
- * @state must be recomputed with the updated @limits.
- *
- * Returns:
- * - 0 if the confugration is valid
- * - %-EAGAIN, if the configuration is invalid and @limits got updated
- * with fallback values with which the configuration of all CRTCs in
- * @state must be recomputed
- * - Other negative error, if the configuration is invalid without a
- * fallback possibility, or the check failed for another reason
- */
-int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
- struct intel_link_bw_limits *limits)
-{
- struct drm_dp_mst_topology_mgr *mgr;
- struct drm_dp_mst_topology_state *mst_state;
- int ret;
- int i;
-
- for_each_new_mst_mgr_in_state(&state->base, mgr, mst_state, i) {
- ret = intel_dp_mst_check_fec_change(state, mgr, limits);
- if (ret)
- return ret;
-
- ret = intel_dp_mst_check_bw(state, mgr, mst_state,
- limits);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int intel_dp_mst_compute_config_late(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_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
-
- /* lowest numbered transcoder will be designated master */
- crtc_state->mst_master_transcoder =
- ffs(intel_dp_mst_transcoder_mask(state, intel_dp)) - 1;
-
- return 0;
-}
-
-/*
- * If one of the connectors in a MST stream needs a modeset, mark all CRTCs
- * that shares the same MST stream as mode changed,
- * intel_modeset_pipe_config()+intel_crtc_check_fastset() will take care to do
- * a fastset when possible.
- *
- * On TGL+ this is required since each stream go through a master transcoder,
- * so if the master transcoder needs modeset, all other streams in the
- * topology need a modeset. All platforms need to add the atomic state
- * for all streams in the topology, since a modeset on one may require
- * changing the MST link BW usage of the others, which in turn needs a
- * recomputation of the corresponding CRTC states.
- */
-static int
-intel_dp_mst_atomic_topology_check(struct intel_connector *connector,
- struct intel_atomic_state *state)
-{
- struct drm_i915_private *dev_priv = to_i915(state->base.dev);
- struct drm_connector_list_iter connector_list_iter;
- struct intel_connector *connector_iter;
- int ret = 0;
-
- if (!intel_connector_needs_modeset(state, &connector->base))
- return 0;
-
- drm_connector_list_iter_begin(&dev_priv->drm, &connector_list_iter);
- for_each_intel_connector_iter(connector_iter, &connector_list_iter) {
- struct intel_digital_connector_state *conn_iter_state;
- struct intel_crtc_state *crtc_state;
- struct intel_crtc *crtc;
-
- if (connector_iter->mst_port != connector->mst_port ||
- connector_iter == connector)
- continue;
-
- conn_iter_state = intel_atomic_get_digital_connector_state(state,
- connector_iter);
- if (IS_ERR(conn_iter_state)) {
- ret = PTR_ERR(conn_iter_state);
- break;
- }
-
- if (!conn_iter_state->base.crtc)
- continue;
-
- crtc = to_intel_crtc(conn_iter_state->base.crtc);
- crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- break;
- }
-
- ret = drm_atomic_add_affected_planes(&state->base, &crtc->base);
- if (ret)
- break;
- crtc_state->uapi.mode_changed = true;
- }
- drm_connector_list_iter_end(&connector_list_iter);
-
- return ret;
-}
-
-static int
-intel_dp_mst_atomic_check(struct drm_connector *connector,
- struct drm_atomic_state *_state)
-{
- struct intel_atomic_state *state = to_intel_atomic_state(_state);
- struct intel_connector *intel_connector =
- to_intel_connector(connector);
- int ret;
-
- ret = intel_digital_connector_atomic_check(connector, &state->base);
- if (ret)
- return ret;
-
- ret = intel_dp_mst_atomic_topology_check(intel_connector, state);
- if (ret)
- return ret;
-
- if (intel_connector_needs_modeset(state, connector)) {
- ret = intel_dp_tunnel_atomic_check_state(state,
- intel_connector->mst_port,
- intel_connector);
- if (ret)
- return ret;
- }
-
- return drm_dp_atomic_release_time_slots(&state->base,
- &intel_connector->mst_port->mst_mgr,
- intel_connector->port);
-}
-
-static void clear_act_sent(struct intel_encoder *encoder,
- const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
-
- intel_de_write(i915, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_ACT_SENT);
-}
-
-static void wait_for_act_sent(struct intel_encoder *encoder,
- const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(encoder->base.dev);
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_dp *intel_dp = &intel_mst->primary->dp;
-
- if (intel_de_wait_for_set(i915, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_ACT_SENT, 1))
- drm_err(&i915->drm, "Timed out waiting for ACT sent\n");
-
- drm_dp_check_act_status(&intel_dp->mst_mgr);
-}
-
-static void intel_mst_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector =
- to_intel_connector(old_conn_state->connector);
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
-
- drm_dbg_kms(&i915->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- if (intel_dp->active_mst_links == 1)
- intel_dp->link_trained = false;
-
- intel_hdcp_disable(intel_mst->connector);
-
- intel_dp_sink_disable_decompression(state, connector, old_crtc_state);
-}
-
-static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector =
- to_intel_connector(old_conn_state->connector);
- struct drm_dp_mst_topology_state *old_mst_state =
- drm_atomic_get_old_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- struct drm_dp_mst_topology_state *new_mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- const struct drm_dp_mst_atomic_payload *old_payload =
- drm_atomic_get_mst_payload_state(old_mst_state, connector->port);
- struct drm_dp_mst_atomic_payload *new_payload =
- drm_atomic_get_mst_payload_state(new_mst_state, connector->port);
- struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- struct intel_crtc *pipe_crtc;
- bool last_mst_stream;
-
- intel_dp->active_mst_links--;
- last_mst_stream = intel_dp->active_mst_links == 0;
- drm_WARN_ON(&dev_priv->drm,
- DISPLAY_VER(dev_priv) >= 12 && last_mst_stream &&
- !intel_dp_mst_is_master_trans(old_crtc_state));
-
- for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(old_crtc_state)) {
- const struct intel_crtc_state *old_pipe_crtc_state =
- intel_atomic_get_old_crtc_state(state, pipe_crtc);
-
- intel_crtc_vblank_off(old_pipe_crtc_state);
- }
-
- intel_disable_transcoder(old_crtc_state);
-
- drm_dp_remove_payload_part1(&intel_dp->mst_mgr, new_mst_state, new_payload);
-
- clear_act_sent(encoder, old_crtc_state);
-
- intel_de_rmw(dev_priv,
- TRANS_DDI_FUNC_CTL(dev_priv, old_crtc_state->cpu_transcoder),
- TRANS_DDI_DP_VC_PAYLOAD_ALLOC, 0);
-
- wait_for_act_sent(encoder, old_crtc_state);
-
- drm_dp_remove_payload_part2(&intel_dp->mst_mgr, new_mst_state,
- old_payload, new_payload);
-
- intel_ddi_disable_transcoder_func(old_crtc_state);
-
- for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(old_crtc_state)) {
- const struct intel_crtc_state *old_pipe_crtc_state =
- intel_atomic_get_old_crtc_state(state, pipe_crtc);
-
- intel_dsc_disable(old_pipe_crtc_state);
-
- if (DISPLAY_VER(dev_priv) >= 9)
- skl_scaler_disable(old_pipe_crtc_state);
- else
- ilk_pfit_disable(old_pipe_crtc_state);
- }
-
- /*
- * Power down mst path before disabling the port, otherwise we end
- * up getting interrupts from the sink upon detecting link loss.
- */
- drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port,
- false);
-
- /*
- * BSpec 4287: disable DIP after the transcoder is disabled and before
- * the transcoder clock select is set to none.
- */
- intel_dp_set_infoframes(&dig_port->base, false,
- old_crtc_state, NULL);
- /*
- * From TGL spec: "If multi-stream slave transcoder: Configure
- * Transcoder Clock Select to direct no clock to the transcoder"
- *
- * From older GENs spec: "Configure Transcoder Clock Select to direct
- * no clock to the transcoder"
- */
- if (DISPLAY_VER(dev_priv) < 12 || !last_mst_stream)
- intel_ddi_disable_transcoder_clock(old_crtc_state);
-
-
- intel_mst->connector = NULL;
- if (last_mst_stream)
- dig_port->base.post_disable(state, &dig_port->base,
- old_crtc_state, NULL);
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-}
-
-static void intel_mst_post_pll_disable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *old_crtc_state,
- const struct drm_connector_state *old_conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (intel_dp->active_mst_links == 0 &&
- dig_port->base.post_pll_disable)
- dig_port->base.post_pll_disable(state, encoder, old_crtc_state, old_conn_state);
-}
-
-static void intel_mst_pre_pll_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (intel_dp->active_mst_links == 0)
- dig_port->base.pre_pll_enable(state, &dig_port->base,
- pipe_config, NULL);
- else
- /*
- * The port PLL state needs to get updated for secondary
- * streams as for the primary stream.
- */
- intel_ddi_update_active_dpll(state, &dig_port->base,
- to_intel_crtc(pipe_config->uapi.crtc));
-}
-
-static void intel_mst_pre_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct intel_connector *connector =
- to_intel_connector(conn_state->connector);
- struct drm_dp_mst_topology_state *mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- int ret;
- bool first_mst_stream;
-
- /* MST encoders are bound to a crtc, not to a connector,
- * force the mapping here for get_hw_state.
- */
- connector->encoder = encoder;
- intel_mst->connector = connector;
- first_mst_stream = intel_dp->active_mst_links == 0;
- drm_WARN_ON(&dev_priv->drm,
- DISPLAY_VER(dev_priv) >= 12 && first_mst_stream &&
- !intel_dp_mst_is_master_trans(pipe_config));
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- if (first_mst_stream)
- intel_dp_set_power(intel_dp, DP_SET_POWER_D0);
-
- drm_dp_send_power_updown_phy(&intel_dp->mst_mgr, connector->port, true);
-
- intel_dp_sink_enable_decompression(state, connector, pipe_config);
-
- if (first_mst_stream)
- dig_port->base.pre_enable(state, &dig_port->base,
- pipe_config, NULL);
-
- intel_dp->active_mst_links++;
-
- ret = drm_dp_add_payload_part1(&intel_dp->mst_mgr, mst_state,
- drm_atomic_get_mst_payload_state(mst_state, connector->port));
- if (ret < 0)
- drm_dbg_kms(&dev_priv->drm, "Failed to create MST payload for %s: %d\n",
- connector->base.name, ret);
-
- /*
- * Before Gen 12 this is not done as part of
- * dig_port->base.pre_enable() and should be done here. For
- * Gen 12+ the step in which this should be done is different for the
- * first MST stream, so it's done on the DDI for the first stream and
- * here for the following ones.
- */
- if (DISPLAY_VER(dev_priv) < 12 || !first_mst_stream)
- intel_ddi_enable_transcoder_clock(encoder, pipe_config);
-
- intel_dsc_dp_pps_write(&dig_port->base, pipe_config);
- intel_ddi_set_dp_msa(pipe_config, conn_state);
-}
-
-static void enable_bs_jitter_was(const struct intel_crtc_state *crtc_state)
-{
- struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
- u32 clear = 0;
- u32 set = 0;
-
- if (!IS_ALDERLAKE_P(i915))
- return;
-
- if (!IS_DISPLAY_STEP(i915, STEP_D0, STEP_FOREVER))
- return;
-
- /* Wa_14013163432:adlp */
- if (crtc_state->fec_enable || intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_FEC_BS_JITTER_WA(crtc_state->cpu_transcoder);
-
- /* Wa_14014143976:adlp */
- if (IS_DISPLAY_STEP(i915, STEP_E0, STEP_FOREVER)) {
- if (intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_SHORT_HBLANK_WA(crtc_state->cpu_transcoder);
- else if (crtc_state->fec_enable)
- clear |= DP_MST_SHORT_HBLANK_WA(crtc_state->cpu_transcoder);
-
- if (crtc_state->fec_enable || intel_dp_is_uhbr(crtc_state))
- set |= DP_MST_DPT_DPTP_ALIGN_WA(crtc_state->cpu_transcoder);
- }
-
- if (!clear && !set)
- return;
-
- intel_de_rmw(i915, CHICKEN_MISC_3, clear, set);
-}
-
-static void intel_mst_enable_dp(struct intel_atomic_state *state,
- struct intel_encoder *encoder,
- const struct intel_crtc_state *pipe_config,
- const struct drm_connector_state *conn_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
- struct intel_dp *intel_dp = &dig_port->dp;
- struct intel_connector *connector = to_intel_connector(conn_state->connector);
- struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
- struct drm_dp_mst_topology_state *mst_state =
- drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
- enum transcoder trans = pipe_config->cpu_transcoder;
- bool first_mst_stream = intel_dp->active_mst_links == 1;
- struct intel_crtc *pipe_crtc;
-
- drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder);
-
- if (intel_dp_is_uhbr(pipe_config)) {
- const struct drm_display_mode *adjusted_mode =
- &pipe_config->hw.adjusted_mode;
- u64 crtc_clock_hz = KHz(adjusted_mode->crtc_clock);
-
- intel_de_write(dev_priv, TRANS_DP2_VFREQHIGH(pipe_config->cpu_transcoder),
- TRANS_DP2_VFREQ_PIXEL_CLOCK(crtc_clock_hz >> 24));
- intel_de_write(dev_priv, TRANS_DP2_VFREQLOW(pipe_config->cpu_transcoder),
- TRANS_DP2_VFREQ_PIXEL_CLOCK(crtc_clock_hz & 0xffffff));
- }
-
- enable_bs_jitter_was(pipe_config);
-
- intel_ddi_enable_transcoder_func(encoder, pipe_config);
-
- clear_act_sent(encoder, pipe_config);
-
- intel_de_rmw(dev_priv, TRANS_DDI_FUNC_CTL(dev_priv, trans), 0,
- TRANS_DDI_DP_VC_PAYLOAD_ALLOC);
-
- drm_dbg_kms(&dev_priv->drm, "active links %d\n",
- intel_dp->active_mst_links);
-
- wait_for_act_sent(encoder, pipe_config);
-
- if (first_mst_stream)
- intel_ddi_wait_for_fec_status(encoder, pipe_config, true);
-
- drm_dp_add_payload_part2(&intel_dp->mst_mgr,
- drm_atomic_get_mst_payload_state(mst_state, connector->port));
-
- if (DISPLAY_VER(dev_priv) >= 12)
- intel_de_rmw(dev_priv, hsw_chicken_trans_reg(dev_priv, trans),
- FECSTALL_DIS_DPTSTREAM_DPTTG,
- pipe_config->fec_enable ? FECSTALL_DIS_DPTSTREAM_DPTTG : 0);
-
- intel_audio_sdp_split_update(pipe_config);
-
- intel_enable_transcoder(pipe_config);
-
- for_each_intel_crtc_in_pipe_mask_reverse(&dev_priv->drm, pipe_crtc,
- intel_crtc_joined_pipe_mask(pipe_config)) {
- const struct intel_crtc_state *pipe_crtc_state =
- intel_atomic_get_new_crtc_state(state, pipe_crtc);
-
- intel_crtc_vblank_on(pipe_crtc_state);
- }
-
- intel_hdcp_enable(state, encoder, pipe_config, conn_state);
-}
-
-static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
- enum pipe *pipe)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- *pipe = intel_mst->pipe;
- if (intel_mst->connector)
- return true;
- return false;
-}
-
-static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
- struct intel_crtc_state *pipe_config)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
-
- dig_port->base.get_config(&dig_port->base, pipe_config);
-}
-
-static bool intel_dp_mst_initial_fastset_check(struct intel_encoder *encoder,
- struct intel_crtc_state *crtc_state)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
- struct intel_digital_port *dig_port = intel_mst->primary;
-
- return intel_dp_initial_fastset_check(&dig_port->base, crtc_state);
-}
-
-static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct drm_i915_private *i915 = to_i915(intel_connector->base.dev);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- const struct drm_edid *drm_edid;
- int ret;
-
- if (drm_connector_is_unregistered(connector))
- return intel_connector_update_modes(connector, NULL);
-
- if (!intel_display_driver_check_access(i915))
- return drm_edid_connector_add_modes(connector);
-
- drm_edid = drm_dp_mst_edid_read(connector, &intel_dp->mst_mgr, intel_connector->port);
-
- ret = intel_connector_update_modes(connector, drm_edid);
-
- drm_edid_free(drm_edid);
-
- return ret;
-}
-
-static int
-intel_dp_mst_connector_late_register(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
- int ret;
-
- ret = drm_dp_mst_connector_late_register(connector,
- intel_connector->port);
- if (ret < 0)
- return ret;
-
- ret = intel_connector_register(connector);
- if (ret < 0)
- drm_dp_mst_connector_early_unregister(connector,
- intel_connector->port);
-
- return ret;
-}
-
-static void
-intel_dp_mst_connector_early_unregister(struct drm_connector *connector)
-{
- struct intel_connector *intel_connector = to_intel_connector(connector);
-
- intel_connector_unregister(connector);
- drm_dp_mst_connector_early_unregister(connector,
- intel_connector->port);
-}
-
-static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
- .fill_modes = drm_helper_probe_single_connector_modes,
- .atomic_get_property = intel_digital_connector_atomic_get_property,
- .atomic_set_property = intel_digital_connector_atomic_set_property,
- .late_register = intel_dp_mst_connector_late_register,
- .early_unregister = intel_dp_mst_connector_early_unregister,
- .destroy = intel_connector_destroy,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_duplicate_state = intel_digital_connector_duplicate_state,
-};
-
-static int intel_dp_mst_get_modes(struct drm_connector *connector)
-{
- return intel_dp_mst_get_ddc_modes(connector);
-}
-
-static int
-intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
- struct drm_display_mode *mode,
- struct drm_modeset_acquire_ctx *ctx,
- enum drm_mode_status *status)
-{
- struct drm_i915_private *dev_priv = to_i915(connector->dev);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- struct drm_dp_mst_topology_mgr *mgr = &intel_dp->mst_mgr;
- struct drm_dp_mst_port *port = intel_connector->port;
- const int min_bpp = 18;
- int max_dotclk = to_i915(connector->dev)->display.cdclk.max_dotclk_freq;
- int max_rate, mode_rate, max_lanes, max_link_clock;
- int ret;
- bool dsc = false, joiner = false;
- u16 dsc_max_compressed_bpp = 0;
- u8 dsc_slice_count = 0;
- int target_clock = mode->clock;
-
- if (drm_connector_is_unregistered(connector)) {
- *status = MODE_ERROR;
- return 0;
- }
-
- *status = intel_cpu_transcoder_mode_valid(dev_priv, mode);
- if (*status != MODE_OK)
- return 0;
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
- *status = MODE_H_ILLEGAL;
- return 0;
- }
-
- if (mode->clock < 10000) {
- *status = MODE_CLOCK_LOW;
- return 0;
- }
-
- max_link_clock = intel_dp_max_link_rate(intel_dp);
- max_lanes = intel_dp_max_lane_count(intel_dp);
-
- max_rate = intel_dp_max_link_data_rate(intel_dp,
- max_link_clock, max_lanes);
- mode_rate = intel_dp_link_required(mode->clock, min_bpp);
-
- /*
- * TODO:
- * - Also check if compression would allow for the mode
- * - Calculate the overhead using drm_dp_bw_overhead() /
- * drm_dp_bw_channel_coding_efficiency(), similarly to the
- * compute config code, as drm_dp_calc_pbn_mode() doesn't
- * account with all the overheads.
- * - Check here and during compute config the BW reported by
- * DFP_Link_Available_Payload_Bandwidth_Number (or the
- * corresponding link capabilities of the sink) in case the
- * stream is uncompressed for it by the last branch device.
- */
- if (intel_dp_need_joiner(intel_dp, intel_connector,
- mode->hdisplay, target_clock)) {
- joiner = true;
- max_dotclk *= 2;
- }
-
- ret = drm_modeset_lock(&mgr->base.lock, ctx);
- if (ret)
- return ret;
-
- if (mode_rate > max_rate || mode->clock > max_dotclk ||
- drm_dp_calc_pbn_mode(mode->clock, min_bpp << 4) > port->full_pbn) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- if (intel_dp_has_dsc(intel_connector)) {
- /*
- * TBD pass the connector BPC,
- * for now U8_MAX so that max BPC on that platform would be picked
- */
- int pipe_bpp = intel_dp_dsc_compute_max_bpp(intel_connector, U8_MAX);
-
- if (drm_dp_sink_supports_fec(intel_connector->dp.fec_capability)) {
- dsc_max_compressed_bpp =
- intel_dp_dsc_get_max_compressed_bpp(dev_priv,
- max_link_clock,
- max_lanes,
- target_clock,
- mode->hdisplay,
- joiner,
- INTEL_OUTPUT_FORMAT_RGB,
- pipe_bpp, 64);
- dsc_slice_count =
- intel_dp_dsc_get_slice_count(intel_connector,
- target_clock,
- mode->hdisplay,
- joiner);
- }
-
- dsc = dsc_max_compressed_bpp && dsc_slice_count;
- }
-
- if (intel_dp_joiner_needs_dsc(dev_priv, joiner) && !dsc) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- if (mode_rate > max_rate && !dsc) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- *status = intel_mode_valid_max_plane_size(dev_priv, mode, joiner);
- return 0;
-}
-
-static struct drm_encoder *intel_mst_atomic_best_encoder(struct drm_connector *connector,
- struct drm_atomic_state *state)
-{
- struct drm_connector_state *connector_state = drm_atomic_get_new_connector_state(state,
- connector);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
- struct intel_crtc *crtc = to_intel_crtc(connector_state->crtc);
-
- return &intel_dp->mst_encoders[crtc->pipe]->base.base;
-}
-
-static int
-intel_dp_mst_detect(struct drm_connector *connector,
- struct drm_modeset_acquire_ctx *ctx, bool force)
-{
- struct drm_i915_private *i915 = to_i915(connector->dev);
- struct intel_connector *intel_connector = to_intel_connector(connector);
- struct intel_dp *intel_dp = intel_connector->mst_port;
-
- if (!intel_display_device_enabled(i915))
- return connector_status_disconnected;
-
- if (drm_connector_is_unregistered(connector))
- return connector_status_disconnected;
-
- if (!intel_display_driver_check_access(i915))
- return connector->status;
-
- return drm_dp_mst_detect_port(connector, ctx, &intel_dp->mst_mgr,
- intel_connector->port);
-}
-
-static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
- .get_modes = intel_dp_mst_get_modes,
- .mode_valid_ctx = intel_dp_mst_mode_valid_ctx,
- .atomic_best_encoder = intel_mst_atomic_best_encoder,
- .atomic_check = intel_dp_mst_atomic_check,
- .detect_ctx = intel_dp_mst_detect,
-};
-
-static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
-{
- struct intel_dp_mst_encoder *intel_mst = enc_to_mst(to_intel_encoder(encoder));
-
- drm_encoder_cleanup(encoder);
- kfree(intel_mst);
-}
-
-static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
- .destroy = intel_dp_mst_encoder_destroy,
-};
-
-static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
-{
- if (intel_attached_encoder(connector) && connector->base.state->crtc) {
- enum pipe pipe;
- if (!intel_attached_encoder(connector)->get_hw_state(intel_attached_encoder(connector), &pipe))
- return false;
- return true;
- }
- return false;
-}
-
-static int intel_dp_mst_add_properties(struct intel_dp *intel_dp,
- struct drm_connector *connector,
- const char *pathprop)
-{
- struct drm_i915_private *i915 = to_i915(connector->dev);
-
- drm_object_attach_property(&connector->base,
- i915->drm.mode_config.path_property, 0);
- drm_object_attach_property(&connector->base,
- i915->drm.mode_config.tile_property, 0);
-
- intel_attach_force_audio_property(connector);
- intel_attach_broadcast_rgb_property(connector);
-
- /*
- * Reuse the prop from the SST connector because we're
- * not allowed to create new props after device registration.
- */
- connector->max_bpc_property =
- intel_dp->attached_connector->base.max_bpc_property;
- if (connector->max_bpc_property)
- drm_connector_attach_max_bpc_property(connector, 6, 12);
-
- return drm_connector_set_path_property(connector, pathprop);
-}
-
-static void
-intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp,
- struct intel_connector *connector)
-{
- u8 dpcd_caps[DP_RECEIVER_CAP_SIZE];
-
- if (!connector->dp.dsc_decompression_aux)
- return;
-
- if (drm_dp_read_dpcd_caps(connector->dp.dsc_decompression_aux, dpcd_caps) < 0)
- return;
-
- intel_dp_get_dsc_sink_cap(dpcd_caps[DP_DPCD_REV], connector);
-}
-
-static bool detect_dsc_hblank_expansion_quirk(const struct intel_connector *connector)
-{
- struct drm_i915_private *i915 = to_i915(connector->base.dev);
- struct drm_dp_aux *aux = connector->dp.dsc_decompression_aux;
- struct drm_dp_desc desc;
- u8 dpcd[DP_RECEIVER_CAP_SIZE];
-
- if (!aux)
- return false;
-
- /*
- * A logical port's OUI (at least for affected sinks) is all 0, so
- * instead of that the parent port's OUI is used for identification.
- */
- if (drm_dp_mst_port_is_logical(connector->port)) {
- aux = drm_dp_mst_aux_for_parent(connector->port);
- if (!aux)
- aux = &connector->mst_port->aux;
- }
-
- if (drm_dp_read_dpcd_caps(aux, dpcd) < 0)
- return false;
-
- if (drm_dp_read_desc(aux, &desc, drm_dp_is_branch(dpcd)) < 0)
- return false;
-
- if (!drm_dp_has_quirk(&desc,
- DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC))
- return false;
-
- /*
- * UHBR (MST sink) devices requiring this quirk don't advertise the
- * HBLANK expansion support. Presuming that they perform HBLANK
- * expansion internally, or are affected by this issue on modes with a
- * short HBLANK for other reasons.
- */
- if (!drm_dp_128b132b_supported(dpcd) &&
- !(dpcd[DP_RECEIVE_PORT_0_CAP_0] & DP_HBLANK_EXPANSION_CAPABLE))
- return false;
-
- drm_dbg_kms(&i915->drm,
- "[CONNECTOR:%d:%s] DSC HBLANK expansion quirk detected\n",
- connector->base.base.id, connector->base.name);
-
- return true;
-}
-
-static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port,
- const char *pathprop)
-{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct drm_device *dev = dig_port->base.base.dev;
- struct drm_i915_private *dev_priv = to_i915(dev);
- struct intel_connector *intel_connector;
- struct drm_connector *connector;
- enum pipe pipe;
- int ret;
-
- intel_connector = intel_connector_alloc();
- if (!intel_connector)
- return NULL;
-
- intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
- intel_connector->sync_state = intel_dp_connector_sync_state;
- intel_connector->mst_port = intel_dp;
- intel_connector->port = port;
- drm_dp_mst_get_port_malloc(port);
-
- intel_dp_init_modeset_retry_work(intel_connector);
-
- intel_connector->dp.dsc_decompression_aux = drm_dp_mst_dsc_aux_for_port(port);
- intel_dp_mst_read_decompression_port_dsc_caps(intel_dp, intel_connector);
- intel_connector->dp.dsc_hblank_expansion_quirk =
- detect_dsc_hblank_expansion_quirk(intel_connector);
-
- connector = &intel_connector->base;
- ret = drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs,
- DRM_MODE_CONNECTOR_DisplayPort);
- if (ret) {
- drm_dp_mst_put_port_malloc(port);
- intel_connector_free(intel_connector);
- return NULL;
- }
-
- drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
-
- for_each_pipe(dev_priv, pipe) {
- struct drm_encoder *enc =
- &intel_dp->mst_encoders[pipe]->base.base;
-
- ret = drm_connector_attach_encoder(&intel_connector->base, enc);
- if (ret)
- goto err;
- }
-
- ret = intel_dp_mst_add_properties(intel_dp, connector, pathprop);
- if (ret)
- goto err;
-
- ret = intel_dp_hdcp_init(dig_port, intel_connector);
- if (ret)
- drm_dbg_kms(&dev_priv->drm, "[%s:%d] HDCP MST init failed, skipping.\n",
- connector->name, connector->base.id);
-
- return connector;
-
-err:
- drm_connector_cleanup(connector);
- return NULL;
-}
-
-static void
-intel_dp_mst_poll_hpd_irq(struct drm_dp_mst_topology_mgr *mgr)
-{
- struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
-
- intel_hpd_trigger_irq(dp_to_dig_port(intel_dp));
-}
-
-static const struct drm_dp_mst_topology_cbs mst_cbs = {
- .add_connector = intel_dp_add_mst_connector,
- .poll_hpd_irq = intel_dp_mst_poll_hpd_irq,
-};
-
-static struct intel_dp_mst_encoder *
-intel_dp_create_fake_mst_encoder(struct intel_digital_port *dig_port, enum pipe pipe)
-{
- struct intel_dp_mst_encoder *intel_mst;
- struct intel_encoder *intel_encoder;
- struct drm_device *dev = dig_port->base.base.dev;
-
- intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
-
- if (!intel_mst)
- return NULL;
-
- intel_mst->pipe = pipe;
- intel_encoder = &intel_mst->base;
- intel_mst->primary = dig_port;
-
- drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
- DRM_MODE_ENCODER_DPMST, "DP-MST %c", pipe_name(pipe));
-
- intel_encoder->type = INTEL_OUTPUT_DP_MST;
- intel_encoder->power_domain = dig_port->base.power_domain;
- intel_encoder->port = dig_port->base.port;
- intel_encoder->cloneable = 0;
- /*
- * This is wrong, but broken userspace uses the intersection
- * of possible_crtcs of all the encoders of a given connector
- * to figure out which crtcs can drive said connector. What
- * should be used instead is the union of possible_crtcs.
- * To keep such userspace functioning we must misconfigure
- * this to make sure the intersection is not empty :(
- */
- intel_encoder->pipe_mask = ~0;
-
- intel_encoder->compute_config = intel_dp_mst_compute_config;
- intel_encoder->compute_config_late = intel_dp_mst_compute_config_late;
- intel_encoder->disable = intel_mst_disable_dp;
- intel_encoder->post_disable = intel_mst_post_disable_dp;
- intel_encoder->post_pll_disable = intel_mst_post_pll_disable_dp;
- intel_encoder->update_pipe = intel_ddi_update_pipe;
- intel_encoder->pre_pll_enable = intel_mst_pre_pll_enable_dp;
- intel_encoder->pre_enable = intel_mst_pre_enable_dp;
- intel_encoder->enable = intel_mst_enable_dp;
- intel_encoder->audio_enable = intel_audio_codec_enable;
- intel_encoder->audio_disable = intel_audio_codec_disable;
- intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
- intel_encoder->get_config = intel_dp_mst_enc_get_config;
- intel_encoder->initial_fastset_check = intel_dp_mst_initial_fastset_check;
-
- return intel_mst;
-
-}
-
-static bool
-intel_dp_create_fake_mst_encoders(struct intel_digital_port *dig_port)
-{
- struct intel_dp *intel_dp = &dig_port->dp;
- struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
- enum pipe pipe;
-
- for_each_pipe(dev_priv, pipe)
- intel_dp->mst_encoders[pipe] = intel_dp_create_fake_mst_encoder(dig_port, pipe);
- return true;
-}
-
-int
-intel_dp_mst_encoder_active_links(struct intel_digital_port *dig_port)
-{
- return dig_port->dp.active_mst_links;
-}
-
-int
-intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_base_id)
-{
- struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev);
- struct intel_dp *intel_dp = &dig_port->dp;
- enum port port = dig_port->base.port;
- int ret;
-
- if (!HAS_DP_MST(i915) || intel_dp_is_edp(intel_dp))
- return 0;
-
- if (DISPLAY_VER(i915) < 12 && port == PORT_A)
- return 0;
-
- if (DISPLAY_VER(i915) < 11 && port == PORT_E)
- return 0;
-
- intel_dp->mst_mgr.cbs = &mst_cbs;
-
- /* create encoders */
- intel_dp_create_fake_mst_encoders(dig_port);
- ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, &i915->drm,
- &intel_dp->aux, 16, 3, conn_base_id);
- if (ret) {
- intel_dp->mst_mgr.cbs = NULL;
- return ret;
- }
-
- return 0;
-}
-
-bool intel_dp_mst_source_support(struct intel_dp *intel_dp)
-{
- return intel_dp->mst_mgr.cbs;
-}
-
-void
-intel_dp_mst_encoder_cleanup(struct intel_digital_port *dig_port)
-{
- struct intel_dp *intel_dp = &dig_port->dp;
-
- if (!intel_dp_mst_source_support(intel_dp))
- return;
-
- drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
- /* encoders will get killed by normal cleanup */
-
- intel_dp->mst_mgr.cbs = NULL;
-}
-
-bool intel_dp_mst_is_master_trans(const struct intel_crtc_state *crtc_state)
-{
- return crtc_state->mst_master_transcoder == crtc_state->cpu_transcoder;
-}
-
-bool intel_dp_mst_is_slave_trans(const struct intel_crtc_state *crtc_state)
-{
- return crtc_state->mst_master_transcoder != INVALID_TRANSCODER &&
- crtc_state->mst_master_transcoder != crtc_state->cpu_transcoder;
-}
-
-/**
- * intel_dp_mst_add_topology_state_for_connector - add MST topology state for a connector
- * @state: atomic state
- * @connector: connector to add the state for
- * @crtc: the CRTC @connector is attached to
- *
- * Add the MST topology state for @connector to @state.
- *
- * Returns 0 on success, negative error code on failure.
- */
-static int
-intel_dp_mst_add_topology_state_for_connector(struct intel_atomic_state *state,
- struct intel_connector *connector,
- struct intel_crtc *crtc)
-{
- struct drm_dp_mst_topology_state *mst_state;
-
- if (!connector->mst_port)
- return 0;
-
- mst_state = drm_atomic_get_mst_topology_state(&state->base,
- &connector->mst_port->mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- mst_state->pending_crtc_mask |= drm_crtc_mask(&crtc->base);
-
- return 0;
-}
-
-/**
- * intel_dp_mst_add_topology_state_for_crtc - add MST topology state for a CRTC
- * @state: atomic state
- * @crtc: CRTC to add the state for
- *
- * Add the MST topology state for @crtc to @state.
- *
- * Returns 0 on success, negative error code on failure.
- */
-int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state *state,
- struct intel_crtc *crtc)
-{
- struct drm_connector *_connector;
- struct drm_connector_state *conn_state;
- int i;
-
- for_each_new_connector_in_state(&state->base, _connector, conn_state, i) {
- struct intel_connector *connector = to_intel_connector(_connector);
- int ret;
-
- if (conn_state->crtc != &crtc->base)
- continue;
-
- ret = intel_dp_mst_add_topology_state_for_connector(state, connector, crtc);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static struct intel_connector *
-get_connector_in_state_for_crtc(struct intel_atomic_state *state,
- const struct intel_crtc *crtc)
-{
- struct drm_connector_state *old_conn_state;
- struct drm_connector_state *new_conn_state;
- struct drm_connector *_connector;
- int i;
-
- for_each_oldnew_connector_in_state(&state->base, _connector,
- old_conn_state, new_conn_state, i) {
- struct intel_connector *connector =
- to_intel_connector(_connector);
-
- if (old_conn_state->crtc == &crtc->base ||
- new_conn_state->crtc == &crtc->base)
- return connector;
- }
-
- return NULL;
-}
-
-/**
- * intel_dp_mst_crtc_needs_modeset - check if changes in topology need to modeset the given CRTC
- * @state: atomic state
- * @crtc: CRTC for which to check the modeset requirement
- *
- * Check if any change in a MST topology requires a forced modeset on @crtc in
- * this topology. One such change is enabling/disabling the DSC decompression
- * state in the first branch device's UFP DPCD as required by one CRTC, while
- * the other @crtc in the same topology is still active, requiring a full modeset
- * on @crtc.
- */
-bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
- struct intel_crtc *crtc)
-{
- const struct intel_connector *crtc_connector;
- const struct drm_connector_state *conn_state;
- const struct drm_connector *_connector;
- int i;
-
- if (!intel_crtc_has_type(intel_atomic_get_new_crtc_state(state, crtc),
- INTEL_OUTPUT_DP_MST))
- return false;
-
- crtc_connector = get_connector_in_state_for_crtc(state, crtc);
-
- if (!crtc_connector)
- /* None of the connectors in the topology needs modeset */
- return false;
-
- for_each_new_connector_in_state(&state->base, _connector, conn_state, i) {
- const struct intel_connector *connector =
- to_intel_connector(_connector);
- const struct intel_crtc_state *new_crtc_state;
- const struct intel_crtc_state *old_crtc_state;
- struct intel_crtc *crtc_iter;
-
- if (connector->mst_port != crtc_connector->mst_port ||
- !conn_state->crtc)
- continue;
-
- crtc_iter = to_intel_crtc(conn_state->crtc);
-
- new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc_iter);
- old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc_iter);
-
- if (!intel_crtc_needs_modeset(new_crtc_state))
- continue;
-
- if (old_crtc_state->dsc.compression_enable ==
- new_crtc_state->dsc.compression_enable)
- continue;
- /*
- * Toggling the decompression flag because of this stream in
- * the first downstream branch device's UFP DPCD may reset the
- * whole branch device. To avoid the reset while other streams
- * are also active modeset the whole MST topology in this
- * case.
- */
- if (connector->dp.dsc_decompression_aux ==
- &connector->mst_port->aux)
- return true;
- }
-
- return false;
-}
-<<<<<<<
-=======
-
-/**
- * intel_dp_mst_prepare_probe - Prepare an MST link for topology probing
- * @intel_dp: DP port object
- *
- * Prepare an MST link for topology probing, programming the target
- * link parameters to DPCD. This step is a requirement of the enumaration
- * of path resources during probing.
- */
-void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp)
-{
- int link_rate = intel_dp_max_link_rate(intel_dp);
- int lane_count = intel_dp_max_lane_count(intel_dp);
- u8 rate_select;
- u8 link_bw;
-
- if (intel_dp->link_trained)
- return;
-
- if (intel_mst_probed_link_params_valid(intel_dp, link_rate, lane_count))
- return;
-
- intel_dp_compute_rate(intel_dp, link_rate, &link_bw, &rate_select);
-
- intel_dp_link_training_set_mode(intel_dp, link_rate, false);
- intel_dp_link_training_set_bw(intel_dp, link_bw, rate_select, lane_count,
- drm_dp_enhanced_frame_cap(intel_dp->dpcd));
-
- intel_mst_set_probed_link_params(intel_dp, link_rate, lane_count);
-}
-
-/*
- * intel_dp_mst_verify_dpcd_state - verify the MST SW enabled state wrt. the DPCD
- * @intel_dp: DP port object
- *
- * Verify if @intel_dp's MST enabled SW state matches the corresponding DPCD
- * state. A long HPD pulse - not long enough to be detected as a disconnected
- * state - could've reset the DPCD state, which requires tearing
- * down/recreating the MST topology.
- *
- * Returns %true if the SW MST enabled and DPCD states match, %false
- * otherwise.
- */
-bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp)
-{
- struct intel_display *display = to_intel_display(intel_dp);
- struct intel_connector *connector = intel_dp->attached_connector;
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct intel_encoder *encoder = &dig_port->base;
- int ret;
- u8 val;
-
- if (!intel_dp->is_mst)
- return true;
-
- ret = drm_dp_dpcd_readb(intel_dp->mst_mgr.aux, DP_MSTM_CTRL, &val);
-
- /* Adjust the expected register value for SST + SideBand. */
- if (ret < 0 || val != (DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC)) {
- drm_dbg_kms(display->drm,
- "[CONNECTOR:%d:%s][ENCODER:%d:%s] MST mode got reset, removing topology (ret=%d, ctrl=0x%02x)\n",
- connector->base.base.id, connector->base.name,
- encoder->base.base.id, encoder->base.name,
- ret, val);
-
- return false;
- }
-
- return true;
-}
->>>>>>>
diff --git a/rr-cache/6e989852f5454d81ebf331bbd2c55116dc711575/preimage.2 b/rr-cache/6e989852f5454d81ebf331bbd2c55116dc711575/preimage.2
deleted file mode 100644
index 42ffb0f8cccc..000000000000
--- a/rr-cache/6e989852f5454d81ebf331bbd2c55116dc711575/preimage.2
+++ /dev/null
@@ -1,982 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-#include "xe_exec_queue.h"
-
-#include <linux/nospec.h>
-
-#include <drm/drm_device.h>
-#include <drm/drm_file.h>
-#include <drm/xe_drm.h>
-
-#include "xe_device.h"
-#include "xe_gt.h"
-#include "xe_hw_engine_class_sysfs.h"
-#include "xe_hw_fence.h"
-#include "xe_lrc.h"
-#include "xe_macros.h"
-#include "xe_migrate.h"
-#include "xe_pm.h"
-#include "xe_ring_ops_types.h"
-#include "xe_trace.h"
-#include "xe_vm.h"
-
-enum xe_exec_queue_sched_prop {
- XE_EXEC_QUEUE_JOB_TIMEOUT = 0,
- XE_EXEC_QUEUE_TIMESLICE = 1,
- XE_EXEC_QUEUE_PREEMPT_TIMEOUT = 2,
- XE_EXEC_QUEUE_SCHED_PROP_MAX = 3,
-};
-
-static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
- u64 extensions, int ext_number);
-
-static void __xe_exec_queue_free(struct xe_exec_queue *q)
-{
- if (q->vm)
- xe_vm_put(q->vm);
-
- if (q->xef)
- xe_file_put(q->xef);
-
- kfree(q);
-}
-
-static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
- struct xe_vm *vm,
- u32 logical_mask,
- u16 width, struct xe_hw_engine *hwe,
- u32 flags, u64 extensions)
-{
- struct xe_exec_queue *q;
- struct xe_gt *gt = hwe->gt;
- int err;
-
- /* only kernel queues can be permanent */
- XE_WARN_ON((flags & EXEC_QUEUE_FLAG_PERMANENT) && !(flags & EXEC_QUEUE_FLAG_KERNEL));
-
- q = kzalloc(struct_size(q, lrc, width), GFP_KERNEL);
- if (!q)
- return ERR_PTR(-ENOMEM);
-
- kref_init(&q->refcount);
- q->flags = flags;
- q->hwe = hwe;
- q->gt = gt;
- q->class = hwe->class;
- q->width = width;
- q->logical_mask = logical_mask;
- q->fence_irq = &gt->fence_irq[hwe->class];
- q->ring_ops = gt->ring_ops[hwe->class];
- q->ops = gt->exec_queue_ops;
- INIT_LIST_HEAD(&q->lr.link);
- INIT_LIST_HEAD(&q->multi_gt_link);
-
- q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us;
- q->sched_props.preempt_timeout_us =
- hwe->eclass->sched_props.preempt_timeout_us;
- q->sched_props.job_timeout_ms =
- hwe->eclass->sched_props.job_timeout_ms;
- if (q->flags & EXEC_QUEUE_FLAG_KERNEL &&
- q->flags & EXEC_QUEUE_FLAG_HIGH_PRIORITY)
- q->sched_props.priority = XE_EXEC_QUEUE_PRIORITY_KERNEL;
- else
- q->sched_props.priority = XE_EXEC_QUEUE_PRIORITY_NORMAL;
-
- if (vm)
- q->vm = xe_vm_get(vm);
-
- if (extensions) {
- /*
- * may set q->usm, must come before xe_lrc_create(),
- * may overwrite q->sched_props, must come before q->ops->init()
- */
- err = exec_queue_user_extensions(xe, q, extensions, 0);
- if (err) {
- __xe_exec_queue_free(q);
- return ERR_PTR(err);
- }
- }
-
- return q;
-}
-
-static int __xe_exec_queue_init(struct xe_exec_queue *q)
-{
- struct xe_vm *vm = q->vm;
- int i, err;
-
- if (vm) {
- err = xe_vm_lock(vm, true);
- if (err)
- return err;
- }
-
- for (i = 0; i < q->width; ++i) {
- q->lrc[i] = xe_lrc_create(q->hwe, q->vm, SZ_16K);
- if (IS_ERR(q->lrc[i])) {
- err = PTR_ERR(q->lrc[i]);
- goto err_unlock;
- }
- }
-
- if (vm)
- xe_vm_unlock(vm);
-
- err = q->ops->init(q);
- if (err)
- goto err_lrc;
-
- return 0;
-
-err_unlock:
- if (vm)
- xe_vm_unlock(vm);
-err_lrc:
- for (i = i - 1; i >= 0; --i)
- xe_lrc_put(q->lrc[i]);
- return err;
-}
-
-struct xe_exec_queue *xe_exec_queue_create(struct xe_device *xe, struct xe_vm *vm,
- u32 logical_mask, u16 width,
- struct xe_hw_engine *hwe, u32 flags,
- u64 extensions)
-{
- struct xe_exec_queue *q;
- int err;
-
- q = __xe_exec_queue_alloc(xe, vm, logical_mask, width, hwe, flags,
- extensions);
- if (IS_ERR(q))
- return q;
-
- err = __xe_exec_queue_init(q);
- if (err)
- goto err_post_alloc;
-
- return q;
-
-err_post_alloc:
- __xe_exec_queue_free(q);
- return ERR_PTR(err);
-}
-
-struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe_gt *gt,
- struct xe_vm *vm,
- enum xe_engine_class class,
- u32 flags, u64 extensions)
-{
- struct xe_hw_engine *hwe, *hwe0 = NULL;
- enum xe_hw_engine_id id;
- u32 logical_mask = 0;
-
- for_each_hw_engine(hwe, gt, id) {
- if (xe_hw_engine_is_reserved(hwe))
- continue;
-
- if (hwe->class == class) {
- logical_mask |= BIT(hwe->logical_instance);
- if (!hwe0)
- hwe0 = hwe;
- }
- }
-
- if (!logical_mask)
- return ERR_PTR(-ENODEV);
-
- return xe_exec_queue_create(xe, vm, logical_mask, 1, hwe0, flags, extensions);
-}
-
-/**
- * xe_exec_queue_create_bind() - Create bind exec queue.
- * @xe: Xe device.
- * @tile: tile which bind exec queue belongs to.
- * @flags: exec queue creation flags
- * @extensions: exec queue creation extensions
- *
- * Normalize bind exec queue creation. Bind exec queue is tied to migration VM
- * for access to physical memory required for page table programming. On a
- * faulting devices the reserved copy engine instance must be used to avoid
- * deadlocking (user binds cannot get stuck behind faults as kernel binds which
- * resolve faults depend on user binds). On non-faulting devices any copy engine
- * can be used.
- *
- * Returns exec queue on success, ERR_PTR on failure
- */
-struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
- struct xe_tile *tile,
- u32 flags, u64 extensions)
-{
- struct xe_gt *gt = tile->primary_gt;
- struct xe_exec_queue *q;
- struct xe_vm *migrate_vm;
-
- migrate_vm = xe_migrate_get_vm(tile->migrate);
- if (xe->info.has_usm) {
- struct xe_hw_engine *hwe = xe_gt_hw_engine(gt,
- XE_ENGINE_CLASS_COPY,
- gt->usm.reserved_bcs_instance,
- false);
-
- if (!hwe)
- return ERR_PTR(-EINVAL);
-
- q = xe_exec_queue_create(xe, migrate_vm,
- BIT(hwe->logical_instance), 1, hwe,
- flags, extensions);
- } else {
- q = xe_exec_queue_create_class(xe, gt, migrate_vm,
- XE_ENGINE_CLASS_COPY, flags,
- extensions);
- }
- xe_vm_put(migrate_vm);
-
- return q;
-}
-
-void xe_exec_queue_destroy(struct kref *ref)
-{
- struct xe_exec_queue *q = container_of(ref, struct xe_exec_queue, refcount);
- struct xe_exec_queue *eq, *next;
-
- xe_exec_queue_last_fence_put_unlocked(q);
- if (!(q->flags & EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD)) {
- list_for_each_entry_safe(eq, next, &q->multi_gt_list,
- multi_gt_link)
- xe_exec_queue_put(eq);
- }
-
- q->ops->fini(q);
-}
-
-void xe_exec_queue_fini(struct xe_exec_queue *q)
-{
- int i;
-
- for (i = 0; i < q->width; ++i)
- xe_lrc_put(q->lrc[i]);
- __xe_exec_queue_free(q);
-}
-
-void xe_exec_queue_assign_name(struct xe_exec_queue *q, u32 instance)
-{
- switch (q->class) {
- case XE_ENGINE_CLASS_RENDER:
- snprintf(q->name, sizeof(q->name), "rcs%d", instance);
- break;
- case XE_ENGINE_CLASS_VIDEO_DECODE:
- snprintf(q->name, sizeof(q->name), "vcs%d", instance);
- break;
- case XE_ENGINE_CLASS_VIDEO_ENHANCE:
- snprintf(q->name, sizeof(q->name), "vecs%d", instance);
- break;
- case XE_ENGINE_CLASS_COPY:
- snprintf(q->name, sizeof(q->name), "bcs%d", instance);
- break;
- case XE_ENGINE_CLASS_COMPUTE:
- snprintf(q->name, sizeof(q->name), "ccs%d", instance);
- break;
- case XE_ENGINE_CLASS_OTHER:
- snprintf(q->name, sizeof(q->name), "gsccs%d", instance);
- break;
- default:
- XE_WARN_ON(q->class);
- }
-}
-
-struct xe_exec_queue *xe_exec_queue_lookup(struct xe_file *xef, u32 id)
-{
- struct xe_exec_queue *q;
-
- mutex_lock(&xef->exec_queue.lock);
- q = xa_load(&xef->exec_queue.xa, id);
- if (q)
- xe_exec_queue_get(q);
- mutex_unlock(&xef->exec_queue.lock);
-
- return q;
-}
-
-enum xe_exec_queue_priority
-xe_exec_queue_device_get_max_priority(struct xe_device *xe)
-{
- return capable(CAP_SYS_NICE) ? XE_EXEC_QUEUE_PRIORITY_HIGH :
- XE_EXEC_QUEUE_PRIORITY_NORMAL;
-}
-
-static int exec_queue_set_priority(struct xe_device *xe, struct xe_exec_queue *q,
- u64 value)
-{
- if (XE_IOCTL_DBG(xe, value > XE_EXEC_QUEUE_PRIORITY_HIGH))
- return -EINVAL;
-
- if (XE_IOCTL_DBG(xe, value > xe_exec_queue_device_get_max_priority(xe)))
- return -EPERM;
-
- q->sched_props.priority = value;
- return 0;
-}
-
-static bool xe_exec_queue_enforce_schedule_limit(void)
-{
-#if IS_ENABLED(CONFIG_DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT)
- return true;
-#else
- return !capable(CAP_SYS_NICE);
-#endif
-}
-
-static void
-xe_exec_queue_get_prop_minmax(struct xe_hw_engine_class_intf *eclass,
- enum xe_exec_queue_sched_prop prop,
- u32 *min, u32 *max)
-{
- switch (prop) {
- case XE_EXEC_QUEUE_JOB_TIMEOUT:
- *min = eclass->sched_props.job_timeout_min;
- *max = eclass->sched_props.job_timeout_max;
- break;
- case XE_EXEC_QUEUE_TIMESLICE:
- *min = eclass->sched_props.timeslice_min;
- *max = eclass->sched_props.timeslice_max;
- break;
- case XE_EXEC_QUEUE_PREEMPT_TIMEOUT:
- *min = eclass->sched_props.preempt_timeout_min;
- *max = eclass->sched_props.preempt_timeout_max;
- break;
- default:
- break;
- }
-#if IS_ENABLED(CONFIG_DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT)
- if (capable(CAP_SYS_NICE)) {
- switch (prop) {
- case XE_EXEC_QUEUE_JOB_TIMEOUT:
- *min = XE_HW_ENGINE_JOB_TIMEOUT_MIN;
- *max = XE_HW_ENGINE_JOB_TIMEOUT_MAX;
- break;
- case XE_EXEC_QUEUE_TIMESLICE:
- *min = XE_HW_ENGINE_TIMESLICE_MIN;
- *max = XE_HW_ENGINE_TIMESLICE_MAX;
- break;
- case XE_EXEC_QUEUE_PREEMPT_TIMEOUT:
- *min = XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN;
- *max = XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX;
- break;
- default:
- break;
- }
- }
-#endif
-}
-
-static int exec_queue_set_timeslice(struct xe_device *xe, struct xe_exec_queue *q,
- u64 value)
-{
- u32 min = 0, max = 0;
-
- xe_exec_queue_get_prop_minmax(q->hwe->eclass,
- XE_EXEC_QUEUE_TIMESLICE, &min, &max);
-
- if (xe_exec_queue_enforce_schedule_limit() &&
- !xe_hw_engine_timeout_in_range(value, min, max))
- return -EINVAL;
-
- q->sched_props.timeslice_us = value;
- return 0;
-}
-
-typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe,
- struct xe_exec_queue *q,
- u64 value);
-
-static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = {
- [DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY] = exec_queue_set_priority,
- [DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE] = exec_queue_set_timeslice,
-};
-
-static int exec_queue_user_ext_set_property(struct xe_device *xe,
- struct xe_exec_queue *q,
- u64 extension)
-{
- u64 __user *address = u64_to_user_ptr(extension);
- struct drm_xe_ext_set_property ext;
- int err;
- u32 idx;
-
- err = __copy_from_user(&ext, address, sizeof(ext));
- if (XE_IOCTL_DBG(xe, err))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, ext.property >=
- ARRAY_SIZE(exec_queue_set_property_funcs)) ||
- XE_IOCTL_DBG(xe, ext.pad) ||
- XE_IOCTL_DBG(xe, ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY &&
- ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE))
- return -EINVAL;
-
- idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs));
- if (!exec_queue_set_property_funcs[idx])
- return -EINVAL;
-
- return exec_queue_set_property_funcs[idx](xe, q, ext.value);
-}
-
-typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe,
- struct xe_exec_queue *q,
- u64 extension);
-
-static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = {
- [DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property,
-};
-
-#define MAX_USER_EXTENSIONS 16
-static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
- u64 extensions, int ext_number)
-{
- u64 __user *address = u64_to_user_ptr(extensions);
- struct drm_xe_user_extension ext;
- int err;
- u32 idx;
-
- if (XE_IOCTL_DBG(xe, ext_number >= MAX_USER_EXTENSIONS))
- return -E2BIG;
-
- err = __copy_from_user(&ext, address, sizeof(ext));
- if (XE_IOCTL_DBG(xe, err))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, ext.pad) ||
- XE_IOCTL_DBG(xe, ext.name >=
- ARRAY_SIZE(exec_queue_user_extension_funcs)))
- return -EINVAL;
-
- idx = array_index_nospec(ext.name,
- ARRAY_SIZE(exec_queue_user_extension_funcs));
- err = exec_queue_user_extension_funcs[idx](xe, q, extensions);
- if (XE_IOCTL_DBG(xe, err))
- return err;
-
- if (ext.next_extension)
- return exec_queue_user_extensions(xe, q, ext.next_extension,
- ++ext_number);
-
- return 0;
-}
-
-<<<<<<<
-=======
-static const enum xe_engine_class user_to_xe_engine_class[] = {
- [DRM_XE_ENGINE_CLASS_RENDER] = XE_ENGINE_CLASS_RENDER,
- [DRM_XE_ENGINE_CLASS_COPY] = XE_ENGINE_CLASS_COPY,
- [DRM_XE_ENGINE_CLASS_VIDEO_DECODE] = XE_ENGINE_CLASS_VIDEO_DECODE,
- [DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE] = XE_ENGINE_CLASS_VIDEO_ENHANCE,
- [DRM_XE_ENGINE_CLASS_COMPUTE] = XE_ENGINE_CLASS_COMPUTE,
-};
-
-static struct xe_hw_engine *
-find_hw_engine(struct xe_device *xe,
- struct drm_xe_engine_class_instance eci)
-{
- u32 idx;
-
- if (eci.engine_class >= ARRAY_SIZE(user_to_xe_engine_class))
- return NULL;
-
- if (eci.gt_id >= xe->info.gt_count)
- return NULL;
-
- idx = array_index_nospec(eci.engine_class,
- ARRAY_SIZE(user_to_xe_engine_class));
-
- return xe_gt_hw_engine(xe_device_get_gt(xe, eci.gt_id),
- user_to_xe_engine_class[idx],
- eci.engine_instance, true);
-}
-
-static u32 bind_exec_queue_logical_mask(struct xe_device *xe, struct xe_gt *gt,
- struct drm_xe_engine_class_instance *eci,
- u16 width, u16 num_placements)
-{
- struct xe_hw_engine *hwe;
- enum xe_hw_engine_id id;
- u32 logical_mask = 0;
-
- if (XE_IOCTL_DBG(xe, width != 1))
- return 0;
- if (XE_IOCTL_DBG(xe, num_placements != 1))
- return 0;
- if (XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
- return 0;
-
- eci[0].engine_class = DRM_XE_ENGINE_CLASS_COPY;
-
- for_each_hw_engine(hwe, gt, id) {
- if (xe_hw_engine_is_reserved(hwe))
- continue;
-
- if (hwe->class ==
- user_to_xe_engine_class[DRM_XE_ENGINE_CLASS_COPY])
- logical_mask |= BIT(hwe->logical_instance);
- }
-
- return logical_mask;
-}
-
->>>>>>>
-static u32 calc_validate_logical_mask(struct xe_device *xe, struct xe_gt *gt,
- struct drm_xe_engine_class_instance *eci,
- u16 width, u16 num_placements)
-{
- int len = width * num_placements;
- int i, j, n;
- u16 class;
- u16 gt_id;
- u32 return_mask = 0, prev_mask;
-
- if (XE_IOCTL_DBG(xe, !xe_device_uc_enabled(xe) &&
- len > 1))
- return 0;
-
- for (i = 0; i < width; ++i) {
- u32 current_mask = 0;
-
- for (j = 0; j < num_placements; ++j) {
- struct xe_hw_engine *hwe;
-
- n = j * width + i;
-
- hwe = find_hw_engine(xe, eci[n]);
- if (XE_IOCTL_DBG(xe, !hwe))
- return 0;
-
- if (XE_IOCTL_DBG(xe, xe_hw_engine_is_reserved(hwe)))
- return 0;
-
- if (XE_IOCTL_DBG(xe, n && eci[n].gt_id != gt_id) ||
- XE_IOCTL_DBG(xe, n && eci[n].engine_class != class))
- return 0;
-
- class = eci[n].engine_class;
- gt_id = eci[n].gt_id;
-
- if (width == 1 || !i)
- return_mask |= BIT(eci[n].engine_instance);
- current_mask |= BIT(eci[n].engine_instance);
- }
-
- /* Parallel submissions must be logically contiguous */
- if (i && XE_IOCTL_DBG(xe, current_mask != prev_mask << 1))
- return 0;
-
- prev_mask = current_mask;
- }
-
- return return_mask;
-}
-
-int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file)
-{
- struct xe_device *xe = to_xe_device(dev);
- struct xe_file *xef = to_xe_file(file);
- struct drm_xe_exec_queue_create *args = data;
- struct drm_xe_engine_class_instance eci[XE_HW_ENGINE_MAX_INSTANCE];
- struct drm_xe_engine_class_instance __user *user_eci =
- u64_to_user_ptr(args->instances);
- struct xe_hw_engine *hwe;
- struct xe_vm *vm;
- struct xe_gt *gt;
- struct xe_tile *tile;
- struct xe_exec_queue *q = NULL;
- u32 logical_mask;
- u32 id;
- u32 len;
- int err;
-
- if (XE_IOCTL_DBG(xe, args->flags) ||
- XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
- return -EINVAL;
-
- len = args->width * args->num_placements;
- if (XE_IOCTL_DBG(xe, !len || len > XE_HW_ENGINE_MAX_INSTANCE))
- return -EINVAL;
-
- err = __copy_from_user(eci, user_eci,
- sizeof(struct drm_xe_engine_class_instance) *
- len);
- if (XE_IOCTL_DBG(xe, err))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, eci[0].gt_id >= xe->info.gt_count))
- return -EINVAL;
-
- if (eci[0].engine_class == DRM_XE_ENGINE_CLASS_VM_BIND) {
- if (XE_IOCTL_DBG(xe, args->width != 1) ||
- XE_IOCTL_DBG(xe, args->num_placements != 1) ||
- XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
- return -EINVAL;
-
- for_each_tile(tile, xe, id) {
- struct xe_exec_queue *new;
- u32 flags = EXEC_QUEUE_FLAG_VM;
-
- if (id)
- flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD;
-
-<<<<<<<
- eci[0].gt_id = gt->info.id;
- logical_mask = bind_exec_queue_logical_mask(xe, gt, eci,
- args->width,
- args->num_placements);
- if (XE_IOCTL_DBG(xe, !logical_mask))
- return -EINVAL;
-
- hwe = find_hw_engine(xe, eci[0]);
- if (XE_IOCTL_DBG(xe, !hwe))
- return -EINVAL;
-
- /* The migration vm doesn't hold rpm ref */
- xe_pm_runtime_get_noresume(xe);
-
- flags = EXEC_QUEUE_FLAG_VM | (id ? EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD : 0);
-
- migrate_vm = xe_migrate_get_vm(gt_to_tile(gt)->migrate);
- new = xe_exec_queue_create(xe, migrate_vm, logical_mask,
- args->width, hwe, flags,
- args->extensions);
-
- xe_pm_runtime_put(xe); /* now held by engine */
-
- xe_vm_put(migrate_vm);
-=======
- new = xe_exec_queue_create_bind(xe, tile, flags,
- args->extensions);
->>>>>>>
- if (IS_ERR(new)) {
- err = PTR_ERR(new);
- if (q)
- goto put_exec_queue;
- return err;
- }
- if (id == 0)
- q = new;
- else
- list_add_tail(&new->multi_gt_list,
- &q->multi_gt_link);
- }
- } else {
- gt = xe_device_get_gt(xe, eci[0].gt_id);
- logical_mask = calc_validate_logical_mask(xe, gt, eci,
- args->width,
- args->num_placements);
- if (XE_IOCTL_DBG(xe, !logical_mask))
- return -EINVAL;
-
- hwe = find_hw_engine(xe, eci[0]);
- if (XE_IOCTL_DBG(xe, !hwe))
- return -EINVAL;
-
- vm = xe_vm_lookup(xef, args->vm_id);
- if (XE_IOCTL_DBG(xe, !vm))
- return -ENOENT;
-
- err = down_read_interruptible(&vm->lock);
- if (err) {
- xe_vm_put(vm);
- return err;
- }
-
- if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) {
- up_read(&vm->lock);
- xe_vm_put(vm);
- return -ENOENT;
- }
-
- q = xe_exec_queue_create(xe, vm, logical_mask,
- args->width, hwe, 0,
- args->extensions);
- up_read(&vm->lock);
- xe_vm_put(vm);
- if (IS_ERR(q))
- return PTR_ERR(q);
-
- if (xe_vm_in_preempt_fence_mode(vm)) {
- q->lr.context = dma_fence_context_alloc(1);
-
- err = xe_vm_add_compute_exec_queue(vm, q);
- if (XE_IOCTL_DBG(xe, err))
- goto put_exec_queue;
- }
- }
-
- mutex_lock(&xef->exec_queue.lock);
- err = xa_alloc(&xef->exec_queue.xa, &id, q, xa_limit_32b, GFP_KERNEL);
- mutex_unlock(&xef->exec_queue.lock);
- if (err)
- goto kill_exec_queue;
-
- args->exec_queue_id = id;
- q->xef = xe_file_get(xef);
-
- return 0;
-
-kill_exec_queue:
- xe_exec_queue_kill(q);
-put_exec_queue:
- xe_exec_queue_put(q);
- return err;
-}
-
-int xe_exec_queue_get_property_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file)
-{
- struct xe_device *xe = to_xe_device(dev);
- struct xe_file *xef = to_xe_file(file);
- struct drm_xe_exec_queue_get_property *args = data;
- struct xe_exec_queue *q;
- int ret;
-
- if (XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
- return -EINVAL;
-
- q = xe_exec_queue_lookup(xef, args->exec_queue_id);
- if (XE_IOCTL_DBG(xe, !q))
- return -ENOENT;
-
- switch (args->property) {
- case DRM_XE_EXEC_QUEUE_GET_PROPERTY_BAN:
- args->value = q->ops->reset_status(q);
- ret = 0;
- break;
- default:
- ret = -EINVAL;
- }
-
- xe_exec_queue_put(q);
-
- return ret;
-}
-
-/**
- * xe_exec_queue_is_lr() - Whether an exec_queue is long-running
- * @q: The exec_queue
- *
- * Return: True if the exec_queue is long-running, false otherwise.
- */
-bool xe_exec_queue_is_lr(struct xe_exec_queue *q)
-{
- return q->vm && xe_vm_in_lr_mode(q->vm) &&
- !(q->flags & EXEC_QUEUE_FLAG_VM);
-}
-
-static s32 xe_exec_queue_num_job_inflight(struct xe_exec_queue *q)
-{
- return q->lrc[0]->fence_ctx.next_seqno - xe_lrc_seqno(q->lrc[0]) - 1;
-}
-
-/**
- * xe_exec_queue_ring_full() - Whether an exec_queue's ring is full
- * @q: The exec_queue
- *
- * Return: True if the exec_queue's ring is full, false otherwise.
- */
-bool xe_exec_queue_ring_full(struct xe_exec_queue *q)
-{
- struct xe_lrc *lrc = q->lrc[0];
- s32 max_job = lrc->ring.size / MAX_JOB_SIZE_BYTES;
-
- return xe_exec_queue_num_job_inflight(q) >= max_job;
-}
-
-/**
- * xe_exec_queue_is_idle() - Whether an exec_queue is idle.
- * @q: The exec_queue
- *
- * FIXME: Need to determine what to use as the short-lived
- * timeline lock for the exec_queues, so that the return value
- * of this function becomes more than just an advisory
- * snapshot in time. The timeline lock must protect the
- * seqno from racing submissions on the same exec_queue.
- * Typically vm->resv, but user-created timeline locks use the migrate vm
- * and never grabs the migrate vm->resv so we have a race there.
- *
- * Return: True if the exec_queue is idle, false otherwise.
- */
-bool xe_exec_queue_is_idle(struct xe_exec_queue *q)
-{
- if (xe_exec_queue_is_parallel(q)) {
- int i;
-
- for (i = 0; i < q->width; ++i) {
- if (xe_lrc_seqno(q->lrc[i]) !=
- q->lrc[i]->fence_ctx.next_seqno - 1)
- return false;
- }
-
- return true;
- }
-
- return xe_lrc_seqno(q->lrc[0]) ==
- q->lrc[0]->fence_ctx.next_seqno - 1;
-}
-
-/**
- * xe_exec_queue_update_run_ticks() - Update run time in ticks for this exec queue
- * from hw
- * @q: The exec queue
- *
- * Update the timestamp saved by HW for this exec queue and save run ticks
- * calculated by using the delta from last update.
- */
-void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q)
-{
- struct xe_file *xef;
- struct xe_lrc *lrc;
- u32 old_ts, new_ts;
-
- /*
- * Jobs that are run during driver load may use an exec_queue, but are
- * not associated with a user xe file, so avoid accumulating busyness
- * for kernel specific work.
- */
- if (!q->vm || !q->vm->xef)
- return;
-
- xef = q->vm->xef;
-
- /*
- * Only sample the first LRC. For parallel submission, all of them are
- * scheduled together and we compensate that below by multiplying by
- * width - this may introduce errors if that premise is not true and
- * they don't exit 100% aligned. On the other hand, looping through
- * the LRCs and reading them in different time could also introduce
- * errors.
- */
- lrc = q->lrc[0];
- new_ts = xe_lrc_update_timestamp(lrc, &old_ts);
- xef->run_ticks[q->class] += (new_ts - old_ts) * q->width;
-}
-
-void xe_exec_queue_kill(struct xe_exec_queue *q)
-{
- struct xe_exec_queue *eq = q, *next;
-
- list_for_each_entry_safe(eq, next, &eq->multi_gt_list,
- multi_gt_link) {
- q->ops->kill(eq);
- xe_vm_remove_compute_exec_queue(q->vm, eq);
- }
-
- q->ops->kill(q);
- xe_vm_remove_compute_exec_queue(q->vm, q);
-}
-
-int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file)
-{
- struct xe_device *xe = to_xe_device(dev);
- struct xe_file *xef = to_xe_file(file);
- struct drm_xe_exec_queue_destroy *args = data;
- struct xe_exec_queue *q;
-
- if (XE_IOCTL_DBG(xe, args->pad) ||
- XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
- return -EINVAL;
-
- mutex_lock(&xef->exec_queue.lock);
- q = xa_erase(&xef->exec_queue.xa, args->exec_queue_id);
- mutex_unlock(&xef->exec_queue.lock);
- if (XE_IOCTL_DBG(xe, !q))
- return -ENOENT;
-
- xe_exec_queue_kill(q);
-
- trace_xe_exec_queue_close(q);
- xe_exec_queue_put(q);
-
- return 0;
-}
-
-static void xe_exec_queue_last_fence_lockdep_assert(struct xe_exec_queue *q,
- struct xe_vm *vm)
-{
- if (q->flags & EXEC_QUEUE_FLAG_VM)
- lockdep_assert_held(&vm->lock);
- else
- xe_vm_assert_held(vm);
-}
-
-/**
- * xe_exec_queue_last_fence_put() - Drop ref to last fence
- * @q: The exec queue
- * @vm: The VM the engine does a bind or exec for
- */
-void xe_exec_queue_last_fence_put(struct xe_exec_queue *q, struct xe_vm *vm)
-{
- xe_exec_queue_last_fence_lockdep_assert(q, vm);
-
- if (q->last_fence) {
- dma_fence_put(q->last_fence);
- q->last_fence = NULL;
- }
-}
-
-/**
- * xe_exec_queue_last_fence_put_unlocked() - Drop ref to last fence unlocked
- * @q: The exec queue
- *
- * Only safe to be called from xe_exec_queue_destroy().
- */
-void xe_exec_queue_last_fence_put_unlocked(struct xe_exec_queue *q)
-{
- if (q->last_fence) {
- dma_fence_put(q->last_fence);
- q->last_fence = NULL;
- }
-}
-
-/**
- * xe_exec_queue_last_fence_get() - Get last fence
- * @q: The exec queue
- * @vm: The VM the engine does a bind or exec for
- *
- * Get last fence, takes a ref
- *
- * Returns: last fence if not signaled, dma fence stub if signaled
- */
-struct dma_fence *xe_exec_queue_last_fence_get(struct xe_exec_queue *q,
- struct xe_vm *vm)
-{
- struct dma_fence *fence;
-
- xe_exec_queue_last_fence_lockdep_assert(q, vm);
-
- if (q->last_fence &&
- test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &q->last_fence->flags))
- xe_exec_queue_last_fence_put(q, vm);
-
- fence = q->last_fence ? q->last_fence : dma_fence_get_stub();
- dma_fence_get(fence);
- return fence;
-}
-
-/**
- * xe_exec_queue_last_fence_set() - Set last fence
- * @q: The exec queue
- * @vm: The VM the engine does a bind or exec for
- * @fence: The fence
- *
- * Set the last fence for the engine. Increases reference count for fence, when
- * closing engine xe_exec_queue_last_fence_put should be called.
- */
-void xe_exec_queue_last_fence_set(struct xe_exec_queue *q, struct xe_vm *vm,
- struct dma_fence *fence)
-{
- xe_exec_queue_last_fence_lockdep_assert(q, vm);
-
- xe_exec_queue_last_fence_put(q, vm);
- q->last_fence = dma_fence_get(fence);
-}
diff --git a/rr-cache/6e989852f5454d81ebf331bbd2c55116dc711575/preimage.3 b/rr-cache/6e989852f5454d81ebf331bbd2c55116dc711575/preimage.3
deleted file mode 100644
index 42ffb0f8cccc..000000000000
--- a/rr-cache/6e989852f5454d81ebf331bbd2c55116dc711575/preimage.3
+++ /dev/null
@@ -1,982 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-#include "xe_exec_queue.h"
-
-#include <linux/nospec.h>
-
-#include <drm/drm_device.h>
-#include <drm/drm_file.h>
-#include <drm/xe_drm.h>
-
-#include "xe_device.h"
-#include "xe_gt.h"
-#include "xe_hw_engine_class_sysfs.h"
-#include "xe_hw_fence.h"
-#include "xe_lrc.h"
-#include "xe_macros.h"
-#include "xe_migrate.h"
-#include "xe_pm.h"
-#include "xe_ring_ops_types.h"
-#include "xe_trace.h"
-#include "xe_vm.h"
-
-enum xe_exec_queue_sched_prop {
- XE_EXEC_QUEUE_JOB_TIMEOUT = 0,
- XE_EXEC_QUEUE_TIMESLICE = 1,
- XE_EXEC_QUEUE_PREEMPT_TIMEOUT = 2,
- XE_EXEC_QUEUE_SCHED_PROP_MAX = 3,
-};
-
-static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
- u64 extensions, int ext_number);
-
-static void __xe_exec_queue_free(struct xe_exec_queue *q)
-{
- if (q->vm)
- xe_vm_put(q->vm);
-
- if (q->xef)
- xe_file_put(q->xef);
-
- kfree(q);
-}
-
-static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
- struct xe_vm *vm,
- u32 logical_mask,
- u16 width, struct xe_hw_engine *hwe,
- u32 flags, u64 extensions)
-{
- struct xe_exec_queue *q;
- struct xe_gt *gt = hwe->gt;
- int err;
-
- /* only kernel queues can be permanent */
- XE_WARN_ON((flags & EXEC_QUEUE_FLAG_PERMANENT) && !(flags & EXEC_QUEUE_FLAG_KERNEL));
-
- q = kzalloc(struct_size(q, lrc, width), GFP_KERNEL);
- if (!q)
- return ERR_PTR(-ENOMEM);
-
- kref_init(&q->refcount);
- q->flags = flags;
- q->hwe = hwe;
- q->gt = gt;
- q->class = hwe->class;
- q->width = width;
- q->logical_mask = logical_mask;
- q->fence_irq = &gt->fence_irq[hwe->class];
- q->ring_ops = gt->ring_ops[hwe->class];
- q->ops = gt->exec_queue_ops;
- INIT_LIST_HEAD(&q->lr.link);
- INIT_LIST_HEAD(&q->multi_gt_link);
-
- q->sched_props.timeslice_us = hwe->eclass->sched_props.timeslice_us;
- q->sched_props.preempt_timeout_us =
- hwe->eclass->sched_props.preempt_timeout_us;
- q->sched_props.job_timeout_ms =
- hwe->eclass->sched_props.job_timeout_ms;
- if (q->flags & EXEC_QUEUE_FLAG_KERNEL &&
- q->flags & EXEC_QUEUE_FLAG_HIGH_PRIORITY)
- q->sched_props.priority = XE_EXEC_QUEUE_PRIORITY_KERNEL;
- else
- q->sched_props.priority = XE_EXEC_QUEUE_PRIORITY_NORMAL;
-
- if (vm)
- q->vm = xe_vm_get(vm);
-
- if (extensions) {
- /*
- * may set q->usm, must come before xe_lrc_create(),
- * may overwrite q->sched_props, must come before q->ops->init()
- */
- err = exec_queue_user_extensions(xe, q, extensions, 0);
- if (err) {
- __xe_exec_queue_free(q);
- return ERR_PTR(err);
- }
- }
-
- return q;
-}
-
-static int __xe_exec_queue_init(struct xe_exec_queue *q)
-{
- struct xe_vm *vm = q->vm;
- int i, err;
-
- if (vm) {
- err = xe_vm_lock(vm, true);
- if (err)
- return err;
- }
-
- for (i = 0; i < q->width; ++i) {
- q->lrc[i] = xe_lrc_create(q->hwe, q->vm, SZ_16K);
- if (IS_ERR(q->lrc[i])) {
- err = PTR_ERR(q->lrc[i]);
- goto err_unlock;
- }
- }
-
- if (vm)
- xe_vm_unlock(vm);
-
- err = q->ops->init(q);
- if (err)
- goto err_lrc;
-
- return 0;
-
-err_unlock:
- if (vm)
- xe_vm_unlock(vm);
-err_lrc:
- for (i = i - 1; i >= 0; --i)
- xe_lrc_put(q->lrc[i]);
- return err;
-}
-
-struct xe_exec_queue *xe_exec_queue_create(struct xe_device *xe, struct xe_vm *vm,
- u32 logical_mask, u16 width,
- struct xe_hw_engine *hwe, u32 flags,
- u64 extensions)
-{
- struct xe_exec_queue *q;
- int err;
-
- q = __xe_exec_queue_alloc(xe, vm, logical_mask, width, hwe, flags,
- extensions);
- if (IS_ERR(q))
- return q;
-
- err = __xe_exec_queue_init(q);
- if (err)
- goto err_post_alloc;
-
- return q;
-
-err_post_alloc:
- __xe_exec_queue_free(q);
- return ERR_PTR(err);
-}
-
-struct xe_exec_queue *xe_exec_queue_create_class(struct xe_device *xe, struct xe_gt *gt,
- struct xe_vm *vm,
- enum xe_engine_class class,
- u32 flags, u64 extensions)
-{
- struct xe_hw_engine *hwe, *hwe0 = NULL;
- enum xe_hw_engine_id id;
- u32 logical_mask = 0;
-
- for_each_hw_engine(hwe, gt, id) {
- if (xe_hw_engine_is_reserved(hwe))
- continue;
-
- if (hwe->class == class) {
- logical_mask |= BIT(hwe->logical_instance);
- if (!hwe0)
- hwe0 = hwe;
- }
- }
-
- if (!logical_mask)
- return ERR_PTR(-ENODEV);
-
- return xe_exec_queue_create(xe, vm, logical_mask, 1, hwe0, flags, extensions);
-}
-
-/**
- * xe_exec_queue_create_bind() - Create bind exec queue.
- * @xe: Xe device.
- * @tile: tile which bind exec queue belongs to.
- * @flags: exec queue creation flags
- * @extensions: exec queue creation extensions
- *
- * Normalize bind exec queue creation. Bind exec queue is tied to migration VM
- * for access to physical memory required for page table programming. On a
- * faulting devices the reserved copy engine instance must be used to avoid
- * deadlocking (user binds cannot get stuck behind faults as kernel binds which
- * resolve faults depend on user binds). On non-faulting devices any copy engine
- * can be used.
- *
- * Returns exec queue on success, ERR_PTR on failure
- */
-struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
- struct xe_tile *tile,
- u32 flags, u64 extensions)
-{
- struct xe_gt *gt = tile->primary_gt;
- struct xe_exec_queue *q;
- struct xe_vm *migrate_vm;
-
- migrate_vm = xe_migrate_get_vm(tile->migrate);
- if (xe->info.has_usm) {
- struct xe_hw_engine *hwe = xe_gt_hw_engine(gt,
- XE_ENGINE_CLASS_COPY,
- gt->usm.reserved_bcs_instance,
- false);
-
- if (!hwe)
- return ERR_PTR(-EINVAL);
-
- q = xe_exec_queue_create(xe, migrate_vm,
- BIT(hwe->logical_instance), 1, hwe,
- flags, extensions);
- } else {
- q = xe_exec_queue_create_class(xe, gt, migrate_vm,
- XE_ENGINE_CLASS_COPY, flags,
- extensions);
- }
- xe_vm_put(migrate_vm);
-
- return q;
-}
-
-void xe_exec_queue_destroy(struct kref *ref)
-{
- struct xe_exec_queue *q = container_of(ref, struct xe_exec_queue, refcount);
- struct xe_exec_queue *eq, *next;
-
- xe_exec_queue_last_fence_put_unlocked(q);
- if (!(q->flags & EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD)) {
- list_for_each_entry_safe(eq, next, &q->multi_gt_list,
- multi_gt_link)
- xe_exec_queue_put(eq);
- }
-
- q->ops->fini(q);
-}
-
-void xe_exec_queue_fini(struct xe_exec_queue *q)
-{
- int i;
-
- for (i = 0; i < q->width; ++i)
- xe_lrc_put(q->lrc[i]);
- __xe_exec_queue_free(q);
-}
-
-void xe_exec_queue_assign_name(struct xe_exec_queue *q, u32 instance)
-{
- switch (q->class) {
- case XE_ENGINE_CLASS_RENDER:
- snprintf(q->name, sizeof(q->name), "rcs%d", instance);
- break;
- case XE_ENGINE_CLASS_VIDEO_DECODE:
- snprintf(q->name, sizeof(q->name), "vcs%d", instance);
- break;
- case XE_ENGINE_CLASS_VIDEO_ENHANCE:
- snprintf(q->name, sizeof(q->name), "vecs%d", instance);
- break;
- case XE_ENGINE_CLASS_COPY:
- snprintf(q->name, sizeof(q->name), "bcs%d", instance);
- break;
- case XE_ENGINE_CLASS_COMPUTE:
- snprintf(q->name, sizeof(q->name), "ccs%d", instance);
- break;
- case XE_ENGINE_CLASS_OTHER:
- snprintf(q->name, sizeof(q->name), "gsccs%d", instance);
- break;
- default:
- XE_WARN_ON(q->class);
- }
-}
-
-struct xe_exec_queue *xe_exec_queue_lookup(struct xe_file *xef, u32 id)
-{
- struct xe_exec_queue *q;
-
- mutex_lock(&xef->exec_queue.lock);
- q = xa_load(&xef->exec_queue.xa, id);
- if (q)
- xe_exec_queue_get(q);
- mutex_unlock(&xef->exec_queue.lock);
-
- return q;
-}
-
-enum xe_exec_queue_priority
-xe_exec_queue_device_get_max_priority(struct xe_device *xe)
-{
- return capable(CAP_SYS_NICE) ? XE_EXEC_QUEUE_PRIORITY_HIGH :
- XE_EXEC_QUEUE_PRIORITY_NORMAL;
-}
-
-static int exec_queue_set_priority(struct xe_device *xe, struct xe_exec_queue *q,
- u64 value)
-{
- if (XE_IOCTL_DBG(xe, value > XE_EXEC_QUEUE_PRIORITY_HIGH))
- return -EINVAL;
-
- if (XE_IOCTL_DBG(xe, value > xe_exec_queue_device_get_max_priority(xe)))
- return -EPERM;
-
- q->sched_props.priority = value;
- return 0;
-}
-
-static bool xe_exec_queue_enforce_schedule_limit(void)
-{
-#if IS_ENABLED(CONFIG_DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT)
- return true;
-#else
- return !capable(CAP_SYS_NICE);
-#endif
-}
-
-static void
-xe_exec_queue_get_prop_minmax(struct xe_hw_engine_class_intf *eclass,
- enum xe_exec_queue_sched_prop prop,
- u32 *min, u32 *max)
-{
- switch (prop) {
- case XE_EXEC_QUEUE_JOB_TIMEOUT:
- *min = eclass->sched_props.job_timeout_min;
- *max = eclass->sched_props.job_timeout_max;
- break;
- case XE_EXEC_QUEUE_TIMESLICE:
- *min = eclass->sched_props.timeslice_min;
- *max = eclass->sched_props.timeslice_max;
- break;
- case XE_EXEC_QUEUE_PREEMPT_TIMEOUT:
- *min = eclass->sched_props.preempt_timeout_min;
- *max = eclass->sched_props.preempt_timeout_max;
- break;
- default:
- break;
- }
-#if IS_ENABLED(CONFIG_DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT)
- if (capable(CAP_SYS_NICE)) {
- switch (prop) {
- case XE_EXEC_QUEUE_JOB_TIMEOUT:
- *min = XE_HW_ENGINE_JOB_TIMEOUT_MIN;
- *max = XE_HW_ENGINE_JOB_TIMEOUT_MAX;
- break;
- case XE_EXEC_QUEUE_TIMESLICE:
- *min = XE_HW_ENGINE_TIMESLICE_MIN;
- *max = XE_HW_ENGINE_TIMESLICE_MAX;
- break;
- case XE_EXEC_QUEUE_PREEMPT_TIMEOUT:
- *min = XE_HW_ENGINE_PREEMPT_TIMEOUT_MIN;
- *max = XE_HW_ENGINE_PREEMPT_TIMEOUT_MAX;
- break;
- default:
- break;
- }
- }
-#endif
-}
-
-static int exec_queue_set_timeslice(struct xe_device *xe, struct xe_exec_queue *q,
- u64 value)
-{
- u32 min = 0, max = 0;
-
- xe_exec_queue_get_prop_minmax(q->hwe->eclass,
- XE_EXEC_QUEUE_TIMESLICE, &min, &max);
-
- if (xe_exec_queue_enforce_schedule_limit() &&
- !xe_hw_engine_timeout_in_range(value, min, max))
- return -EINVAL;
-
- q->sched_props.timeslice_us = value;
- return 0;
-}
-
-typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe,
- struct xe_exec_queue *q,
- u64 value);
-
-static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = {
- [DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY] = exec_queue_set_priority,
- [DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE] = exec_queue_set_timeslice,
-};
-
-static int exec_queue_user_ext_set_property(struct xe_device *xe,
- struct xe_exec_queue *q,
- u64 extension)
-{
- u64 __user *address = u64_to_user_ptr(extension);
- struct drm_xe_ext_set_property ext;
- int err;
- u32 idx;
-
- err = __copy_from_user(&ext, address, sizeof(ext));
- if (XE_IOCTL_DBG(xe, err))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, ext.property >=
- ARRAY_SIZE(exec_queue_set_property_funcs)) ||
- XE_IOCTL_DBG(xe, ext.pad) ||
- XE_IOCTL_DBG(xe, ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PRIORITY &&
- ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE))
- return -EINVAL;
-
- idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs));
- if (!exec_queue_set_property_funcs[idx])
- return -EINVAL;
-
- return exec_queue_set_property_funcs[idx](xe, q, ext.value);
-}
-
-typedef int (*xe_exec_queue_user_extension_fn)(struct xe_device *xe,
- struct xe_exec_queue *q,
- u64 extension);
-
-static const xe_exec_queue_user_extension_fn exec_queue_user_extension_funcs[] = {
- [DRM_XE_EXEC_QUEUE_EXTENSION_SET_PROPERTY] = exec_queue_user_ext_set_property,
-};
-
-#define MAX_USER_EXTENSIONS 16
-static int exec_queue_user_extensions(struct xe_device *xe, struct xe_exec_queue *q,
- u64 extensions, int ext_number)
-{
- u64 __user *address = u64_to_user_ptr(extensions);
- struct drm_xe_user_extension ext;
- int err;
- u32 idx;
-
- if (XE_IOCTL_DBG(xe, ext_number >= MAX_USER_EXTENSIONS))
- return -E2BIG;
-
- err = __copy_from_user(&ext, address, sizeof(ext));
- if (XE_IOCTL_DBG(xe, err))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, ext.pad) ||
- XE_IOCTL_DBG(xe, ext.name >=
- ARRAY_SIZE(exec_queue_user_extension_funcs)))
- return -EINVAL;
-
- idx = array_index_nospec(ext.name,
- ARRAY_SIZE(exec_queue_user_extension_funcs));
- err = exec_queue_user_extension_funcs[idx](xe, q, extensions);
- if (XE_IOCTL_DBG(xe, err))
- return err;
-
- if (ext.next_extension)
- return exec_queue_user_extensions(xe, q, ext.next_extension,
- ++ext_number);
-
- return 0;
-}
-
-<<<<<<<
-=======
-static const enum xe_engine_class user_to_xe_engine_class[] = {
- [DRM_XE_ENGINE_CLASS_RENDER] = XE_ENGINE_CLASS_RENDER,
- [DRM_XE_ENGINE_CLASS_COPY] = XE_ENGINE_CLASS_COPY,
- [DRM_XE_ENGINE_CLASS_VIDEO_DECODE] = XE_ENGINE_CLASS_VIDEO_DECODE,
- [DRM_XE_ENGINE_CLASS_VIDEO_ENHANCE] = XE_ENGINE_CLASS_VIDEO_ENHANCE,
- [DRM_XE_ENGINE_CLASS_COMPUTE] = XE_ENGINE_CLASS_COMPUTE,
-};
-
-static struct xe_hw_engine *
-find_hw_engine(struct xe_device *xe,
- struct drm_xe_engine_class_instance eci)
-{
- u32 idx;
-
- if (eci.engine_class >= ARRAY_SIZE(user_to_xe_engine_class))
- return NULL;
-
- if (eci.gt_id >= xe->info.gt_count)
- return NULL;
-
- idx = array_index_nospec(eci.engine_class,
- ARRAY_SIZE(user_to_xe_engine_class));
-
- return xe_gt_hw_engine(xe_device_get_gt(xe, eci.gt_id),
- user_to_xe_engine_class[idx],
- eci.engine_instance, true);
-}
-
-static u32 bind_exec_queue_logical_mask(struct xe_device *xe, struct xe_gt *gt,
- struct drm_xe_engine_class_instance *eci,
- u16 width, u16 num_placements)
-{
- struct xe_hw_engine *hwe;
- enum xe_hw_engine_id id;
- u32 logical_mask = 0;
-
- if (XE_IOCTL_DBG(xe, width != 1))
- return 0;
- if (XE_IOCTL_DBG(xe, num_placements != 1))
- return 0;
- if (XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
- return 0;
-
- eci[0].engine_class = DRM_XE_ENGINE_CLASS_COPY;
-
- for_each_hw_engine(hwe, gt, id) {
- if (xe_hw_engine_is_reserved(hwe))
- continue;
-
- if (hwe->class ==
- user_to_xe_engine_class[DRM_XE_ENGINE_CLASS_COPY])
- logical_mask |= BIT(hwe->logical_instance);
- }
-
- return logical_mask;
-}
-
->>>>>>>
-static u32 calc_validate_logical_mask(struct xe_device *xe, struct xe_gt *gt,
- struct drm_xe_engine_class_instance *eci,
- u16 width, u16 num_placements)
-{
- int len = width * num_placements;
- int i, j, n;
- u16 class;
- u16 gt_id;
- u32 return_mask = 0, prev_mask;
-
- if (XE_IOCTL_DBG(xe, !xe_device_uc_enabled(xe) &&
- len > 1))
- return 0;
-
- for (i = 0; i < width; ++i) {
- u32 current_mask = 0;
-
- for (j = 0; j < num_placements; ++j) {
- struct xe_hw_engine *hwe;
-
- n = j * width + i;
-
- hwe = find_hw_engine(xe, eci[n]);
- if (XE_IOCTL_DBG(xe, !hwe))
- return 0;
-
- if (XE_IOCTL_DBG(xe, xe_hw_engine_is_reserved(hwe)))
- return 0;
-
- if (XE_IOCTL_DBG(xe, n && eci[n].gt_id != gt_id) ||
- XE_IOCTL_DBG(xe, n && eci[n].engine_class != class))
- return 0;
-
- class = eci[n].engine_class;
- gt_id = eci[n].gt_id;
-
- if (width == 1 || !i)
- return_mask |= BIT(eci[n].engine_instance);
- current_mask |= BIT(eci[n].engine_instance);
- }
-
- /* Parallel submissions must be logically contiguous */
- if (i && XE_IOCTL_DBG(xe, current_mask != prev_mask << 1))
- return 0;
-
- prev_mask = current_mask;
- }
-
- return return_mask;
-}
-
-int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file)
-{
- struct xe_device *xe = to_xe_device(dev);
- struct xe_file *xef = to_xe_file(file);
- struct drm_xe_exec_queue_create *args = data;
- struct drm_xe_engine_class_instance eci[XE_HW_ENGINE_MAX_INSTANCE];
- struct drm_xe_engine_class_instance __user *user_eci =
- u64_to_user_ptr(args->instances);
- struct xe_hw_engine *hwe;
- struct xe_vm *vm;
- struct xe_gt *gt;
- struct xe_tile *tile;
- struct xe_exec_queue *q = NULL;
- u32 logical_mask;
- u32 id;
- u32 len;
- int err;
-
- if (XE_IOCTL_DBG(xe, args->flags) ||
- XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
- return -EINVAL;
-
- len = args->width * args->num_placements;
- if (XE_IOCTL_DBG(xe, !len || len > XE_HW_ENGINE_MAX_INSTANCE))
- return -EINVAL;
-
- err = __copy_from_user(eci, user_eci,
- sizeof(struct drm_xe_engine_class_instance) *
- len);
- if (XE_IOCTL_DBG(xe, err))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, eci[0].gt_id >= xe->info.gt_count))
- return -EINVAL;
-
- if (eci[0].engine_class == DRM_XE_ENGINE_CLASS_VM_BIND) {
- if (XE_IOCTL_DBG(xe, args->width != 1) ||
- XE_IOCTL_DBG(xe, args->num_placements != 1) ||
- XE_IOCTL_DBG(xe, eci[0].engine_instance != 0))
- return -EINVAL;
-
- for_each_tile(tile, xe, id) {
- struct xe_exec_queue *new;
- u32 flags = EXEC_QUEUE_FLAG_VM;
-
- if (id)
- flags |= EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD;
-
-<<<<<<<
- eci[0].gt_id = gt->info.id;
- logical_mask = bind_exec_queue_logical_mask(xe, gt, eci,
- args->width,
- args->num_placements);
- if (XE_IOCTL_DBG(xe, !logical_mask))
- return -EINVAL;
-
- hwe = find_hw_engine(xe, eci[0]);
- if (XE_IOCTL_DBG(xe, !hwe))
- return -EINVAL;
-
- /* The migration vm doesn't hold rpm ref */
- xe_pm_runtime_get_noresume(xe);
-
- flags = EXEC_QUEUE_FLAG_VM | (id ? EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD : 0);
-
- migrate_vm = xe_migrate_get_vm(gt_to_tile(gt)->migrate);
- new = xe_exec_queue_create(xe, migrate_vm, logical_mask,
- args->width, hwe, flags,
- args->extensions);
-
- xe_pm_runtime_put(xe); /* now held by engine */
-
- xe_vm_put(migrate_vm);
-=======
- new = xe_exec_queue_create_bind(xe, tile, flags,
- args->extensions);
->>>>>>>
- if (IS_ERR(new)) {
- err = PTR_ERR(new);
- if (q)
- goto put_exec_queue;
- return err;
- }
- if (id == 0)
- q = new;
- else
- list_add_tail(&new->multi_gt_list,
- &q->multi_gt_link);
- }
- } else {
- gt = xe_device_get_gt(xe, eci[0].gt_id);
- logical_mask = calc_validate_logical_mask(xe, gt, eci,
- args->width,
- args->num_placements);
- if (XE_IOCTL_DBG(xe, !logical_mask))
- return -EINVAL;
-
- hwe = find_hw_engine(xe, eci[0]);
- if (XE_IOCTL_DBG(xe, !hwe))
- return -EINVAL;
-
- vm = xe_vm_lookup(xef, args->vm_id);
- if (XE_IOCTL_DBG(xe, !vm))
- return -ENOENT;
-
- err = down_read_interruptible(&vm->lock);
- if (err) {
- xe_vm_put(vm);
- return err;
- }
-
- if (XE_IOCTL_DBG(xe, xe_vm_is_closed_or_banned(vm))) {
- up_read(&vm->lock);
- xe_vm_put(vm);
- return -ENOENT;
- }
-
- q = xe_exec_queue_create(xe, vm, logical_mask,
- args->width, hwe, 0,
- args->extensions);
- up_read(&vm->lock);
- xe_vm_put(vm);
- if (IS_ERR(q))
- return PTR_ERR(q);
-
- if (xe_vm_in_preempt_fence_mode(vm)) {
- q->lr.context = dma_fence_context_alloc(1);
-
- err = xe_vm_add_compute_exec_queue(vm, q);
- if (XE_IOCTL_DBG(xe, err))
- goto put_exec_queue;
- }
- }
-
- mutex_lock(&xef->exec_queue.lock);
- err = xa_alloc(&xef->exec_queue.xa, &id, q, xa_limit_32b, GFP_KERNEL);
- mutex_unlock(&xef->exec_queue.lock);
- if (err)
- goto kill_exec_queue;
-
- args->exec_queue_id = id;
- q->xef = xe_file_get(xef);
-
- return 0;
-
-kill_exec_queue:
- xe_exec_queue_kill(q);
-put_exec_queue:
- xe_exec_queue_put(q);
- return err;
-}
-
-int xe_exec_queue_get_property_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file)
-{
- struct xe_device *xe = to_xe_device(dev);
- struct xe_file *xef = to_xe_file(file);
- struct drm_xe_exec_queue_get_property *args = data;
- struct xe_exec_queue *q;
- int ret;
-
- if (XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
- return -EINVAL;
-
- q = xe_exec_queue_lookup(xef, args->exec_queue_id);
- if (XE_IOCTL_DBG(xe, !q))
- return -ENOENT;
-
- switch (args->property) {
- case DRM_XE_EXEC_QUEUE_GET_PROPERTY_BAN:
- args->value = q->ops->reset_status(q);
- ret = 0;
- break;
- default:
- ret = -EINVAL;
- }
-
- xe_exec_queue_put(q);
-
- return ret;
-}
-
-/**
- * xe_exec_queue_is_lr() - Whether an exec_queue is long-running
- * @q: The exec_queue
- *
- * Return: True if the exec_queue is long-running, false otherwise.
- */
-bool xe_exec_queue_is_lr(struct xe_exec_queue *q)
-{
- return q->vm && xe_vm_in_lr_mode(q->vm) &&
- !(q->flags & EXEC_QUEUE_FLAG_VM);
-}
-
-static s32 xe_exec_queue_num_job_inflight(struct xe_exec_queue *q)
-{
- return q->lrc[0]->fence_ctx.next_seqno - xe_lrc_seqno(q->lrc[0]) - 1;
-}
-
-/**
- * xe_exec_queue_ring_full() - Whether an exec_queue's ring is full
- * @q: The exec_queue
- *
- * Return: True if the exec_queue's ring is full, false otherwise.
- */
-bool xe_exec_queue_ring_full(struct xe_exec_queue *q)
-{
- struct xe_lrc *lrc = q->lrc[0];
- s32 max_job = lrc->ring.size / MAX_JOB_SIZE_BYTES;
-
- return xe_exec_queue_num_job_inflight(q) >= max_job;
-}
-
-/**
- * xe_exec_queue_is_idle() - Whether an exec_queue is idle.
- * @q: The exec_queue
- *
- * FIXME: Need to determine what to use as the short-lived
- * timeline lock for the exec_queues, so that the return value
- * of this function becomes more than just an advisory
- * snapshot in time. The timeline lock must protect the
- * seqno from racing submissions on the same exec_queue.
- * Typically vm->resv, but user-created timeline locks use the migrate vm
- * and never grabs the migrate vm->resv so we have a race there.
- *
- * Return: True if the exec_queue is idle, false otherwise.
- */
-bool xe_exec_queue_is_idle(struct xe_exec_queue *q)
-{
- if (xe_exec_queue_is_parallel(q)) {
- int i;
-
- for (i = 0; i < q->width; ++i) {
- if (xe_lrc_seqno(q->lrc[i]) !=
- q->lrc[i]->fence_ctx.next_seqno - 1)
- return false;
- }
-
- return true;
- }
-
- return xe_lrc_seqno(q->lrc[0]) ==
- q->lrc[0]->fence_ctx.next_seqno - 1;
-}
-
-/**
- * xe_exec_queue_update_run_ticks() - Update run time in ticks for this exec queue
- * from hw
- * @q: The exec queue
- *
- * Update the timestamp saved by HW for this exec queue and save run ticks
- * calculated by using the delta from last update.
- */
-void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q)
-{
- struct xe_file *xef;
- struct xe_lrc *lrc;
- u32 old_ts, new_ts;
-
- /*
- * Jobs that are run during driver load may use an exec_queue, but are
- * not associated with a user xe file, so avoid accumulating busyness
- * for kernel specific work.
- */
- if (!q->vm || !q->vm->xef)
- return;
-
- xef = q->vm->xef;
-
- /*
- * Only sample the first LRC. For parallel submission, all of them are
- * scheduled together and we compensate that below by multiplying by
- * width - this may introduce errors if that premise is not true and
- * they don't exit 100% aligned. On the other hand, looping through
- * the LRCs and reading them in different time could also introduce
- * errors.
- */
- lrc = q->lrc[0];
- new_ts = xe_lrc_update_timestamp(lrc, &old_ts);
- xef->run_ticks[q->class] += (new_ts - old_ts) * q->width;
-}
-
-void xe_exec_queue_kill(struct xe_exec_queue *q)
-{
- struct xe_exec_queue *eq = q, *next;
-
- list_for_each_entry_safe(eq, next, &eq->multi_gt_list,
- multi_gt_link) {
- q->ops->kill(eq);
- xe_vm_remove_compute_exec_queue(q->vm, eq);
- }
-
- q->ops->kill(q);
- xe_vm_remove_compute_exec_queue(q->vm, q);
-}
-
-int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file)
-{
- struct xe_device *xe = to_xe_device(dev);
- struct xe_file *xef = to_xe_file(file);
- struct drm_xe_exec_queue_destroy *args = data;
- struct xe_exec_queue *q;
-
- if (XE_IOCTL_DBG(xe, args->pad) ||
- XE_IOCTL_DBG(xe, args->reserved[0] || args->reserved[1]))
- return -EINVAL;
-
- mutex_lock(&xef->exec_queue.lock);
- q = xa_erase(&xef->exec_queue.xa, args->exec_queue_id);
- mutex_unlock(&xef->exec_queue.lock);
- if (XE_IOCTL_DBG(xe, !q))
- return -ENOENT;
-
- xe_exec_queue_kill(q);
-
- trace_xe_exec_queue_close(q);
- xe_exec_queue_put(q);
-
- return 0;
-}
-
-static void xe_exec_queue_last_fence_lockdep_assert(struct xe_exec_queue *q,
- struct xe_vm *vm)
-{
- if (q->flags & EXEC_QUEUE_FLAG_VM)
- lockdep_assert_held(&vm->lock);
- else
- xe_vm_assert_held(vm);
-}
-
-/**
- * xe_exec_queue_last_fence_put() - Drop ref to last fence
- * @q: The exec queue
- * @vm: The VM the engine does a bind or exec for
- */
-void xe_exec_queue_last_fence_put(struct xe_exec_queue *q, struct xe_vm *vm)
-{
- xe_exec_queue_last_fence_lockdep_assert(q, vm);
-
- if (q->last_fence) {
- dma_fence_put(q->last_fence);
- q->last_fence = NULL;
- }
-}
-
-/**
- * xe_exec_queue_last_fence_put_unlocked() - Drop ref to last fence unlocked
- * @q: The exec queue
- *
- * Only safe to be called from xe_exec_queue_destroy().
- */
-void xe_exec_queue_last_fence_put_unlocked(struct xe_exec_queue *q)
-{
- if (q->last_fence) {
- dma_fence_put(q->last_fence);
- q->last_fence = NULL;
- }
-}
-
-/**
- * xe_exec_queue_last_fence_get() - Get last fence
- * @q: The exec queue
- * @vm: The VM the engine does a bind or exec for
- *
- * Get last fence, takes a ref
- *
- * Returns: last fence if not signaled, dma fence stub if signaled
- */
-struct dma_fence *xe_exec_queue_last_fence_get(struct xe_exec_queue *q,
- struct xe_vm *vm)
-{
- struct dma_fence *fence;
-
- xe_exec_queue_last_fence_lockdep_assert(q, vm);
-
- if (q->last_fence &&
- test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &q->last_fence->flags))
- xe_exec_queue_last_fence_put(q, vm);
-
- fence = q->last_fence ? q->last_fence : dma_fence_get_stub();
- dma_fence_get(fence);
- return fence;
-}
-
-/**
- * xe_exec_queue_last_fence_set() - Set last fence
- * @q: The exec queue
- * @vm: The VM the engine does a bind or exec for
- * @fence: The fence
- *
- * Set the last fence for the engine. Increases reference count for fence, when
- * closing engine xe_exec_queue_last_fence_put should be called.
- */
-void xe_exec_queue_last_fence_set(struct xe_exec_queue *q, struct xe_vm *vm,
- struct dma_fence *fence)
-{
- xe_exec_queue_last_fence_lockdep_assert(q, vm);
-
- xe_exec_queue_last_fence_put(q, vm);
- q->last_fence = dma_fence_get(fence);
-}
diff --git a/rr-cache/882012b4af13c77de852328870770019afd5505e/postimage b/rr-cache/882012b4af13c77de852328870770019afd5505e/postimage
deleted file mode 100644
index 8343804ce3f8..000000000000
--- a/rr-cache/882012b4af13c77de852328870770019afd5505e/postimage
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2019 Intel Corporation
- */
-
-#ifndef __INTEL_DP_MST_H__
-#define __INTEL_DP_MST_H__
-
-#include <linux/types.h>
-
-struct intel_atomic_state;
-struct intel_crtc;
-struct intel_crtc_state;
-struct intel_digital_port;
-struct intel_dp;
-struct intel_link_bw_limits;
-
-int intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_id);
-void intel_dp_mst_encoder_cleanup(struct intel_digital_port *dig_port);
-int intel_dp_mst_encoder_active_links(struct intel_digital_port *dig_port);
-bool intel_dp_mst_is_master_trans(const struct intel_crtc_state *crtc_state);
-bool intel_dp_mst_is_slave_trans(const struct intel_crtc_state *crtc_state);
-bool intel_dp_mst_source_support(struct intel_dp *intel_dp);
-int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state *state,
- struct intel_crtc *crtc);
-int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
- struct intel_link_bw_limits *limits);
-bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
- struct intel_crtc *crtc);
-void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp);
-bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp);
-
-#endif /* __INTEL_DP_MST_H__ */
diff --git a/rr-cache/882012b4af13c77de852328870770019afd5505e/preimage b/rr-cache/882012b4af13c77de852328870770019afd5505e/preimage
deleted file mode 100644
index 10b54afa1242..000000000000
--- a/rr-cache/882012b4af13c77de852328870770019afd5505e/preimage
+++ /dev/null
@@ -1,36 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2019 Intel Corporation
- */
-
-#ifndef __INTEL_DP_MST_H__
-#define __INTEL_DP_MST_H__
-
-#include <linux/types.h>
-
-struct intel_atomic_state;
-struct intel_crtc;
-struct intel_crtc_state;
-struct intel_digital_port;
-struct intel_dp;
-struct intel_link_bw_limits;
-
-int intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_id);
-void intel_dp_mst_encoder_cleanup(struct intel_digital_port *dig_port);
-int intel_dp_mst_encoder_active_links(struct intel_digital_port *dig_port);
-bool intel_dp_mst_is_master_trans(const struct intel_crtc_state *crtc_state);
-bool intel_dp_mst_is_slave_trans(const struct intel_crtc_state *crtc_state);
-bool intel_dp_mst_source_support(struct intel_dp *intel_dp);
-int intel_dp_mst_add_topology_state_for_crtc(struct intel_atomic_state *state,
- struct intel_crtc *crtc);
-int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
- struct intel_link_bw_limits *limits);
-bool intel_dp_mst_crtc_needs_modeset(struct intel_atomic_state *state,
- struct intel_crtc *crtc);
-<<<<<<<
-bool intel_dp_mst_verify_dpcd_state(struct intel_dp *intel_dp);
-=======
-void intel_dp_mst_prepare_probe(struct intel_dp *intel_dp);
->>>>>>>
-
-#endif /* __INTEL_DP_MST_H__ */
diff --git a/rr-cache/e7c9aafc2297a37f89715cfeed48ccbfb82f76bb/preimage.5 b/rr-cache/e7c9aafc2297a37f89715cfeed48ccbfb82f76bb/preimage.5
deleted file mode 100644
index 110e70f7ee7b..000000000000
--- a/rr-cache/e7c9aafc2297a37f89715cfeed48ccbfb82f76bb/preimage.5
+++ /dev/null
@@ -1,2244 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2022 Intel Corporation
- */
-
-#include "xe_guc_submit.h"
-
-#include <linux/bitfield.h>
-#include <linux/bitmap.h>
-#include <linux/circ_buf.h>
-#include <linux/delay.h>
-#include <linux/dma-fence-array.h>
-#include <linux/math64.h>
-
-#include <drm/drm_managed.h>
-
-#include "abi/guc_actions_abi.h"
-#include "abi/guc_klvs_abi.h"
-#include "regs/xe_lrc_layout.h"
-#include "xe_assert.h"
-#include "xe_devcoredump.h"
-#include "xe_device.h"
-#include "xe_exec_queue.h"
-#include "xe_force_wake.h"
-#include "xe_gpu_scheduler.h"
-#include "xe_gt.h"
-#include "xe_gt_clock.h"
-#include "xe_gt_printk.h"
-#include "xe_guc.h"
-#include "xe_guc_ct.h"
-#include "xe_guc_exec_queue_types.h"
-#include "xe_guc_id_mgr.h"
-#include "xe_guc_submit_types.h"
-#include "xe_hw_engine.h"
-#include "xe_hw_fence.h"
-#include "xe_lrc.h"
-#include "xe_macros.h"
-#include "xe_map.h"
-#include "xe_mocs.h"
-#include "xe_pm.h"
-#include "xe_ring_ops_types.h"
-#include "xe_sched_job.h"
-#include "xe_trace.h"
-#include "xe_vm.h"
-
-static struct xe_guc *
-exec_queue_to_guc(struct xe_exec_queue *q)
-{
- return &q->gt->uc.guc;
-}
-
-/*
- * Helpers for engine state, using an atomic as some of the bits can transition
- * as the same time (e.g. a suspend can be happning at the same time as schedule
- * engine done being processed).
- */
-#define EXEC_QUEUE_STATE_REGISTERED (1 << 0)
-#define EXEC_QUEUE_STATE_ENABLED (1 << 1)
-#define EXEC_QUEUE_STATE_PENDING_ENABLE (1 << 2)
-#define EXEC_QUEUE_STATE_PENDING_DISABLE (1 << 3)
-#define EXEC_QUEUE_STATE_DESTROYED (1 << 4)
-#define EXEC_QUEUE_STATE_SUSPENDED (1 << 5)
-#define EXEC_QUEUE_STATE_RESET (1 << 6)
-#define EXEC_QUEUE_STATE_KILLED (1 << 7)
-#define EXEC_QUEUE_STATE_WEDGED (1 << 8)
-#define EXEC_QUEUE_STATE_BANNED (1 << 9)
-#define EXEC_QUEUE_STATE_CHECK_TIMEOUT (1 << 10)
-#define EXEC_QUEUE_STATE_EXTRA_REF (1 << 11)
-
-static bool exec_queue_registered(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_REGISTERED;
-}
-
-static void set_exec_queue_registered(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_REGISTERED, &q->guc->state);
-}
-
-static void clear_exec_queue_registered(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_REGISTERED, &q->guc->state);
-}
-
-static bool exec_queue_enabled(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_ENABLED;
-}
-
-static void set_exec_queue_enabled(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_ENABLED, &q->guc->state);
-}
-
-static void clear_exec_queue_enabled(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_ENABLED, &q->guc->state);
-}
-
-static bool exec_queue_pending_enable(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_ENABLE;
-}
-
-static void set_exec_queue_pending_enable(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_PENDING_ENABLE, &q->guc->state);
-}
-
-static void clear_exec_queue_pending_enable(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_PENDING_ENABLE, &q->guc->state);
-}
-
-static bool exec_queue_pending_disable(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_DISABLE;
-}
-
-static void set_exec_queue_pending_disable(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_PENDING_DISABLE, &q->guc->state);
-}
-
-static void clear_exec_queue_pending_disable(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_PENDING_DISABLE, &q->guc->state);
-}
-
-static bool exec_queue_destroyed(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_DESTROYED;
-}
-
-static void set_exec_queue_destroyed(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_DESTROYED, &q->guc->state);
-}
-
-static bool exec_queue_banned(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_BANNED;
-}
-
-static void set_exec_queue_banned(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_BANNED, &q->guc->state);
-}
-
-static bool exec_queue_suspended(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_SUSPENDED;
-}
-
-static void set_exec_queue_suspended(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_SUSPENDED, &q->guc->state);
-}
-
-static void clear_exec_queue_suspended(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_SUSPENDED, &q->guc->state);
-}
-
-static bool exec_queue_reset(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_RESET;
-}
-
-static void set_exec_queue_reset(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_RESET, &q->guc->state);
-}
-
-static bool exec_queue_killed(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_KILLED;
-}
-
-static void set_exec_queue_killed(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_KILLED, &q->guc->state);
-}
-
-static bool exec_queue_wedged(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_WEDGED;
-}
-
-static void set_exec_queue_wedged(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_WEDGED, &q->guc->state);
-}
-
-static bool exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_CHECK_TIMEOUT;
-}
-
-static void set_exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_CHECK_TIMEOUT, &q->guc->state);
-}
-
-static void clear_exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_CHECK_TIMEOUT, &q->guc->state);
-}
-
-static bool exec_queue_extra_ref(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_EXTRA_REF;
-}
-
-static void set_exec_queue_extra_ref(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_EXTRA_REF, &q->guc->state);
-}
-
-static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
-{
- return (atomic_read(&q->guc->state) &
- (EXEC_QUEUE_STATE_WEDGED | EXEC_QUEUE_STATE_KILLED |
- EXEC_QUEUE_STATE_BANNED));
-}
-
-#ifdef CONFIG_PROVE_LOCKING
-static int alloc_submit_wq(struct xe_guc *guc)
-{
- int i;
-
- for (i = 0; i < NUM_SUBMIT_WQ; ++i) {
- guc->submission_state.submit_wq_pool[i] =
- alloc_ordered_workqueue("submit_wq", 0);
- if (!guc->submission_state.submit_wq_pool[i])
- goto err_free;
- }
-
- return 0;
-
-err_free:
- while (i)
- destroy_workqueue(guc->submission_state.submit_wq_pool[--i]);
-
- return -ENOMEM;
-}
-
-static void free_submit_wq(struct xe_guc *guc)
-{
- int i;
-
- for (i = 0; i < NUM_SUBMIT_WQ; ++i)
- destroy_workqueue(guc->submission_state.submit_wq_pool[i]);
-}
-
-static struct workqueue_struct *get_submit_wq(struct xe_guc *guc)
-{
- int idx = guc->submission_state.submit_wq_idx++ % NUM_SUBMIT_WQ;
-
- return guc->submission_state.submit_wq_pool[idx];
-}
-#else
-static int alloc_submit_wq(struct xe_guc *guc)
-{
- return 0;
-}
-
-static void free_submit_wq(struct xe_guc *guc)
-{
-
-}
-
-static struct workqueue_struct *get_submit_wq(struct xe_guc *guc)
-{
- return NULL;
-}
-#endif
-
-static void guc_submit_fini(struct drm_device *drm, void *arg)
-{
- struct xe_guc *guc = arg;
-
- xa_destroy(&guc->submission_state.exec_queue_lookup);
- free_submit_wq(guc);
-}
-
-static void guc_submit_wedged_fini(void *arg)
-{
- struct xe_guc *guc = arg;
- struct xe_exec_queue *q;
- unsigned long index;
-
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- if (exec_queue_wedged(q))
- xe_exec_queue_put(q);
-}
-
-static const struct xe_exec_queue_ops guc_exec_queue_ops;
-
-static void primelockdep(struct xe_guc *guc)
-{
- if (!IS_ENABLED(CONFIG_LOCKDEP))
- return;
-
- fs_reclaim_acquire(GFP_KERNEL);
-
- mutex_lock(&guc->submission_state.lock);
- mutex_unlock(&guc->submission_state.lock);
-
- fs_reclaim_release(GFP_KERNEL);
-}
-
-/**
- * xe_guc_submit_init() - Initialize GuC submission.
- * @guc: the &xe_guc to initialize
- * @num_ids: number of GuC context IDs to use
- *
- * The bare-metal or PF driver can pass ~0 as &num_ids to indicate that all
- * GuC context IDs supported by the GuC firmware should be used for submission.
- *
- * Only VF drivers will have to provide explicit number of GuC context IDs
- * that they can use for submission.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_gt *gt = guc_to_gt(guc);
- int err;
-
- err = drmm_mutex_init(&xe->drm, &guc->submission_state.lock);
- if (err)
- return err;
-
- err = xe_guc_id_mgr_init(&guc->submission_state.idm, num_ids);
- if (err)
- return err;
-
- err = alloc_submit_wq(guc);
- if (err)
- return err;
-
- gt->exec_queue_ops = &guc_exec_queue_ops;
-
- xa_init(&guc->submission_state.exec_queue_lookup);
-
- primelockdep(guc);
-
- return drmm_add_action_or_reset(&xe->drm, guc_submit_fini, guc);
-}
-
-static void __release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q, u32 xa_count)
-{
- int i;
-
- lockdep_assert_held(&guc->submission_state.lock);
-
- for (i = 0; i < xa_count; ++i)
- xa_erase(&guc->submission_state.exec_queue_lookup, q->guc->id + i);
-
- xe_guc_id_mgr_release_locked(&guc->submission_state.idm,
- q->guc->id, q->width);
-}
-
-static int alloc_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- int ret;
- void *ptr;
- int i;
-
- /*
- * Must use GFP_NOWAIT as this lock is in the dma fence signalling path,
- * worse case user gets -ENOMEM on engine create and has to try again.
- *
- * FIXME: Have caller pre-alloc or post-alloc /w GFP_KERNEL to prevent
- * failure.
- */
- lockdep_assert_held(&guc->submission_state.lock);
-
- ret = xe_guc_id_mgr_reserve_locked(&guc->submission_state.idm,
- q->width);
- if (ret < 0)
- return ret;
-
- q->guc->id = ret;
-
- for (i = 0; i < q->width; ++i) {
- ptr = xa_store(&guc->submission_state.exec_queue_lookup,
- q->guc->id + i, q, GFP_NOWAIT);
- if (IS_ERR(ptr)) {
- ret = PTR_ERR(ptr);
- goto err_release;
- }
- }
-
- return 0;
-
-err_release:
- __release_guc_id(guc, q, i);
-
- return ret;
-}
-
-static void release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- mutex_lock(&guc->submission_state.lock);
- __release_guc_id(guc, q, q->width);
- mutex_unlock(&guc->submission_state.lock);
-}
-
-struct exec_queue_policy {
- u32 count;
- struct guc_update_exec_queue_policy h2g;
-};
-
-static u32 __guc_exec_queue_policy_action_size(struct exec_queue_policy *policy)
-{
- size_t bytes = sizeof(policy->h2g.header) +
- (sizeof(policy->h2g.klv[0]) * policy->count);
-
- return bytes / sizeof(u32);
-}
-
-static void __guc_exec_queue_policy_start_klv(struct exec_queue_policy *policy,
- u16 guc_id)
-{
- policy->h2g.header.action =
- XE_GUC_ACTION_HOST2GUC_UPDATE_CONTEXT_POLICIES;
- policy->h2g.header.guc_id = guc_id;
- policy->count = 0;
-}
-
-#define MAKE_EXEC_QUEUE_POLICY_ADD(func, id) \
-static void __guc_exec_queue_policy_add_##func(struct exec_queue_policy *policy, \
- u32 data) \
-{ \
- XE_WARN_ON(policy->count >= GUC_CONTEXT_POLICIES_KLV_NUM_IDS); \
-\
- policy->h2g.klv[policy->count].kl = \
- FIELD_PREP(GUC_KLV_0_KEY, \
- GUC_CONTEXT_POLICIES_KLV_ID_##id) | \
- FIELD_PREP(GUC_KLV_0_LEN, 1); \
- policy->h2g.klv[policy->count].value = data; \
- policy->count++; \
-}
-
-MAKE_EXEC_QUEUE_POLICY_ADD(execution_quantum, EXECUTION_QUANTUM)
-MAKE_EXEC_QUEUE_POLICY_ADD(preemption_timeout, PREEMPTION_TIMEOUT)
-MAKE_EXEC_QUEUE_POLICY_ADD(priority, SCHEDULING_PRIORITY)
-#undef MAKE_EXEC_QUEUE_POLICY_ADD
-
-static const int xe_exec_queue_prio_to_guc[] = {
- [XE_EXEC_QUEUE_PRIORITY_LOW] = GUC_CLIENT_PRIORITY_NORMAL,
- [XE_EXEC_QUEUE_PRIORITY_NORMAL] = GUC_CLIENT_PRIORITY_KMD_NORMAL,
- [XE_EXEC_QUEUE_PRIORITY_HIGH] = GUC_CLIENT_PRIORITY_HIGH,
- [XE_EXEC_QUEUE_PRIORITY_KERNEL] = GUC_CLIENT_PRIORITY_KMD_HIGH,
-};
-
-static void init_policies(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- struct exec_queue_policy policy;
- struct xe_device *xe = guc_to_xe(guc);
- enum xe_exec_queue_priority prio = q->sched_props.priority;
- u32 timeslice_us = q->sched_props.timeslice_us;
- u32 preempt_timeout_us = q->sched_props.preempt_timeout_us;
-
- xe_assert(xe, exec_queue_registered(q));
-
- __guc_exec_queue_policy_start_klv(&policy, q->guc->id);
- __guc_exec_queue_policy_add_priority(&policy, xe_exec_queue_prio_to_guc[prio]);
- __guc_exec_queue_policy_add_execution_quantum(&policy, timeslice_us);
- __guc_exec_queue_policy_add_preemption_timeout(&policy, preempt_timeout_us);
-
- xe_guc_ct_send(&guc->ct, (u32 *)&policy.h2g,
- __guc_exec_queue_policy_action_size(&policy), 0, 0);
-}
-
-static void set_min_preemption_timeout(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- struct exec_queue_policy policy;
-
- __guc_exec_queue_policy_start_klv(&policy, q->guc->id);
- __guc_exec_queue_policy_add_preemption_timeout(&policy, 1);
-
- xe_guc_ct_send(&guc->ct, (u32 *)&policy.h2g,
- __guc_exec_queue_policy_action_size(&policy), 0, 0);
-}
-
-#define parallel_read(xe_, map_, field_) \
- xe_map_rd_field(xe_, &map_, 0, struct guc_submit_parallel_scratch, \
- field_)
-#define parallel_write(xe_, map_, field_, val_) \
- xe_map_wr_field(xe_, &map_, 0, struct guc_submit_parallel_scratch, \
- field_, val_)
-
-static void __register_mlrc_exec_queue(struct xe_guc *guc,
- struct xe_exec_queue *q,
- struct guc_ctxt_registration_info *info)
-{
-#define MAX_MLRC_REG_SIZE (13 + XE_HW_ENGINE_MAX_INSTANCE * 2)
- struct xe_device *xe = guc_to_xe(guc);
- u32 action[MAX_MLRC_REG_SIZE];
- int len = 0;
- int i;
-
- xe_assert(xe, xe_exec_queue_is_parallel(q));
-
- action[len++] = XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC;
- action[len++] = info->flags;
- action[len++] = info->context_idx;
- action[len++] = info->engine_class;
- action[len++] = info->engine_submit_mask;
- action[len++] = info->wq_desc_lo;
- action[len++] = info->wq_desc_hi;
- action[len++] = info->wq_base_lo;
- action[len++] = info->wq_base_hi;
- action[len++] = info->wq_size;
- action[len++] = q->width;
- action[len++] = info->hwlrca_lo;
- action[len++] = info->hwlrca_hi;
-
- for (i = 1; i < q->width; ++i) {
- struct xe_lrc *lrc = q->lrc[i];
-
- action[len++] = lower_32_bits(xe_lrc_descriptor(lrc));
- action[len++] = upper_32_bits(xe_lrc_descriptor(lrc));
- }
-
- xe_assert(xe, len <= MAX_MLRC_REG_SIZE);
-#undef MAX_MLRC_REG_SIZE
-
- xe_guc_ct_send(&guc->ct, action, len, 0, 0);
-}
-
-static void __register_exec_queue(struct xe_guc *guc,
- struct guc_ctxt_registration_info *info)
-{
- u32 action[] = {
- XE_GUC_ACTION_REGISTER_CONTEXT,
- info->flags,
- info->context_idx,
- info->engine_class,
- info->engine_submit_mask,
- info->wq_desc_lo,
- info->wq_desc_hi,
- info->wq_base_lo,
- info->wq_base_hi,
- info->wq_size,
- info->hwlrca_lo,
- info->hwlrca_hi,
- };
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
-}
-
-static void register_exec_queue(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_lrc *lrc = q->lrc[0];
- struct guc_ctxt_registration_info info;
-
- xe_assert(xe, !exec_queue_registered(q));
-
- memset(&info, 0, sizeof(info));
- info.context_idx = q->guc->id;
- info.engine_class = xe_engine_class_to_guc_class(q->class);
- info.engine_submit_mask = q->logical_mask;
- info.hwlrca_lo = lower_32_bits(xe_lrc_descriptor(lrc));
- info.hwlrca_hi = upper_32_bits(xe_lrc_descriptor(lrc));
- info.flags = CONTEXT_REGISTRATION_FLAG_KMD;
-
- if (xe_exec_queue_is_parallel(q)) {
- u64 ggtt_addr = xe_lrc_parallel_ggtt_addr(lrc);
- struct iosys_map map = xe_lrc_parallel_map(lrc);
-
- info.wq_desc_lo = lower_32_bits(ggtt_addr +
- offsetof(struct guc_submit_parallel_scratch, wq_desc));
- info.wq_desc_hi = upper_32_bits(ggtt_addr +
- offsetof(struct guc_submit_parallel_scratch, wq_desc));
- info.wq_base_lo = lower_32_bits(ggtt_addr +
- offsetof(struct guc_submit_parallel_scratch, wq[0]));
- info.wq_base_hi = upper_32_bits(ggtt_addr +
- offsetof(struct guc_submit_parallel_scratch, wq[0]));
- info.wq_size = WQ_SIZE;
-
- q->guc->wqi_head = 0;
- q->guc->wqi_tail = 0;
- xe_map_memset(xe, &map, 0, 0, PARALLEL_SCRATCH_SIZE - WQ_SIZE);
- parallel_write(xe, map, wq_desc.wq_status, WQ_STATUS_ACTIVE);
- }
-
- /*
- * We must keep a reference for LR engines if engine is registered with
- * the GuC as jobs signal immediately and can't destroy an engine if the
- * GuC has a reference to it.
- */
- if (xe_exec_queue_is_lr(q))
- xe_exec_queue_get(q);
-
- set_exec_queue_registered(q);
- trace_xe_exec_queue_register(q);
- if (xe_exec_queue_is_parallel(q))
- __register_mlrc_exec_queue(guc, q, &info);
- else
- __register_exec_queue(guc, &info);
- init_policies(guc, q);
-}
-
-static u32 wq_space_until_wrap(struct xe_exec_queue *q)
-{
- return (WQ_SIZE - q->guc->wqi_tail);
-}
-
-static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- unsigned int sleep_period_ms = 1;
-
-#define AVAILABLE_SPACE \
- CIRC_SPACE(q->guc->wqi_tail, q->guc->wqi_head, WQ_SIZE)
- if (wqi_size > AVAILABLE_SPACE) {
-try_again:
- q->guc->wqi_head = parallel_read(xe, map, wq_desc.head);
- if (wqi_size > AVAILABLE_SPACE) {
- if (sleep_period_ms == 1024) {
- xe_gt_reset_async(q->gt);
- return -ENODEV;
- }
-
- msleep(sleep_period_ms);
- sleep_period_ms <<= 1;
- goto try_again;
- }
- }
-#undef AVAILABLE_SPACE
-
- return 0;
-}
-
-static int wq_noop_append(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- u32 len_dw = wq_space_until_wrap(q) / sizeof(u32) - 1;
-
- if (wq_wait_for_space(q, wq_space_until_wrap(q)))
- return -ENODEV;
-
- xe_assert(xe, FIELD_FIT(WQ_LEN_MASK, len_dw));
-
- parallel_write(xe, map, wq[q->guc->wqi_tail / sizeof(u32)],
- FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_NOOP) |
- FIELD_PREP(WQ_LEN_MASK, len_dw));
- q->guc->wqi_tail = 0;
-
- return 0;
-}
-
-static void wq_item_append(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
-#define WQ_HEADER_SIZE 4 /* Includes 1 LRC address too */
- u32 wqi[XE_HW_ENGINE_MAX_INSTANCE + (WQ_HEADER_SIZE - 1)];
- u32 wqi_size = (q->width + (WQ_HEADER_SIZE - 1)) * sizeof(u32);
- u32 len_dw = (wqi_size / sizeof(u32)) - 1;
- int i = 0, j;
-
- if (wqi_size > wq_space_until_wrap(q)) {
- if (wq_noop_append(q))
- return;
- }
- if (wq_wait_for_space(q, wqi_size))
- return;
-
- wqi[i++] = FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_MULTI_LRC) |
- FIELD_PREP(WQ_LEN_MASK, len_dw);
- wqi[i++] = xe_lrc_descriptor(q->lrc[0]);
- wqi[i++] = FIELD_PREP(WQ_GUC_ID_MASK, q->guc->id) |
- FIELD_PREP(WQ_RING_TAIL_MASK, q->lrc[0]->ring.tail / sizeof(u64));
- wqi[i++] = 0;
- for (j = 1; j < q->width; ++j) {
- struct xe_lrc *lrc = q->lrc[j];
-
- wqi[i++] = lrc->ring.tail / sizeof(u64);
- }
-
- xe_assert(xe, i == wqi_size / sizeof(u32));
-
- iosys_map_incr(&map, offsetof(struct guc_submit_parallel_scratch,
- wq[q->guc->wqi_tail / sizeof(u32)]));
- xe_map_memcpy_to(xe, &map, 0, wqi, wqi_size);
- q->guc->wqi_tail += wqi_size;
- xe_assert(xe, q->guc->wqi_tail <= WQ_SIZE);
-
- xe_device_wmb(xe);
-
- map = xe_lrc_parallel_map(q->lrc[0]);
- parallel_write(xe, map, wq_desc.tail, q->guc->wqi_tail);
-}
-
-#define RESUME_PENDING ~0x0ull
-static void submit_exec_queue(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_lrc *lrc = q->lrc[0];
- u32 action[3];
- u32 g2h_len = 0;
- u32 num_g2h = 0;
- int len = 0;
- bool extra_submit = false;
-
- xe_assert(xe, exec_queue_registered(q));
-
- if (xe_exec_queue_is_parallel(q))
- wq_item_append(q);
- else
- xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
-
- if (exec_queue_suspended(q) && !xe_exec_queue_is_parallel(q))
- return;
-
- if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) {
- action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET;
- action[len++] = q->guc->id;
- action[len++] = GUC_CONTEXT_ENABLE;
- g2h_len = G2H_LEN_DW_SCHED_CONTEXT_MODE_SET;
- num_g2h = 1;
- if (xe_exec_queue_is_parallel(q))
- extra_submit = true;
-
- q->guc->resume_time = RESUME_PENDING;
- set_exec_queue_pending_enable(q);
- set_exec_queue_enabled(q);
- trace_xe_exec_queue_scheduling_enable(q);
- } else {
- action[len++] = XE_GUC_ACTION_SCHED_CONTEXT;
- action[len++] = q->guc->id;
- trace_xe_exec_queue_submit(q);
- }
-
- xe_guc_ct_send(&guc->ct, action, len, g2h_len, num_g2h);
-
- if (extra_submit) {
- len = 0;
- action[len++] = XE_GUC_ACTION_SCHED_CONTEXT;
- action[len++] = q->guc->id;
- trace_xe_exec_queue_submit(q);
-
- xe_guc_ct_send(&guc->ct, action, len, 0, 0);
- }
-}
-
-static struct dma_fence *
-guc_exec_queue_run_job(struct drm_sched_job *drm_job)
-{
- struct xe_sched_job *job = to_xe_sched_job(drm_job);
- struct xe_exec_queue *q = job->q;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- bool lr = xe_exec_queue_is_lr(q);
-
- xe_assert(xe, !(exec_queue_destroyed(q) || exec_queue_pending_disable(q)) ||
- exec_queue_banned(q) || exec_queue_suspended(q));
-
- trace_xe_sched_job_run(job);
-
- if (!exec_queue_killed_or_banned_or_wedged(q) && !xe_sched_job_is_error(job)) {
- if (!exec_queue_registered(q))
- register_exec_queue(q);
- if (!lr) /* LR jobs are emitted in the exec IOCTL */
- q->ring_ops->emit_job(job);
- submit_exec_queue(q);
- }
-
- if (lr) {
- xe_sched_job_set_error(job, -EOPNOTSUPP);
- return NULL;
- } else if (test_and_set_bit(JOB_FLAG_SUBMIT, &job->fence->flags)) {
- return job->fence;
- } else {
- return dma_fence_get(job->fence);
- }
-}
-
-static void guc_exec_queue_free_job(struct drm_sched_job *drm_job)
-{
- struct xe_sched_job *job = to_xe_sched_job(drm_job);
-
- xe_exec_queue_update_run_ticks(job->q);
-
- trace_xe_sched_job_free(job);
- xe_sched_job_put(job);
-}
-
-static int guc_read_stopped(struct xe_guc *guc)
-{
- return atomic_read(&guc->submission_state.stopped);
-}
-
-#define MAKE_SCHED_CONTEXT_ACTION(q, enable_disable) \
- u32 action[] = { \
- XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET, \
- q->guc->id, \
- GUC_CONTEXT_##enable_disable, \
- }
-
-static void disable_scheduling_deregister(struct xe_guc *guc,
- struct xe_exec_queue *q)
-{
- MAKE_SCHED_CONTEXT_ACTION(q, DISABLE);
- struct xe_device *xe = guc_to_xe(guc);
- int ret;
-
- set_min_preemption_timeout(guc, q);
- smp_rmb();
- ret = wait_event_timeout(guc->ct.wq, !exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret) {
- struct xe_gpu_scheduler *sched = &q->guc->sched;
-
- drm_warn(&xe->drm, "Pending enable failed to respond");
- xe_sched_submission_start(sched);
- xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(sched);
- return;
- }
-
- clear_exec_queue_enabled(q);
- set_exec_queue_pending_disable(q);
- set_exec_queue_destroyed(q);
- trace_xe_exec_queue_scheduling_disable(q);
-
- /*
- * Reserve space for both G2H here as the 2nd G2H is sent from a G2H
- * handler and we are not allowed to reserved G2H space in handlers.
- */
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET +
- G2H_LEN_DW_DEREGISTER_CONTEXT, 2);
-}
-
-static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- /** to wakeup xe_wait_user_fence ioctl if exec queue is reset */
- wake_up_all(&xe->ufence_wq);
-
- if (xe_exec_queue_is_lr(q))
- queue_work(guc_to_gt(guc)->ordered_wq, &q->guc->lr_tdr);
- else
- xe_sched_tdr_queue_imm(&q->guc->sched);
-}
-
-/**
- * xe_guc_submit_wedge() - Wedge GuC submission
- * @guc: the GuC object
- *
- * Save exec queue's registered with GuC state by taking a ref to each queue.
- * Register a DRMM handler to drop refs upon driver unload.
- */
-void xe_guc_submit_wedge(struct xe_guc *guc)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- unsigned long index;
- int err;
-
- xe_gt_assert(guc_to_gt(guc), guc_to_xe(guc)->wedged.mode);
-
- err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev,
- guc_submit_wedged_fini, guc);
- if (err) {
- drm_err(&xe->drm, "Failed to register xe_guc_submit clean-up on wedged.mode=2. Although device is wedged.\n");
- return;
- }
-
- mutex_lock(&guc->submission_state.lock);
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- if (xe_exec_queue_get_unless_zero(q))
- set_exec_queue_wedged(q);
- mutex_unlock(&guc->submission_state.lock);
-}
-
-static bool guc_submit_hint_wedged(struct xe_guc *guc)
-{
- struct xe_device *xe = guc_to_xe(guc);
-
- if (xe->wedged.mode != 2)
- return false;
-
- if (xe_device_wedged(xe))
- return true;
-
- xe_device_declare_wedged(xe);
-
- return true;
-}
-
-static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
-{
- struct xe_guc_exec_queue *ge =
- container_of(w, struct xe_guc_exec_queue, lr_tdr);
- struct xe_exec_queue *q = ge->q;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_gpu_scheduler *sched = &ge->sched;
- bool wedged;
-
- xe_assert(xe, xe_exec_queue_is_lr(q));
- trace_xe_exec_queue_lr_cleanup(q);
-
- wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
-
- /* Kill the run_job / process_msg entry points */
- xe_sched_submission_stop(sched);
-
- /*
- * Engine state now mostly stable, disable scheduling / deregister if
- * needed. This cleanup routine might be called multiple times, where
- * the actual async engine deregister drops the final engine ref.
- * Calling disable_scheduling_deregister will mark the engine as
- * destroyed and fire off the CT requests to disable scheduling /
- * deregister, which we only want to do once. We also don't want to mark
- * the engine as pending_disable again as this may race with the
- * xe_guc_deregister_done_handler() which treats it as an unexpected
- * state.
- */
- if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) {
- struct xe_guc *guc = exec_queue_to_guc(q);
- int ret;
-
- set_exec_queue_banned(q);
- disable_scheduling_deregister(guc, q);
-
- /*
- * Must wait for scheduling to be disabled before signalling
- * any fences, if GT broken the GT reset code should signal us.
- */
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_disable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret) {
- drm_warn(&xe->drm, "Schedule disable failed to respond");
- xe_sched_submission_start(sched);
- xe_gt_reset_async(q->gt);
- return;
- }
- }
-
- xe_sched_submission_start(sched);
-}
-
-#define ADJUST_FIVE_PERCENT(__t) mul_u64_u32_div(__t, 105, 100)
-
-static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job)
-{
- struct xe_gt *gt = guc_to_gt(exec_queue_to_guc(q));
- u32 ctx_timestamp = xe_lrc_ctx_timestamp(q->lrc[0]);
- u32 ctx_job_timestamp = xe_lrc_ctx_job_timestamp(q->lrc[0]);
- u32 timeout_ms = q->sched_props.job_timeout_ms;
- u32 diff;
- u64 running_time_ms;
-
- /*
- * Counter wraps at ~223s at the usual 19.2MHz, be paranoid catch
- * possible overflows with a high timeout.
- */
- xe_gt_assert(gt, timeout_ms < 100 * MSEC_PER_SEC);
-
- if (ctx_timestamp < ctx_job_timestamp)
- diff = ctx_timestamp + U32_MAX - ctx_job_timestamp;
- else
- diff = ctx_timestamp - ctx_job_timestamp;
-
- /*
- * Ensure timeout is within 5% to account for an GuC scheduling latency
- */
- running_time_ms =
- ADJUST_FIVE_PERCENT(xe_gt_clock_interval_to_ms(gt, diff));
-
- xe_gt_dbg(gt,
- "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, running_time_ms=%llu, timeout_ms=%u, diff=0x%08x",
- xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
- q->guc->id, running_time_ms, timeout_ms, diff);
-
- return running_time_ms >= timeout_ms;
-}
-
-static void enable_scheduling(struct xe_exec_queue *q)
-{
- MAKE_SCHED_CONTEXT_ACTION(q, ENABLE);
- struct xe_guc *guc = exec_queue_to_guc(q);
- int ret;
-
- xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
-
- set_exec_queue_pending_enable(q);
- set_exec_queue_enabled(q);
- trace_xe_exec_queue_scheduling_enable(q);
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1);
-
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc)) {
- xe_gt_warn(guc_to_gt(guc), "Schedule enable failed to respond");
- set_exec_queue_banned(q);
- xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(&q->guc->sched);
- }
-}
-
-static void disable_scheduling(struct xe_exec_queue *q, bool immediate)
-{
- MAKE_SCHED_CONTEXT_ACTION(q, DISABLE);
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
-
- if (immediate)
- set_min_preemption_timeout(guc, q);
- clear_exec_queue_enabled(q);
- set_exec_queue_pending_disable(q);
- trace_xe_exec_queue_scheduling_disable(q);
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1);
-}
-
-static void __deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- u32 action[] = {
- XE_GUC_ACTION_DEREGISTER_CONTEXT,
- q->guc->id,
- };
-
- xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
-
- set_exec_queue_destroyed(q);
- trace_xe_exec_queue_deregister(q);
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_DEREGISTER_CONTEXT, 1);
-}
-
-static enum drm_gpu_sched_stat
-guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
-{
- struct xe_sched_job *job = to_xe_sched_job(drm_job);
- struct xe_sched_job *tmp_job;
- struct xe_exec_queue *q = job->q;
- struct xe_gpu_scheduler *sched = &q->guc->sched;
- struct xe_guc *guc = exec_queue_to_guc(q);
-<<<<<<<
-=======
- const char *process_name = "no process";
->>>>>>>
- int err = -ETIME;
- int i = 0;
- bool wedged, skip_timeout_check;
-
- /*
- * TDR has fired before free job worker. Common if exec queue
- * immediately closed after last fence signaled.
- */
- if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &job->fence->flags)) {
- guc_exec_queue_free_job(drm_job);
-
- return DRM_GPU_SCHED_STAT_NOMINAL;
- }
-
- /* Kill the run_job entry point */
- xe_sched_submission_stop(sched);
-
- /* Must check all state after stopping scheduler */
- skip_timeout_check = exec_queue_reset(q) ||
- exec_queue_killed_or_banned_or_wedged(q) ||
- exec_queue_destroyed(q);
-
- /* Job hasn't started, can't be timed out */
- if (!skip_timeout_check && !xe_sched_job_started(job))
- goto rearm;
-
- /*
- * XXX: Sampling timeout doesn't work in wedged mode as we have to
- * modify scheduling state to read timestamp. We could read the
- * timestamp from a register to accumulate current running time but this
- * doesn't work for SRIOV. For now assuming timeouts in wedged mode are
- * genuine timeouts.
- */
- wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
-
- /* Engine state now stable, disable scheduling to check timestamp */
- if (!wedged && exec_queue_registered(q)) {
- int ret;
-
- if (exec_queue_reset(q))
- err = -EIO;
-
- if (!exec_queue_destroyed(q)) {
- /*
- * Wait for any pending G2H to flush out before
- * modifying state
- */
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc))
- goto trigger_reset;
-
- /*
- * Flag communicates to G2H handler that schedule
- * disable originated from a timeout check. The G2H then
- * avoid triggering cleanup or deregistering the exec
- * queue.
- */
- set_exec_queue_check_timeout(q);
- disable_scheduling(q, skip_timeout_check);
- }
-
- /*
- * Must wait for scheduling to be disabled before signalling
- * any fences, if GT broken the GT reset code should signal us.
- *
- * FIXME: Tests can generate a ton of 0x6000 (IOMMU CAT fault
- * error) messages which can cause the schedule disable to get
- * lost. If this occurs, trigger a GT reset to recover.
- */
- smp_rmb();
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_disable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc)) {
-trigger_reset:
- if (!ret)
- xe_gt_warn(guc_to_gt(guc), "Schedule disable failed to respond");
- set_exec_queue_extra_ref(q);
- xe_exec_queue_get(q); /* GT reset owns this */
- set_exec_queue_banned(q);
- xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(sched);
- goto rearm;
- }
- }
-
- /*
- * Check if job is actually timed out, if so restart job execution and TDR
- */
- if (!wedged && !skip_timeout_check && !check_timeout(q, job) &&
- !exec_queue_reset(q) && exec_queue_registered(q)) {
- clear_exec_queue_check_timeout(q);
- goto sched_enable;
- }
-
-<<<<<<<
- if (q->vm && q->vm->xef) {
- process_name = q->vm->xef->process_name;
- pid = q->vm->xef->pid;
- }
- xe_gt_notice(guc_to_gt(guc), "Timedout job: seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx in %s [%d]",
- xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
- q->guc->id, q->flags, process_name, pid);
-
-=======
- xe_gt_notice(guc_to_gt(guc), "Timedout job: seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx",
- xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
- q->guc->id, q->flags);
->>>>>>>
- trace_xe_sched_job_timedout(job);
-
- if (!exec_queue_killed(q))
- xe_devcoredump(job);
-
- /*
- * Kernel jobs should never fail, nor should VM jobs if they do
- * somethings has gone wrong and the GT needs a reset
- */
- xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_KERNEL,
- "Kernel-submitted job timed out\n");
- xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q),
- "VM job timed out on non-killed execqueue\n");
- if (!wedged && (q->flags & EXEC_QUEUE_FLAG_KERNEL ||
- (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)))) {
- if (!xe_sched_invalidate_job(job, 2)) {
- clear_exec_queue_check_timeout(q);
- xe_gt_reset_async(q->gt);
- goto rearm;
- }
- }
-
- /* Finish cleaning up exec queue via deregister */
- set_exec_queue_banned(q);
- if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) {
- set_exec_queue_extra_ref(q);
- xe_exec_queue_get(q);
- __deregister_exec_queue(guc, q);
- }
-
- /* Stop fence signaling */
- xe_hw_fence_irq_stop(q->fence_irq);
-
- /*
- * Fence state now stable, stop / start scheduler which cleans up any
- * fences that are complete
- */
- xe_sched_add_pending_job(sched, job);
- xe_sched_submission_start(sched);
-
- xe_guc_exec_queue_trigger_cleanup(q);
-
- /* Mark all outstanding jobs as bad, thus completing them */
- spin_lock(&sched->base.job_list_lock);
- list_for_each_entry(tmp_job, &sched->base.pending_list, drm.list)
- xe_sched_job_set_error(tmp_job, !i++ ? err : -ECANCELED);
- spin_unlock(&sched->base.job_list_lock);
-
- /* Start fence signaling */
- xe_hw_fence_irq_start(q->fence_irq);
-
- return DRM_GPU_SCHED_STAT_NOMINAL;
-
-sched_enable:
- enable_scheduling(q);
-rearm:
- /*
- * XXX: Ideally want to adjust timeout based on current exection time
- * but there is not currently an easy way to do in DRM scheduler. With
- * some thought, do this in a follow up.
- */
- xe_sched_add_pending_job(sched, job);
- xe_sched_submission_start(sched);
-
- return DRM_GPU_SCHED_STAT_NOMINAL;
-}
-
-static void __guc_exec_queue_fini_async(struct work_struct *w)
-{
- struct xe_guc_exec_queue *ge =
- container_of(w, struct xe_guc_exec_queue, fini_async);
- struct xe_exec_queue *q = ge->q;
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- xe_pm_runtime_get(guc_to_xe(guc));
- trace_xe_exec_queue_destroy(q);
-
- if (xe_exec_queue_is_lr(q))
- cancel_work_sync(&ge->lr_tdr);
- release_guc_id(guc, q);
- xe_sched_entity_fini(&ge->entity);
- xe_sched_fini(&ge->sched);
-
- kfree(ge);
- xe_exec_queue_fini(q);
- xe_pm_runtime_put(guc_to_xe(guc));
-}
-
-static void guc_exec_queue_fini_async(struct xe_exec_queue *q)
-{
- INIT_WORK(&q->guc->fini_async, __guc_exec_queue_fini_async);
-
- /* We must block on kernel engines so slabs are empty on driver unload */
- if (q->flags & EXEC_QUEUE_FLAG_PERMANENT || exec_queue_wedged(q))
- __guc_exec_queue_fini_async(&q->guc->fini_async);
- else
- queue_work(system_wq, &q->guc->fini_async);
-}
-
-static void __guc_exec_queue_fini(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- /*
- * Might be done from within the GPU scheduler, need to do async as we
- * fini the scheduler when the engine is fini'd, the scheduler can't
- * complete fini within itself (circular dependency). Async resolves
- * this we and don't really care when everything is fini'd, just that it
- * is.
- */
- guc_exec_queue_fini_async(q);
-}
-
-static void __guc_exec_queue_process_msg_cleanup(struct xe_sched_msg *msg)
-{
- struct xe_exec_queue *q = msg->private_data;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_PERMANENT));
- trace_xe_exec_queue_cleanup_entity(q);
-
- if (exec_queue_registered(q))
- disable_scheduling_deregister(guc, q);
- else
- __guc_exec_queue_fini(guc, q);
-}
-
-static bool guc_exec_queue_allowed_to_change_state(struct xe_exec_queue *q)
-{
- return !exec_queue_killed_or_banned_or_wedged(q) && exec_queue_registered(q);
-}
-
-static void __guc_exec_queue_process_msg_set_sched_props(struct xe_sched_msg *msg)
-{
- struct xe_exec_queue *q = msg->private_data;
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- if (guc_exec_queue_allowed_to_change_state(q))
- init_policies(guc, q);
- kfree(msg);
-}
-
-static void suspend_fence_signal(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, exec_queue_suspended(q) || exec_queue_killed(q) ||
- guc_read_stopped(guc));
- xe_assert(xe, q->guc->suspend_pending);
-
- q->guc->suspend_pending = false;
- smp_wmb();
- wake_up(&q->guc->suspend_wait);
-}
-
-static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
-{
- struct xe_exec_queue *q = msg->private_data;
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) &&
- exec_queue_enabled(q)) {
- wait_event(guc->ct.wq, q->guc->resume_time != RESUME_PENDING ||
- guc_read_stopped(guc));
-
- if (!guc_read_stopped(guc)) {
- s64 since_resume_ms =
- ktime_ms_delta(ktime_get(),
- q->guc->resume_time);
- s64 wait_ms = q->vm->preempt.min_run_period_ms -
- since_resume_ms;
-
- if (wait_ms > 0 && q->guc->resume_time)
- msleep(wait_ms);
-
- set_exec_queue_suspended(q);
- disable_scheduling(q, false);
- }
- } else if (q->guc->suspend_pending) {
- set_exec_queue_suspended(q);
- suspend_fence_signal(q);
- }
-}
-
-static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
-{
- struct xe_exec_queue *q = msg->private_data;
-
- if (guc_exec_queue_allowed_to_change_state(q)) {
- q->guc->resume_time = RESUME_PENDING;
- clear_exec_queue_suspended(q);
- enable_scheduling(q);
- } else {
- clear_exec_queue_suspended(q);
- }
-}
-
-#define CLEANUP 1 /* Non-zero values to catch uninitialized msg */
-#define SET_SCHED_PROPS 2
-#define SUSPEND 3
-#define RESUME 4
-
-static void guc_exec_queue_process_msg(struct xe_sched_msg *msg)
-{
- trace_xe_sched_msg_recv(msg);
-
- switch (msg->opcode) {
- case CLEANUP:
- __guc_exec_queue_process_msg_cleanup(msg);
- break;
- case SET_SCHED_PROPS:
- __guc_exec_queue_process_msg_set_sched_props(msg);
- break;
- case SUSPEND:
- __guc_exec_queue_process_msg_suspend(msg);
- break;
- case RESUME:
- __guc_exec_queue_process_msg_resume(msg);
- break;
- default:
- XE_WARN_ON("Unknown message type");
- }
-
- xe_pm_runtime_put(guc_to_xe(exec_queue_to_guc(msg->private_data)));
-}
-
-static const struct drm_sched_backend_ops drm_sched_ops = {
- .run_job = guc_exec_queue_run_job,
- .free_job = guc_exec_queue_free_job,
- .timedout_job = guc_exec_queue_timedout_job,
-};
-
-static const struct xe_sched_backend_ops xe_sched_ops = {
- .process_msg = guc_exec_queue_process_msg,
-};
-
-static int guc_exec_queue_init(struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_guc_exec_queue *ge;
- long timeout;
- int err;
-
- xe_assert(xe, xe_device_uc_enabled(guc_to_xe(guc)));
-
- ge = kzalloc(sizeof(*ge), GFP_KERNEL);
- if (!ge)
- return -ENOMEM;
-
- q->guc = ge;
- ge->q = q;
- init_waitqueue_head(&ge->suspend_wait);
-
- timeout = (q->vm && xe_vm_in_lr_mode(q->vm)) ? MAX_SCHEDULE_TIMEOUT :
- msecs_to_jiffies(q->sched_props.job_timeout_ms);
- err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
- get_submit_wq(guc),
- q->lrc[0]->ring.size / MAX_JOB_SIZE_BYTES, 64,
- timeout, guc_to_gt(guc)->ordered_wq, NULL,
- q->name, gt_to_xe(q->gt)->drm.dev);
- if (err)
- goto err_free;
-
- sched = &ge->sched;
- err = xe_sched_entity_init(&ge->entity, sched);
- if (err)
- goto err_sched;
-
- if (xe_exec_queue_is_lr(q))
- INIT_WORK(&q->guc->lr_tdr, xe_guc_exec_queue_lr_cleanup);
-
- mutex_lock(&guc->submission_state.lock);
-
- err = alloc_guc_id(guc, q);
- if (err)
- goto err_entity;
-
- q->entity = &ge->entity;
-
- if (guc_read_stopped(guc))
- xe_sched_stop(sched);
-
- mutex_unlock(&guc->submission_state.lock);
-
- xe_exec_queue_assign_name(q, q->guc->id);
-
- trace_xe_exec_queue_create(q);
-
- return 0;
-
-err_entity:
- mutex_unlock(&guc->submission_state.lock);
- xe_sched_entity_fini(&ge->entity);
-err_sched:
- xe_sched_fini(&ge->sched);
-err_free:
- kfree(ge);
-
- return err;
-}
-
-static void guc_exec_queue_kill(struct xe_exec_queue *q)
-{
- trace_xe_exec_queue_kill(q);
- set_exec_queue_killed(q);
- xe_guc_exec_queue_trigger_cleanup(q);
-}
-
-static void guc_exec_queue_add_msg(struct xe_exec_queue *q, struct xe_sched_msg *msg,
- u32 opcode)
-{
- xe_pm_runtime_get_noresume(guc_to_xe(exec_queue_to_guc(q)));
-
- INIT_LIST_HEAD(&msg->link);
- msg->opcode = opcode;
- msg->private_data = q;
-
- trace_xe_sched_msg_add(msg);
- xe_sched_add_msg(&q->guc->sched, msg);
-}
-
-#define STATIC_MSG_CLEANUP 0
-#define STATIC_MSG_SUSPEND 1
-#define STATIC_MSG_RESUME 2
-static void guc_exec_queue_fini(struct xe_exec_queue *q)
-{
- struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_CLEANUP;
-
- if (!(q->flags & EXEC_QUEUE_FLAG_PERMANENT) && !exec_queue_wedged(q))
- guc_exec_queue_add_msg(q, msg, CLEANUP);
- else
- __guc_exec_queue_fini(exec_queue_to_guc(q), q);
-}
-
-static int guc_exec_queue_set_priority(struct xe_exec_queue *q,
- enum xe_exec_queue_priority priority)
-{
- struct xe_sched_msg *msg;
-
- if (q->sched_props.priority == priority ||
- exec_queue_killed_or_banned_or_wedged(q))
- return 0;
-
- msg = kmalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- q->sched_props.priority = priority;
- guc_exec_queue_add_msg(q, msg, SET_SCHED_PROPS);
-
- return 0;
-}
-
-static int guc_exec_queue_set_timeslice(struct xe_exec_queue *q, u32 timeslice_us)
-{
- struct xe_sched_msg *msg;
-
- if (q->sched_props.timeslice_us == timeslice_us ||
- exec_queue_killed_or_banned_or_wedged(q))
- return 0;
-
- msg = kmalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- q->sched_props.timeslice_us = timeslice_us;
- guc_exec_queue_add_msg(q, msg, SET_SCHED_PROPS);
-
- return 0;
-}
-
-static int guc_exec_queue_set_preempt_timeout(struct xe_exec_queue *q,
- u32 preempt_timeout_us)
-{
- struct xe_sched_msg *msg;
-
- if (q->sched_props.preempt_timeout_us == preempt_timeout_us ||
- exec_queue_killed_or_banned_or_wedged(q))
- return 0;
-
- msg = kmalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- q->sched_props.preempt_timeout_us = preempt_timeout_us;
- guc_exec_queue_add_msg(q, msg, SET_SCHED_PROPS);
-
- return 0;
-}
-
-static int guc_exec_queue_suspend(struct xe_exec_queue *q)
-{
- struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_SUSPEND;
-
- if (exec_queue_killed_or_banned_or_wedged(q) || q->guc->suspend_pending)
- return -EINVAL;
-
- q->guc->suspend_pending = true;
- guc_exec_queue_add_msg(q, msg, SUSPEND);
-
- return 0;
-}
-
-static void guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- wait_event(q->guc->suspend_wait, !q->guc->suspend_pending ||
- guc_read_stopped(guc));
-}
-
-static void guc_exec_queue_resume(struct xe_exec_queue *q)
-{
- struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_RESUME;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, !q->guc->suspend_pending);
-
- guc_exec_queue_add_msg(q, msg, RESUME);
-}
-
-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);
-}
-
-/*
- * 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,
- * really shouldn't do much other than trap into the DRM scheduler which
- * synchronizes these operations.
- */
-static const struct xe_exec_queue_ops guc_exec_queue_ops = {
- .init = guc_exec_queue_init,
- .kill = guc_exec_queue_kill,
- .fini = guc_exec_queue_fini,
- .set_priority = guc_exec_queue_set_priority,
- .set_timeslice = guc_exec_queue_set_timeslice,
- .set_preempt_timeout = guc_exec_queue_set_preempt_timeout,
- .suspend = guc_exec_queue_suspend,
- .suspend_wait = guc_exec_queue_suspend_wait,
- .resume = guc_exec_queue_resume,
- .reset_status = guc_exec_queue_reset_status,
-};
-
-static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched = &q->guc->sched;
-
- /* Stop scheduling + flush any DRM scheduler operations */
- xe_sched_submission_stop(sched);
-
- /* Clean up lost G2H + reset engine state */
- if (exec_queue_registered(q)) {
- if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q))
- xe_exec_queue_put(q);
- else if (exec_queue_destroyed(q))
- __guc_exec_queue_fini(guc, q);
- }
- if (q->guc->suspend_pending) {
- set_exec_queue_suspended(q);
- suspend_fence_signal(q);
- }
- atomic_and(EXEC_QUEUE_STATE_WEDGED | EXEC_QUEUE_STATE_BANNED |
- EXEC_QUEUE_STATE_KILLED | EXEC_QUEUE_STATE_DESTROYED |
- EXEC_QUEUE_STATE_SUSPENDED,
- &q->guc->state);
- q->guc->resume_time = 0;
- trace_xe_exec_queue_stop(q);
-
- /*
- * Ban any engine (aside from kernel and engines used for VM ops) with a
- * started but not complete job or if a job has gone through a GT reset
- * more than twice.
- */
- if (!(q->flags & (EXEC_QUEUE_FLAG_KERNEL | EXEC_QUEUE_FLAG_VM))) {
- struct xe_sched_job *job = xe_sched_first_pending_job(sched);
- bool ban = false;
-
- if (job) {
- if ((xe_sched_job_started(job) &&
- !xe_sched_job_completed(job)) ||
- xe_sched_invalidate_job(job, 2)) {
- trace_xe_sched_job_ban(job);
- ban = true;
- }
- } else if (xe_exec_queue_is_lr(q) &&
- (xe_lrc_ring_head(q->lrc[0]) != xe_lrc_ring_tail(q->lrc[0]))) {
- ban = true;
- }
-
- if (ban) {
- set_exec_queue_banned(q);
- xe_guc_exec_queue_trigger_cleanup(q);
- }
- }
-}
-
-int xe_guc_submit_reset_prepare(struct xe_guc *guc)
-{
- int ret;
-
- /*
- * Using an atomic here rather than submission_state.lock as this
- * function can be called while holding the CT lock (engine reset
- * failure). submission_state.lock needs the CT lock to resubmit jobs.
- * Atomic is not ideal, but it works to prevent against concurrent reset
- * and releasing any TDRs waiting on guc->submission_state.stopped.
- */
- ret = atomic_fetch_or(1, &guc->submission_state.stopped);
- smp_wmb();
- wake_up_all(&guc->ct.wq);
-
- return ret;
-}
-
-void xe_guc_submit_reset_wait(struct xe_guc *guc)
-{
- wait_event(guc->ct.wq, xe_device_wedged(guc_to_xe(guc)) ||
- !guc_read_stopped(guc));
-}
-
-void xe_guc_submit_stop(struct xe_guc *guc)
-{
- struct xe_exec_queue *q;
- unsigned long index;
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, guc_read_stopped(guc) == 1);
-
- mutex_lock(&guc->submission_state.lock);
-
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- guc_exec_queue_stop(guc, q);
-
- mutex_unlock(&guc->submission_state.lock);
-
- /*
- * No one can enter the backend at this point, aside from new engine
- * creation which is protected by guc->submission_state.lock.
- */
-
-}
-
-static void guc_exec_queue_start(struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched = &q->guc->sched;
-
- if (!exec_queue_killed_or_banned_or_wedged(q)) {
- int i;
-
- trace_xe_exec_queue_resubmit(q);
- for (i = 0; i < q->width; ++i)
- xe_lrc_set_ring_head(q->lrc[i], q->lrc[i]->ring.tail);
- xe_sched_resubmit_jobs(sched);
- }
-
- xe_sched_submission_start(sched);
-}
-
-int xe_guc_submit_start(struct xe_guc *guc)
-{
- struct xe_exec_queue *q;
- unsigned long index;
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, guc_read_stopped(guc) == 1);
-
- mutex_lock(&guc->submission_state.lock);
- atomic_dec(&guc->submission_state.stopped);
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- guc_exec_queue_start(q);
- mutex_unlock(&guc->submission_state.lock);
-
- wake_up_all(&guc->ct.wq);
-
- return 0;
-}
-
-static struct xe_exec_queue *
-g2h_exec_queue_lookup(struct xe_guc *guc, u32 guc_id)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
-
- if (unlikely(guc_id >= GUC_ID_MAX)) {
- drm_err(&xe->drm, "Invalid guc_id %u", guc_id);
- return NULL;
- }
-
- q = xa_load(&guc->submission_state.exec_queue_lookup, guc_id);
- if (unlikely(!q)) {
- drm_err(&xe->drm, "Not engine present for guc_id %u", guc_id);
- return NULL;
- }
-
- xe_assert(xe, guc_id >= q->guc->id);
- xe_assert(xe, guc_id < (q->guc->id + q->width));
-
- return q;
-}
-
-static void deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- u32 action[] = {
- XE_GUC_ACTION_DEREGISTER_CONTEXT,
- q->guc->id,
- };
-
- xe_gt_assert(guc_to_gt(guc), exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
-
- trace_xe_exec_queue_deregister(q);
-
- xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action));
-}
-
-static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
- u32 runnable_state)
-{
- trace_xe_exec_queue_scheduling_done(q);
-
- if (runnable_state == 1) {
- xe_gt_assert(guc_to_gt(guc), exec_queue_pending_enable(q));
-
- q->guc->resume_time = ktime_get();
- clear_exec_queue_pending_enable(q);
- smp_wmb();
- wake_up_all(&guc->ct.wq);
- } else {
- bool check_timeout = exec_queue_check_timeout(q);
-
- xe_gt_assert(guc_to_gt(guc), runnable_state == 0);
- xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q));
-
- clear_exec_queue_pending_disable(q);
- if (q->guc->suspend_pending) {
- suspend_fence_signal(q);
- } else {
- if (exec_queue_banned(q) || check_timeout) {
- smp_wmb();
- wake_up_all(&guc->ct.wq);
- }
- if (!check_timeout)
- deregister_exec_queue(guc, q);
- }
- }
-}
-
-int xe_guc_sched_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- u32 guc_id = msg[0];
- u32 runnable_state = msg[1];
-
- if (unlikely(len < 2)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- q = g2h_exec_queue_lookup(guc, guc_id);
- if (unlikely(!q))
- return -EPROTO;
-
- if (unlikely(!exec_queue_pending_enable(q) &&
- !exec_queue_pending_disable(q))) {
- xe_gt_err(guc_to_gt(guc),
- "SCHED_DONE: Unexpected engine state 0x%04x, guc_id=%d, runnable_state=%u",
- atomic_read(&q->guc->state), q->guc->id,
- runnable_state);
- return -EPROTO;
- }
-
- handle_sched_done(guc, q, runnable_state);
-
- return 0;
-}
-
-static void handle_deregister_done(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- trace_xe_exec_queue_deregister_done(q);
-
- clear_exec_queue_registered(q);
-
- if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q))
- xe_exec_queue_put(q);
- else
- __guc_exec_queue_fini(guc, q);
-}
-
-int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- u32 guc_id = msg[0];
-
- if (unlikely(len < 1)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- q = g2h_exec_queue_lookup(guc, guc_id);
- if (unlikely(!q))
- return -EPROTO;
-
- if (!exec_queue_destroyed(q) || exec_queue_pending_disable(q) ||
- exec_queue_pending_enable(q) || exec_queue_enabled(q)) {
- xe_gt_err(guc_to_gt(guc),
- "DEREGISTER_DONE: Unexpected engine state 0x%04x, guc_id=%d",
- atomic_read(&q->guc->state), q->guc->id);
- return -EPROTO;
- }
-
- handle_deregister_done(guc, q);
-
- return 0;
-}
-
-int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_gt *gt = guc_to_gt(guc);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- u32 guc_id = msg[0];
-
- if (unlikely(len < 1)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- q = g2h_exec_queue_lookup(guc, guc_id);
- if (unlikely(!q))
- return -EPROTO;
-
- xe_gt_info(gt, "Engine reset: engine_class=%s, logical_mask: 0x%x, guc_id=%d",
- xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
-
- /* FIXME: Do error capture, most likely async */
-
- trace_xe_exec_queue_reset(q);
-
- /*
- * A banned engine is a NOP at this point (came from
- * guc_exec_queue_timedout_job). Otherwise, kick drm scheduler to cancel
- * jobs by setting timeout of the job to the minimum value kicking
- * guc_exec_queue_timedout_job.
- */
- set_exec_queue_reset(q);
- if (!exec_queue_banned(q) && !exec_queue_check_timeout(q))
- xe_guc_exec_queue_trigger_cleanup(q);
-
- return 0;
-}
-
-int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
- u32 len)
-{
- struct xe_gt *gt = guc_to_gt(guc);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- u32 guc_id = msg[0];
-
- if (unlikely(len < 1)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- q = g2h_exec_queue_lookup(guc, guc_id);
- if (unlikely(!q))
- return -EPROTO;
-
- xe_gt_dbg(gt, "Engine memory cat error: engine_class=%s, logical_mask: 0x%x, guc_id=%d",
- xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
-
- trace_xe_exec_queue_memory_cat_error(q);
-
- /* Treat the same as engine reset */
- set_exec_queue_reset(q);
- if (!exec_queue_banned(q) && !exec_queue_check_timeout(q))
- xe_guc_exec_queue_trigger_cleanup(q);
-
- return 0;
-}
-
-int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_device *xe = guc_to_xe(guc);
- u8 guc_class, instance;
- u32 reason;
-
- if (unlikely(len != 3)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- guc_class = msg[0];
- instance = msg[1];
- reason = msg[2];
-
- /* Unexpected failure of a hardware feature, log an actual error */
- drm_err(&xe->drm, "GuC engine reset request failed on %d:%d because 0x%08X",
- guc_class, instance, reason);
-
- xe_gt_reset_async(guc_to_gt(guc));
-
- return 0;
-}
-
-static void
-guc_exec_queue_wq_snapshot_capture(struct xe_exec_queue *q,
- struct xe_guc_submit_exec_queue_snapshot *snapshot)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- int i;
-
- snapshot->guc.wqi_head = q->guc->wqi_head;
- snapshot->guc.wqi_tail = q->guc->wqi_tail;
- snapshot->parallel.wq_desc.head = parallel_read(xe, map, wq_desc.head);
- snapshot->parallel.wq_desc.tail = parallel_read(xe, map, wq_desc.tail);
- snapshot->parallel.wq_desc.status = parallel_read(xe, map,
- wq_desc.wq_status);
-
- if (snapshot->parallel.wq_desc.head !=
- snapshot->parallel.wq_desc.tail) {
- for (i = snapshot->parallel.wq_desc.head;
- i != snapshot->parallel.wq_desc.tail;
- i = (i + sizeof(u32)) % WQ_SIZE)
- snapshot->parallel.wq[i / sizeof(u32)] =
- parallel_read(xe, map, wq[i / sizeof(u32)]);
- }
-}
-
-static void
-guc_exec_queue_wq_snapshot_print(struct xe_guc_submit_exec_queue_snapshot *snapshot,
- struct drm_printer *p)
-{
- int i;
-
- drm_printf(p, "\tWQ head: %u (internal), %d (memory)\n",
- snapshot->guc.wqi_head, snapshot->parallel.wq_desc.head);
- drm_printf(p, "\tWQ tail: %u (internal), %d (memory)\n",
- snapshot->guc.wqi_tail, snapshot->parallel.wq_desc.tail);
- drm_printf(p, "\tWQ status: %u\n", snapshot->parallel.wq_desc.status);
-
- if (snapshot->parallel.wq_desc.head !=
- snapshot->parallel.wq_desc.tail) {
- for (i = snapshot->parallel.wq_desc.head;
- i != snapshot->parallel.wq_desc.tail;
- i = (i + sizeof(u32)) % WQ_SIZE)
- drm_printf(p, "\tWQ[%zu]: 0x%08x\n", i / sizeof(u32),
- snapshot->parallel.wq[i / sizeof(u32)]);
- }
-}
-
-/**
- * xe_guc_exec_queue_snapshot_capture - Take a quick snapshot of the GuC Engine.
- * @q: faulty exec queue
- *
- * This can be printed out in a later stage like during dev_coredump
- * analysis.
- *
- * Returns: a GuC Submit Engine snapshot object that must be freed by the
- * caller, using `xe_guc_exec_queue_snapshot_free`.
- */
-struct xe_guc_submit_exec_queue_snapshot *
-xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched = &q->guc->sched;
- struct xe_guc_submit_exec_queue_snapshot *snapshot;
- int i;
-
- snapshot = kzalloc(sizeof(*snapshot), GFP_ATOMIC);
-
- if (!snapshot)
- return NULL;
-
- snapshot->guc.id = q->guc->id;
- memcpy(&snapshot->name, &q->name, sizeof(snapshot->name));
- snapshot->class = q->class;
- snapshot->logical_mask = q->logical_mask;
- snapshot->width = q->width;
- snapshot->refcount = kref_read(&q->refcount);
- snapshot->sched_timeout = sched->base.timeout;
- snapshot->sched_props.timeslice_us = q->sched_props.timeslice_us;
- snapshot->sched_props.preempt_timeout_us =
- q->sched_props.preempt_timeout_us;
-
- snapshot->lrc = kmalloc_array(q->width, sizeof(struct xe_lrc_snapshot *),
- GFP_ATOMIC);
-
- if (snapshot->lrc) {
- for (i = 0; i < q->width; ++i) {
- struct xe_lrc *lrc = q->lrc[i];
-
- snapshot->lrc[i] = xe_lrc_snapshot_capture(lrc);
- }
- }
-
- snapshot->schedule_state = atomic_read(&q->guc->state);
- snapshot->exec_queue_flags = q->flags;
-
- snapshot->parallel_execution = xe_exec_queue_is_parallel(q);
- if (snapshot->parallel_execution)
- guc_exec_queue_wq_snapshot_capture(q, snapshot);
-
- spin_lock(&sched->base.job_list_lock);
- snapshot->pending_list_size = list_count_nodes(&sched->base.pending_list);
- snapshot->pending_list = kmalloc_array(snapshot->pending_list_size,
- sizeof(struct pending_list_snapshot),
- GFP_ATOMIC);
-
- if (snapshot->pending_list) {
- struct xe_sched_job *job_iter;
-
- i = 0;
- list_for_each_entry(job_iter, &sched->base.pending_list, drm.list) {
- snapshot->pending_list[i].seqno =
- xe_sched_job_seqno(job_iter);
- snapshot->pending_list[i].fence =
- dma_fence_is_signaled(job_iter->fence) ? 1 : 0;
- snapshot->pending_list[i].finished =
- dma_fence_is_signaled(&job_iter->drm.s_fence->finished)
- ? 1 : 0;
- i++;
- }
- }
-
- spin_unlock(&sched->base.job_list_lock);
-
- return snapshot;
-}
-
-/**
- * xe_guc_exec_queue_snapshot_capture_delayed - Take delayed part of snapshot of the GuC Engine.
- * @snapshot: Previously captured snapshot of job.
- *
- * This captures some data that requires taking some locks, so it cannot be done in signaling path.
- */
-void
-xe_guc_exec_queue_snapshot_capture_delayed(struct xe_guc_submit_exec_queue_snapshot *snapshot)
-{
- int i;
-
- if (!snapshot || !snapshot->lrc)
- return;
-
- for (i = 0; i < snapshot->width; ++i)
- xe_lrc_snapshot_capture_delayed(snapshot->lrc[i]);
-}
-
-/**
- * xe_guc_exec_queue_snapshot_print - Print out a given GuC Engine snapshot.
- * @snapshot: GuC Submit Engine snapshot object.
- * @p: drm_printer where it will be printed out.
- *
- * This function prints out a given GuC Submit Engine snapshot object.
- */
-void
-xe_guc_exec_queue_snapshot_print(struct xe_guc_submit_exec_queue_snapshot *snapshot,
- struct drm_printer *p)
-{
- int i;
-
- if (!snapshot)
- return;
-
- drm_printf(p, "\nGuC ID: %d\n", snapshot->guc.id);
- drm_printf(p, "\tName: %s\n", snapshot->name);
- drm_printf(p, "\tClass: %d\n", snapshot->class);
- drm_printf(p, "\tLogical mask: 0x%x\n", snapshot->logical_mask);
- drm_printf(p, "\tWidth: %d\n", snapshot->width);
- drm_printf(p, "\tRef: %d\n", snapshot->refcount);
- drm_printf(p, "\tTimeout: %ld (ms)\n", snapshot->sched_timeout);
- drm_printf(p, "\tTimeslice: %u (us)\n",
- snapshot->sched_props.timeslice_us);
- drm_printf(p, "\tPreempt timeout: %u (us)\n",
- snapshot->sched_props.preempt_timeout_us);
-
- for (i = 0; snapshot->lrc && i < snapshot->width; ++i)
- xe_lrc_snapshot_print(snapshot->lrc[i], p);
-
- drm_printf(p, "\tSchedule State: 0x%x\n", snapshot->schedule_state);
- drm_printf(p, "\tFlags: 0x%lx\n", snapshot->exec_queue_flags);
-
- if (snapshot->parallel_execution)
- guc_exec_queue_wq_snapshot_print(snapshot, p);
-
- for (i = 0; snapshot->pending_list && i < snapshot->pending_list_size;
- i++)
- drm_printf(p, "\tJob: seqno=%d, fence=%d, finished=%d\n",
- snapshot->pending_list[i].seqno,
- snapshot->pending_list[i].fence,
- snapshot->pending_list[i].finished);
-}
-
-/**
- * xe_guc_exec_queue_snapshot_free - Free all allocated objects for a given
- * snapshot.
- * @snapshot: GuC Submit Engine snapshot object.
- *
- * This function free all the memory that needed to be allocated at capture
- * time.
- */
-void xe_guc_exec_queue_snapshot_free(struct xe_guc_submit_exec_queue_snapshot *snapshot)
-{
- int i;
-
- if (!snapshot)
- return;
-
- if (snapshot->lrc) {
- for (i = 0; i < snapshot->width; i++)
- xe_lrc_snapshot_free(snapshot->lrc[i]);
- kfree(snapshot->lrc);
- }
- kfree(snapshot->pending_list);
- kfree(snapshot);
-}
-
-static void guc_exec_queue_print(struct xe_exec_queue *q, struct drm_printer *p)
-{
- struct xe_guc_submit_exec_queue_snapshot *snapshot;
-
- snapshot = xe_guc_exec_queue_snapshot_capture(q);
- xe_guc_exec_queue_snapshot_print(snapshot, p);
- xe_guc_exec_queue_snapshot_free(snapshot);
-}
-
-/**
- * xe_guc_submit_print - GuC Submit Print.
- * @guc: GuC.
- * @p: drm_printer where it will be printed out.
- *
- * This function capture and prints snapshots of **all** GuC Engines.
- */
-void xe_guc_submit_print(struct xe_guc *guc, struct drm_printer *p)
-{
- struct xe_exec_queue *q;
- unsigned long index;
-
- if (!xe_device_uc_enabled(guc_to_xe(guc)))
- return;
-
- mutex_lock(&guc->submission_state.lock);
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- guc_exec_queue_print(q, p);
- mutex_unlock(&guc->submission_state.lock);
-}
diff --git a/rr-cache/e7c9aafc2297a37f89715cfeed48ccbfb82f76bb/preimage.6 b/rr-cache/e7c9aafc2297a37f89715cfeed48ccbfb82f76bb/preimage.6
deleted file mode 100644
index 110e70f7ee7b..000000000000
--- a/rr-cache/e7c9aafc2297a37f89715cfeed48ccbfb82f76bb/preimage.6
+++ /dev/null
@@ -1,2244 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2022 Intel Corporation
- */
-
-#include "xe_guc_submit.h"
-
-#include <linux/bitfield.h>
-#include <linux/bitmap.h>
-#include <linux/circ_buf.h>
-#include <linux/delay.h>
-#include <linux/dma-fence-array.h>
-#include <linux/math64.h>
-
-#include <drm/drm_managed.h>
-
-#include "abi/guc_actions_abi.h"
-#include "abi/guc_klvs_abi.h"
-#include "regs/xe_lrc_layout.h"
-#include "xe_assert.h"
-#include "xe_devcoredump.h"
-#include "xe_device.h"
-#include "xe_exec_queue.h"
-#include "xe_force_wake.h"
-#include "xe_gpu_scheduler.h"
-#include "xe_gt.h"
-#include "xe_gt_clock.h"
-#include "xe_gt_printk.h"
-#include "xe_guc.h"
-#include "xe_guc_ct.h"
-#include "xe_guc_exec_queue_types.h"
-#include "xe_guc_id_mgr.h"
-#include "xe_guc_submit_types.h"
-#include "xe_hw_engine.h"
-#include "xe_hw_fence.h"
-#include "xe_lrc.h"
-#include "xe_macros.h"
-#include "xe_map.h"
-#include "xe_mocs.h"
-#include "xe_pm.h"
-#include "xe_ring_ops_types.h"
-#include "xe_sched_job.h"
-#include "xe_trace.h"
-#include "xe_vm.h"
-
-static struct xe_guc *
-exec_queue_to_guc(struct xe_exec_queue *q)
-{
- return &q->gt->uc.guc;
-}
-
-/*
- * Helpers for engine state, using an atomic as some of the bits can transition
- * as the same time (e.g. a suspend can be happning at the same time as schedule
- * engine done being processed).
- */
-#define EXEC_QUEUE_STATE_REGISTERED (1 << 0)
-#define EXEC_QUEUE_STATE_ENABLED (1 << 1)
-#define EXEC_QUEUE_STATE_PENDING_ENABLE (1 << 2)
-#define EXEC_QUEUE_STATE_PENDING_DISABLE (1 << 3)
-#define EXEC_QUEUE_STATE_DESTROYED (1 << 4)
-#define EXEC_QUEUE_STATE_SUSPENDED (1 << 5)
-#define EXEC_QUEUE_STATE_RESET (1 << 6)
-#define EXEC_QUEUE_STATE_KILLED (1 << 7)
-#define EXEC_QUEUE_STATE_WEDGED (1 << 8)
-#define EXEC_QUEUE_STATE_BANNED (1 << 9)
-#define EXEC_QUEUE_STATE_CHECK_TIMEOUT (1 << 10)
-#define EXEC_QUEUE_STATE_EXTRA_REF (1 << 11)
-
-static bool exec_queue_registered(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_REGISTERED;
-}
-
-static void set_exec_queue_registered(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_REGISTERED, &q->guc->state);
-}
-
-static void clear_exec_queue_registered(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_REGISTERED, &q->guc->state);
-}
-
-static bool exec_queue_enabled(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_ENABLED;
-}
-
-static void set_exec_queue_enabled(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_ENABLED, &q->guc->state);
-}
-
-static void clear_exec_queue_enabled(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_ENABLED, &q->guc->state);
-}
-
-static bool exec_queue_pending_enable(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_ENABLE;
-}
-
-static void set_exec_queue_pending_enable(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_PENDING_ENABLE, &q->guc->state);
-}
-
-static void clear_exec_queue_pending_enable(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_PENDING_ENABLE, &q->guc->state);
-}
-
-static bool exec_queue_pending_disable(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_DISABLE;
-}
-
-static void set_exec_queue_pending_disable(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_PENDING_DISABLE, &q->guc->state);
-}
-
-static void clear_exec_queue_pending_disable(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_PENDING_DISABLE, &q->guc->state);
-}
-
-static bool exec_queue_destroyed(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_DESTROYED;
-}
-
-static void set_exec_queue_destroyed(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_DESTROYED, &q->guc->state);
-}
-
-static bool exec_queue_banned(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_BANNED;
-}
-
-static void set_exec_queue_banned(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_BANNED, &q->guc->state);
-}
-
-static bool exec_queue_suspended(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_SUSPENDED;
-}
-
-static void set_exec_queue_suspended(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_SUSPENDED, &q->guc->state);
-}
-
-static void clear_exec_queue_suspended(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_SUSPENDED, &q->guc->state);
-}
-
-static bool exec_queue_reset(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_RESET;
-}
-
-static void set_exec_queue_reset(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_RESET, &q->guc->state);
-}
-
-static bool exec_queue_killed(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_KILLED;
-}
-
-static void set_exec_queue_killed(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_KILLED, &q->guc->state);
-}
-
-static bool exec_queue_wedged(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_WEDGED;
-}
-
-static void set_exec_queue_wedged(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_WEDGED, &q->guc->state);
-}
-
-static bool exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_CHECK_TIMEOUT;
-}
-
-static void set_exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_CHECK_TIMEOUT, &q->guc->state);
-}
-
-static void clear_exec_queue_check_timeout(struct xe_exec_queue *q)
-{
- atomic_and(~EXEC_QUEUE_STATE_CHECK_TIMEOUT, &q->guc->state);
-}
-
-static bool exec_queue_extra_ref(struct xe_exec_queue *q)
-{
- return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_EXTRA_REF;
-}
-
-static void set_exec_queue_extra_ref(struct xe_exec_queue *q)
-{
- atomic_or(EXEC_QUEUE_STATE_EXTRA_REF, &q->guc->state);
-}
-
-static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
-{
- return (atomic_read(&q->guc->state) &
- (EXEC_QUEUE_STATE_WEDGED | EXEC_QUEUE_STATE_KILLED |
- EXEC_QUEUE_STATE_BANNED));
-}
-
-#ifdef CONFIG_PROVE_LOCKING
-static int alloc_submit_wq(struct xe_guc *guc)
-{
- int i;
-
- for (i = 0; i < NUM_SUBMIT_WQ; ++i) {
- guc->submission_state.submit_wq_pool[i] =
- alloc_ordered_workqueue("submit_wq", 0);
- if (!guc->submission_state.submit_wq_pool[i])
- goto err_free;
- }
-
- return 0;
-
-err_free:
- while (i)
- destroy_workqueue(guc->submission_state.submit_wq_pool[--i]);
-
- return -ENOMEM;
-}
-
-static void free_submit_wq(struct xe_guc *guc)
-{
- int i;
-
- for (i = 0; i < NUM_SUBMIT_WQ; ++i)
- destroy_workqueue(guc->submission_state.submit_wq_pool[i]);
-}
-
-static struct workqueue_struct *get_submit_wq(struct xe_guc *guc)
-{
- int idx = guc->submission_state.submit_wq_idx++ % NUM_SUBMIT_WQ;
-
- return guc->submission_state.submit_wq_pool[idx];
-}
-#else
-static int alloc_submit_wq(struct xe_guc *guc)
-{
- return 0;
-}
-
-static void free_submit_wq(struct xe_guc *guc)
-{
-
-}
-
-static struct workqueue_struct *get_submit_wq(struct xe_guc *guc)
-{
- return NULL;
-}
-#endif
-
-static void guc_submit_fini(struct drm_device *drm, void *arg)
-{
- struct xe_guc *guc = arg;
-
- xa_destroy(&guc->submission_state.exec_queue_lookup);
- free_submit_wq(guc);
-}
-
-static void guc_submit_wedged_fini(void *arg)
-{
- struct xe_guc *guc = arg;
- struct xe_exec_queue *q;
- unsigned long index;
-
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- if (exec_queue_wedged(q))
- xe_exec_queue_put(q);
-}
-
-static const struct xe_exec_queue_ops guc_exec_queue_ops;
-
-static void primelockdep(struct xe_guc *guc)
-{
- if (!IS_ENABLED(CONFIG_LOCKDEP))
- return;
-
- fs_reclaim_acquire(GFP_KERNEL);
-
- mutex_lock(&guc->submission_state.lock);
- mutex_unlock(&guc->submission_state.lock);
-
- fs_reclaim_release(GFP_KERNEL);
-}
-
-/**
- * xe_guc_submit_init() - Initialize GuC submission.
- * @guc: the &xe_guc to initialize
- * @num_ids: number of GuC context IDs to use
- *
- * The bare-metal or PF driver can pass ~0 as &num_ids to indicate that all
- * GuC context IDs supported by the GuC firmware should be used for submission.
- *
- * Only VF drivers will have to provide explicit number of GuC context IDs
- * that they can use for submission.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_gt *gt = guc_to_gt(guc);
- int err;
-
- err = drmm_mutex_init(&xe->drm, &guc->submission_state.lock);
- if (err)
- return err;
-
- err = xe_guc_id_mgr_init(&guc->submission_state.idm, num_ids);
- if (err)
- return err;
-
- err = alloc_submit_wq(guc);
- if (err)
- return err;
-
- gt->exec_queue_ops = &guc_exec_queue_ops;
-
- xa_init(&guc->submission_state.exec_queue_lookup);
-
- primelockdep(guc);
-
- return drmm_add_action_or_reset(&xe->drm, guc_submit_fini, guc);
-}
-
-static void __release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q, u32 xa_count)
-{
- int i;
-
- lockdep_assert_held(&guc->submission_state.lock);
-
- for (i = 0; i < xa_count; ++i)
- xa_erase(&guc->submission_state.exec_queue_lookup, q->guc->id + i);
-
- xe_guc_id_mgr_release_locked(&guc->submission_state.idm,
- q->guc->id, q->width);
-}
-
-static int alloc_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- int ret;
- void *ptr;
- int i;
-
- /*
- * Must use GFP_NOWAIT as this lock is in the dma fence signalling path,
- * worse case user gets -ENOMEM on engine create and has to try again.
- *
- * FIXME: Have caller pre-alloc or post-alloc /w GFP_KERNEL to prevent
- * failure.
- */
- lockdep_assert_held(&guc->submission_state.lock);
-
- ret = xe_guc_id_mgr_reserve_locked(&guc->submission_state.idm,
- q->width);
- if (ret < 0)
- return ret;
-
- q->guc->id = ret;
-
- for (i = 0; i < q->width; ++i) {
- ptr = xa_store(&guc->submission_state.exec_queue_lookup,
- q->guc->id + i, q, GFP_NOWAIT);
- if (IS_ERR(ptr)) {
- ret = PTR_ERR(ptr);
- goto err_release;
- }
- }
-
- return 0;
-
-err_release:
- __release_guc_id(guc, q, i);
-
- return ret;
-}
-
-static void release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- mutex_lock(&guc->submission_state.lock);
- __release_guc_id(guc, q, q->width);
- mutex_unlock(&guc->submission_state.lock);
-}
-
-struct exec_queue_policy {
- u32 count;
- struct guc_update_exec_queue_policy h2g;
-};
-
-static u32 __guc_exec_queue_policy_action_size(struct exec_queue_policy *policy)
-{
- size_t bytes = sizeof(policy->h2g.header) +
- (sizeof(policy->h2g.klv[0]) * policy->count);
-
- return bytes / sizeof(u32);
-}
-
-static void __guc_exec_queue_policy_start_klv(struct exec_queue_policy *policy,
- u16 guc_id)
-{
- policy->h2g.header.action =
- XE_GUC_ACTION_HOST2GUC_UPDATE_CONTEXT_POLICIES;
- policy->h2g.header.guc_id = guc_id;
- policy->count = 0;
-}
-
-#define MAKE_EXEC_QUEUE_POLICY_ADD(func, id) \
-static void __guc_exec_queue_policy_add_##func(struct exec_queue_policy *policy, \
- u32 data) \
-{ \
- XE_WARN_ON(policy->count >= GUC_CONTEXT_POLICIES_KLV_NUM_IDS); \
-\
- policy->h2g.klv[policy->count].kl = \
- FIELD_PREP(GUC_KLV_0_KEY, \
- GUC_CONTEXT_POLICIES_KLV_ID_##id) | \
- FIELD_PREP(GUC_KLV_0_LEN, 1); \
- policy->h2g.klv[policy->count].value = data; \
- policy->count++; \
-}
-
-MAKE_EXEC_QUEUE_POLICY_ADD(execution_quantum, EXECUTION_QUANTUM)
-MAKE_EXEC_QUEUE_POLICY_ADD(preemption_timeout, PREEMPTION_TIMEOUT)
-MAKE_EXEC_QUEUE_POLICY_ADD(priority, SCHEDULING_PRIORITY)
-#undef MAKE_EXEC_QUEUE_POLICY_ADD
-
-static const int xe_exec_queue_prio_to_guc[] = {
- [XE_EXEC_QUEUE_PRIORITY_LOW] = GUC_CLIENT_PRIORITY_NORMAL,
- [XE_EXEC_QUEUE_PRIORITY_NORMAL] = GUC_CLIENT_PRIORITY_KMD_NORMAL,
- [XE_EXEC_QUEUE_PRIORITY_HIGH] = GUC_CLIENT_PRIORITY_HIGH,
- [XE_EXEC_QUEUE_PRIORITY_KERNEL] = GUC_CLIENT_PRIORITY_KMD_HIGH,
-};
-
-static void init_policies(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- struct exec_queue_policy policy;
- struct xe_device *xe = guc_to_xe(guc);
- enum xe_exec_queue_priority prio = q->sched_props.priority;
- u32 timeslice_us = q->sched_props.timeslice_us;
- u32 preempt_timeout_us = q->sched_props.preempt_timeout_us;
-
- xe_assert(xe, exec_queue_registered(q));
-
- __guc_exec_queue_policy_start_klv(&policy, q->guc->id);
- __guc_exec_queue_policy_add_priority(&policy, xe_exec_queue_prio_to_guc[prio]);
- __guc_exec_queue_policy_add_execution_quantum(&policy, timeslice_us);
- __guc_exec_queue_policy_add_preemption_timeout(&policy, preempt_timeout_us);
-
- xe_guc_ct_send(&guc->ct, (u32 *)&policy.h2g,
- __guc_exec_queue_policy_action_size(&policy), 0, 0);
-}
-
-static void set_min_preemption_timeout(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- struct exec_queue_policy policy;
-
- __guc_exec_queue_policy_start_klv(&policy, q->guc->id);
- __guc_exec_queue_policy_add_preemption_timeout(&policy, 1);
-
- xe_guc_ct_send(&guc->ct, (u32 *)&policy.h2g,
- __guc_exec_queue_policy_action_size(&policy), 0, 0);
-}
-
-#define parallel_read(xe_, map_, field_) \
- xe_map_rd_field(xe_, &map_, 0, struct guc_submit_parallel_scratch, \
- field_)
-#define parallel_write(xe_, map_, field_, val_) \
- xe_map_wr_field(xe_, &map_, 0, struct guc_submit_parallel_scratch, \
- field_, val_)
-
-static void __register_mlrc_exec_queue(struct xe_guc *guc,
- struct xe_exec_queue *q,
- struct guc_ctxt_registration_info *info)
-{
-#define MAX_MLRC_REG_SIZE (13 + XE_HW_ENGINE_MAX_INSTANCE * 2)
- struct xe_device *xe = guc_to_xe(guc);
- u32 action[MAX_MLRC_REG_SIZE];
- int len = 0;
- int i;
-
- xe_assert(xe, xe_exec_queue_is_parallel(q));
-
- action[len++] = XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC;
- action[len++] = info->flags;
- action[len++] = info->context_idx;
- action[len++] = info->engine_class;
- action[len++] = info->engine_submit_mask;
- action[len++] = info->wq_desc_lo;
- action[len++] = info->wq_desc_hi;
- action[len++] = info->wq_base_lo;
- action[len++] = info->wq_base_hi;
- action[len++] = info->wq_size;
- action[len++] = q->width;
- action[len++] = info->hwlrca_lo;
- action[len++] = info->hwlrca_hi;
-
- for (i = 1; i < q->width; ++i) {
- struct xe_lrc *lrc = q->lrc[i];
-
- action[len++] = lower_32_bits(xe_lrc_descriptor(lrc));
- action[len++] = upper_32_bits(xe_lrc_descriptor(lrc));
- }
-
- xe_assert(xe, len <= MAX_MLRC_REG_SIZE);
-#undef MAX_MLRC_REG_SIZE
-
- xe_guc_ct_send(&guc->ct, action, len, 0, 0);
-}
-
-static void __register_exec_queue(struct xe_guc *guc,
- struct guc_ctxt_registration_info *info)
-{
- u32 action[] = {
- XE_GUC_ACTION_REGISTER_CONTEXT,
- info->flags,
- info->context_idx,
- info->engine_class,
- info->engine_submit_mask,
- info->wq_desc_lo,
- info->wq_desc_hi,
- info->wq_base_lo,
- info->wq_base_hi,
- info->wq_size,
- info->hwlrca_lo,
- info->hwlrca_hi,
- };
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
-}
-
-static void register_exec_queue(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_lrc *lrc = q->lrc[0];
- struct guc_ctxt_registration_info info;
-
- xe_assert(xe, !exec_queue_registered(q));
-
- memset(&info, 0, sizeof(info));
- info.context_idx = q->guc->id;
- info.engine_class = xe_engine_class_to_guc_class(q->class);
- info.engine_submit_mask = q->logical_mask;
- info.hwlrca_lo = lower_32_bits(xe_lrc_descriptor(lrc));
- info.hwlrca_hi = upper_32_bits(xe_lrc_descriptor(lrc));
- info.flags = CONTEXT_REGISTRATION_FLAG_KMD;
-
- if (xe_exec_queue_is_parallel(q)) {
- u64 ggtt_addr = xe_lrc_parallel_ggtt_addr(lrc);
- struct iosys_map map = xe_lrc_parallel_map(lrc);
-
- info.wq_desc_lo = lower_32_bits(ggtt_addr +
- offsetof(struct guc_submit_parallel_scratch, wq_desc));
- info.wq_desc_hi = upper_32_bits(ggtt_addr +
- offsetof(struct guc_submit_parallel_scratch, wq_desc));
- info.wq_base_lo = lower_32_bits(ggtt_addr +
- offsetof(struct guc_submit_parallel_scratch, wq[0]));
- info.wq_base_hi = upper_32_bits(ggtt_addr +
- offsetof(struct guc_submit_parallel_scratch, wq[0]));
- info.wq_size = WQ_SIZE;
-
- q->guc->wqi_head = 0;
- q->guc->wqi_tail = 0;
- xe_map_memset(xe, &map, 0, 0, PARALLEL_SCRATCH_SIZE - WQ_SIZE);
- parallel_write(xe, map, wq_desc.wq_status, WQ_STATUS_ACTIVE);
- }
-
- /*
- * We must keep a reference for LR engines if engine is registered with
- * the GuC as jobs signal immediately and can't destroy an engine if the
- * GuC has a reference to it.
- */
- if (xe_exec_queue_is_lr(q))
- xe_exec_queue_get(q);
-
- set_exec_queue_registered(q);
- trace_xe_exec_queue_register(q);
- if (xe_exec_queue_is_parallel(q))
- __register_mlrc_exec_queue(guc, q, &info);
- else
- __register_exec_queue(guc, &info);
- init_policies(guc, q);
-}
-
-static u32 wq_space_until_wrap(struct xe_exec_queue *q)
-{
- return (WQ_SIZE - q->guc->wqi_tail);
-}
-
-static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- unsigned int sleep_period_ms = 1;
-
-#define AVAILABLE_SPACE \
- CIRC_SPACE(q->guc->wqi_tail, q->guc->wqi_head, WQ_SIZE)
- if (wqi_size > AVAILABLE_SPACE) {
-try_again:
- q->guc->wqi_head = parallel_read(xe, map, wq_desc.head);
- if (wqi_size > AVAILABLE_SPACE) {
- if (sleep_period_ms == 1024) {
- xe_gt_reset_async(q->gt);
- return -ENODEV;
- }
-
- msleep(sleep_period_ms);
- sleep_period_ms <<= 1;
- goto try_again;
- }
- }
-#undef AVAILABLE_SPACE
-
- return 0;
-}
-
-static int wq_noop_append(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- u32 len_dw = wq_space_until_wrap(q) / sizeof(u32) - 1;
-
- if (wq_wait_for_space(q, wq_space_until_wrap(q)))
- return -ENODEV;
-
- xe_assert(xe, FIELD_FIT(WQ_LEN_MASK, len_dw));
-
- parallel_write(xe, map, wq[q->guc->wqi_tail / sizeof(u32)],
- FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_NOOP) |
- FIELD_PREP(WQ_LEN_MASK, len_dw));
- q->guc->wqi_tail = 0;
-
- return 0;
-}
-
-static void wq_item_append(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
-#define WQ_HEADER_SIZE 4 /* Includes 1 LRC address too */
- u32 wqi[XE_HW_ENGINE_MAX_INSTANCE + (WQ_HEADER_SIZE - 1)];
- u32 wqi_size = (q->width + (WQ_HEADER_SIZE - 1)) * sizeof(u32);
- u32 len_dw = (wqi_size / sizeof(u32)) - 1;
- int i = 0, j;
-
- if (wqi_size > wq_space_until_wrap(q)) {
- if (wq_noop_append(q))
- return;
- }
- if (wq_wait_for_space(q, wqi_size))
- return;
-
- wqi[i++] = FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_MULTI_LRC) |
- FIELD_PREP(WQ_LEN_MASK, len_dw);
- wqi[i++] = xe_lrc_descriptor(q->lrc[0]);
- wqi[i++] = FIELD_PREP(WQ_GUC_ID_MASK, q->guc->id) |
- FIELD_PREP(WQ_RING_TAIL_MASK, q->lrc[0]->ring.tail / sizeof(u64));
- wqi[i++] = 0;
- for (j = 1; j < q->width; ++j) {
- struct xe_lrc *lrc = q->lrc[j];
-
- wqi[i++] = lrc->ring.tail / sizeof(u64);
- }
-
- xe_assert(xe, i == wqi_size / sizeof(u32));
-
- iosys_map_incr(&map, offsetof(struct guc_submit_parallel_scratch,
- wq[q->guc->wqi_tail / sizeof(u32)]));
- xe_map_memcpy_to(xe, &map, 0, wqi, wqi_size);
- q->guc->wqi_tail += wqi_size;
- xe_assert(xe, q->guc->wqi_tail <= WQ_SIZE);
-
- xe_device_wmb(xe);
-
- map = xe_lrc_parallel_map(q->lrc[0]);
- parallel_write(xe, map, wq_desc.tail, q->guc->wqi_tail);
-}
-
-#define RESUME_PENDING ~0x0ull
-static void submit_exec_queue(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_lrc *lrc = q->lrc[0];
- u32 action[3];
- u32 g2h_len = 0;
- u32 num_g2h = 0;
- int len = 0;
- bool extra_submit = false;
-
- xe_assert(xe, exec_queue_registered(q));
-
- if (xe_exec_queue_is_parallel(q))
- wq_item_append(q);
- else
- xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
-
- if (exec_queue_suspended(q) && !xe_exec_queue_is_parallel(q))
- return;
-
- if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) {
- action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET;
- action[len++] = q->guc->id;
- action[len++] = GUC_CONTEXT_ENABLE;
- g2h_len = G2H_LEN_DW_SCHED_CONTEXT_MODE_SET;
- num_g2h = 1;
- if (xe_exec_queue_is_parallel(q))
- extra_submit = true;
-
- q->guc->resume_time = RESUME_PENDING;
- set_exec_queue_pending_enable(q);
- set_exec_queue_enabled(q);
- trace_xe_exec_queue_scheduling_enable(q);
- } else {
- action[len++] = XE_GUC_ACTION_SCHED_CONTEXT;
- action[len++] = q->guc->id;
- trace_xe_exec_queue_submit(q);
- }
-
- xe_guc_ct_send(&guc->ct, action, len, g2h_len, num_g2h);
-
- if (extra_submit) {
- len = 0;
- action[len++] = XE_GUC_ACTION_SCHED_CONTEXT;
- action[len++] = q->guc->id;
- trace_xe_exec_queue_submit(q);
-
- xe_guc_ct_send(&guc->ct, action, len, 0, 0);
- }
-}
-
-static struct dma_fence *
-guc_exec_queue_run_job(struct drm_sched_job *drm_job)
-{
- struct xe_sched_job *job = to_xe_sched_job(drm_job);
- struct xe_exec_queue *q = job->q;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- bool lr = xe_exec_queue_is_lr(q);
-
- xe_assert(xe, !(exec_queue_destroyed(q) || exec_queue_pending_disable(q)) ||
- exec_queue_banned(q) || exec_queue_suspended(q));
-
- trace_xe_sched_job_run(job);
-
- if (!exec_queue_killed_or_banned_or_wedged(q) && !xe_sched_job_is_error(job)) {
- if (!exec_queue_registered(q))
- register_exec_queue(q);
- if (!lr) /* LR jobs are emitted in the exec IOCTL */
- q->ring_ops->emit_job(job);
- submit_exec_queue(q);
- }
-
- if (lr) {
- xe_sched_job_set_error(job, -EOPNOTSUPP);
- return NULL;
- } else if (test_and_set_bit(JOB_FLAG_SUBMIT, &job->fence->flags)) {
- return job->fence;
- } else {
- return dma_fence_get(job->fence);
- }
-}
-
-static void guc_exec_queue_free_job(struct drm_sched_job *drm_job)
-{
- struct xe_sched_job *job = to_xe_sched_job(drm_job);
-
- xe_exec_queue_update_run_ticks(job->q);
-
- trace_xe_sched_job_free(job);
- xe_sched_job_put(job);
-}
-
-static int guc_read_stopped(struct xe_guc *guc)
-{
- return atomic_read(&guc->submission_state.stopped);
-}
-
-#define MAKE_SCHED_CONTEXT_ACTION(q, enable_disable) \
- u32 action[] = { \
- XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET, \
- q->guc->id, \
- GUC_CONTEXT_##enable_disable, \
- }
-
-static void disable_scheduling_deregister(struct xe_guc *guc,
- struct xe_exec_queue *q)
-{
- MAKE_SCHED_CONTEXT_ACTION(q, DISABLE);
- struct xe_device *xe = guc_to_xe(guc);
- int ret;
-
- set_min_preemption_timeout(guc, q);
- smp_rmb();
- ret = wait_event_timeout(guc->ct.wq, !exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret) {
- struct xe_gpu_scheduler *sched = &q->guc->sched;
-
- drm_warn(&xe->drm, "Pending enable failed to respond");
- xe_sched_submission_start(sched);
- xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(sched);
- return;
- }
-
- clear_exec_queue_enabled(q);
- set_exec_queue_pending_disable(q);
- set_exec_queue_destroyed(q);
- trace_xe_exec_queue_scheduling_disable(q);
-
- /*
- * Reserve space for both G2H here as the 2nd G2H is sent from a G2H
- * handler and we are not allowed to reserved G2H space in handlers.
- */
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET +
- G2H_LEN_DW_DEREGISTER_CONTEXT, 2);
-}
-
-static void xe_guc_exec_queue_trigger_cleanup(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- /** to wakeup xe_wait_user_fence ioctl if exec queue is reset */
- wake_up_all(&xe->ufence_wq);
-
- if (xe_exec_queue_is_lr(q))
- queue_work(guc_to_gt(guc)->ordered_wq, &q->guc->lr_tdr);
- else
- xe_sched_tdr_queue_imm(&q->guc->sched);
-}
-
-/**
- * xe_guc_submit_wedge() - Wedge GuC submission
- * @guc: the GuC object
- *
- * Save exec queue's registered with GuC state by taking a ref to each queue.
- * Register a DRMM handler to drop refs upon driver unload.
- */
-void xe_guc_submit_wedge(struct xe_guc *guc)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- unsigned long index;
- int err;
-
- xe_gt_assert(guc_to_gt(guc), guc_to_xe(guc)->wedged.mode);
-
- err = devm_add_action_or_reset(guc_to_xe(guc)->drm.dev,
- guc_submit_wedged_fini, guc);
- if (err) {
- drm_err(&xe->drm, "Failed to register xe_guc_submit clean-up on wedged.mode=2. Although device is wedged.\n");
- return;
- }
-
- mutex_lock(&guc->submission_state.lock);
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- if (xe_exec_queue_get_unless_zero(q))
- set_exec_queue_wedged(q);
- mutex_unlock(&guc->submission_state.lock);
-}
-
-static bool guc_submit_hint_wedged(struct xe_guc *guc)
-{
- struct xe_device *xe = guc_to_xe(guc);
-
- if (xe->wedged.mode != 2)
- return false;
-
- if (xe_device_wedged(xe))
- return true;
-
- xe_device_declare_wedged(xe);
-
- return true;
-}
-
-static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
-{
- struct xe_guc_exec_queue *ge =
- container_of(w, struct xe_guc_exec_queue, lr_tdr);
- struct xe_exec_queue *q = ge->q;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_gpu_scheduler *sched = &ge->sched;
- bool wedged;
-
- xe_assert(xe, xe_exec_queue_is_lr(q));
- trace_xe_exec_queue_lr_cleanup(q);
-
- wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
-
- /* Kill the run_job / process_msg entry points */
- xe_sched_submission_stop(sched);
-
- /*
- * Engine state now mostly stable, disable scheduling / deregister if
- * needed. This cleanup routine might be called multiple times, where
- * the actual async engine deregister drops the final engine ref.
- * Calling disable_scheduling_deregister will mark the engine as
- * destroyed and fire off the CT requests to disable scheduling /
- * deregister, which we only want to do once. We also don't want to mark
- * the engine as pending_disable again as this may race with the
- * xe_guc_deregister_done_handler() which treats it as an unexpected
- * state.
- */
- if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) {
- struct xe_guc *guc = exec_queue_to_guc(q);
- int ret;
-
- set_exec_queue_banned(q);
- disable_scheduling_deregister(guc, q);
-
- /*
- * Must wait for scheduling to be disabled before signalling
- * any fences, if GT broken the GT reset code should signal us.
- */
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_disable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret) {
- drm_warn(&xe->drm, "Schedule disable failed to respond");
- xe_sched_submission_start(sched);
- xe_gt_reset_async(q->gt);
- return;
- }
- }
-
- xe_sched_submission_start(sched);
-}
-
-#define ADJUST_FIVE_PERCENT(__t) mul_u64_u32_div(__t, 105, 100)
-
-static bool check_timeout(struct xe_exec_queue *q, struct xe_sched_job *job)
-{
- struct xe_gt *gt = guc_to_gt(exec_queue_to_guc(q));
- u32 ctx_timestamp = xe_lrc_ctx_timestamp(q->lrc[0]);
- u32 ctx_job_timestamp = xe_lrc_ctx_job_timestamp(q->lrc[0]);
- u32 timeout_ms = q->sched_props.job_timeout_ms;
- u32 diff;
- u64 running_time_ms;
-
- /*
- * Counter wraps at ~223s at the usual 19.2MHz, be paranoid catch
- * possible overflows with a high timeout.
- */
- xe_gt_assert(gt, timeout_ms < 100 * MSEC_PER_SEC);
-
- if (ctx_timestamp < ctx_job_timestamp)
- diff = ctx_timestamp + U32_MAX - ctx_job_timestamp;
- else
- diff = ctx_timestamp - ctx_job_timestamp;
-
- /*
- * Ensure timeout is within 5% to account for an GuC scheduling latency
- */
- running_time_ms =
- ADJUST_FIVE_PERCENT(xe_gt_clock_interval_to_ms(gt, diff));
-
- xe_gt_dbg(gt,
- "Check job timeout: seqno=%u, lrc_seqno=%u, guc_id=%d, running_time_ms=%llu, timeout_ms=%u, diff=0x%08x",
- xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
- q->guc->id, running_time_ms, timeout_ms, diff);
-
- return running_time_ms >= timeout_ms;
-}
-
-static void enable_scheduling(struct xe_exec_queue *q)
-{
- MAKE_SCHED_CONTEXT_ACTION(q, ENABLE);
- struct xe_guc *guc = exec_queue_to_guc(q);
- int ret;
-
- xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
-
- set_exec_queue_pending_enable(q);
- set_exec_queue_enabled(q);
- trace_xe_exec_queue_scheduling_enable(q);
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1);
-
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc)) {
- xe_gt_warn(guc_to_gt(guc), "Schedule enable failed to respond");
- set_exec_queue_banned(q);
- xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(&q->guc->sched);
- }
-}
-
-static void disable_scheduling(struct xe_exec_queue *q, bool immediate)
-{
- MAKE_SCHED_CONTEXT_ACTION(q, DISABLE);
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
-
- if (immediate)
- set_min_preemption_timeout(guc, q);
- clear_exec_queue_enabled(q);
- set_exec_queue_pending_disable(q);
- trace_xe_exec_queue_scheduling_disable(q);
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_SCHED_CONTEXT_MODE_SET, 1);
-}
-
-static void __deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- u32 action[] = {
- XE_GUC_ACTION_DEREGISTER_CONTEXT,
- q->guc->id,
- };
-
- xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
-
- set_exec_queue_destroyed(q);
- trace_xe_exec_queue_deregister(q);
-
- xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action),
- G2H_LEN_DW_DEREGISTER_CONTEXT, 1);
-}
-
-static enum drm_gpu_sched_stat
-guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
-{
- struct xe_sched_job *job = to_xe_sched_job(drm_job);
- struct xe_sched_job *tmp_job;
- struct xe_exec_queue *q = job->q;
- struct xe_gpu_scheduler *sched = &q->guc->sched;
- struct xe_guc *guc = exec_queue_to_guc(q);
-<<<<<<<
-=======
- const char *process_name = "no process";
->>>>>>>
- int err = -ETIME;
- int i = 0;
- bool wedged, skip_timeout_check;
-
- /*
- * TDR has fired before free job worker. Common if exec queue
- * immediately closed after last fence signaled.
- */
- if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &job->fence->flags)) {
- guc_exec_queue_free_job(drm_job);
-
- return DRM_GPU_SCHED_STAT_NOMINAL;
- }
-
- /* Kill the run_job entry point */
- xe_sched_submission_stop(sched);
-
- /* Must check all state after stopping scheduler */
- skip_timeout_check = exec_queue_reset(q) ||
- exec_queue_killed_or_banned_or_wedged(q) ||
- exec_queue_destroyed(q);
-
- /* Job hasn't started, can't be timed out */
- if (!skip_timeout_check && !xe_sched_job_started(job))
- goto rearm;
-
- /*
- * XXX: Sampling timeout doesn't work in wedged mode as we have to
- * modify scheduling state to read timestamp. We could read the
- * timestamp from a register to accumulate current running time but this
- * doesn't work for SRIOV. For now assuming timeouts in wedged mode are
- * genuine timeouts.
- */
- wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
-
- /* Engine state now stable, disable scheduling to check timestamp */
- if (!wedged && exec_queue_registered(q)) {
- int ret;
-
- if (exec_queue_reset(q))
- err = -EIO;
-
- if (!exec_queue_destroyed(q)) {
- /*
- * Wait for any pending G2H to flush out before
- * modifying state
- */
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_enable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc))
- goto trigger_reset;
-
- /*
- * Flag communicates to G2H handler that schedule
- * disable originated from a timeout check. The G2H then
- * avoid triggering cleanup or deregistering the exec
- * queue.
- */
- set_exec_queue_check_timeout(q);
- disable_scheduling(q, skip_timeout_check);
- }
-
- /*
- * Must wait for scheduling to be disabled before signalling
- * any fences, if GT broken the GT reset code should signal us.
- *
- * FIXME: Tests can generate a ton of 0x6000 (IOMMU CAT fault
- * error) messages which can cause the schedule disable to get
- * lost. If this occurs, trigger a GT reset to recover.
- */
- smp_rmb();
- ret = wait_event_timeout(guc->ct.wq,
- !exec_queue_pending_disable(q) ||
- guc_read_stopped(guc), HZ * 5);
- if (!ret || guc_read_stopped(guc)) {
-trigger_reset:
- if (!ret)
- xe_gt_warn(guc_to_gt(guc), "Schedule disable failed to respond");
- set_exec_queue_extra_ref(q);
- xe_exec_queue_get(q); /* GT reset owns this */
- set_exec_queue_banned(q);
- xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(sched);
- goto rearm;
- }
- }
-
- /*
- * Check if job is actually timed out, if so restart job execution and TDR
- */
- if (!wedged && !skip_timeout_check && !check_timeout(q, job) &&
- !exec_queue_reset(q) && exec_queue_registered(q)) {
- clear_exec_queue_check_timeout(q);
- goto sched_enable;
- }
-
-<<<<<<<
- if (q->vm && q->vm->xef) {
- process_name = q->vm->xef->process_name;
- pid = q->vm->xef->pid;
- }
- xe_gt_notice(guc_to_gt(guc), "Timedout job: seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx in %s [%d]",
- xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
- q->guc->id, q->flags, process_name, pid);
-
-=======
- xe_gt_notice(guc_to_gt(guc), "Timedout job: seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx",
- xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
- q->guc->id, q->flags);
->>>>>>>
- trace_xe_sched_job_timedout(job);
-
- if (!exec_queue_killed(q))
- xe_devcoredump(job);
-
- /*
- * Kernel jobs should never fail, nor should VM jobs if they do
- * somethings has gone wrong and the GT needs a reset
- */
- xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_KERNEL,
- "Kernel-submitted job timed out\n");
- xe_gt_WARN(q->gt, q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q),
- "VM job timed out on non-killed execqueue\n");
- if (!wedged && (q->flags & EXEC_QUEUE_FLAG_KERNEL ||
- (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)))) {
- if (!xe_sched_invalidate_job(job, 2)) {
- clear_exec_queue_check_timeout(q);
- xe_gt_reset_async(q->gt);
- goto rearm;
- }
- }
-
- /* Finish cleaning up exec queue via deregister */
- set_exec_queue_banned(q);
- if (!wedged && exec_queue_registered(q) && !exec_queue_destroyed(q)) {
- set_exec_queue_extra_ref(q);
- xe_exec_queue_get(q);
- __deregister_exec_queue(guc, q);
- }
-
- /* Stop fence signaling */
- xe_hw_fence_irq_stop(q->fence_irq);
-
- /*
- * Fence state now stable, stop / start scheduler which cleans up any
- * fences that are complete
- */
- xe_sched_add_pending_job(sched, job);
- xe_sched_submission_start(sched);
-
- xe_guc_exec_queue_trigger_cleanup(q);
-
- /* Mark all outstanding jobs as bad, thus completing them */
- spin_lock(&sched->base.job_list_lock);
- list_for_each_entry(tmp_job, &sched->base.pending_list, drm.list)
- xe_sched_job_set_error(tmp_job, !i++ ? err : -ECANCELED);
- spin_unlock(&sched->base.job_list_lock);
-
- /* Start fence signaling */
- xe_hw_fence_irq_start(q->fence_irq);
-
- return DRM_GPU_SCHED_STAT_NOMINAL;
-
-sched_enable:
- enable_scheduling(q);
-rearm:
- /*
- * XXX: Ideally want to adjust timeout based on current exection time
- * but there is not currently an easy way to do in DRM scheduler. With
- * some thought, do this in a follow up.
- */
- xe_sched_add_pending_job(sched, job);
- xe_sched_submission_start(sched);
-
- return DRM_GPU_SCHED_STAT_NOMINAL;
-}
-
-static void __guc_exec_queue_fini_async(struct work_struct *w)
-{
- struct xe_guc_exec_queue *ge =
- container_of(w, struct xe_guc_exec_queue, fini_async);
- struct xe_exec_queue *q = ge->q;
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- xe_pm_runtime_get(guc_to_xe(guc));
- trace_xe_exec_queue_destroy(q);
-
- if (xe_exec_queue_is_lr(q))
- cancel_work_sync(&ge->lr_tdr);
- release_guc_id(guc, q);
- xe_sched_entity_fini(&ge->entity);
- xe_sched_fini(&ge->sched);
-
- kfree(ge);
- xe_exec_queue_fini(q);
- xe_pm_runtime_put(guc_to_xe(guc));
-}
-
-static void guc_exec_queue_fini_async(struct xe_exec_queue *q)
-{
- INIT_WORK(&q->guc->fini_async, __guc_exec_queue_fini_async);
-
- /* We must block on kernel engines so slabs are empty on driver unload */
- if (q->flags & EXEC_QUEUE_FLAG_PERMANENT || exec_queue_wedged(q))
- __guc_exec_queue_fini_async(&q->guc->fini_async);
- else
- queue_work(system_wq, &q->guc->fini_async);
-}
-
-static void __guc_exec_queue_fini(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- /*
- * Might be done from within the GPU scheduler, need to do async as we
- * fini the scheduler when the engine is fini'd, the scheduler can't
- * complete fini within itself (circular dependency). Async resolves
- * this we and don't really care when everything is fini'd, just that it
- * is.
- */
- guc_exec_queue_fini_async(q);
-}
-
-static void __guc_exec_queue_process_msg_cleanup(struct xe_sched_msg *msg)
-{
- struct xe_exec_queue *q = msg->private_data;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, !(q->flags & EXEC_QUEUE_FLAG_PERMANENT));
- trace_xe_exec_queue_cleanup_entity(q);
-
- if (exec_queue_registered(q))
- disable_scheduling_deregister(guc, q);
- else
- __guc_exec_queue_fini(guc, q);
-}
-
-static bool guc_exec_queue_allowed_to_change_state(struct xe_exec_queue *q)
-{
- return !exec_queue_killed_or_banned_or_wedged(q) && exec_queue_registered(q);
-}
-
-static void __guc_exec_queue_process_msg_set_sched_props(struct xe_sched_msg *msg)
-{
- struct xe_exec_queue *q = msg->private_data;
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- if (guc_exec_queue_allowed_to_change_state(q))
- init_policies(guc, q);
- kfree(msg);
-}
-
-static void suspend_fence_signal(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, exec_queue_suspended(q) || exec_queue_killed(q) ||
- guc_read_stopped(guc));
- xe_assert(xe, q->guc->suspend_pending);
-
- q->guc->suspend_pending = false;
- smp_wmb();
- wake_up(&q->guc->suspend_wait);
-}
-
-static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
-{
- struct xe_exec_queue *q = msg->private_data;
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) &&
- exec_queue_enabled(q)) {
- wait_event(guc->ct.wq, q->guc->resume_time != RESUME_PENDING ||
- guc_read_stopped(guc));
-
- if (!guc_read_stopped(guc)) {
- s64 since_resume_ms =
- ktime_ms_delta(ktime_get(),
- q->guc->resume_time);
- s64 wait_ms = q->vm->preempt.min_run_period_ms -
- since_resume_ms;
-
- if (wait_ms > 0 && q->guc->resume_time)
- msleep(wait_ms);
-
- set_exec_queue_suspended(q);
- disable_scheduling(q, false);
- }
- } else if (q->guc->suspend_pending) {
- set_exec_queue_suspended(q);
- suspend_fence_signal(q);
- }
-}
-
-static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
-{
- struct xe_exec_queue *q = msg->private_data;
-
- if (guc_exec_queue_allowed_to_change_state(q)) {
- q->guc->resume_time = RESUME_PENDING;
- clear_exec_queue_suspended(q);
- enable_scheduling(q);
- } else {
- clear_exec_queue_suspended(q);
- }
-}
-
-#define CLEANUP 1 /* Non-zero values to catch uninitialized msg */
-#define SET_SCHED_PROPS 2
-#define SUSPEND 3
-#define RESUME 4
-
-static void guc_exec_queue_process_msg(struct xe_sched_msg *msg)
-{
- trace_xe_sched_msg_recv(msg);
-
- switch (msg->opcode) {
- case CLEANUP:
- __guc_exec_queue_process_msg_cleanup(msg);
- break;
- case SET_SCHED_PROPS:
- __guc_exec_queue_process_msg_set_sched_props(msg);
- break;
- case SUSPEND:
- __guc_exec_queue_process_msg_suspend(msg);
- break;
- case RESUME:
- __guc_exec_queue_process_msg_resume(msg);
- break;
- default:
- XE_WARN_ON("Unknown message type");
- }
-
- xe_pm_runtime_put(guc_to_xe(exec_queue_to_guc(msg->private_data)));
-}
-
-static const struct drm_sched_backend_ops drm_sched_ops = {
- .run_job = guc_exec_queue_run_job,
- .free_job = guc_exec_queue_free_job,
- .timedout_job = guc_exec_queue_timedout_job,
-};
-
-static const struct xe_sched_backend_ops xe_sched_ops = {
- .process_msg = guc_exec_queue_process_msg,
-};
-
-static int guc_exec_queue_init(struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_guc_exec_queue *ge;
- long timeout;
- int err;
-
- xe_assert(xe, xe_device_uc_enabled(guc_to_xe(guc)));
-
- ge = kzalloc(sizeof(*ge), GFP_KERNEL);
- if (!ge)
- return -ENOMEM;
-
- q->guc = ge;
- ge->q = q;
- init_waitqueue_head(&ge->suspend_wait);
-
- timeout = (q->vm && xe_vm_in_lr_mode(q->vm)) ? MAX_SCHEDULE_TIMEOUT :
- msecs_to_jiffies(q->sched_props.job_timeout_ms);
- err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
- get_submit_wq(guc),
- q->lrc[0]->ring.size / MAX_JOB_SIZE_BYTES, 64,
- timeout, guc_to_gt(guc)->ordered_wq, NULL,
- q->name, gt_to_xe(q->gt)->drm.dev);
- if (err)
- goto err_free;
-
- sched = &ge->sched;
- err = xe_sched_entity_init(&ge->entity, sched);
- if (err)
- goto err_sched;
-
- if (xe_exec_queue_is_lr(q))
- INIT_WORK(&q->guc->lr_tdr, xe_guc_exec_queue_lr_cleanup);
-
- mutex_lock(&guc->submission_state.lock);
-
- err = alloc_guc_id(guc, q);
- if (err)
- goto err_entity;
-
- q->entity = &ge->entity;
-
- if (guc_read_stopped(guc))
- xe_sched_stop(sched);
-
- mutex_unlock(&guc->submission_state.lock);
-
- xe_exec_queue_assign_name(q, q->guc->id);
-
- trace_xe_exec_queue_create(q);
-
- return 0;
-
-err_entity:
- mutex_unlock(&guc->submission_state.lock);
- xe_sched_entity_fini(&ge->entity);
-err_sched:
- xe_sched_fini(&ge->sched);
-err_free:
- kfree(ge);
-
- return err;
-}
-
-static void guc_exec_queue_kill(struct xe_exec_queue *q)
-{
- trace_xe_exec_queue_kill(q);
- set_exec_queue_killed(q);
- xe_guc_exec_queue_trigger_cleanup(q);
-}
-
-static void guc_exec_queue_add_msg(struct xe_exec_queue *q, struct xe_sched_msg *msg,
- u32 opcode)
-{
- xe_pm_runtime_get_noresume(guc_to_xe(exec_queue_to_guc(q)));
-
- INIT_LIST_HEAD(&msg->link);
- msg->opcode = opcode;
- msg->private_data = q;
-
- trace_xe_sched_msg_add(msg);
- xe_sched_add_msg(&q->guc->sched, msg);
-}
-
-#define STATIC_MSG_CLEANUP 0
-#define STATIC_MSG_SUSPEND 1
-#define STATIC_MSG_RESUME 2
-static void guc_exec_queue_fini(struct xe_exec_queue *q)
-{
- struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_CLEANUP;
-
- if (!(q->flags & EXEC_QUEUE_FLAG_PERMANENT) && !exec_queue_wedged(q))
- guc_exec_queue_add_msg(q, msg, CLEANUP);
- else
- __guc_exec_queue_fini(exec_queue_to_guc(q), q);
-}
-
-static int guc_exec_queue_set_priority(struct xe_exec_queue *q,
- enum xe_exec_queue_priority priority)
-{
- struct xe_sched_msg *msg;
-
- if (q->sched_props.priority == priority ||
- exec_queue_killed_or_banned_or_wedged(q))
- return 0;
-
- msg = kmalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- q->sched_props.priority = priority;
- guc_exec_queue_add_msg(q, msg, SET_SCHED_PROPS);
-
- return 0;
-}
-
-static int guc_exec_queue_set_timeslice(struct xe_exec_queue *q, u32 timeslice_us)
-{
- struct xe_sched_msg *msg;
-
- if (q->sched_props.timeslice_us == timeslice_us ||
- exec_queue_killed_or_banned_or_wedged(q))
- return 0;
-
- msg = kmalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- q->sched_props.timeslice_us = timeslice_us;
- guc_exec_queue_add_msg(q, msg, SET_SCHED_PROPS);
-
- return 0;
-}
-
-static int guc_exec_queue_set_preempt_timeout(struct xe_exec_queue *q,
- u32 preempt_timeout_us)
-{
- struct xe_sched_msg *msg;
-
- if (q->sched_props.preempt_timeout_us == preempt_timeout_us ||
- exec_queue_killed_or_banned_or_wedged(q))
- return 0;
-
- msg = kmalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
- q->sched_props.preempt_timeout_us = preempt_timeout_us;
- guc_exec_queue_add_msg(q, msg, SET_SCHED_PROPS);
-
- return 0;
-}
-
-static int guc_exec_queue_suspend(struct xe_exec_queue *q)
-{
- struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_SUSPEND;
-
- if (exec_queue_killed_or_banned_or_wedged(q) || q->guc->suspend_pending)
- return -EINVAL;
-
- q->guc->suspend_pending = true;
- guc_exec_queue_add_msg(q, msg, SUSPEND);
-
- return 0;
-}
-
-static void guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
-
- wait_event(q->guc->suspend_wait, !q->guc->suspend_pending ||
- guc_read_stopped(guc));
-}
-
-static void guc_exec_queue_resume(struct xe_exec_queue *q)
-{
- struct xe_sched_msg *msg = q->guc->static_msgs + STATIC_MSG_RESUME;
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, !q->guc->suspend_pending);
-
- guc_exec_queue_add_msg(q, msg, RESUME);
-}
-
-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);
-}
-
-/*
- * 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,
- * really shouldn't do much other than trap into the DRM scheduler which
- * synchronizes these operations.
- */
-static const struct xe_exec_queue_ops guc_exec_queue_ops = {
- .init = guc_exec_queue_init,
- .kill = guc_exec_queue_kill,
- .fini = guc_exec_queue_fini,
- .set_priority = guc_exec_queue_set_priority,
- .set_timeslice = guc_exec_queue_set_timeslice,
- .set_preempt_timeout = guc_exec_queue_set_preempt_timeout,
- .suspend = guc_exec_queue_suspend,
- .suspend_wait = guc_exec_queue_suspend_wait,
- .resume = guc_exec_queue_resume,
- .reset_status = guc_exec_queue_reset_status,
-};
-
-static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched = &q->guc->sched;
-
- /* Stop scheduling + flush any DRM scheduler operations */
- xe_sched_submission_stop(sched);
-
- /* Clean up lost G2H + reset engine state */
- if (exec_queue_registered(q)) {
- if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q))
- xe_exec_queue_put(q);
- else if (exec_queue_destroyed(q))
- __guc_exec_queue_fini(guc, q);
- }
- if (q->guc->suspend_pending) {
- set_exec_queue_suspended(q);
- suspend_fence_signal(q);
- }
- atomic_and(EXEC_QUEUE_STATE_WEDGED | EXEC_QUEUE_STATE_BANNED |
- EXEC_QUEUE_STATE_KILLED | EXEC_QUEUE_STATE_DESTROYED |
- EXEC_QUEUE_STATE_SUSPENDED,
- &q->guc->state);
- q->guc->resume_time = 0;
- trace_xe_exec_queue_stop(q);
-
- /*
- * Ban any engine (aside from kernel and engines used for VM ops) with a
- * started but not complete job or if a job has gone through a GT reset
- * more than twice.
- */
- if (!(q->flags & (EXEC_QUEUE_FLAG_KERNEL | EXEC_QUEUE_FLAG_VM))) {
- struct xe_sched_job *job = xe_sched_first_pending_job(sched);
- bool ban = false;
-
- if (job) {
- if ((xe_sched_job_started(job) &&
- !xe_sched_job_completed(job)) ||
- xe_sched_invalidate_job(job, 2)) {
- trace_xe_sched_job_ban(job);
- ban = true;
- }
- } else if (xe_exec_queue_is_lr(q) &&
- (xe_lrc_ring_head(q->lrc[0]) != xe_lrc_ring_tail(q->lrc[0]))) {
- ban = true;
- }
-
- if (ban) {
- set_exec_queue_banned(q);
- xe_guc_exec_queue_trigger_cleanup(q);
- }
- }
-}
-
-int xe_guc_submit_reset_prepare(struct xe_guc *guc)
-{
- int ret;
-
- /*
- * Using an atomic here rather than submission_state.lock as this
- * function can be called while holding the CT lock (engine reset
- * failure). submission_state.lock needs the CT lock to resubmit jobs.
- * Atomic is not ideal, but it works to prevent against concurrent reset
- * and releasing any TDRs waiting on guc->submission_state.stopped.
- */
- ret = atomic_fetch_or(1, &guc->submission_state.stopped);
- smp_wmb();
- wake_up_all(&guc->ct.wq);
-
- return ret;
-}
-
-void xe_guc_submit_reset_wait(struct xe_guc *guc)
-{
- wait_event(guc->ct.wq, xe_device_wedged(guc_to_xe(guc)) ||
- !guc_read_stopped(guc));
-}
-
-void xe_guc_submit_stop(struct xe_guc *guc)
-{
- struct xe_exec_queue *q;
- unsigned long index;
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, guc_read_stopped(guc) == 1);
-
- mutex_lock(&guc->submission_state.lock);
-
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- guc_exec_queue_stop(guc, q);
-
- mutex_unlock(&guc->submission_state.lock);
-
- /*
- * No one can enter the backend at this point, aside from new engine
- * creation which is protected by guc->submission_state.lock.
- */
-
-}
-
-static void guc_exec_queue_start(struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched = &q->guc->sched;
-
- if (!exec_queue_killed_or_banned_or_wedged(q)) {
- int i;
-
- trace_xe_exec_queue_resubmit(q);
- for (i = 0; i < q->width; ++i)
- xe_lrc_set_ring_head(q->lrc[i], q->lrc[i]->ring.tail);
- xe_sched_resubmit_jobs(sched);
- }
-
- xe_sched_submission_start(sched);
-}
-
-int xe_guc_submit_start(struct xe_guc *guc)
-{
- struct xe_exec_queue *q;
- unsigned long index;
- struct xe_device *xe = guc_to_xe(guc);
-
- xe_assert(xe, guc_read_stopped(guc) == 1);
-
- mutex_lock(&guc->submission_state.lock);
- atomic_dec(&guc->submission_state.stopped);
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- guc_exec_queue_start(q);
- mutex_unlock(&guc->submission_state.lock);
-
- wake_up_all(&guc->ct.wq);
-
- return 0;
-}
-
-static struct xe_exec_queue *
-g2h_exec_queue_lookup(struct xe_guc *guc, u32 guc_id)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
-
- if (unlikely(guc_id >= GUC_ID_MAX)) {
- drm_err(&xe->drm, "Invalid guc_id %u", guc_id);
- return NULL;
- }
-
- q = xa_load(&guc->submission_state.exec_queue_lookup, guc_id);
- if (unlikely(!q)) {
- drm_err(&xe->drm, "Not engine present for guc_id %u", guc_id);
- return NULL;
- }
-
- xe_assert(xe, guc_id >= q->guc->id);
- xe_assert(xe, guc_id < (q->guc->id + q->width));
-
- return q;
-}
-
-static void deregister_exec_queue(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- u32 action[] = {
- XE_GUC_ACTION_DEREGISTER_CONTEXT,
- q->guc->id,
- };
-
- xe_gt_assert(guc_to_gt(guc), exec_queue_destroyed(q));
- xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_disable(q));
- xe_gt_assert(guc_to_gt(guc), !exec_queue_pending_enable(q));
-
- trace_xe_exec_queue_deregister(q);
-
- xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action));
-}
-
-static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
- u32 runnable_state)
-{
- trace_xe_exec_queue_scheduling_done(q);
-
- if (runnable_state == 1) {
- xe_gt_assert(guc_to_gt(guc), exec_queue_pending_enable(q));
-
- q->guc->resume_time = ktime_get();
- clear_exec_queue_pending_enable(q);
- smp_wmb();
- wake_up_all(&guc->ct.wq);
- } else {
- bool check_timeout = exec_queue_check_timeout(q);
-
- xe_gt_assert(guc_to_gt(guc), runnable_state == 0);
- xe_gt_assert(guc_to_gt(guc), exec_queue_pending_disable(q));
-
- clear_exec_queue_pending_disable(q);
- if (q->guc->suspend_pending) {
- suspend_fence_signal(q);
- } else {
- if (exec_queue_banned(q) || check_timeout) {
- smp_wmb();
- wake_up_all(&guc->ct.wq);
- }
- if (!check_timeout)
- deregister_exec_queue(guc, q);
- }
- }
-}
-
-int xe_guc_sched_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- u32 guc_id = msg[0];
- u32 runnable_state = msg[1];
-
- if (unlikely(len < 2)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- q = g2h_exec_queue_lookup(guc, guc_id);
- if (unlikely(!q))
- return -EPROTO;
-
- if (unlikely(!exec_queue_pending_enable(q) &&
- !exec_queue_pending_disable(q))) {
- xe_gt_err(guc_to_gt(guc),
- "SCHED_DONE: Unexpected engine state 0x%04x, guc_id=%d, runnable_state=%u",
- atomic_read(&q->guc->state), q->guc->id,
- runnable_state);
- return -EPROTO;
- }
-
- handle_sched_done(guc, q, runnable_state);
-
- return 0;
-}
-
-static void handle_deregister_done(struct xe_guc *guc, struct xe_exec_queue *q)
-{
- trace_xe_exec_queue_deregister_done(q);
-
- clear_exec_queue_registered(q);
-
- if (exec_queue_extra_ref(q) || xe_exec_queue_is_lr(q))
- xe_exec_queue_put(q);
- else
- __guc_exec_queue_fini(guc, q);
-}
-
-int xe_guc_deregister_done_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- u32 guc_id = msg[0];
-
- if (unlikely(len < 1)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- q = g2h_exec_queue_lookup(guc, guc_id);
- if (unlikely(!q))
- return -EPROTO;
-
- if (!exec_queue_destroyed(q) || exec_queue_pending_disable(q) ||
- exec_queue_pending_enable(q) || exec_queue_enabled(q)) {
- xe_gt_err(guc_to_gt(guc),
- "DEREGISTER_DONE: Unexpected engine state 0x%04x, guc_id=%d",
- atomic_read(&q->guc->state), q->guc->id);
- return -EPROTO;
- }
-
- handle_deregister_done(guc, q);
-
- return 0;
-}
-
-int xe_guc_exec_queue_reset_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_gt *gt = guc_to_gt(guc);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- u32 guc_id = msg[0];
-
- if (unlikely(len < 1)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- q = g2h_exec_queue_lookup(guc, guc_id);
- if (unlikely(!q))
- return -EPROTO;
-
- xe_gt_info(gt, "Engine reset: engine_class=%s, logical_mask: 0x%x, guc_id=%d",
- xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
-
- /* FIXME: Do error capture, most likely async */
-
- trace_xe_exec_queue_reset(q);
-
- /*
- * A banned engine is a NOP at this point (came from
- * guc_exec_queue_timedout_job). Otherwise, kick drm scheduler to cancel
- * jobs by setting timeout of the job to the minimum value kicking
- * guc_exec_queue_timedout_job.
- */
- set_exec_queue_reset(q);
- if (!exec_queue_banned(q) && !exec_queue_check_timeout(q))
- xe_guc_exec_queue_trigger_cleanup(q);
-
- return 0;
-}
-
-int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
- u32 len)
-{
- struct xe_gt *gt = guc_to_gt(guc);
- struct xe_device *xe = guc_to_xe(guc);
- struct xe_exec_queue *q;
- u32 guc_id = msg[0];
-
- if (unlikely(len < 1)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- q = g2h_exec_queue_lookup(guc, guc_id);
- if (unlikely(!q))
- return -EPROTO;
-
- xe_gt_dbg(gt, "Engine memory cat error: engine_class=%s, logical_mask: 0x%x, guc_id=%d",
- xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
-
- trace_xe_exec_queue_memory_cat_error(q);
-
- /* Treat the same as engine reset */
- set_exec_queue_reset(q);
- if (!exec_queue_banned(q) && !exec_queue_check_timeout(q))
- xe_guc_exec_queue_trigger_cleanup(q);
-
- return 0;
-}
-
-int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_device *xe = guc_to_xe(guc);
- u8 guc_class, instance;
- u32 reason;
-
- if (unlikely(len != 3)) {
- drm_err(&xe->drm, "Invalid length %u", len);
- return -EPROTO;
- }
-
- guc_class = msg[0];
- instance = msg[1];
- reason = msg[2];
-
- /* Unexpected failure of a hardware feature, log an actual error */
- drm_err(&xe->drm, "GuC engine reset request failed on %d:%d because 0x%08X",
- guc_class, instance, reason);
-
- xe_gt_reset_async(guc_to_gt(guc));
-
- return 0;
-}
-
-static void
-guc_exec_queue_wq_snapshot_capture(struct xe_exec_queue *q,
- struct xe_guc_submit_exec_queue_snapshot *snapshot)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- int i;
-
- snapshot->guc.wqi_head = q->guc->wqi_head;
- snapshot->guc.wqi_tail = q->guc->wqi_tail;
- snapshot->parallel.wq_desc.head = parallel_read(xe, map, wq_desc.head);
- snapshot->parallel.wq_desc.tail = parallel_read(xe, map, wq_desc.tail);
- snapshot->parallel.wq_desc.status = parallel_read(xe, map,
- wq_desc.wq_status);
-
- if (snapshot->parallel.wq_desc.head !=
- snapshot->parallel.wq_desc.tail) {
- for (i = snapshot->parallel.wq_desc.head;
- i != snapshot->parallel.wq_desc.tail;
- i = (i + sizeof(u32)) % WQ_SIZE)
- snapshot->parallel.wq[i / sizeof(u32)] =
- parallel_read(xe, map, wq[i / sizeof(u32)]);
- }
-}
-
-static void
-guc_exec_queue_wq_snapshot_print(struct xe_guc_submit_exec_queue_snapshot *snapshot,
- struct drm_printer *p)
-{
- int i;
-
- drm_printf(p, "\tWQ head: %u (internal), %d (memory)\n",
- snapshot->guc.wqi_head, snapshot->parallel.wq_desc.head);
- drm_printf(p, "\tWQ tail: %u (internal), %d (memory)\n",
- snapshot->guc.wqi_tail, snapshot->parallel.wq_desc.tail);
- drm_printf(p, "\tWQ status: %u\n", snapshot->parallel.wq_desc.status);
-
- if (snapshot->parallel.wq_desc.head !=
- snapshot->parallel.wq_desc.tail) {
- for (i = snapshot->parallel.wq_desc.head;
- i != snapshot->parallel.wq_desc.tail;
- i = (i + sizeof(u32)) % WQ_SIZE)
- drm_printf(p, "\tWQ[%zu]: 0x%08x\n", i / sizeof(u32),
- snapshot->parallel.wq[i / sizeof(u32)]);
- }
-}
-
-/**
- * xe_guc_exec_queue_snapshot_capture - Take a quick snapshot of the GuC Engine.
- * @q: faulty exec queue
- *
- * This can be printed out in a later stage like during dev_coredump
- * analysis.
- *
- * Returns: a GuC Submit Engine snapshot object that must be freed by the
- * caller, using `xe_guc_exec_queue_snapshot_free`.
- */
-struct xe_guc_submit_exec_queue_snapshot *
-xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched = &q->guc->sched;
- struct xe_guc_submit_exec_queue_snapshot *snapshot;
- int i;
-
- snapshot = kzalloc(sizeof(*snapshot), GFP_ATOMIC);
-
- if (!snapshot)
- return NULL;
-
- snapshot->guc.id = q->guc->id;
- memcpy(&snapshot->name, &q->name, sizeof(snapshot->name));
- snapshot->class = q->class;
- snapshot->logical_mask = q->logical_mask;
- snapshot->width = q->width;
- snapshot->refcount = kref_read(&q->refcount);
- snapshot->sched_timeout = sched->base.timeout;
- snapshot->sched_props.timeslice_us = q->sched_props.timeslice_us;
- snapshot->sched_props.preempt_timeout_us =
- q->sched_props.preempt_timeout_us;
-
- snapshot->lrc = kmalloc_array(q->width, sizeof(struct xe_lrc_snapshot *),
- GFP_ATOMIC);
-
- if (snapshot->lrc) {
- for (i = 0; i < q->width; ++i) {
- struct xe_lrc *lrc = q->lrc[i];
-
- snapshot->lrc[i] = xe_lrc_snapshot_capture(lrc);
- }
- }
-
- snapshot->schedule_state = atomic_read(&q->guc->state);
- snapshot->exec_queue_flags = q->flags;
-
- snapshot->parallel_execution = xe_exec_queue_is_parallel(q);
- if (snapshot->parallel_execution)
- guc_exec_queue_wq_snapshot_capture(q, snapshot);
-
- spin_lock(&sched->base.job_list_lock);
- snapshot->pending_list_size = list_count_nodes(&sched->base.pending_list);
- snapshot->pending_list = kmalloc_array(snapshot->pending_list_size,
- sizeof(struct pending_list_snapshot),
- GFP_ATOMIC);
-
- if (snapshot->pending_list) {
- struct xe_sched_job *job_iter;
-
- i = 0;
- list_for_each_entry(job_iter, &sched->base.pending_list, drm.list) {
- snapshot->pending_list[i].seqno =
- xe_sched_job_seqno(job_iter);
- snapshot->pending_list[i].fence =
- dma_fence_is_signaled(job_iter->fence) ? 1 : 0;
- snapshot->pending_list[i].finished =
- dma_fence_is_signaled(&job_iter->drm.s_fence->finished)
- ? 1 : 0;
- i++;
- }
- }
-
- spin_unlock(&sched->base.job_list_lock);
-
- return snapshot;
-}
-
-/**
- * xe_guc_exec_queue_snapshot_capture_delayed - Take delayed part of snapshot of the GuC Engine.
- * @snapshot: Previously captured snapshot of job.
- *
- * This captures some data that requires taking some locks, so it cannot be done in signaling path.
- */
-void
-xe_guc_exec_queue_snapshot_capture_delayed(struct xe_guc_submit_exec_queue_snapshot *snapshot)
-{
- int i;
-
- if (!snapshot || !snapshot->lrc)
- return;
-
- for (i = 0; i < snapshot->width; ++i)
- xe_lrc_snapshot_capture_delayed(snapshot->lrc[i]);
-}
-
-/**
- * xe_guc_exec_queue_snapshot_print - Print out a given GuC Engine snapshot.
- * @snapshot: GuC Submit Engine snapshot object.
- * @p: drm_printer where it will be printed out.
- *
- * This function prints out a given GuC Submit Engine snapshot object.
- */
-void
-xe_guc_exec_queue_snapshot_print(struct xe_guc_submit_exec_queue_snapshot *snapshot,
- struct drm_printer *p)
-{
- int i;
-
- if (!snapshot)
- return;
-
- drm_printf(p, "\nGuC ID: %d\n", snapshot->guc.id);
- drm_printf(p, "\tName: %s\n", snapshot->name);
- drm_printf(p, "\tClass: %d\n", snapshot->class);
- drm_printf(p, "\tLogical mask: 0x%x\n", snapshot->logical_mask);
- drm_printf(p, "\tWidth: %d\n", snapshot->width);
- drm_printf(p, "\tRef: %d\n", snapshot->refcount);
- drm_printf(p, "\tTimeout: %ld (ms)\n", snapshot->sched_timeout);
- drm_printf(p, "\tTimeslice: %u (us)\n",
- snapshot->sched_props.timeslice_us);
- drm_printf(p, "\tPreempt timeout: %u (us)\n",
- snapshot->sched_props.preempt_timeout_us);
-
- for (i = 0; snapshot->lrc && i < snapshot->width; ++i)
- xe_lrc_snapshot_print(snapshot->lrc[i], p);
-
- drm_printf(p, "\tSchedule State: 0x%x\n", snapshot->schedule_state);
- drm_printf(p, "\tFlags: 0x%lx\n", snapshot->exec_queue_flags);
-
- if (snapshot->parallel_execution)
- guc_exec_queue_wq_snapshot_print(snapshot, p);
-
- for (i = 0; snapshot->pending_list && i < snapshot->pending_list_size;
- i++)
- drm_printf(p, "\tJob: seqno=%d, fence=%d, finished=%d\n",
- snapshot->pending_list[i].seqno,
- snapshot->pending_list[i].fence,
- snapshot->pending_list[i].finished);
-}
-
-/**
- * xe_guc_exec_queue_snapshot_free - Free all allocated objects for a given
- * snapshot.
- * @snapshot: GuC Submit Engine snapshot object.
- *
- * This function free all the memory that needed to be allocated at capture
- * time.
- */
-void xe_guc_exec_queue_snapshot_free(struct xe_guc_submit_exec_queue_snapshot *snapshot)
-{
- int i;
-
- if (!snapshot)
- return;
-
- if (snapshot->lrc) {
- for (i = 0; i < snapshot->width; i++)
- xe_lrc_snapshot_free(snapshot->lrc[i]);
- kfree(snapshot->lrc);
- }
- kfree(snapshot->pending_list);
- kfree(snapshot);
-}
-
-static void guc_exec_queue_print(struct xe_exec_queue *q, struct drm_printer *p)
-{
- struct xe_guc_submit_exec_queue_snapshot *snapshot;
-
- snapshot = xe_guc_exec_queue_snapshot_capture(q);
- xe_guc_exec_queue_snapshot_print(snapshot, p);
- xe_guc_exec_queue_snapshot_free(snapshot);
-}
-
-/**
- * xe_guc_submit_print - GuC Submit Print.
- * @guc: GuC.
- * @p: drm_printer where it will be printed out.
- *
- * This function capture and prints snapshots of **all** GuC Engines.
- */
-void xe_guc_submit_print(struct xe_guc *guc, struct drm_printer *p)
-{
- struct xe_exec_queue *q;
- unsigned long index;
-
- if (!xe_device_uc_enabled(guc_to_xe(guc)))
- return;
-
- mutex_lock(&guc->submission_state.lock);
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- guc_exec_queue_print(q, p);
- mutex_unlock(&guc->submission_state.lock);
-}
diff --git a/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/postimage.2 b/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/postimage.2
deleted file mode 100644
index 436faff09bac..000000000000
--- a/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/postimage.2
+++ /dev/null
@@ -1,377 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-#include "xe_sync.h"
-
-#include <linux/dma-fence-array.h>
-#include <linux/kthread.h>
-#include <linux/sched/mm.h>
-#include <linux/uaccess.h>
-
-#include <drm/drm_print.h>
-#include <drm/drm_syncobj.h>
-#include <drm/xe_drm.h>
-
-#include "xe_device_types.h"
-#include "xe_exec_queue.h"
-#include "xe_macros.h"
-#include "xe_sched_job_types.h"
-
-struct xe_user_fence {
- struct xe_device *xe;
- struct kref refcount;
- struct dma_fence_cb cb;
- struct work_struct worker;
- struct mm_struct *mm;
- u64 __user *addr;
- u64 value;
- int signalled;
-};
-
-static void user_fence_destroy(struct kref *kref)
-{
- struct xe_user_fence *ufence = container_of(kref, struct xe_user_fence,
- refcount);
-
- mmdrop(ufence->mm);
- kfree(ufence);
-}
-
-static void user_fence_get(struct xe_user_fence *ufence)
-{
- kref_get(&ufence->refcount);
-}
-
-static void user_fence_put(struct xe_user_fence *ufence)
-{
- kref_put(&ufence->refcount, user_fence_destroy);
-}
-
-static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
- u64 value)
-{
- struct xe_user_fence *ufence;
- u64 __user *ptr = u64_to_user_ptr(addr);
-
- if (!access_ok(ptr, sizeof(*ptr)))
- return ERR_PTR(-EFAULT);
-
- ufence = kmalloc(sizeof(*ufence), GFP_KERNEL);
- if (!ufence)
- return ERR_PTR(-ENOMEM);
-
- ufence->xe = xe;
- kref_init(&ufence->refcount);
- ufence->addr = ptr;
- ufence->value = value;
- ufence->mm = current->mm;
- mmgrab(ufence->mm);
-
- return ufence;
-}
-
-static void user_fence_worker(struct work_struct *w)
-{
- struct xe_user_fence *ufence = container_of(w, struct xe_user_fence, worker);
-
- if (mmget_not_zero(ufence->mm)) {
- kthread_use_mm(ufence->mm);
- if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
- XE_WARN_ON("Copy to user failed");
- kthread_unuse_mm(ufence->mm);
- mmput(ufence->mm);
- }
-
- wake_up_all(&ufence->xe->ufence_wq);
- WRITE_ONCE(ufence->signalled, 1);
- user_fence_put(ufence);
-}
-
-static void kick_ufence(struct xe_user_fence *ufence, struct dma_fence *fence)
-{
- INIT_WORK(&ufence->worker, user_fence_worker);
- queue_work(ufence->xe->ordered_wq, &ufence->worker);
- dma_fence_put(fence);
-}
-
-static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
-{
- struct xe_user_fence *ufence = container_of(cb, struct xe_user_fence, cb);
-
- kick_ufence(ufence, fence);
-}
-
-int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
- struct xe_sync_entry *sync,
- struct drm_xe_sync __user *sync_user,
- unsigned int flags)
-{
- struct drm_xe_sync sync_in;
- int err;
- bool exec = flags & SYNC_PARSE_FLAG_EXEC;
- bool in_lr_mode = flags & SYNC_PARSE_FLAG_LR_MODE;
- bool disallow_user_fence = flags & SYNC_PARSE_FLAG_DISALLOW_USER_FENCE;
- bool signal;
-
- if (copy_from_user(&sync_in, sync_user, sizeof(*sync_user)))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, sync_in.flags & ~DRM_XE_SYNC_FLAG_SIGNAL) ||
- XE_IOCTL_DBG(xe, sync_in.reserved[0] || sync_in.reserved[1]))
- return -EINVAL;
-
- signal = sync_in.flags & DRM_XE_SYNC_FLAG_SIGNAL;
- switch (sync_in.type) {
- case DRM_XE_SYNC_TYPE_SYNCOBJ:
- if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
- return -EINVAL;
-
- sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
- if (XE_IOCTL_DBG(xe, !sync->syncobj))
- return -ENOENT;
-
- if (!signal) {
- sync->fence = drm_syncobj_fence_get(sync->syncobj);
- if (XE_IOCTL_DBG(xe, !sync->fence))
- return -EINVAL;
- }
- break;
-
- case DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ:
- if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
- return -EINVAL;
-
- if (XE_IOCTL_DBG(xe, sync_in.timeline_value == 0))
- return -EINVAL;
-
- sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
- if (XE_IOCTL_DBG(xe, !sync->syncobj))
- return -ENOENT;
-
- if (signal) {
- sync->chain_fence = dma_fence_chain_alloc();
- if (!sync->chain_fence)
- return -ENOMEM;
- } else {
- sync->fence = drm_syncobj_fence_get(sync->syncobj);
- if (XE_IOCTL_DBG(xe, !sync->fence))
- return -EINVAL;
-
- err = dma_fence_chain_find_seqno(&sync->fence,
- sync_in.timeline_value);
- if (err)
- return err;
- }
- break;
-
- case DRM_XE_SYNC_TYPE_USER_FENCE:
- if (XE_IOCTL_DBG(xe, disallow_user_fence))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, !signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, sync_in.addr & 0x7))
- return -EINVAL;
-
- if (exec) {
- sync->addr = sync_in.addr;
- } else {
- sync->ufence = user_fence_create(xe, sync_in.addr,
- sync_in.timeline_value);
- if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
- return PTR_ERR(sync->ufence);
- }
-
- break;
-
- default:
- return -EINVAL;
- }
-
- sync->type = sync_in.type;
- sync->flags = sync_in.flags;
- sync->timeline_value = sync_in.timeline_value;
-
- return 0;
-}
-
-int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job)
-{
- if (sync->fence)
- return drm_sched_job_add_dependency(&job->drm,
- dma_fence_get(sync->fence));
-
- return 0;
-}
-
-void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
-{
- if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL))
- return;
-
- if (sync->chain_fence) {
- drm_syncobj_add_point(sync->syncobj, sync->chain_fence,
- fence, sync->timeline_value);
- /*
- * The chain's ownership is transferred to the
- * timeline.
- */
- sync->chain_fence = NULL;
- } else if (sync->syncobj) {
- drm_syncobj_replace_fence(sync->syncobj, fence);
- } else if (sync->ufence) {
- int err;
-
- dma_fence_get(fence);
- user_fence_get(sync->ufence);
- err = dma_fence_add_callback(fence, &sync->ufence->cb,
- user_fence_cb);
- if (err == -ENOENT) {
- kick_ufence(sync->ufence, fence);
- } else if (err) {
- XE_WARN_ON("failed to add user fence");
- user_fence_put(sync->ufence);
- dma_fence_put(fence);
- }
- }
-}
-
-void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
-{
- if (sync->syncobj)
- drm_syncobj_put(sync->syncobj);
- dma_fence_put(sync->fence);
- dma_fence_chain_free(sync->chain_fence);
- if (sync->ufence)
- user_fence_put(sync->ufence);
-}
-
-/**
- * xe_sync_in_fence_get() - Get a fence from syncs, exec queue, and VM
- * @sync: input syncs
- * @num_sync: number of syncs
- * @q: exec queue
- * @vm: VM
- *
- * Get a fence from syncs, exec queue, and VM. If syncs contain in-fences create
- * and return a composite fence of all in-fences + last fence. If no in-fences
- * return last fence on input exec queue. Caller must drop reference to
- * returned fence.
- *
- * Return: fence on success, ERR_PTR(-ENOMEM) on failure
- */
-struct dma_fence *
-xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
- struct xe_exec_queue *q, struct xe_vm *vm)
-{
- struct dma_fence **fences = NULL;
- struct dma_fence_array *cf = NULL;
- struct dma_fence *fence;
- int i, num_in_fence = 0, current_fence = 0;
-
- lockdep_assert_held(&vm->lock);
-
- /* Count in-fences */
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- ++num_in_fence;
- fence = sync[i].fence;
- }
- }
-
- /* Easy case... */
- if (!num_in_fence) {
- fence = xe_exec_queue_last_fence_get(q, vm);
- return fence;
- }
-
- /* Create composite fence */
- fences = kmalloc_array(num_in_fence + 1, sizeof(*fences), GFP_KERNEL);
- if (!fences)
- return ERR_PTR(-ENOMEM);
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- dma_fence_get(sync[i].fence);
- fences[current_fence++] = sync[i].fence;
- }
- }
- fences[current_fence++] = xe_exec_queue_last_fence_get(q, vm);
- cf = dma_fence_array_create(num_in_fence, fences,
- vm->composite_fence_ctx,
- vm->composite_fence_seqno++,
- false);
- if (!cf) {
- --vm->composite_fence_seqno;
- goto err_out;
- }
-
- return &cf->base;
-
-err_out:
- while (current_fence)
- dma_fence_put(fences[--current_fence]);
- kfree(fences);
- kfree(cf);
-
- return ERR_PTR(-ENOMEM);
-}
-
-/**
- * __xe_sync_ufence_get() - Get user fence from user fence
- * @ufence: input user fence
- *
- * Get a user fence reference from user fence
- *
- * Return: xe_user_fence pointer with reference
- */
-struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence)
-{
- user_fence_get(ufence);
-
- return ufence;
-}
-
-/**
- * xe_sync_ufence_get() - Get user fence from sync
- * @sync: input sync
- *
- * Get a user fence reference from sync.
- *
- * Return: xe_user_fence pointer with reference
- */
-struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync)
-{
- user_fence_get(sync->ufence);
-
- return sync->ufence;
-}
-
-/**
- * xe_sync_ufence_put() - Put user fence reference
- * @ufence: user fence reference
- *
- */
-void xe_sync_ufence_put(struct xe_user_fence *ufence)
-{
- user_fence_put(ufence);
-}
-
-/**
- * xe_sync_ufence_get_status() - Get user fence status
- * @ufence: user fence
- *
- * Return: 1 if signalled, 0 not signalled, <0 on error
- */
-int xe_sync_ufence_get_status(struct xe_user_fence *ufence)
-{
- return READ_ONCE(ufence->signalled);
-}
diff --git a/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage b/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage
deleted file mode 100644
index c21acad49d14..000000000000
--- a/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage
+++ /dev/null
@@ -1,384 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-#include "xe_sync.h"
-
-#include <linux/dma-fence-array.h>
-#include <linux/kthread.h>
-#include <linux/sched/mm.h>
-#include <linux/uaccess.h>
-
-#include <drm/drm_print.h>
-#include <drm/drm_syncobj.h>
-#include <drm/xe_drm.h>
-
-#include "xe_device_types.h"
-#include "xe_exec_queue.h"
-#include "xe_macros.h"
-#include "xe_sched_job_types.h"
-
-struct xe_user_fence {
- struct xe_device *xe;
- struct kref refcount;
- struct dma_fence_cb cb;
- struct work_struct worker;
- struct mm_struct *mm;
- u64 __user *addr;
- u64 value;
- int signalled;
-};
-
-static void user_fence_destroy(struct kref *kref)
-{
- struct xe_user_fence *ufence = container_of(kref, struct xe_user_fence,
- refcount);
-
- mmdrop(ufence->mm);
- kfree(ufence);
-}
-
-static void user_fence_get(struct xe_user_fence *ufence)
-{
- kref_get(&ufence->refcount);
-}
-
-static void user_fence_put(struct xe_user_fence *ufence)
-{
- kref_put(&ufence->refcount, user_fence_destroy);
-}
-
-static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
- u64 value)
-{
- struct xe_user_fence *ufence;
- u64 __user *ptr = u64_to_user_ptr(addr);
-
- if (!access_ok(ptr, sizeof(*ptr)))
- return ERR_PTR(-EFAULT);
-
- ufence = kmalloc(sizeof(*ufence), GFP_KERNEL);
- if (!ufence)
- return ERR_PTR(-ENOMEM);
-
- ufence->xe = xe;
- kref_init(&ufence->refcount);
- ufence->addr = ptr;
- ufence->value = value;
- ufence->mm = current->mm;
- mmgrab(ufence->mm);
-
- return ufence;
-}
-
-static void user_fence_worker(struct work_struct *w)
-{
- struct xe_user_fence *ufence = container_of(w, struct xe_user_fence, worker);
-
- if (mmget_not_zero(ufence->mm)) {
- kthread_use_mm(ufence->mm);
- if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
- XE_WARN_ON("Copy to user failed");
- kthread_unuse_mm(ufence->mm);
- mmput(ufence->mm);
- }
-
- wake_up_all(&ufence->xe->ufence_wq);
- WRITE_ONCE(ufence->signalled, 1);
- user_fence_put(ufence);
-}
-
-static void kick_ufence(struct xe_user_fence *ufence, struct dma_fence *fence)
-{
- INIT_WORK(&ufence->worker, user_fence_worker);
- queue_work(ufence->xe->ordered_wq, &ufence->worker);
- dma_fence_put(fence);
-}
-
-static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
-{
- struct xe_user_fence *ufence = container_of(cb, struct xe_user_fence, cb);
-
- kick_ufence(ufence, fence);
-}
-
-int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
- struct xe_sync_entry *sync,
- struct drm_xe_sync __user *sync_user,
- unsigned int flags)
-{
- struct drm_xe_sync sync_in;
- int err;
- bool exec = flags & SYNC_PARSE_FLAG_EXEC;
- bool in_lr_mode = flags & SYNC_PARSE_FLAG_LR_MODE;
- bool disallow_user_fence = flags & SYNC_PARSE_FLAG_DISALLOW_USER_FENCE;
- bool signal;
-
- if (copy_from_user(&sync_in, sync_user, sizeof(*sync_user)))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, sync_in.flags & ~DRM_XE_SYNC_FLAG_SIGNAL) ||
- XE_IOCTL_DBG(xe, sync_in.reserved[0] || sync_in.reserved[1]))
- return -EINVAL;
-
- signal = sync_in.flags & DRM_XE_SYNC_FLAG_SIGNAL;
- switch (sync_in.type) {
- case DRM_XE_SYNC_TYPE_SYNCOBJ:
- if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
- return -EINVAL;
-
- sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
- if (XE_IOCTL_DBG(xe, !sync->syncobj))
- return -ENOENT;
-
- if (!signal) {
- sync->fence = drm_syncobj_fence_get(sync->syncobj);
- if (XE_IOCTL_DBG(xe, !sync->fence))
- return -EINVAL;
- }
- break;
-
- case DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ:
- if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
- return -EINVAL;
-
- if (XE_IOCTL_DBG(xe, sync_in.timeline_value == 0))
- return -EINVAL;
-
- sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
- if (XE_IOCTL_DBG(xe, !sync->syncobj))
- return -ENOENT;
-
- if (signal) {
- sync->chain_fence = dma_fence_chain_alloc();
- if (!sync->chain_fence)
- return -ENOMEM;
- } else {
- sync->fence = drm_syncobj_fence_get(sync->syncobj);
- if (XE_IOCTL_DBG(xe, !sync->fence))
- return -EINVAL;
-
- err = dma_fence_chain_find_seqno(&sync->fence,
- sync_in.timeline_value);
- if (err)
- return err;
- }
- break;
-
- case DRM_XE_SYNC_TYPE_USER_FENCE:
- if (XE_IOCTL_DBG(xe, disallow_user_fence))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, !signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, sync_in.addr & 0x7))
- return -EINVAL;
-
- if (exec) {
- sync->addr = sync_in.addr;
- } else {
- sync->ufence = user_fence_create(xe, sync_in.addr,
- sync_in.timeline_value);
- if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
- return PTR_ERR(sync->ufence);
- }
-
- break;
-
- default:
- return -EINVAL;
- }
-
- sync->type = sync_in.type;
- sync->flags = sync_in.flags;
- sync->timeline_value = sync_in.timeline_value;
-
- return 0;
-}
-
-int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job)
-{
- if (sync->fence)
- return drm_sched_job_add_dependency(&job->drm,
- dma_fence_get(sync->fence));
-
- return 0;
-}
-
-void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
-{
- if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL))
- return;
-
- if (sync->chain_fence) {
- drm_syncobj_add_point(sync->syncobj, sync->chain_fence,
- fence, sync->timeline_value);
- /*
- * The chain's ownership is transferred to the
- * timeline.
- */
- sync->chain_fence = NULL;
- } else if (sync->syncobj) {
- drm_syncobj_replace_fence(sync->syncobj, fence);
- } else if (sync->ufence) {
- int err;
-
- dma_fence_get(fence);
- user_fence_get(sync->ufence);
- err = dma_fence_add_callback(fence, &sync->ufence->cb,
- user_fence_cb);
- if (err == -ENOENT) {
- kick_ufence(sync->ufence, fence);
- } else if (err) {
- XE_WARN_ON("failed to add user fence");
- user_fence_put(sync->ufence);
- dma_fence_put(fence);
- }
- }
-}
-
-void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
-{
- if (sync->syncobj)
- drm_syncobj_put(sync->syncobj);
-<<<<<<<
- dma_fence_put(sync->fence);
- dma_fence_chain_free(sync->chain_fence);
-=======
- if (sync->fence)
- dma_fence_put(sync->fence);
- if (sync->chain_fence)
- dma_fence_chain_free(sync->chain_fence);
->>>>>>>
- if (sync->ufence)
- user_fence_put(sync->ufence);
-}
-
-/**
- * xe_sync_in_fence_get() - Get a fence from syncs, exec queue, and VM
- * @sync: input syncs
- * @num_sync: number of syncs
- * @q: exec queue
- * @vm: VM
- *
- * Get a fence from syncs, exec queue, and VM. If syncs contain in-fences create
- * and return a composite fence of all in-fences + last fence. If no in-fences
- * return last fence on input exec queue. Caller must drop reference to
- * returned fence.
- *
- * Return: fence on success, ERR_PTR(-ENOMEM) on failure
- */
-struct dma_fence *
-xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
- struct xe_exec_queue *q, struct xe_vm *vm)
-{
- struct dma_fence **fences = NULL;
- struct dma_fence_array *cf = NULL;
- struct dma_fence *fence;
- int i, num_in_fence = 0, current_fence = 0;
-
- lockdep_assert_held(&vm->lock);
-
- /* Count in-fences */
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- ++num_in_fence;
- fence = sync[i].fence;
- }
- }
-
- /* Easy case... */
- if (!num_in_fence) {
- fence = xe_exec_queue_last_fence_get(q, vm);
- return fence;
- }
-
- /* Create composite fence */
- fences = kmalloc_array(num_in_fence + 1, sizeof(*fences), GFP_KERNEL);
- if (!fences)
- return ERR_PTR(-ENOMEM);
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- dma_fence_get(sync[i].fence);
- fences[current_fence++] = sync[i].fence;
- }
- }
- fences[current_fence++] = xe_exec_queue_last_fence_get(q, vm);
- cf = dma_fence_array_create(num_in_fence, fences,
- vm->composite_fence_ctx,
- vm->composite_fence_seqno++,
- false);
- if (!cf) {
- --vm->composite_fence_seqno;
- goto err_out;
- }
-
- return &cf->base;
-
-err_out:
- while (current_fence)
- dma_fence_put(fences[--current_fence]);
- kfree(fences);
- kfree(cf);
-
- return ERR_PTR(-ENOMEM);
-}
-
-/**
- * __xe_sync_ufence_get() - Get user fence from user fence
- * @ufence: input user fence
- *
- * Get a user fence reference from user fence
- *
- * Return: xe_user_fence pointer with reference
- */
-struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence)
-{
- user_fence_get(ufence);
-
- return ufence;
-}
-
-/**
- * xe_sync_ufence_get() - Get user fence from sync
- * @sync: input sync
- *
- * Get a user fence reference from sync.
- *
- * Return: xe_user_fence pointer with reference
- */
-struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync)
-{
- user_fence_get(sync->ufence);
-
- return sync->ufence;
-}
-
-/**
- * xe_sync_ufence_put() - Put user fence reference
- * @ufence: user fence reference
- *
- */
-void xe_sync_ufence_put(struct xe_user_fence *ufence)
-{
- user_fence_put(ufence);
-}
-
-/**
- * xe_sync_ufence_get_status() - Get user fence status
- * @ufence: user fence
- *
- * Return: 1 if signalled, 0 not signalled, <0 on error
- */
-int xe_sync_ufence_get_status(struct xe_user_fence *ufence)
-{
- return READ_ONCE(ufence->signalled);
-}
diff --git a/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage.1 b/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage.1
deleted file mode 100644
index c21acad49d14..000000000000
--- a/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage.1
+++ /dev/null
@@ -1,384 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-#include "xe_sync.h"
-
-#include <linux/dma-fence-array.h>
-#include <linux/kthread.h>
-#include <linux/sched/mm.h>
-#include <linux/uaccess.h>
-
-#include <drm/drm_print.h>
-#include <drm/drm_syncobj.h>
-#include <drm/xe_drm.h>
-
-#include "xe_device_types.h"
-#include "xe_exec_queue.h"
-#include "xe_macros.h"
-#include "xe_sched_job_types.h"
-
-struct xe_user_fence {
- struct xe_device *xe;
- struct kref refcount;
- struct dma_fence_cb cb;
- struct work_struct worker;
- struct mm_struct *mm;
- u64 __user *addr;
- u64 value;
- int signalled;
-};
-
-static void user_fence_destroy(struct kref *kref)
-{
- struct xe_user_fence *ufence = container_of(kref, struct xe_user_fence,
- refcount);
-
- mmdrop(ufence->mm);
- kfree(ufence);
-}
-
-static void user_fence_get(struct xe_user_fence *ufence)
-{
- kref_get(&ufence->refcount);
-}
-
-static void user_fence_put(struct xe_user_fence *ufence)
-{
- kref_put(&ufence->refcount, user_fence_destroy);
-}
-
-static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
- u64 value)
-{
- struct xe_user_fence *ufence;
- u64 __user *ptr = u64_to_user_ptr(addr);
-
- if (!access_ok(ptr, sizeof(*ptr)))
- return ERR_PTR(-EFAULT);
-
- ufence = kmalloc(sizeof(*ufence), GFP_KERNEL);
- if (!ufence)
- return ERR_PTR(-ENOMEM);
-
- ufence->xe = xe;
- kref_init(&ufence->refcount);
- ufence->addr = ptr;
- ufence->value = value;
- ufence->mm = current->mm;
- mmgrab(ufence->mm);
-
- return ufence;
-}
-
-static void user_fence_worker(struct work_struct *w)
-{
- struct xe_user_fence *ufence = container_of(w, struct xe_user_fence, worker);
-
- if (mmget_not_zero(ufence->mm)) {
- kthread_use_mm(ufence->mm);
- if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
- XE_WARN_ON("Copy to user failed");
- kthread_unuse_mm(ufence->mm);
- mmput(ufence->mm);
- }
-
- wake_up_all(&ufence->xe->ufence_wq);
- WRITE_ONCE(ufence->signalled, 1);
- user_fence_put(ufence);
-}
-
-static void kick_ufence(struct xe_user_fence *ufence, struct dma_fence *fence)
-{
- INIT_WORK(&ufence->worker, user_fence_worker);
- queue_work(ufence->xe->ordered_wq, &ufence->worker);
- dma_fence_put(fence);
-}
-
-static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
-{
- struct xe_user_fence *ufence = container_of(cb, struct xe_user_fence, cb);
-
- kick_ufence(ufence, fence);
-}
-
-int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
- struct xe_sync_entry *sync,
- struct drm_xe_sync __user *sync_user,
- unsigned int flags)
-{
- struct drm_xe_sync sync_in;
- int err;
- bool exec = flags & SYNC_PARSE_FLAG_EXEC;
- bool in_lr_mode = flags & SYNC_PARSE_FLAG_LR_MODE;
- bool disallow_user_fence = flags & SYNC_PARSE_FLAG_DISALLOW_USER_FENCE;
- bool signal;
-
- if (copy_from_user(&sync_in, sync_user, sizeof(*sync_user)))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, sync_in.flags & ~DRM_XE_SYNC_FLAG_SIGNAL) ||
- XE_IOCTL_DBG(xe, sync_in.reserved[0] || sync_in.reserved[1]))
- return -EINVAL;
-
- signal = sync_in.flags & DRM_XE_SYNC_FLAG_SIGNAL;
- switch (sync_in.type) {
- case DRM_XE_SYNC_TYPE_SYNCOBJ:
- if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
- return -EINVAL;
-
- sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
- if (XE_IOCTL_DBG(xe, !sync->syncobj))
- return -ENOENT;
-
- if (!signal) {
- sync->fence = drm_syncobj_fence_get(sync->syncobj);
- if (XE_IOCTL_DBG(xe, !sync->fence))
- return -EINVAL;
- }
- break;
-
- case DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ:
- if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
- return -EINVAL;
-
- if (XE_IOCTL_DBG(xe, sync_in.timeline_value == 0))
- return -EINVAL;
-
- sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
- if (XE_IOCTL_DBG(xe, !sync->syncobj))
- return -ENOENT;
-
- if (signal) {
- sync->chain_fence = dma_fence_chain_alloc();
- if (!sync->chain_fence)
- return -ENOMEM;
- } else {
- sync->fence = drm_syncobj_fence_get(sync->syncobj);
- if (XE_IOCTL_DBG(xe, !sync->fence))
- return -EINVAL;
-
- err = dma_fence_chain_find_seqno(&sync->fence,
- sync_in.timeline_value);
- if (err)
- return err;
- }
- break;
-
- case DRM_XE_SYNC_TYPE_USER_FENCE:
- if (XE_IOCTL_DBG(xe, disallow_user_fence))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, !signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, sync_in.addr & 0x7))
- return -EINVAL;
-
- if (exec) {
- sync->addr = sync_in.addr;
- } else {
- sync->ufence = user_fence_create(xe, sync_in.addr,
- sync_in.timeline_value);
- if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
- return PTR_ERR(sync->ufence);
- }
-
- break;
-
- default:
- return -EINVAL;
- }
-
- sync->type = sync_in.type;
- sync->flags = sync_in.flags;
- sync->timeline_value = sync_in.timeline_value;
-
- return 0;
-}
-
-int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job)
-{
- if (sync->fence)
- return drm_sched_job_add_dependency(&job->drm,
- dma_fence_get(sync->fence));
-
- return 0;
-}
-
-void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
-{
- if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL))
- return;
-
- if (sync->chain_fence) {
- drm_syncobj_add_point(sync->syncobj, sync->chain_fence,
- fence, sync->timeline_value);
- /*
- * The chain's ownership is transferred to the
- * timeline.
- */
- sync->chain_fence = NULL;
- } else if (sync->syncobj) {
- drm_syncobj_replace_fence(sync->syncobj, fence);
- } else if (sync->ufence) {
- int err;
-
- dma_fence_get(fence);
- user_fence_get(sync->ufence);
- err = dma_fence_add_callback(fence, &sync->ufence->cb,
- user_fence_cb);
- if (err == -ENOENT) {
- kick_ufence(sync->ufence, fence);
- } else if (err) {
- XE_WARN_ON("failed to add user fence");
- user_fence_put(sync->ufence);
- dma_fence_put(fence);
- }
- }
-}
-
-void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
-{
- if (sync->syncobj)
- drm_syncobj_put(sync->syncobj);
-<<<<<<<
- dma_fence_put(sync->fence);
- dma_fence_chain_free(sync->chain_fence);
-=======
- if (sync->fence)
- dma_fence_put(sync->fence);
- if (sync->chain_fence)
- dma_fence_chain_free(sync->chain_fence);
->>>>>>>
- if (sync->ufence)
- user_fence_put(sync->ufence);
-}
-
-/**
- * xe_sync_in_fence_get() - Get a fence from syncs, exec queue, and VM
- * @sync: input syncs
- * @num_sync: number of syncs
- * @q: exec queue
- * @vm: VM
- *
- * Get a fence from syncs, exec queue, and VM. If syncs contain in-fences create
- * and return a composite fence of all in-fences + last fence. If no in-fences
- * return last fence on input exec queue. Caller must drop reference to
- * returned fence.
- *
- * Return: fence on success, ERR_PTR(-ENOMEM) on failure
- */
-struct dma_fence *
-xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
- struct xe_exec_queue *q, struct xe_vm *vm)
-{
- struct dma_fence **fences = NULL;
- struct dma_fence_array *cf = NULL;
- struct dma_fence *fence;
- int i, num_in_fence = 0, current_fence = 0;
-
- lockdep_assert_held(&vm->lock);
-
- /* Count in-fences */
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- ++num_in_fence;
- fence = sync[i].fence;
- }
- }
-
- /* Easy case... */
- if (!num_in_fence) {
- fence = xe_exec_queue_last_fence_get(q, vm);
- return fence;
- }
-
- /* Create composite fence */
- fences = kmalloc_array(num_in_fence + 1, sizeof(*fences), GFP_KERNEL);
- if (!fences)
- return ERR_PTR(-ENOMEM);
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- dma_fence_get(sync[i].fence);
- fences[current_fence++] = sync[i].fence;
- }
- }
- fences[current_fence++] = xe_exec_queue_last_fence_get(q, vm);
- cf = dma_fence_array_create(num_in_fence, fences,
- vm->composite_fence_ctx,
- vm->composite_fence_seqno++,
- false);
- if (!cf) {
- --vm->composite_fence_seqno;
- goto err_out;
- }
-
- return &cf->base;
-
-err_out:
- while (current_fence)
- dma_fence_put(fences[--current_fence]);
- kfree(fences);
- kfree(cf);
-
- return ERR_PTR(-ENOMEM);
-}
-
-/**
- * __xe_sync_ufence_get() - Get user fence from user fence
- * @ufence: input user fence
- *
- * Get a user fence reference from user fence
- *
- * Return: xe_user_fence pointer with reference
- */
-struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence)
-{
- user_fence_get(ufence);
-
- return ufence;
-}
-
-/**
- * xe_sync_ufence_get() - Get user fence from sync
- * @sync: input sync
- *
- * Get a user fence reference from sync.
- *
- * Return: xe_user_fence pointer with reference
- */
-struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync)
-{
- user_fence_get(sync->ufence);
-
- return sync->ufence;
-}
-
-/**
- * xe_sync_ufence_put() - Put user fence reference
- * @ufence: user fence reference
- *
- */
-void xe_sync_ufence_put(struct xe_user_fence *ufence)
-{
- user_fence_put(ufence);
-}
-
-/**
- * xe_sync_ufence_get_status() - Get user fence status
- * @ufence: user fence
- *
- * Return: 1 if signalled, 0 not signalled, <0 on error
- */
-int xe_sync_ufence_get_status(struct xe_user_fence *ufence)
-{
- return READ_ONCE(ufence->signalled);
-}
diff --git a/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage.2 b/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage.2
deleted file mode 100644
index c21acad49d14..000000000000
--- a/rr-cache/eff95ddb428389e7f45eee42234372282a8ebd37/preimage.2
+++ /dev/null
@@ -1,384 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2021 Intel Corporation
- */
-
-#include "xe_sync.h"
-
-#include <linux/dma-fence-array.h>
-#include <linux/kthread.h>
-#include <linux/sched/mm.h>
-#include <linux/uaccess.h>
-
-#include <drm/drm_print.h>
-#include <drm/drm_syncobj.h>
-#include <drm/xe_drm.h>
-
-#include "xe_device_types.h"
-#include "xe_exec_queue.h"
-#include "xe_macros.h"
-#include "xe_sched_job_types.h"
-
-struct xe_user_fence {
- struct xe_device *xe;
- struct kref refcount;
- struct dma_fence_cb cb;
- struct work_struct worker;
- struct mm_struct *mm;
- u64 __user *addr;
- u64 value;
- int signalled;
-};
-
-static void user_fence_destroy(struct kref *kref)
-{
- struct xe_user_fence *ufence = container_of(kref, struct xe_user_fence,
- refcount);
-
- mmdrop(ufence->mm);
- kfree(ufence);
-}
-
-static void user_fence_get(struct xe_user_fence *ufence)
-{
- kref_get(&ufence->refcount);
-}
-
-static void user_fence_put(struct xe_user_fence *ufence)
-{
- kref_put(&ufence->refcount, user_fence_destroy);
-}
-
-static struct xe_user_fence *user_fence_create(struct xe_device *xe, u64 addr,
- u64 value)
-{
- struct xe_user_fence *ufence;
- u64 __user *ptr = u64_to_user_ptr(addr);
-
- if (!access_ok(ptr, sizeof(*ptr)))
- return ERR_PTR(-EFAULT);
-
- ufence = kmalloc(sizeof(*ufence), GFP_KERNEL);
- if (!ufence)
- return ERR_PTR(-ENOMEM);
-
- ufence->xe = xe;
- kref_init(&ufence->refcount);
- ufence->addr = ptr;
- ufence->value = value;
- ufence->mm = current->mm;
- mmgrab(ufence->mm);
-
- return ufence;
-}
-
-static void user_fence_worker(struct work_struct *w)
-{
- struct xe_user_fence *ufence = container_of(w, struct xe_user_fence, worker);
-
- if (mmget_not_zero(ufence->mm)) {
- kthread_use_mm(ufence->mm);
- if (copy_to_user(ufence->addr, &ufence->value, sizeof(ufence->value)))
- XE_WARN_ON("Copy to user failed");
- kthread_unuse_mm(ufence->mm);
- mmput(ufence->mm);
- }
-
- wake_up_all(&ufence->xe->ufence_wq);
- WRITE_ONCE(ufence->signalled, 1);
- user_fence_put(ufence);
-}
-
-static void kick_ufence(struct xe_user_fence *ufence, struct dma_fence *fence)
-{
- INIT_WORK(&ufence->worker, user_fence_worker);
- queue_work(ufence->xe->ordered_wq, &ufence->worker);
- dma_fence_put(fence);
-}
-
-static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
-{
- struct xe_user_fence *ufence = container_of(cb, struct xe_user_fence, cb);
-
- kick_ufence(ufence, fence);
-}
-
-int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
- struct xe_sync_entry *sync,
- struct drm_xe_sync __user *sync_user,
- unsigned int flags)
-{
- struct drm_xe_sync sync_in;
- int err;
- bool exec = flags & SYNC_PARSE_FLAG_EXEC;
- bool in_lr_mode = flags & SYNC_PARSE_FLAG_LR_MODE;
- bool disallow_user_fence = flags & SYNC_PARSE_FLAG_DISALLOW_USER_FENCE;
- bool signal;
-
- if (copy_from_user(&sync_in, sync_user, sizeof(*sync_user)))
- return -EFAULT;
-
- if (XE_IOCTL_DBG(xe, sync_in.flags & ~DRM_XE_SYNC_FLAG_SIGNAL) ||
- XE_IOCTL_DBG(xe, sync_in.reserved[0] || sync_in.reserved[1]))
- return -EINVAL;
-
- signal = sync_in.flags & DRM_XE_SYNC_FLAG_SIGNAL;
- switch (sync_in.type) {
- case DRM_XE_SYNC_TYPE_SYNCOBJ:
- if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
- return -EINVAL;
-
- sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
- if (XE_IOCTL_DBG(xe, !sync->syncobj))
- return -ENOENT;
-
- if (!signal) {
- sync->fence = drm_syncobj_fence_get(sync->syncobj);
- if (XE_IOCTL_DBG(xe, !sync->fence))
- return -EINVAL;
- }
- break;
-
- case DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ:
- if (XE_IOCTL_DBG(xe, in_lr_mode && signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, upper_32_bits(sync_in.addr)))
- return -EINVAL;
-
- if (XE_IOCTL_DBG(xe, sync_in.timeline_value == 0))
- return -EINVAL;
-
- sync->syncobj = drm_syncobj_find(xef->drm, sync_in.handle);
- if (XE_IOCTL_DBG(xe, !sync->syncobj))
- return -ENOENT;
-
- if (signal) {
- sync->chain_fence = dma_fence_chain_alloc();
- if (!sync->chain_fence)
- return -ENOMEM;
- } else {
- sync->fence = drm_syncobj_fence_get(sync->syncobj);
- if (XE_IOCTL_DBG(xe, !sync->fence))
- return -EINVAL;
-
- err = dma_fence_chain_find_seqno(&sync->fence,
- sync_in.timeline_value);
- if (err)
- return err;
- }
- break;
-
- case DRM_XE_SYNC_TYPE_USER_FENCE:
- if (XE_IOCTL_DBG(xe, disallow_user_fence))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, !signal))
- return -EOPNOTSUPP;
-
- if (XE_IOCTL_DBG(xe, sync_in.addr & 0x7))
- return -EINVAL;
-
- if (exec) {
- sync->addr = sync_in.addr;
- } else {
- sync->ufence = user_fence_create(xe, sync_in.addr,
- sync_in.timeline_value);
- if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
- return PTR_ERR(sync->ufence);
- }
-
- break;
-
- default:
- return -EINVAL;
- }
-
- sync->type = sync_in.type;
- sync->flags = sync_in.flags;
- sync->timeline_value = sync_in.timeline_value;
-
- return 0;
-}
-
-int xe_sync_entry_add_deps(struct xe_sync_entry *sync, struct xe_sched_job *job)
-{
- if (sync->fence)
- return drm_sched_job_add_dependency(&job->drm,
- dma_fence_get(sync->fence));
-
- return 0;
-}
-
-void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
-{
- if (!(sync->flags & DRM_XE_SYNC_FLAG_SIGNAL))
- return;
-
- if (sync->chain_fence) {
- drm_syncobj_add_point(sync->syncobj, sync->chain_fence,
- fence, sync->timeline_value);
- /*
- * The chain's ownership is transferred to the
- * timeline.
- */
- sync->chain_fence = NULL;
- } else if (sync->syncobj) {
- drm_syncobj_replace_fence(sync->syncobj, fence);
- } else if (sync->ufence) {
- int err;
-
- dma_fence_get(fence);
- user_fence_get(sync->ufence);
- err = dma_fence_add_callback(fence, &sync->ufence->cb,
- user_fence_cb);
- if (err == -ENOENT) {
- kick_ufence(sync->ufence, fence);
- } else if (err) {
- XE_WARN_ON("failed to add user fence");
- user_fence_put(sync->ufence);
- dma_fence_put(fence);
- }
- }
-}
-
-void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
-{
- if (sync->syncobj)
- drm_syncobj_put(sync->syncobj);
-<<<<<<<
- dma_fence_put(sync->fence);
- dma_fence_chain_free(sync->chain_fence);
-=======
- if (sync->fence)
- dma_fence_put(sync->fence);
- if (sync->chain_fence)
- dma_fence_chain_free(sync->chain_fence);
->>>>>>>
- if (sync->ufence)
- user_fence_put(sync->ufence);
-}
-
-/**
- * xe_sync_in_fence_get() - Get a fence from syncs, exec queue, and VM
- * @sync: input syncs
- * @num_sync: number of syncs
- * @q: exec queue
- * @vm: VM
- *
- * Get a fence from syncs, exec queue, and VM. If syncs contain in-fences create
- * and return a composite fence of all in-fences + last fence. If no in-fences
- * return last fence on input exec queue. Caller must drop reference to
- * returned fence.
- *
- * Return: fence on success, ERR_PTR(-ENOMEM) on failure
- */
-struct dma_fence *
-xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
- struct xe_exec_queue *q, struct xe_vm *vm)
-{
- struct dma_fence **fences = NULL;
- struct dma_fence_array *cf = NULL;
- struct dma_fence *fence;
- int i, num_in_fence = 0, current_fence = 0;
-
- lockdep_assert_held(&vm->lock);
-
- /* Count in-fences */
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- ++num_in_fence;
- fence = sync[i].fence;
- }
- }
-
- /* Easy case... */
- if (!num_in_fence) {
- fence = xe_exec_queue_last_fence_get(q, vm);
- return fence;
- }
-
- /* Create composite fence */
- fences = kmalloc_array(num_in_fence + 1, sizeof(*fences), GFP_KERNEL);
- if (!fences)
- return ERR_PTR(-ENOMEM);
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- dma_fence_get(sync[i].fence);
- fences[current_fence++] = sync[i].fence;
- }
- }
- fences[current_fence++] = xe_exec_queue_last_fence_get(q, vm);
- cf = dma_fence_array_create(num_in_fence, fences,
- vm->composite_fence_ctx,
- vm->composite_fence_seqno++,
- false);
- if (!cf) {
- --vm->composite_fence_seqno;
- goto err_out;
- }
-
- return &cf->base;
-
-err_out:
- while (current_fence)
- dma_fence_put(fences[--current_fence]);
- kfree(fences);
- kfree(cf);
-
- return ERR_PTR(-ENOMEM);
-}
-
-/**
- * __xe_sync_ufence_get() - Get user fence from user fence
- * @ufence: input user fence
- *
- * Get a user fence reference from user fence
- *
- * Return: xe_user_fence pointer with reference
- */
-struct xe_user_fence *__xe_sync_ufence_get(struct xe_user_fence *ufence)
-{
- user_fence_get(ufence);
-
- return ufence;
-}
-
-/**
- * xe_sync_ufence_get() - Get user fence from sync
- * @sync: input sync
- *
- * Get a user fence reference from sync.
- *
- * Return: xe_user_fence pointer with reference
- */
-struct xe_user_fence *xe_sync_ufence_get(struct xe_sync_entry *sync)
-{
- user_fence_get(sync->ufence);
-
- return sync->ufence;
-}
-
-/**
- * xe_sync_ufence_put() - Put user fence reference
- * @ufence: user fence reference
- *
- */
-void xe_sync_ufence_put(struct xe_user_fence *ufence)
-{
- user_fence_put(ufence);
-}
-
-/**
- * xe_sync_ufence_get_status() - Get user fence status
- * @ufence: user fence
- *
- * Return: 1 if signalled, 0 not signalled, <0 on error
- */
-int xe_sync_ufence_get_status(struct xe_user_fence *ufence)
-{
- return READ_ONCE(ufence->signalled);
-}
diff --git a/rr-cache/f013472cad3d9dd7cdbe39311eced4b9997712a7/preimage b/rr-cache/f013472cad3d9dd7cdbe39311eced4b9997712a7/preimage
deleted file mode 100644
index edcbe7fec8d5..000000000000
--- a/rr-cache/f013472cad3d9dd7cdbe39311eced4b9997712a7/preimage
+++ /dev/null
@@ -1,1974 +0,0 @@
-/*
- * Copyright 2019 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/delay.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-
-#include "amdgpu.h"
-#include "amdgpu_ucode.h"
-#include "amdgpu_trace.h"
-
-#include "gc/gc_10_3_0_offset.h"
-#include "gc/gc_10_3_0_sh_mask.h"
-#include "ivsrcid/sdma0/irqsrcs_sdma0_5_0.h"
-#include "ivsrcid/sdma1/irqsrcs_sdma1_5_0.h"
-#include "ivsrcid/sdma2/irqsrcs_sdma2_5_0.h"
-#include "ivsrcid/sdma3/irqsrcs_sdma3_5_0.h"
-
-#include "soc15_common.h"
-#include "soc15.h"
-#include "navi10_sdma_pkt_open.h"
-#include "nbio_v2_3.h"
-#include "sdma_common.h"
-#include "sdma_v5_2.h"
-
-MODULE_FIRMWARE("amdgpu/sienna_cichlid_sdma.bin");
-MODULE_FIRMWARE("amdgpu/navy_flounder_sdma.bin");
-MODULE_FIRMWARE("amdgpu/dimgrey_cavefish_sdma.bin");
-MODULE_FIRMWARE("amdgpu/beige_goby_sdma.bin");
-
-MODULE_FIRMWARE("amdgpu/vangogh_sdma.bin");
-MODULE_FIRMWARE("amdgpu/yellow_carp_sdma.bin");
-MODULE_FIRMWARE("amdgpu/sdma_5_2_6.bin");
-MODULE_FIRMWARE("amdgpu/sdma_5_2_7.bin");
-
-#define SDMA1_REG_OFFSET 0x600
-#define SDMA3_REG_OFFSET 0x400
-#define SDMA0_HYP_DEC_REG_START 0x5880
-#define SDMA0_HYP_DEC_REG_END 0x5893
-#define SDMA1_HYP_DEC_REG_OFFSET 0x20
-
-static const struct amdgpu_hwip_reg_entry sdma_reg_list_5_2[] = {
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_STATUS_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_STATUS1_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_STATUS2_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_STATUS3_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UCODE_CHECKSUM),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RB_RPTR_FETCH_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RB_RPTR_FETCH),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_RD_STATUS),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_WR_STATUS),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_RD_XNACK0),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_RD_XNACK1),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_WR_XNACK0),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_WR_XNACK1),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_RPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_RPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_WPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_WPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_OFFSET),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_BASE_LO),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_BASE_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_RPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_SUB_REMAIN),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_DUMMY_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_RPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_RPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_WPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_WPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_IB_OFFSET),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_IB_BASE_LO),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_IB_BASE_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_DUMMY_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_RPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_RPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_WPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_WPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_IB_OFFSET),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_IB_BASE_LO),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_IB_BASE_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_DUMMY_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_INT_STATUS),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_VM_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmGRBM_STATUS2)
-};
-
-static void sdma_v5_2_set_ring_funcs(struct amdgpu_device *adev);
-static void sdma_v5_2_set_buffer_funcs(struct amdgpu_device *adev);
-static void sdma_v5_2_set_vm_pte_funcs(struct amdgpu_device *adev);
-static void sdma_v5_2_set_irq_funcs(struct amdgpu_device *adev);
-
-static u32 sdma_v5_2_get_reg_offset(struct amdgpu_device *adev, u32 instance, u32 internal_offset)
-{
- u32 base;
-
- if (internal_offset >= SDMA0_HYP_DEC_REG_START &&
- internal_offset <= SDMA0_HYP_DEC_REG_END) {
- base = adev->reg_offset[GC_HWIP][0][1];
- if (instance != 0)
- internal_offset += SDMA1_HYP_DEC_REG_OFFSET * instance;
- } else {
- if (instance < 2) {
- base = adev->reg_offset[GC_HWIP][0][0];
- if (instance == 1)
- internal_offset += SDMA1_REG_OFFSET;
- } else {
- base = adev->reg_offset[GC_HWIP][0][2];
- if (instance == 3)
- internal_offset += SDMA3_REG_OFFSET;
- }
- }
-
- return base + internal_offset;
-}
-
-static unsigned sdma_v5_2_ring_init_cond_exec(struct amdgpu_ring *ring,
- uint64_t addr)
-{
- unsigned ret;
-
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_COND_EXE));
- amdgpu_ring_write(ring, lower_32_bits(addr));
- amdgpu_ring_write(ring, upper_32_bits(addr));
- amdgpu_ring_write(ring, 1);
- /* this is the offset we need patch later */
- ret = ring->wptr & ring->buf_mask;
- /* insert dummy here and patch it later */
- amdgpu_ring_write(ring, 0);
-
- return ret;
-}
-
-/**
- * sdma_v5_2_ring_get_rptr - get the current read pointer
- *
- * @ring: amdgpu ring pointer
- *
- * Get the current rptr from the hardware (NAVI10+).
- */
-static uint64_t sdma_v5_2_ring_get_rptr(struct amdgpu_ring *ring)
-{
- u64 *rptr;
-
- /* XXX check if swapping is necessary on BE */
- rptr = (u64 *)ring->rptr_cpu_addr;
-
- DRM_DEBUG("rptr before shift == 0x%016llx\n", *rptr);
- return ((*rptr) >> 2);
-}
-
-/**
- * sdma_v5_2_ring_get_wptr - get the current write pointer
- *
- * @ring: amdgpu ring pointer
- *
- * Get the current wptr from the hardware (NAVI10+).
- */
-static uint64_t sdma_v5_2_ring_get_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- u64 wptr;
-
- if (ring->use_doorbell) {
- /* XXX check if swapping is necessary on BE */
- wptr = READ_ONCE(*((u64 *)ring->wptr_cpu_addr));
- DRM_DEBUG("wptr/doorbell before shift == 0x%016llx\n", wptr);
- } else {
- wptr = RREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR_HI));
- wptr = wptr << 32;
- wptr |= RREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR));
- DRM_DEBUG("wptr before shift [%i] wptr == 0x%016llx\n", ring->me, wptr);
- }
-
- return wptr >> 2;
-}
-
-/**
- * sdma_v5_2_ring_set_wptr - commit the write pointer
- *
- * @ring: amdgpu ring pointer
- *
- * Write the wptr back to the hardware (NAVI10+).
- */
-static void sdma_v5_2_ring_set_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- DRM_DEBUG("Setting write pointer\n");
- if (ring->use_doorbell) {
- DRM_DEBUG("Using doorbell -- "
- "wptr_offs == 0x%08x "
- "lower_32_bits(ring->wptr << 2) == 0x%08x "
- "upper_32_bits(ring->wptr << 2) == 0x%08x\n",
- ring->wptr_offs,
- lower_32_bits(ring->wptr << 2),
- upper_32_bits(ring->wptr << 2));
- /* XXX check if swapping is necessary on BE */
- atomic64_set((atomic64_t *)ring->wptr_cpu_addr,
- ring->wptr << 2);
- DRM_DEBUG("calling WDOORBELL64(0x%08x, 0x%016llx)\n",
- ring->doorbell_index, ring->wptr << 2);
- WDOORBELL64(ring->doorbell_index, ring->wptr << 2);
-<<<<<<<
- /* SDMA seems to miss doorbells sometimes when powergating kicks in.
- * Updating the wptr directly will wake it. This is only safe because
- * we disallow gfxoff in begin_use() and then allow it again in end_use().
- */
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR),
- lower_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR_HI),
- upper_32_bits(ring->wptr << 2));
-=======
- if (amdgpu_ip_version(adev, SDMA0_HWIP, 0) == IP_VERSION(5, 2, 1)) {
- /* SDMA seems to miss doorbells sometimes when powergating kicks in.
- * Updating the wptr directly will wake it. This is only safe because
- * we disallow gfxoff in begin_use() and then allow it again in end_use().
- */
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR),
- lower_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR_HI),
- upper_32_bits(ring->wptr << 2));
- }
->>>>>>>
- } else {
- DRM_DEBUG("Not using doorbell -- "
- "mmSDMA%i_GFX_RB_WPTR == 0x%08x "
- "mmSDMA%i_GFX_RB_WPTR_HI == 0x%08x\n",
- ring->me,
- lower_32_bits(ring->wptr << 2),
- ring->me,
- upper_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR),
- lower_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR_HI),
- upper_32_bits(ring->wptr << 2));
- }
-}
-
-static void sdma_v5_2_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count)
-{
- struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring);
- int i;
-
- for (i = 0; i < count; i++)
- if (sdma && sdma->burst_nop && (i == 0))
- amdgpu_ring_write(ring, ring->funcs->nop |
- SDMA_PKT_NOP_HEADER_COUNT(count - 1));
- else
- amdgpu_ring_write(ring, ring->funcs->nop);
-}
-
-/**
- * sdma_v5_2_ring_emit_ib - Schedule an IB on the DMA engine
- *
- * @ring: amdgpu ring pointer
- * @job: job to retrieve vmid from
- * @ib: IB object to schedule
- * @flags: unused
- *
- * Schedule an IB in the DMA ring.
- */
-static void sdma_v5_2_ring_emit_ib(struct amdgpu_ring *ring,
- struct amdgpu_job *job,
- struct amdgpu_ib *ib,
- uint32_t flags)
-{
- unsigned vmid = AMDGPU_JOB_GET_VMID(job);
- uint64_t csa_mc_addr = amdgpu_sdma_get_csa_mc_addr(ring, vmid);
-
- /* An IB packet must end on a 8 DW boundary--the next dword
- * must be on a 8-dword boundary. Our IB packet below is 6
- * dwords long, thus add x number of NOPs, such that, in
- * modular arithmetic,
- * wptr + 6 + x = 8k, k >= 0, which in C is,
- * (wptr + 6 + x) % 8 = 0.
- * The expression below, is a solution of x.
- */
- sdma_v5_2_ring_insert_nop(ring, (2 - lower_32_bits(ring->wptr)) & 7);
-
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_INDIRECT) |
- SDMA_PKT_INDIRECT_HEADER_VMID(vmid & 0xf));
- /* base must be 32 byte aligned */
- amdgpu_ring_write(ring, lower_32_bits(ib->gpu_addr) & 0xffffffe0);
- amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr));
- amdgpu_ring_write(ring, ib->length_dw);
- amdgpu_ring_write(ring, lower_32_bits(csa_mc_addr));
- amdgpu_ring_write(ring, upper_32_bits(csa_mc_addr));
-}
-
-/**
- * sdma_v5_2_ring_emit_mem_sync - flush the IB by graphics cache rinse
- *
- * @ring: amdgpu ring pointer
- *
- * flush the IB by graphics cache rinse.
- */
-static void sdma_v5_2_ring_emit_mem_sync(struct amdgpu_ring *ring)
-{
- uint32_t gcr_cntl = SDMA_GCR_GL2_INV | SDMA_GCR_GL2_WB |
- SDMA_GCR_GLM_INV | SDMA_GCR_GL1_INV |
- SDMA_GCR_GLV_INV | SDMA_GCR_GLK_INV |
- SDMA_GCR_GLI_INV(1);
-
- /* flush entire cache L0/L1/L2, this can be optimized by performance requirement */
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_GCR_REQ));
- amdgpu_ring_write(ring, SDMA_PKT_GCR_REQ_PAYLOAD1_BASE_VA_31_7(0));
- amdgpu_ring_write(ring, SDMA_PKT_GCR_REQ_PAYLOAD2_GCR_CONTROL_15_0(gcr_cntl) |
- SDMA_PKT_GCR_REQ_PAYLOAD2_BASE_VA_47_32(0));
- amdgpu_ring_write(ring, SDMA_PKT_GCR_REQ_PAYLOAD3_LIMIT_VA_31_7(0) |
- SDMA_PKT_GCR_REQ_PAYLOAD3_GCR_CONTROL_18_16(gcr_cntl >> 16));
- amdgpu_ring_write(ring, SDMA_PKT_GCR_REQ_PAYLOAD4_LIMIT_VA_47_32(0) |
- SDMA_PKT_GCR_REQ_PAYLOAD4_VMID(0));
-}
-
-/**
- * sdma_v5_2_ring_emit_hdp_flush - emit an hdp flush on the DMA ring
- *
- * @ring: amdgpu ring pointer
- *
- * Emit an hdp flush packet on the requested DMA ring.
- */
-static void sdma_v5_2_ring_emit_hdp_flush(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- u32 ref_and_mask = 0;
- const struct nbio_hdp_flush_reg *nbio_hf_reg = adev->nbio.hdp_flush_reg;
-
- if (ring->me > 1) {
- amdgpu_asic_flush_hdp(adev, ring);
- } else {
- ref_and_mask = nbio_hf_reg->ref_and_mask_sdma0 << ring->me;
-
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
- SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(1) |
- SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3)); /* == */
- amdgpu_ring_write(ring, (adev->nbio.funcs->get_hdp_flush_done_offset(adev)) << 2);
- amdgpu_ring_write(ring, (adev->nbio.funcs->get_hdp_flush_req_offset(adev)) << 2);
- amdgpu_ring_write(ring, ref_and_mask); /* reference */
- amdgpu_ring_write(ring, ref_and_mask); /* mask */
- amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
- SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10)); /* retry count, poll interval */
- }
-}
-
-/**
- * sdma_v5_2_ring_emit_fence - emit a fence on the DMA ring
- *
- * @ring: amdgpu ring pointer
- * @addr: address
- * @seq: sequence number
- * @flags: fence related flags
- *
- * Add a DMA fence packet to the ring to write
- * the fence seq number and DMA trap packet to generate
- * an interrupt if needed.
- */
-static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,
- unsigned flags)
-{
- bool write64bit = flags & AMDGPU_FENCE_FLAG_64BIT;
- /* write the fence */
- 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);
- amdgpu_ring_write(ring, lower_32_bits(addr));
- amdgpu_ring_write(ring, upper_32_bits(addr));
- amdgpu_ring_write(ring, lower_32_bits(seq));
-
- /* optionally write high bits as well */
- if (write64bit) {
- addr += 4;
- 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);
- amdgpu_ring_write(ring, lower_32_bits(addr));
- amdgpu_ring_write(ring, upper_32_bits(addr));
- amdgpu_ring_write(ring, upper_32_bits(seq));
- }
-
- if ((flags & AMDGPU_FENCE_FLAG_INT)) {
- uint32_t ctx = ring->is_mes_queue ?
- (ring->hw_queue_id | AMDGPU_FENCE_MES_QUEUE_FLAG) : 0;
- /* generate an interrupt */
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_TRAP));
- amdgpu_ring_write(ring, SDMA_PKT_TRAP_INT_CONTEXT_INT_CONTEXT(ctx));
- }
-}
-
-
-/**
- * sdma_v5_2_gfx_stop - stop the gfx async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Stop the gfx async dma ring buffers.
- */
-static void sdma_v5_2_gfx_stop(struct amdgpu_device *adev)
-{
- u32 rb_cntl, ib_cntl;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- rb_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL));
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_ENABLE, 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL), rb_cntl);
- ib_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_IB_CNTL));
- ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_ENABLE, 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_IB_CNTL), ib_cntl);
- }
-}
-
-/**
- * sdma_v5_2_rlc_stop - stop the compute async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Stop the compute async dma queues.
- */
-static void sdma_v5_2_rlc_stop(struct amdgpu_device *adev)
-{
- /* XXX todo */
-}
-
-/**
- * sdma_v5_2_ctx_switch_enable - stop the async dma engines context switch
- *
- * @adev: amdgpu_device pointer
- * @enable: enable/disable the DMA MEs context switch.
- *
- * Halt or unhalt the async dma engines context switch.
- */
-static void sdma_v5_2_ctx_switch_enable(struct amdgpu_device *adev, bool enable)
-{
- u32 f32_cntl, phase_quantum = 0;
- int i;
-
- if (amdgpu_sdma_phase_quantum) {
- unsigned value = amdgpu_sdma_phase_quantum;
- unsigned unit = 0;
-
- while (value > (SDMA0_PHASE0_QUANTUM__VALUE_MASK >>
- SDMA0_PHASE0_QUANTUM__VALUE__SHIFT)) {
- value = (value + 1) >> 1;
- unit++;
- }
- if (unit > (SDMA0_PHASE0_QUANTUM__UNIT_MASK >>
- SDMA0_PHASE0_QUANTUM__UNIT__SHIFT)) {
- value = (SDMA0_PHASE0_QUANTUM__VALUE_MASK >>
- SDMA0_PHASE0_QUANTUM__VALUE__SHIFT);
- unit = (SDMA0_PHASE0_QUANTUM__UNIT_MASK >>
- SDMA0_PHASE0_QUANTUM__UNIT__SHIFT);
- WARN_ONCE(1,
- "clamping sdma_phase_quantum to %uK clock cycles\n",
- value << unit);
- }
- phase_quantum =
- value << SDMA0_PHASE0_QUANTUM__VALUE__SHIFT |
- unit << SDMA0_PHASE0_QUANTUM__UNIT__SHIFT;
- }
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- if (enable && amdgpu_sdma_phase_quantum) {
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_PHASE0_QUANTUM),
- phase_quantum);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_PHASE1_QUANTUM),
- phase_quantum);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_PHASE2_QUANTUM),
- phase_quantum);
- }
-
- if (!amdgpu_sriov_vf(adev)) {
- f32_cntl = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CNTL));
- f32_cntl = REG_SET_FIELD(f32_cntl, SDMA0_CNTL,
- AUTO_CTXSW_ENABLE, enable ? 1 : 0);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CNTL), f32_cntl);
- }
- }
-
-}
-
-/**
- * sdma_v5_2_enable - stop the async dma engines
- *
- * @adev: amdgpu_device pointer
- * @enable: enable/disable the DMA MEs.
- *
- * Halt or unhalt the async dma engines.
- */
-static void sdma_v5_2_enable(struct amdgpu_device *adev, bool enable)
-{
- u32 f32_cntl;
- int i;
-
- if (!enable) {
- sdma_v5_2_gfx_stop(adev);
- sdma_v5_2_rlc_stop(adev);
- }
-
- if (!amdgpu_sriov_vf(adev)) {
- for (i = 0; i < adev->sdma.num_instances; i++) {
- f32_cntl = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_F32_CNTL));
- f32_cntl = REG_SET_FIELD(f32_cntl, SDMA0_F32_CNTL, HALT, enable ? 0 : 1);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_F32_CNTL), f32_cntl);
- }
- }
-}
-
-/**
- * sdma_v5_2_gfx_resume - setup and start the async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Set up the gfx DMA ring buffers and enable them.
- * Returns 0 for success, error for failure.
- */
-static int sdma_v5_2_gfx_resume(struct amdgpu_device *adev)
-{
- struct amdgpu_ring *ring;
- u32 rb_cntl, ib_cntl;
- u32 rb_bufsz;
- u32 doorbell;
- u32 doorbell_offset;
- u32 temp;
- u32 wptr_poll_cntl;
- u64 wptr_gpu_addr;
- int i, r;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- ring = &adev->sdma.instance[i].ring;
-
- if (!amdgpu_sriov_vf(adev))
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_SEM_WAIT_FAIL_TIMER_CNTL), 0);
-
- /* Set ring buffer size in dwords */
- rb_bufsz = order_base_2(ring->ring_size / 4);
- rb_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL));
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_SIZE, rb_bufsz);
-#ifdef __BIG_ENDIAN
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_SWAP_ENABLE, 1);
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL,
- RPTR_WRITEBACK_SWAP_ENABLE, 1);
-#endif
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL), rb_cntl);
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_RPTR), 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_RPTR_HI), 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR), 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_HI), 0);
-
- /* setup the wptr shadow polling */
- wptr_gpu_addr = ring->wptr_gpu_addr;
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_POLL_ADDR_LO),
- lower_32_bits(wptr_gpu_addr));
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_POLL_ADDR_HI),
- upper_32_bits(wptr_gpu_addr));
- wptr_poll_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i,
- mmSDMA0_GFX_RB_WPTR_POLL_CNTL));
- wptr_poll_cntl = REG_SET_FIELD(wptr_poll_cntl,
- SDMA0_GFX_RB_WPTR_POLL_CNTL,
- F32_POLL_ENABLE, 1);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_POLL_CNTL),
- wptr_poll_cntl);
-
- /* set the wb address whether it's enabled or not */
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_RPTR_ADDR_HI),
- upper_32_bits(ring->rptr_gpu_addr) & 0xFFFFFFFF);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_RPTR_ADDR_LO),
- lower_32_bits(ring->rptr_gpu_addr) & 0xFFFFFFFC);
-
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RPTR_WRITEBACK_ENABLE, 1);
-
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_BASE), ring->gpu_addr >> 8);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_BASE_HI), ring->gpu_addr >> 40);
-
- ring->wptr = 0;
-
- /* before programing wptr to a less value, need set minor_ptr_update first */
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_MINOR_PTR_UPDATE), 1);
-
- if (!amdgpu_sriov_vf(adev)) { /* only bare-metal use register write for wptr */
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR), lower_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_HI), upper_32_bits(ring->wptr << 2));
- }
-
- doorbell = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_DOORBELL));
- doorbell_offset = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_DOORBELL_OFFSET));
-
- if (ring->use_doorbell) {
- doorbell = REG_SET_FIELD(doorbell, SDMA0_GFX_DOORBELL, ENABLE, 1);
- doorbell_offset = REG_SET_FIELD(doorbell_offset, SDMA0_GFX_DOORBELL_OFFSET,
- OFFSET, ring->doorbell_index);
- } else {
- doorbell = REG_SET_FIELD(doorbell, SDMA0_GFX_DOORBELL, ENABLE, 0);
- }
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_DOORBELL), doorbell);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_DOORBELL_OFFSET), doorbell_offset);
-
- adev->nbio.funcs->sdma_doorbell_range(adev, i, ring->use_doorbell,
- ring->doorbell_index,
- adev->doorbell_index.sdma_doorbell_range);
-
- if (amdgpu_sriov_vf(adev))
- sdma_v5_2_ring_set_wptr(ring);
-
- /* set minor_ptr_update to 0 after wptr programed */
-
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_MINOR_PTR_UPDATE), 0);
-
- /* SRIOV VF has no control of any of registers below */
- if (!amdgpu_sriov_vf(adev)) {
- /* set utc l1 enable flag always to 1 */
- temp = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CNTL));
- temp = REG_SET_FIELD(temp, SDMA0_CNTL, UTC_L1_ENABLE, 1);
-
- /* enable MCBP */
- temp = REG_SET_FIELD(temp, SDMA0_CNTL, MIDCMD_PREEMPT_ENABLE, 1);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CNTL), temp);
-
- /* Set up RESP_MODE to non-copy addresses */
- temp = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UTCL1_CNTL));
- temp = REG_SET_FIELD(temp, SDMA0_UTCL1_CNTL, RESP_MODE, 3);
- temp = REG_SET_FIELD(temp, SDMA0_UTCL1_CNTL, REDO_DELAY, 9);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UTCL1_CNTL), temp);
-
- /* program default cache read and write policy */
- temp = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UTCL1_PAGE));
- /* clean read policy and write policy bits */
- temp &= 0xFF0FFF;
- temp |= ((CACHE_READ_POLICY_L2__DEFAULT << 12) |
- (CACHE_WRITE_POLICY_L2__DEFAULT << 14) |
- SDMA0_UTCL1_PAGE__LLC_NOALLOC_MASK);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UTCL1_PAGE), temp);
-
- /* unhalt engine */
- temp = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_F32_CNTL));
- temp = REG_SET_FIELD(temp, SDMA0_F32_CNTL, HALT, 0);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_F32_CNTL), temp);
- }
-
- /* enable DMA RB */
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_ENABLE, 1);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL), rb_cntl);
-
- ib_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_IB_CNTL));
- ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_ENABLE, 1);
-#ifdef __BIG_ENDIAN
- ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_SWAP_ENABLE, 1);
-#endif
- /* enable DMA IBs */
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_IB_CNTL), ib_cntl);
-
- if (amdgpu_sriov_vf(adev)) { /* bare-metal sequence doesn't need below to lines */
- sdma_v5_2_ctx_switch_enable(adev, true);
- sdma_v5_2_enable(adev, true);
- }
-
- r = amdgpu_ring_test_helper(ring);
- if (r)
- return r;
- }
-
- return 0;
-}
-
-/**
- * sdma_v5_2_rlc_resume - setup and start the async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Set up the compute DMA queues and enable them.
- * Returns 0 for success, error for failure.
- */
-static int sdma_v5_2_rlc_resume(struct amdgpu_device *adev)
-{
- return 0;
-}
-
-/**
- * sdma_v5_2_load_microcode - load the sDMA ME ucode
- *
- * @adev: amdgpu_device pointer
- *
- * Loads the sDMA0/1/2/3 ucode.
- * Returns 0 for success, -EINVAL if the ucode is not available.
- */
-static int sdma_v5_2_load_microcode(struct amdgpu_device *adev)
-{
- const struct sdma_firmware_header_v1_0 *hdr;
- const __le32 *fw_data;
- u32 fw_size;
- int i, j;
-
- /* halt the MEs */
- sdma_v5_2_enable(adev, false);
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- if (!adev->sdma.instance[i].fw)
- return -EINVAL;
-
- hdr = (const struct sdma_firmware_header_v1_0 *)adev->sdma.instance[i].fw->data;
- amdgpu_ucode_print_sdma_hdr(&hdr->header);
- fw_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
-
- fw_data = (const __le32 *)
- (adev->sdma.instance[i].fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes));
-
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UCODE_ADDR), 0);
-
- for (j = 0; j < fw_size; j++) {
- if (amdgpu_emu_mode == 1 && j % 500 == 0)
- msleep(1);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UCODE_DATA), le32_to_cpup(fw_data++));
- }
-
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UCODE_ADDR), adev->sdma.instance[i].fw_version);
- }
-
- return 0;
-}
-
-static int sdma_v5_2_soft_reset(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- u32 grbm_soft_reset;
- u32 tmp;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- grbm_soft_reset = REG_SET_FIELD(0,
- GRBM_SOFT_RESET, SOFT_RESET_SDMA0,
- 1);
- grbm_soft_reset <<= i;
-
- tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET);
- tmp |= grbm_soft_reset;
- DRM_DEBUG("GRBM_SOFT_RESET=0x%08X\n", tmp);
- WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp);
- tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET);
-
- udelay(50);
-
- tmp &= ~grbm_soft_reset;
- WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp);
- tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET);
-
- udelay(50);
- }
-
- return 0;
-}
-
-/**
- * sdma_v5_2_start - setup and start the async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Set up the DMA engines and enable them.
- * Returns 0 for success, error for failure.
- */
-static int sdma_v5_2_start(struct amdgpu_device *adev)
-{
- int r = 0;
-
- if (amdgpu_sriov_vf(adev)) {
- sdma_v5_2_ctx_switch_enable(adev, false);
- sdma_v5_2_enable(adev, false);
-
- /* set RB registers */
- r = sdma_v5_2_gfx_resume(adev);
- return r;
- }
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT) {
- r = sdma_v5_2_load_microcode(adev);
- if (r)
- return r;
-
- /* The value of mmSDMA_F32_CNTL is invalid the moment after loading fw */
- if (amdgpu_emu_mode == 1)
- msleep(1000);
- }
-
- sdma_v5_2_soft_reset(adev);
- /* unhalt the MEs */
- sdma_v5_2_enable(adev, true);
- /* enable sdma ring preemption */
- sdma_v5_2_ctx_switch_enable(adev, true);
-
- /* start the gfx rings and rlc compute queues */
- r = sdma_v5_2_gfx_resume(adev);
- if (r)
- return r;
- r = sdma_v5_2_rlc_resume(adev);
-
- return r;
-}
-
-static int sdma_v5_2_mqd_init(struct amdgpu_device *adev, void *mqd,
- struct amdgpu_mqd_prop *prop)
-{
- struct v10_sdma_mqd *m = mqd;
- uint64_t wb_gpu_addr;
-
- m->sdmax_rlcx_rb_cntl =
- order_base_2(prop->queue_size / 4) << SDMA0_RLC0_RB_CNTL__RB_SIZE__SHIFT |
- 1 << SDMA0_RLC0_RB_CNTL__RPTR_WRITEBACK_ENABLE__SHIFT |
- 6 << SDMA0_RLC0_RB_CNTL__RPTR_WRITEBACK_TIMER__SHIFT |
- 1 << SDMA0_RLC0_RB_CNTL__RB_PRIV__SHIFT;
-
- m->sdmax_rlcx_rb_base = lower_32_bits(prop->hqd_base_gpu_addr >> 8);
- m->sdmax_rlcx_rb_base_hi = upper_32_bits(prop->hqd_base_gpu_addr >> 8);
-
- m->sdmax_rlcx_rb_wptr_poll_cntl = RREG32(sdma_v5_2_get_reg_offset(adev, 0,
- mmSDMA0_GFX_RB_WPTR_POLL_CNTL));
-
- wb_gpu_addr = prop->wptr_gpu_addr;
- m->sdmax_rlcx_rb_wptr_poll_addr_lo = lower_32_bits(wb_gpu_addr);
- m->sdmax_rlcx_rb_wptr_poll_addr_hi = upper_32_bits(wb_gpu_addr);
-
- wb_gpu_addr = prop->rptr_gpu_addr;
- m->sdmax_rlcx_rb_rptr_addr_lo = lower_32_bits(wb_gpu_addr);
- m->sdmax_rlcx_rb_rptr_addr_hi = upper_32_bits(wb_gpu_addr);
-
- m->sdmax_rlcx_ib_cntl = RREG32(sdma_v5_2_get_reg_offset(adev, 0,
- mmSDMA0_GFX_IB_CNTL));
-
- m->sdmax_rlcx_doorbell_offset =
- prop->doorbell_index << SDMA0_RLC0_DOORBELL_OFFSET__OFFSET__SHIFT;
-
- m->sdmax_rlcx_doorbell = REG_SET_FIELD(0, SDMA0_RLC0_DOORBELL, ENABLE, 1);
-
- return 0;
-}
-
-static void sdma_v5_2_set_mqd_funcs(struct amdgpu_device *adev)
-{
- adev->mqds[AMDGPU_HW_IP_DMA].mqd_size = sizeof(struct v10_sdma_mqd);
- adev->mqds[AMDGPU_HW_IP_DMA].init_mqd = sdma_v5_2_mqd_init;
-}
-
-/**
- * sdma_v5_2_ring_test_ring - simple async dma engine test
- *
- * @ring: amdgpu_ring structure holding ring information
- *
- * Test the DMA engine by writing using it to write an
- * value to memory.
- * Returns 0 for success, error for failure.
- */
-static int sdma_v5_2_ring_test_ring(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- unsigned i;
- unsigned index;
- int r;
- u32 tmp;
- u64 gpu_addr;
- volatile uint32_t *cpu_ptr = NULL;
-
- tmp = 0xCAFEDEAD;
-
- if (ring->is_mes_queue) {
- uint32_t offset = 0;
- offset = amdgpu_mes_ctx_get_offs(ring,
- AMDGPU_MES_CTX_PADDING_OFFS);
- gpu_addr = amdgpu_mes_ctx_get_offs_gpu_addr(ring, offset);
- cpu_ptr = amdgpu_mes_ctx_get_offs_cpu_addr(ring, offset);
- *cpu_ptr = tmp;
- } else {
- r = amdgpu_device_wb_get(adev, &index);
- if (r) {
- dev_err(adev->dev, "(%d) failed to allocate wb slot\n", r);
- return r;
- }
-
- gpu_addr = adev->wb.gpu_addr + (index * 4);
- adev->wb.wb[index] = cpu_to_le32(tmp);
- }
-
- r = amdgpu_ring_alloc(ring, 20);
- if (r) {
- DRM_ERROR("amdgpu: dma failed to lock ring %d (%d).\n", ring->idx, r);
- if (!ring->is_mes_queue)
- amdgpu_device_wb_free(adev, index);
- return r;
- }
-
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR));
- amdgpu_ring_write(ring, lower_32_bits(gpu_addr));
- amdgpu_ring_write(ring, upper_32_bits(gpu_addr));
- amdgpu_ring_write(ring, SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(0));
- amdgpu_ring_write(ring, 0xDEADBEEF);
- amdgpu_ring_commit(ring);
-
- for (i = 0; i < adev->usec_timeout; i++) {
- if (ring->is_mes_queue)
- tmp = le32_to_cpu(*cpu_ptr);
- else
- tmp = le32_to_cpu(adev->wb.wb[index]);
- if (tmp == 0xDEADBEEF)
- break;
- if (amdgpu_emu_mode == 1)
- msleep(1);
- else
- udelay(1);
- }
-
- if (i >= adev->usec_timeout)
- r = -ETIMEDOUT;
-
- if (!ring->is_mes_queue)
- amdgpu_device_wb_free(adev, index);
-
- return r;
-}
-
-/**
- * sdma_v5_2_ring_test_ib - test an IB on the DMA engine
- *
- * @ring: amdgpu_ring structure holding ring information
- * @timeout: timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT
- *
- * Test a simple IB in the DMA ring.
- * Returns 0 on success, error on failure.
- */
-static int sdma_v5_2_ring_test_ib(struct amdgpu_ring *ring, long timeout)
-{
- struct amdgpu_device *adev = ring->adev;
- struct amdgpu_ib ib;
- struct dma_fence *f = NULL;
- unsigned index;
- long r;
- u32 tmp = 0;
- u64 gpu_addr;
- volatile uint32_t *cpu_ptr = NULL;
-
- tmp = 0xCAFEDEAD;
- memset(&ib, 0, sizeof(ib));
-
- if (ring->is_mes_queue) {
- uint32_t offset = 0;
- offset = amdgpu_mes_ctx_get_offs(ring, AMDGPU_MES_CTX_IB_OFFS);
- ib.gpu_addr = amdgpu_mes_ctx_get_offs_gpu_addr(ring, offset);
- ib.ptr = (void *)amdgpu_mes_ctx_get_offs_cpu_addr(ring, offset);
-
- offset = amdgpu_mes_ctx_get_offs(ring,
- AMDGPU_MES_CTX_PADDING_OFFS);
- gpu_addr = amdgpu_mes_ctx_get_offs_gpu_addr(ring, offset);
- cpu_ptr = amdgpu_mes_ctx_get_offs_cpu_addr(ring, offset);
- *cpu_ptr = tmp;
- } else {
- r = amdgpu_device_wb_get(adev, &index);
- if (r) {
- dev_err(adev->dev, "(%ld) failed to allocate wb slot\n", r);
- return r;
- }
-
- gpu_addr = adev->wb.gpu_addr + (index * 4);
- adev->wb.wb[index] = cpu_to_le32(tmp);
-
- r = amdgpu_ib_get(adev, NULL, 256, AMDGPU_IB_POOL_DIRECT, &ib);
- if (r) {
- DRM_ERROR("amdgpu: failed to get ib (%ld).\n", r);
- goto err0;
- }
- }
-
- ib.ptr[0] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR);
- ib.ptr[1] = lower_32_bits(gpu_addr);
- ib.ptr[2] = upper_32_bits(gpu_addr);
- ib.ptr[3] = SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(0);
- ib.ptr[4] = 0xDEADBEEF;
- ib.ptr[5] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
- ib.ptr[6] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
- ib.ptr[7] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
- ib.length_dw = 8;
-
- r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f);
- if (r)
- goto err1;
-
- r = dma_fence_wait_timeout(f, false, timeout);
- if (r == 0) {
- DRM_ERROR("amdgpu: IB test timed out\n");
- r = -ETIMEDOUT;
- goto err1;
- } else if (r < 0) {
- DRM_ERROR("amdgpu: fence wait failed (%ld).\n", r);
- goto err1;
- }
-
- if (ring->is_mes_queue)
- tmp = le32_to_cpu(*cpu_ptr);
- else
- tmp = le32_to_cpu(adev->wb.wb[index]);
-
- if (tmp == 0xDEADBEEF)
- r = 0;
- else
- r = -EINVAL;
-
-err1:
- amdgpu_ib_free(adev, &ib, NULL);
- dma_fence_put(f);
-err0:
- if (!ring->is_mes_queue)
- amdgpu_device_wb_free(adev, index);
- return r;
-}
-
-
-/**
- * sdma_v5_2_vm_copy_pte - update PTEs by copying them from the GART
- *
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @src: src addr to copy from
- * @count: number of page entries to update
- *
- * Update PTEs by copying them from the GART using sDMA.
- */
-static void sdma_v5_2_vm_copy_pte(struct amdgpu_ib *ib,
- uint64_t pe, uint64_t src,
- unsigned count)
-{
- unsigned bytes = count * 8;
-
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
- ib->ptr[ib->length_dw++] = bytes - 1;
- ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
- ib->ptr[ib->length_dw++] = lower_32_bits(src);
- ib->ptr[ib->length_dw++] = upper_32_bits(src);
- ib->ptr[ib->length_dw++] = lower_32_bits(pe);
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-
-}
-
-/**
- * sdma_v5_2_vm_write_pte - update PTEs by writing them manually
- *
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @value: dst addr to write into pe
- * @count: number of page entries to update
- * @incr: increase next addr by incr bytes
- *
- * Update PTEs by writing them manually using sDMA.
- */
-static void sdma_v5_2_vm_write_pte(struct amdgpu_ib *ib, uint64_t pe,
- uint64_t value, unsigned count,
- uint32_t incr)
-{
- unsigned ndw = count * 2;
-
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR);
- ib->ptr[ib->length_dw++] = lower_32_bits(pe);
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
- ib->ptr[ib->length_dw++] = ndw - 1;
- for (; ndw > 0; ndw -= 2) {
- ib->ptr[ib->length_dw++] = lower_32_bits(value);
- ib->ptr[ib->length_dw++] = upper_32_bits(value);
- value += incr;
- }
-}
-
-/**
- * sdma_v5_2_vm_set_pte_pde - update the page tables using sDMA
- *
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @addr: dst addr to write into pe
- * @count: number of page entries to update
- * @incr: increase next addr by incr bytes
- * @flags: access flags
- *
- * Update the page tables using sDMA.
- */
-static void sdma_v5_2_vm_set_pte_pde(struct amdgpu_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint64_t flags)
-{
- /* for physically contiguous pages (vram) */
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_PTEPDE);
- ib->ptr[ib->length_dw++] = lower_32_bits(pe); /* dst addr */
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
- ib->ptr[ib->length_dw++] = lower_32_bits(flags); /* mask */
- ib->ptr[ib->length_dw++] = upper_32_bits(flags);
- ib->ptr[ib->length_dw++] = lower_32_bits(addr); /* value */
- ib->ptr[ib->length_dw++] = upper_32_bits(addr);
- ib->ptr[ib->length_dw++] = incr; /* increment size */
- ib->ptr[ib->length_dw++] = 0;
- ib->ptr[ib->length_dw++] = count - 1; /* number of entries */
-}
-
-/**
- * sdma_v5_2_ring_pad_ib - pad the IB
- *
- * @ib: indirect buffer to fill with padding
- * @ring: amdgpu_ring structure holding ring information
- *
- * Pad the IB with NOPs to a boundary multiple of 8.
- */
-static void sdma_v5_2_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib)
-{
- struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring);
- u32 pad_count;
- int i;
-
- pad_count = (-ib->length_dw) & 0x7;
- for (i = 0; i < pad_count; i++)
- if (sdma && sdma->burst_nop && (i == 0))
- ib->ptr[ib->length_dw++] =
- SDMA_PKT_HEADER_OP(SDMA_OP_NOP) |
- SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1);
- else
- ib->ptr[ib->length_dw++] =
- SDMA_PKT_HEADER_OP(SDMA_OP_NOP);
-}
-
-
-/**
- * sdma_v5_2_ring_emit_pipeline_sync - sync the pipeline
- *
- * @ring: amdgpu_ring pointer
- *
- * Make sure all previous operations are completed (CIK).
- */
-static void sdma_v5_2_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
-{
- uint32_t seq = ring->fence_drv.sync_seq;
- uint64_t addr = ring->fence_drv.gpu_addr;
-
- /* wait for idle */
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
- SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) |
- SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3) | /* equal */
- SDMA_PKT_POLL_REGMEM_HEADER_MEM_POLL(1));
- amdgpu_ring_write(ring, addr & 0xfffffffc);
- amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
- amdgpu_ring_write(ring, seq); /* reference */
- amdgpu_ring_write(ring, 0xffffffff); /* mask */
- amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
- SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(4)); /* retry count, poll interval */
-}
-
-
-/**
- * sdma_v5_2_ring_emit_vm_flush - vm flush using sDMA
- *
- * @ring: amdgpu_ring pointer
- * @vmid: vmid number to use
- * @pd_addr: address
- *
- * Update the page table base and flush the VM TLB
- * using sDMA.
- */
-static void sdma_v5_2_ring_emit_vm_flush(struct amdgpu_ring *ring,
- unsigned vmid, uint64_t pd_addr)
-{
- amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
-}
-
-static void sdma_v5_2_ring_emit_wreg(struct amdgpu_ring *ring,
- uint32_t reg, uint32_t val)
-{
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
- SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
- amdgpu_ring_write(ring, reg);
- amdgpu_ring_write(ring, val);
-}
-
-static void sdma_v5_2_ring_emit_reg_wait(struct amdgpu_ring *ring, uint32_t reg,
- uint32_t val, uint32_t mask)
-{
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
- SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) |
- SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3)); /* equal */
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, 0);
- amdgpu_ring_write(ring, val); /* reference */
- amdgpu_ring_write(ring, mask); /* mask */
- amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
- SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10));
-}
-
-static void sdma_v5_2_ring_emit_reg_write_reg_wait(struct amdgpu_ring *ring,
- uint32_t reg0, uint32_t reg1,
- uint32_t ref, uint32_t mask)
-{
- amdgpu_ring_emit_wreg(ring, reg0, ref);
- /* wait for a cycle to reset vm_inv_eng*_ack */
- amdgpu_ring_emit_reg_wait(ring, reg0, 0, 0);
- amdgpu_ring_emit_reg_wait(ring, reg1, mask, mask);
-}
-
-static int sdma_v5_2_early_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = amdgpu_sdma_init_microcode(adev, 0, true);
- if (r)
- return r;
-
- sdma_v5_2_set_ring_funcs(adev);
- sdma_v5_2_set_buffer_funcs(adev);
- sdma_v5_2_set_vm_pte_funcs(adev);
- sdma_v5_2_set_irq_funcs(adev);
- sdma_v5_2_set_mqd_funcs(adev);
-
- return 0;
-}
-
-static unsigned sdma_v5_2_seq_to_irq_id(int seq_num)
-{
- switch (seq_num) {
- case 0:
- return SOC15_IH_CLIENTID_SDMA0;
- case 1:
- return SOC15_IH_CLIENTID_SDMA1;
- case 2:
- return SOC15_IH_CLIENTID_SDMA2;
- case 3:
- return SOC15_IH_CLIENTID_SDMA3_Sienna_Cichlid;
- default:
- break;
- }
- return -EINVAL;
-}
-
-static unsigned sdma_v5_2_seq_to_trap_id(int seq_num)
-{
- switch (seq_num) {
- case 0:
- return SDMA0_5_0__SRCID__SDMA_TRAP;
- case 1:
- return SDMA1_5_0__SRCID__SDMA_TRAP;
- case 2:
- return SDMA2_5_0__SRCID__SDMA_TRAP;
- case 3:
- return SDMA3_5_0__SRCID__SDMA_TRAP;
- default:
- break;
- }
- return -EINVAL;
-}
-
-static int sdma_v5_2_sw_init(void *handle)
-{
- struct amdgpu_ring *ring;
- int r, i;
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- uint32_t reg_count = ARRAY_SIZE(sdma_reg_list_5_2);
- uint32_t *ptr;
-
- /* SDMA trap event */
- for (i = 0; i < adev->sdma.num_instances; i++) {
- r = amdgpu_irq_add_id(adev, sdma_v5_2_seq_to_irq_id(i),
- sdma_v5_2_seq_to_trap_id(i),
- &adev->sdma.trap_irq);
- if (r)
- return r;
- }
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- ring = &adev->sdma.instance[i].ring;
- ring->ring_obj = NULL;
- ring->use_doorbell = true;
- ring->me = i;
-
- DRM_INFO("use_doorbell being set to: [%s]\n",
- ring->use_doorbell?"true":"false");
-
- ring->doorbell_index =
- (adev->doorbell_index.sdma_engine[i] << 1); //get DWORD offset
-
- ring->vm_hub = AMDGPU_GFXHUB(0);
- sprintf(ring->name, "sdma%d", i);
- r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq,
- AMDGPU_SDMA_IRQ_INSTANCE0 + i,
- AMDGPU_RING_PRIO_DEFAULT, NULL);
- if (r)
- return r;
- }
-
- /* Allocate memory for SDMA IP Dump buffer */
- ptr = kcalloc(adev->sdma.num_instances * reg_count, sizeof(uint32_t), GFP_KERNEL);
- if (ptr)
- adev->sdma.ip_dump = ptr;
- else
- DRM_ERROR("Failed to allocated memory for SDMA IP Dump\n");
-
- return r;
-}
-
-static int sdma_v5_2_sw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++)
- amdgpu_ring_fini(&adev->sdma.instance[i].ring);
-
- amdgpu_sdma_destroy_inst_ctx(adev, true);
-
- kfree(adev->sdma.ip_dump);
-
- return 0;
-}
-
-static int sdma_v5_2_hw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- return sdma_v5_2_start(adev);
-}
-
-static int sdma_v5_2_hw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- if (amdgpu_sriov_vf(adev))
- return 0;
-
- sdma_v5_2_ctx_switch_enable(adev, false);
- sdma_v5_2_enable(adev, false);
-
- return 0;
-}
-
-static int sdma_v5_2_suspend(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- return sdma_v5_2_hw_fini(adev);
-}
-
-static int sdma_v5_2_resume(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- return sdma_v5_2_hw_init(adev);
-}
-
-static bool sdma_v5_2_is_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- u32 i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- u32 tmp = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_STATUS_REG));
-
- if (!(tmp & SDMA0_STATUS_REG__IDLE_MASK))
- return false;
- }
-
- return true;
-}
-
-static int sdma_v5_2_wait_for_idle(void *handle)
-{
- unsigned i;
- u32 sdma0, sdma1, sdma2, sdma3;
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- for (i = 0; i < adev->usec_timeout; i++) {
- sdma0 = RREG32(sdma_v5_2_get_reg_offset(adev, 0, mmSDMA0_STATUS_REG));
- sdma1 = RREG32(sdma_v5_2_get_reg_offset(adev, 1, mmSDMA0_STATUS_REG));
- sdma2 = RREG32(sdma_v5_2_get_reg_offset(adev, 2, mmSDMA0_STATUS_REG));
- sdma3 = RREG32(sdma_v5_2_get_reg_offset(adev, 3, mmSDMA0_STATUS_REG));
-
- if (sdma0 & sdma1 & sdma2 & sdma3 & SDMA0_STATUS_REG__IDLE_MASK)
- return 0;
- udelay(1);
- }
- return -ETIMEDOUT;
-}
-
-static int sdma_v5_2_ring_preempt_ib(struct amdgpu_ring *ring)
-{
- int i, r = 0;
- struct amdgpu_device *adev = ring->adev;
- u32 index = 0;
- u64 sdma_gfx_preempt;
-
- amdgpu_sdma_get_index_from_ring(ring, &index);
- sdma_gfx_preempt =
- sdma_v5_2_get_reg_offset(adev, index, mmSDMA0_GFX_PREEMPT);
-
- /* assert preemption condition */
- amdgpu_ring_set_preempt_cond_exec(ring, false);
-
- /* emit the trailing fence */
- ring->trail_seq += 1;
- amdgpu_ring_alloc(ring, 10);
- sdma_v5_2_ring_emit_fence(ring, ring->trail_fence_gpu_addr,
- ring->trail_seq, 0);
- amdgpu_ring_commit(ring);
-
- /* assert IB preemption */
- WREG32(sdma_gfx_preempt, 1);
-
- /* poll the trailing fence */
- for (i = 0; i < adev->usec_timeout; i++) {
- if (ring->trail_seq ==
- le32_to_cpu(*(ring->trail_fence_cpu_addr)))
- break;
- udelay(1);
- }
-
- if (i >= adev->usec_timeout) {
- r = -EINVAL;
- DRM_ERROR("ring %d failed to be preempted\n", ring->idx);
- }
-
- /* deassert IB preemption */
- WREG32(sdma_gfx_preempt, 0);
-
- /* deassert the preemption condition */
- amdgpu_ring_set_preempt_cond_exec(ring, true);
- return r;
-}
-
-static int sdma_v5_2_set_trap_irq_state(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- unsigned type,
- enum amdgpu_interrupt_state state)
-{
- u32 sdma_cntl;
- u32 reg_offset = sdma_v5_2_get_reg_offset(adev, type, mmSDMA0_CNTL);
-
- if (!amdgpu_sriov_vf(adev)) {
- sdma_cntl = RREG32(reg_offset);
- sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE,
- state == AMDGPU_IRQ_STATE_ENABLE ? 1 : 0);
- WREG32(reg_offset, sdma_cntl);
- }
-
- return 0;
-}
-
-static int sdma_v5_2_process_trap_irq(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- struct amdgpu_iv_entry *entry)
-{
- uint32_t mes_queue_id = entry->src_data[0];
-
- DRM_DEBUG("IH: SDMA trap\n");
-
- if (adev->enable_mes && (mes_queue_id & AMDGPU_FENCE_MES_QUEUE_FLAG)) {
- struct amdgpu_mes_queue *queue;
-
- mes_queue_id &= AMDGPU_FENCE_MES_QUEUE_ID_MASK;
-
- spin_lock(&adev->mes.queue_id_lock);
- queue = idr_find(&adev->mes.queue_id_idr, mes_queue_id);
- if (queue) {
- DRM_DEBUG("process smda queue id = %d\n", mes_queue_id);
- amdgpu_fence_process(queue->ring);
- }
- spin_unlock(&adev->mes.queue_id_lock);
- return 0;
- }
-
- switch (entry->client_id) {
- case SOC15_IH_CLIENTID_SDMA0:
- switch (entry->ring_id) {
- case 0:
- amdgpu_fence_process(&adev->sdma.instance[0].ring);
- break;
- case 1:
- /* XXX compute */
- break;
- case 2:
- /* XXX compute */
- break;
- case 3:
- /* XXX page queue*/
- break;
- }
- break;
- case SOC15_IH_CLIENTID_SDMA1:
- switch (entry->ring_id) {
- case 0:
- amdgpu_fence_process(&adev->sdma.instance[1].ring);
- break;
- case 1:
- /* XXX compute */
- break;
- case 2:
- /* XXX compute */
- break;
- case 3:
- /* XXX page queue*/
- break;
- }
- break;
- case SOC15_IH_CLIENTID_SDMA2:
- switch (entry->ring_id) {
- case 0:
- amdgpu_fence_process(&adev->sdma.instance[2].ring);
- break;
- case 1:
- /* XXX compute */
- break;
- case 2:
- /* XXX compute */
- break;
- case 3:
- /* XXX page queue*/
- break;
- }
- break;
- case SOC15_IH_CLIENTID_SDMA3_Sienna_Cichlid:
- switch (entry->ring_id) {
- case 0:
- amdgpu_fence_process(&adev->sdma.instance[3].ring);
- break;
- case 1:
- /* XXX compute */
- break;
- case 2:
- /* XXX compute */
- break;
- case 3:
- /* XXX page queue*/
- break;
- }
- break;
- }
- return 0;
-}
-
-static int sdma_v5_2_process_illegal_inst_irq(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- struct amdgpu_iv_entry *entry)
-{
- return 0;
-}
-
-static bool sdma_v5_2_firmware_mgcg_support(struct amdgpu_device *adev,
- int i)
-{
- switch (amdgpu_ip_version(adev, SDMA0_HWIP, 0)) {
- case IP_VERSION(5, 2, 1):
- if (adev->sdma.instance[i].fw_version < 70)
- return false;
- break;
- case IP_VERSION(5, 2, 3):
- if (adev->sdma.instance[i].fw_version < 47)
- return false;
- break;
- case IP_VERSION(5, 2, 7):
- if (adev->sdma.instance[i].fw_version < 9)
- return false;
- break;
- default:
- return true;
- }
-
- return true;
-
-}
-
-static void sdma_v5_2_update_medium_grain_clock_gating(struct amdgpu_device *adev,
- bool enable)
-{
- uint32_t data, def;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
-
- if (!sdma_v5_2_firmware_mgcg_support(adev, i))
- adev->cg_flags &= ~AMD_CG_SUPPORT_SDMA_MGCG;
-
- if (enable && (adev->cg_flags & AMD_CG_SUPPORT_SDMA_MGCG)) {
- /* Enable sdma clock gating */
- def = data = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CLK_CTRL));
- data &= ~(SDMA0_CLK_CTRL__SOFT_OVERRIDE4_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE3_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE2_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE1_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE0_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDER_REG_MASK);
- if (def != data)
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CLK_CTRL), data);
- } else {
- /* Disable sdma clock gating */
- def = data = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CLK_CTRL));
- data |= (SDMA0_CLK_CTRL__SOFT_OVERRIDE4_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE3_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE2_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE1_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE0_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDER_REG_MASK);
- if (def != data)
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CLK_CTRL), data);
- }
- }
-}
-
-static void sdma_v5_2_update_medium_grain_light_sleep(struct amdgpu_device *adev,
- bool enable)
-{
- uint32_t data, def;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- if (adev->sdma.instance[i].fw_version < 70 &&
- amdgpu_ip_version(adev, SDMA0_HWIP, 0) ==
- IP_VERSION(5, 2, 1))
- adev->cg_flags &= ~AMD_CG_SUPPORT_SDMA_LS;
-
- if (enable && (adev->cg_flags & AMD_CG_SUPPORT_SDMA_LS)) {
- /* Enable sdma mem light sleep */
- def = data = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_POWER_CNTL));
- data |= SDMA0_POWER_CNTL__MEM_POWER_OVERRIDE_MASK;
- if (def != data)
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_POWER_CNTL), data);
-
- } else {
- /* Disable sdma mem light sleep */
- def = data = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_POWER_CNTL));
- data &= ~SDMA0_POWER_CNTL__MEM_POWER_OVERRIDE_MASK;
- if (def != data)
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_POWER_CNTL), data);
-
- }
- }
-}
-
-static int sdma_v5_2_set_clockgating_state(void *handle,
- enum amd_clockgating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- if (amdgpu_sriov_vf(adev))
- return 0;
-
- switch (amdgpu_ip_version(adev, SDMA0_HWIP, 0)) {
- case IP_VERSION(5, 2, 0):
- case IP_VERSION(5, 2, 2):
- case IP_VERSION(5, 2, 1):
- case IP_VERSION(5, 2, 4):
- case IP_VERSION(5, 2, 5):
- case IP_VERSION(5, 2, 6):
- case IP_VERSION(5, 2, 3):
- case IP_VERSION(5, 2, 7):
- sdma_v5_2_update_medium_grain_clock_gating(adev,
- state == AMD_CG_STATE_GATE);
- sdma_v5_2_update_medium_grain_light_sleep(adev,
- state == AMD_CG_STATE_GATE);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int sdma_v5_2_set_powergating_state(void *handle,
- enum amd_powergating_state state)
-{
- return 0;
-}
-
-static void sdma_v5_2_get_clockgating_state(void *handle, u64 *flags)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int data;
-
- if (amdgpu_sriov_vf(adev))
- *flags = 0;
-
- /* AMD_CG_SUPPORT_SDMA_MGCG */
- data = RREG32(sdma_v5_2_get_reg_offset(adev, 0, mmSDMA0_CLK_CTRL));
- if (!(data & SDMA0_CLK_CTRL__CGCG_EN_OVERRIDE_MASK))
- *flags |= AMD_CG_SUPPORT_SDMA_MGCG;
-
- /* AMD_CG_SUPPORT_SDMA_LS */
- data = RREG32_KIQ(sdma_v5_2_get_reg_offset(adev, 0, mmSDMA0_POWER_CNTL));
- if (data & SDMA0_POWER_CNTL__MEM_POWER_OVERRIDE_MASK)
- *flags |= AMD_CG_SUPPORT_SDMA_LS;
-}
-
-static void sdma_v5_2_ring_begin_use(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- /* SDMA 5.2.3 (RMB) FW doesn't seem to properly
- * disallow GFXOFF in some cases leading to
- * hangs in SDMA. Disallow GFXOFF while SDMA is active.
- * We can probably just limit this to 5.2.3,
- * but it shouldn't hurt for other parts since
- * this GFXOFF will be disallowed anyway when SDMA is
- * active, this just makes it explicit.
- * sdma_v5_2_ring_set_wptr() takes advantage of this
- * to update the wptr because sometimes SDMA seems to miss
- * doorbells when entering PG. If you remove this, update
- * sdma_v5_2_ring_set_wptr() as well!
- */
- amdgpu_gfx_off_ctrl(adev, false);
-}
-
-static void sdma_v5_2_ring_end_use(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- /* SDMA 5.2.3 (RMB) FW doesn't seem to properly
- * disallow GFXOFF in some cases leading to
- * hangs in SDMA. Allow GFXOFF when SDMA is complete.
- */
- amdgpu_gfx_off_ctrl(adev, true);
-}
-
-static void sdma_v5_2_print_ip_state(void *handle, struct drm_printer *p)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- uint32_t reg_count = ARRAY_SIZE(sdma_reg_list_5_2);
- uint32_t instance_offset;
-
- if (!adev->sdma.ip_dump)
- return;
-
- drm_printf(p, "num_instances:%d\n", adev->sdma.num_instances);
- for (i = 0; i < adev->sdma.num_instances; i++) {
- instance_offset = i * reg_count;
- drm_printf(p, "\nInstance:%d\n", i);
-
- for (j = 0; j < reg_count; j++)
- drm_printf(p, "%-50s \t 0x%08x\n", sdma_reg_list_5_2[j].reg_name,
- adev->sdma.ip_dump[instance_offset + j]);
- }
-}
-
-static void sdma_v5_2_dump_ip_state(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- uint32_t instance_offset;
- uint32_t reg_count = ARRAY_SIZE(sdma_reg_list_5_2);
-
- if (!adev->sdma.ip_dump)
- return;
-
- amdgpu_gfx_off_ctrl(adev, false);
- for (i = 0; i < adev->sdma.num_instances; i++) {
- instance_offset = i * reg_count;
- for (j = 0; j < reg_count; j++)
- adev->sdma.ip_dump[instance_offset + j] =
- RREG32(sdma_v5_2_get_reg_offset(adev, i,
- sdma_reg_list_5_2[j].reg_offset));
- }
- amdgpu_gfx_off_ctrl(adev, true);
-}
-
-static const struct amd_ip_funcs sdma_v5_2_ip_funcs = {
- .name = "sdma_v5_2",
- .early_init = sdma_v5_2_early_init,
- .late_init = NULL,
- .sw_init = sdma_v5_2_sw_init,
- .sw_fini = sdma_v5_2_sw_fini,
- .hw_init = sdma_v5_2_hw_init,
- .hw_fini = sdma_v5_2_hw_fini,
- .suspend = sdma_v5_2_suspend,
- .resume = sdma_v5_2_resume,
- .is_idle = sdma_v5_2_is_idle,
- .wait_for_idle = sdma_v5_2_wait_for_idle,
- .soft_reset = sdma_v5_2_soft_reset,
- .set_clockgating_state = sdma_v5_2_set_clockgating_state,
- .set_powergating_state = sdma_v5_2_set_powergating_state,
- .get_clockgating_state = sdma_v5_2_get_clockgating_state,
- .dump_ip_state = sdma_v5_2_dump_ip_state,
- .print_ip_state = sdma_v5_2_print_ip_state,
-};
-
-static const struct amdgpu_ring_funcs sdma_v5_2_ring_funcs = {
- .type = AMDGPU_RING_TYPE_SDMA,
- .align_mask = 0xf,
- .nop = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP),
- .support_64bit_ptrs = true,
- .secure_submission_supported = true,
- .get_rptr = sdma_v5_2_ring_get_rptr,
- .get_wptr = sdma_v5_2_ring_get_wptr,
- .set_wptr = sdma_v5_2_ring_set_wptr,
- .emit_frame_size =
- 5 + /* sdma_v5_2_ring_init_cond_exec */
- 6 + /* sdma_v5_2_ring_emit_hdp_flush */
- 3 + /* hdp_invalidate */
- 6 + /* sdma_v5_2_ring_emit_pipeline_sync */
- /* sdma_v5_2_ring_emit_vm_flush */
- SOC15_FLUSH_GPU_TLB_NUM_WREG * 3 +
- SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 6 +
- 10 + 10 + 10, /* sdma_v5_2_ring_emit_fence x3 for user fence, vm fence */
- .emit_ib_size = 7 + 6, /* sdma_v5_2_ring_emit_ib */
- .emit_ib = sdma_v5_2_ring_emit_ib,
- .emit_mem_sync = sdma_v5_2_ring_emit_mem_sync,
- .emit_fence = sdma_v5_2_ring_emit_fence,
- .emit_pipeline_sync = sdma_v5_2_ring_emit_pipeline_sync,
- .emit_vm_flush = sdma_v5_2_ring_emit_vm_flush,
- .emit_hdp_flush = sdma_v5_2_ring_emit_hdp_flush,
- .test_ring = sdma_v5_2_ring_test_ring,
- .test_ib = sdma_v5_2_ring_test_ib,
- .insert_nop = sdma_v5_2_ring_insert_nop,
- .pad_ib = sdma_v5_2_ring_pad_ib,
- .begin_use = sdma_v5_2_ring_begin_use,
- .end_use = sdma_v5_2_ring_end_use,
- .emit_wreg = sdma_v5_2_ring_emit_wreg,
- .emit_reg_wait = sdma_v5_2_ring_emit_reg_wait,
- .emit_reg_write_reg_wait = sdma_v5_2_ring_emit_reg_write_reg_wait,
- .init_cond_exec = sdma_v5_2_ring_init_cond_exec,
- .preempt_ib = sdma_v5_2_ring_preempt_ib,
-};
-
-static void sdma_v5_2_set_ring_funcs(struct amdgpu_device *adev)
-{
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- adev->sdma.instance[i].ring.funcs = &sdma_v5_2_ring_funcs;
- adev->sdma.instance[i].ring.me = i;
- }
-}
-
-static const struct amdgpu_irq_src_funcs sdma_v5_2_trap_irq_funcs = {
- .set = sdma_v5_2_set_trap_irq_state,
- .process = sdma_v5_2_process_trap_irq,
-};
-
-static const struct amdgpu_irq_src_funcs sdma_v5_2_illegal_inst_irq_funcs = {
- .process = sdma_v5_2_process_illegal_inst_irq,
-};
-
-static void sdma_v5_2_set_irq_funcs(struct amdgpu_device *adev)
-{
- adev->sdma.trap_irq.num_types = AMDGPU_SDMA_IRQ_INSTANCE0 +
- adev->sdma.num_instances;
- adev->sdma.trap_irq.funcs = &sdma_v5_2_trap_irq_funcs;
- adev->sdma.illegal_inst_irq.funcs = &sdma_v5_2_illegal_inst_irq_funcs;
-}
-
-/**
- * sdma_v5_2_emit_copy_buffer - copy buffer using the sDMA engine
- *
- * @ib: indirect buffer to copy to
- * @src_offset: src GPU address
- * @dst_offset: dst GPU address
- * @byte_count: number of bytes to xfer
- * @copy_flags: copy flags for the buffers
- *
- * Copy GPU buffers using the DMA engine.
- * Used by the amdgpu ttm implementation to move pages if
- * registered as the asic copy callback.
- */
-static void sdma_v5_2_emit_copy_buffer(struct amdgpu_ib *ib,
- uint64_t src_offset,
- uint64_t dst_offset,
- uint32_t byte_count,
- uint32_t copy_flags)
-{
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR) |
- SDMA_PKT_COPY_LINEAR_HEADER_TMZ((copy_flags & AMDGPU_COPY_FLAGS_TMZ) ? 1 : 0);
- ib->ptr[ib->length_dw++] = byte_count - 1;
- ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
- ib->ptr[ib->length_dw++] = lower_32_bits(src_offset);
- ib->ptr[ib->length_dw++] = upper_32_bits(src_offset);
- ib->ptr[ib->length_dw++] = lower_32_bits(dst_offset);
- ib->ptr[ib->length_dw++] = upper_32_bits(dst_offset);
-}
-
-/**
- * sdma_v5_2_emit_fill_buffer - fill buffer using the sDMA engine
- *
- * @ib: indirect buffer to fill
- * @src_data: value to write to buffer
- * @dst_offset: dst GPU address
- * @byte_count: number of bytes to xfer
- *
- * Fill GPU buffers using the DMA engine.
- */
-static void sdma_v5_2_emit_fill_buffer(struct amdgpu_ib *ib,
- uint32_t src_data,
- uint64_t dst_offset,
- uint32_t byte_count)
-{
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_CONST_FILL);
- ib->ptr[ib->length_dw++] = lower_32_bits(dst_offset);
- ib->ptr[ib->length_dw++] = upper_32_bits(dst_offset);
- ib->ptr[ib->length_dw++] = src_data;
- ib->ptr[ib->length_dw++] = byte_count - 1;
-}
-
-static const struct amdgpu_buffer_funcs sdma_v5_2_buffer_funcs = {
- .copy_max_bytes = 0x400000,
- .copy_num_dw = 7,
- .emit_copy_buffer = sdma_v5_2_emit_copy_buffer,
-
- .fill_max_bytes = 0x400000,
- .fill_num_dw = 5,
- .emit_fill_buffer = sdma_v5_2_emit_fill_buffer,
-};
-
-static void sdma_v5_2_set_buffer_funcs(struct amdgpu_device *adev)
-{
- if (adev->mman.buffer_funcs == NULL) {
- adev->mman.buffer_funcs = &sdma_v5_2_buffer_funcs;
- adev->mman.buffer_funcs_ring = &adev->sdma.instance[0].ring;
- }
-}
-
-static const struct amdgpu_vm_pte_funcs sdma_v5_2_vm_pte_funcs = {
- .copy_pte_num_dw = 7,
- .copy_pte = sdma_v5_2_vm_copy_pte,
- .write_pte = sdma_v5_2_vm_write_pte,
- .set_pte_pde = sdma_v5_2_vm_set_pte_pde,
-};
-
-static void sdma_v5_2_set_vm_pte_funcs(struct amdgpu_device *adev)
-{
- unsigned i;
-
- if (adev->vm_manager.vm_pte_funcs == NULL) {
- adev->vm_manager.vm_pte_funcs = &sdma_v5_2_vm_pte_funcs;
- for (i = 0; i < adev->sdma.num_instances; i++) {
- adev->vm_manager.vm_pte_scheds[i] =
- &adev->sdma.instance[i].ring.sched;
- }
- adev->vm_manager.vm_pte_num_scheds = adev->sdma.num_instances;
- }
-}
-
-const struct amdgpu_ip_block_version sdma_v5_2_ip_block = {
- .type = AMD_IP_BLOCK_TYPE_SDMA,
- .major = 5,
- .minor = 2,
- .rev = 0,
- .funcs = &sdma_v5_2_ip_funcs,
-};
diff --git a/rr-cache/f013472cad3d9dd7cdbe39311eced4b9997712a7/preimage.1 b/rr-cache/f013472cad3d9dd7cdbe39311eced4b9997712a7/preimage.1
deleted file mode 100644
index edcbe7fec8d5..000000000000
--- a/rr-cache/f013472cad3d9dd7cdbe39311eced4b9997712a7/preimage.1
+++ /dev/null
@@ -1,1974 +0,0 @@
-/*
- * Copyright 2019 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/delay.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-
-#include "amdgpu.h"
-#include "amdgpu_ucode.h"
-#include "amdgpu_trace.h"
-
-#include "gc/gc_10_3_0_offset.h"
-#include "gc/gc_10_3_0_sh_mask.h"
-#include "ivsrcid/sdma0/irqsrcs_sdma0_5_0.h"
-#include "ivsrcid/sdma1/irqsrcs_sdma1_5_0.h"
-#include "ivsrcid/sdma2/irqsrcs_sdma2_5_0.h"
-#include "ivsrcid/sdma3/irqsrcs_sdma3_5_0.h"
-
-#include "soc15_common.h"
-#include "soc15.h"
-#include "navi10_sdma_pkt_open.h"
-#include "nbio_v2_3.h"
-#include "sdma_common.h"
-#include "sdma_v5_2.h"
-
-MODULE_FIRMWARE("amdgpu/sienna_cichlid_sdma.bin");
-MODULE_FIRMWARE("amdgpu/navy_flounder_sdma.bin");
-MODULE_FIRMWARE("amdgpu/dimgrey_cavefish_sdma.bin");
-MODULE_FIRMWARE("amdgpu/beige_goby_sdma.bin");
-
-MODULE_FIRMWARE("amdgpu/vangogh_sdma.bin");
-MODULE_FIRMWARE("amdgpu/yellow_carp_sdma.bin");
-MODULE_FIRMWARE("amdgpu/sdma_5_2_6.bin");
-MODULE_FIRMWARE("amdgpu/sdma_5_2_7.bin");
-
-#define SDMA1_REG_OFFSET 0x600
-#define SDMA3_REG_OFFSET 0x400
-#define SDMA0_HYP_DEC_REG_START 0x5880
-#define SDMA0_HYP_DEC_REG_END 0x5893
-#define SDMA1_HYP_DEC_REG_OFFSET 0x20
-
-static const struct amdgpu_hwip_reg_entry sdma_reg_list_5_2[] = {
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_STATUS_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_STATUS1_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_STATUS2_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_STATUS3_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UCODE_CHECKSUM),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RB_RPTR_FETCH_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RB_RPTR_FETCH),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_RD_STATUS),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_WR_STATUS),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_RD_XNACK0),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_RD_XNACK1),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_WR_XNACK0),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_UTCL1_WR_XNACK1),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_RPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_RPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_WPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_RB_WPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_OFFSET),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_BASE_LO),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_BASE_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_RPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_IB_SUB_REMAIN),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_GFX_DUMMY_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_RPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_RPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_WPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_RB_WPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_IB_OFFSET),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_IB_BASE_LO),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_IB_BASE_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_PAGE_DUMMY_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_RPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_RPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_WPTR),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_RB_WPTR_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_IB_OFFSET),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_IB_BASE_LO),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_IB_BASE_HI),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_RLC0_DUMMY_REG),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_INT_STATUS),
- SOC15_REG_ENTRY_STR(GC, 0, mmSDMA0_VM_CNTL),
- SOC15_REG_ENTRY_STR(GC, 0, mmGRBM_STATUS2)
-};
-
-static void sdma_v5_2_set_ring_funcs(struct amdgpu_device *adev);
-static void sdma_v5_2_set_buffer_funcs(struct amdgpu_device *adev);
-static void sdma_v5_2_set_vm_pte_funcs(struct amdgpu_device *adev);
-static void sdma_v5_2_set_irq_funcs(struct amdgpu_device *adev);
-
-static u32 sdma_v5_2_get_reg_offset(struct amdgpu_device *adev, u32 instance, u32 internal_offset)
-{
- u32 base;
-
- if (internal_offset >= SDMA0_HYP_DEC_REG_START &&
- internal_offset <= SDMA0_HYP_DEC_REG_END) {
- base = adev->reg_offset[GC_HWIP][0][1];
- if (instance != 0)
- internal_offset += SDMA1_HYP_DEC_REG_OFFSET * instance;
- } else {
- if (instance < 2) {
- base = adev->reg_offset[GC_HWIP][0][0];
- if (instance == 1)
- internal_offset += SDMA1_REG_OFFSET;
- } else {
- base = adev->reg_offset[GC_HWIP][0][2];
- if (instance == 3)
- internal_offset += SDMA3_REG_OFFSET;
- }
- }
-
- return base + internal_offset;
-}
-
-static unsigned sdma_v5_2_ring_init_cond_exec(struct amdgpu_ring *ring,
- uint64_t addr)
-{
- unsigned ret;
-
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_COND_EXE));
- amdgpu_ring_write(ring, lower_32_bits(addr));
- amdgpu_ring_write(ring, upper_32_bits(addr));
- amdgpu_ring_write(ring, 1);
- /* this is the offset we need patch later */
- ret = ring->wptr & ring->buf_mask;
- /* insert dummy here and patch it later */
- amdgpu_ring_write(ring, 0);
-
- return ret;
-}
-
-/**
- * sdma_v5_2_ring_get_rptr - get the current read pointer
- *
- * @ring: amdgpu ring pointer
- *
- * Get the current rptr from the hardware (NAVI10+).
- */
-static uint64_t sdma_v5_2_ring_get_rptr(struct amdgpu_ring *ring)
-{
- u64 *rptr;
-
- /* XXX check if swapping is necessary on BE */
- rptr = (u64 *)ring->rptr_cpu_addr;
-
- DRM_DEBUG("rptr before shift == 0x%016llx\n", *rptr);
- return ((*rptr) >> 2);
-}
-
-/**
- * sdma_v5_2_ring_get_wptr - get the current write pointer
- *
- * @ring: amdgpu ring pointer
- *
- * Get the current wptr from the hardware (NAVI10+).
- */
-static uint64_t sdma_v5_2_ring_get_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- u64 wptr;
-
- if (ring->use_doorbell) {
- /* XXX check if swapping is necessary on BE */
- wptr = READ_ONCE(*((u64 *)ring->wptr_cpu_addr));
- DRM_DEBUG("wptr/doorbell before shift == 0x%016llx\n", wptr);
- } else {
- wptr = RREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR_HI));
- wptr = wptr << 32;
- wptr |= RREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR));
- DRM_DEBUG("wptr before shift [%i] wptr == 0x%016llx\n", ring->me, wptr);
- }
-
- return wptr >> 2;
-}
-
-/**
- * sdma_v5_2_ring_set_wptr - commit the write pointer
- *
- * @ring: amdgpu ring pointer
- *
- * Write the wptr back to the hardware (NAVI10+).
- */
-static void sdma_v5_2_ring_set_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- DRM_DEBUG("Setting write pointer\n");
- if (ring->use_doorbell) {
- DRM_DEBUG("Using doorbell -- "
- "wptr_offs == 0x%08x "
- "lower_32_bits(ring->wptr << 2) == 0x%08x "
- "upper_32_bits(ring->wptr << 2) == 0x%08x\n",
- ring->wptr_offs,
- lower_32_bits(ring->wptr << 2),
- upper_32_bits(ring->wptr << 2));
- /* XXX check if swapping is necessary on BE */
- atomic64_set((atomic64_t *)ring->wptr_cpu_addr,
- ring->wptr << 2);
- DRM_DEBUG("calling WDOORBELL64(0x%08x, 0x%016llx)\n",
- ring->doorbell_index, ring->wptr << 2);
- WDOORBELL64(ring->doorbell_index, ring->wptr << 2);
-<<<<<<<
- /* SDMA seems to miss doorbells sometimes when powergating kicks in.
- * Updating the wptr directly will wake it. This is only safe because
- * we disallow gfxoff in begin_use() and then allow it again in end_use().
- */
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR),
- lower_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR_HI),
- upper_32_bits(ring->wptr << 2));
-=======
- if (amdgpu_ip_version(adev, SDMA0_HWIP, 0) == IP_VERSION(5, 2, 1)) {
- /* SDMA seems to miss doorbells sometimes when powergating kicks in.
- * Updating the wptr directly will wake it. This is only safe because
- * we disallow gfxoff in begin_use() and then allow it again in end_use().
- */
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR),
- lower_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR_HI),
- upper_32_bits(ring->wptr << 2));
- }
->>>>>>>
- } else {
- DRM_DEBUG("Not using doorbell -- "
- "mmSDMA%i_GFX_RB_WPTR == 0x%08x "
- "mmSDMA%i_GFX_RB_WPTR_HI == 0x%08x\n",
- ring->me,
- lower_32_bits(ring->wptr << 2),
- ring->me,
- upper_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR),
- lower_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, ring->me, mmSDMA0_GFX_RB_WPTR_HI),
- upper_32_bits(ring->wptr << 2));
- }
-}
-
-static void sdma_v5_2_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count)
-{
- struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring);
- int i;
-
- for (i = 0; i < count; i++)
- if (sdma && sdma->burst_nop && (i == 0))
- amdgpu_ring_write(ring, ring->funcs->nop |
- SDMA_PKT_NOP_HEADER_COUNT(count - 1));
- else
- amdgpu_ring_write(ring, ring->funcs->nop);
-}
-
-/**
- * sdma_v5_2_ring_emit_ib - Schedule an IB on the DMA engine
- *
- * @ring: amdgpu ring pointer
- * @job: job to retrieve vmid from
- * @ib: IB object to schedule
- * @flags: unused
- *
- * Schedule an IB in the DMA ring.
- */
-static void sdma_v5_2_ring_emit_ib(struct amdgpu_ring *ring,
- struct amdgpu_job *job,
- struct amdgpu_ib *ib,
- uint32_t flags)
-{
- unsigned vmid = AMDGPU_JOB_GET_VMID(job);
- uint64_t csa_mc_addr = amdgpu_sdma_get_csa_mc_addr(ring, vmid);
-
- /* An IB packet must end on a 8 DW boundary--the next dword
- * must be on a 8-dword boundary. Our IB packet below is 6
- * dwords long, thus add x number of NOPs, such that, in
- * modular arithmetic,
- * wptr + 6 + x = 8k, k >= 0, which in C is,
- * (wptr + 6 + x) % 8 = 0.
- * The expression below, is a solution of x.
- */
- sdma_v5_2_ring_insert_nop(ring, (2 - lower_32_bits(ring->wptr)) & 7);
-
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_INDIRECT) |
- SDMA_PKT_INDIRECT_HEADER_VMID(vmid & 0xf));
- /* base must be 32 byte aligned */
- amdgpu_ring_write(ring, lower_32_bits(ib->gpu_addr) & 0xffffffe0);
- amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr));
- amdgpu_ring_write(ring, ib->length_dw);
- amdgpu_ring_write(ring, lower_32_bits(csa_mc_addr));
- amdgpu_ring_write(ring, upper_32_bits(csa_mc_addr));
-}
-
-/**
- * sdma_v5_2_ring_emit_mem_sync - flush the IB by graphics cache rinse
- *
- * @ring: amdgpu ring pointer
- *
- * flush the IB by graphics cache rinse.
- */
-static void sdma_v5_2_ring_emit_mem_sync(struct amdgpu_ring *ring)
-{
- uint32_t gcr_cntl = SDMA_GCR_GL2_INV | SDMA_GCR_GL2_WB |
- SDMA_GCR_GLM_INV | SDMA_GCR_GL1_INV |
- SDMA_GCR_GLV_INV | SDMA_GCR_GLK_INV |
- SDMA_GCR_GLI_INV(1);
-
- /* flush entire cache L0/L1/L2, this can be optimized by performance requirement */
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_GCR_REQ));
- amdgpu_ring_write(ring, SDMA_PKT_GCR_REQ_PAYLOAD1_BASE_VA_31_7(0));
- amdgpu_ring_write(ring, SDMA_PKT_GCR_REQ_PAYLOAD2_GCR_CONTROL_15_0(gcr_cntl) |
- SDMA_PKT_GCR_REQ_PAYLOAD2_BASE_VA_47_32(0));
- amdgpu_ring_write(ring, SDMA_PKT_GCR_REQ_PAYLOAD3_LIMIT_VA_31_7(0) |
- SDMA_PKT_GCR_REQ_PAYLOAD3_GCR_CONTROL_18_16(gcr_cntl >> 16));
- amdgpu_ring_write(ring, SDMA_PKT_GCR_REQ_PAYLOAD4_LIMIT_VA_47_32(0) |
- SDMA_PKT_GCR_REQ_PAYLOAD4_VMID(0));
-}
-
-/**
- * sdma_v5_2_ring_emit_hdp_flush - emit an hdp flush on the DMA ring
- *
- * @ring: amdgpu ring pointer
- *
- * Emit an hdp flush packet on the requested DMA ring.
- */
-static void sdma_v5_2_ring_emit_hdp_flush(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- u32 ref_and_mask = 0;
- const struct nbio_hdp_flush_reg *nbio_hf_reg = adev->nbio.hdp_flush_reg;
-
- if (ring->me > 1) {
- amdgpu_asic_flush_hdp(adev, ring);
- } else {
- ref_and_mask = nbio_hf_reg->ref_and_mask_sdma0 << ring->me;
-
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
- SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(1) |
- SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3)); /* == */
- amdgpu_ring_write(ring, (adev->nbio.funcs->get_hdp_flush_done_offset(adev)) << 2);
- amdgpu_ring_write(ring, (adev->nbio.funcs->get_hdp_flush_req_offset(adev)) << 2);
- amdgpu_ring_write(ring, ref_and_mask); /* reference */
- amdgpu_ring_write(ring, ref_and_mask); /* mask */
- amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
- SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10)); /* retry count, poll interval */
- }
-}
-
-/**
- * sdma_v5_2_ring_emit_fence - emit a fence on the DMA ring
- *
- * @ring: amdgpu ring pointer
- * @addr: address
- * @seq: sequence number
- * @flags: fence related flags
- *
- * Add a DMA fence packet to the ring to write
- * the fence seq number and DMA trap packet to generate
- * an interrupt if needed.
- */
-static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,
- unsigned flags)
-{
- bool write64bit = flags & AMDGPU_FENCE_FLAG_64BIT;
- /* write the fence */
- 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);
- amdgpu_ring_write(ring, lower_32_bits(addr));
- amdgpu_ring_write(ring, upper_32_bits(addr));
- amdgpu_ring_write(ring, lower_32_bits(seq));
-
- /* optionally write high bits as well */
- if (write64bit) {
- addr += 4;
- 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);
- amdgpu_ring_write(ring, lower_32_bits(addr));
- amdgpu_ring_write(ring, upper_32_bits(addr));
- amdgpu_ring_write(ring, upper_32_bits(seq));
- }
-
- if ((flags & AMDGPU_FENCE_FLAG_INT)) {
- uint32_t ctx = ring->is_mes_queue ?
- (ring->hw_queue_id | AMDGPU_FENCE_MES_QUEUE_FLAG) : 0;
- /* generate an interrupt */
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_TRAP));
- amdgpu_ring_write(ring, SDMA_PKT_TRAP_INT_CONTEXT_INT_CONTEXT(ctx));
- }
-}
-
-
-/**
- * sdma_v5_2_gfx_stop - stop the gfx async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Stop the gfx async dma ring buffers.
- */
-static void sdma_v5_2_gfx_stop(struct amdgpu_device *adev)
-{
- u32 rb_cntl, ib_cntl;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- rb_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL));
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_ENABLE, 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL), rb_cntl);
- ib_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_IB_CNTL));
- ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_ENABLE, 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_IB_CNTL), ib_cntl);
- }
-}
-
-/**
- * sdma_v5_2_rlc_stop - stop the compute async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Stop the compute async dma queues.
- */
-static void sdma_v5_2_rlc_stop(struct amdgpu_device *adev)
-{
- /* XXX todo */
-}
-
-/**
- * sdma_v5_2_ctx_switch_enable - stop the async dma engines context switch
- *
- * @adev: amdgpu_device pointer
- * @enable: enable/disable the DMA MEs context switch.
- *
- * Halt or unhalt the async dma engines context switch.
- */
-static void sdma_v5_2_ctx_switch_enable(struct amdgpu_device *adev, bool enable)
-{
- u32 f32_cntl, phase_quantum = 0;
- int i;
-
- if (amdgpu_sdma_phase_quantum) {
- unsigned value = amdgpu_sdma_phase_quantum;
- unsigned unit = 0;
-
- while (value > (SDMA0_PHASE0_QUANTUM__VALUE_MASK >>
- SDMA0_PHASE0_QUANTUM__VALUE__SHIFT)) {
- value = (value + 1) >> 1;
- unit++;
- }
- if (unit > (SDMA0_PHASE0_QUANTUM__UNIT_MASK >>
- SDMA0_PHASE0_QUANTUM__UNIT__SHIFT)) {
- value = (SDMA0_PHASE0_QUANTUM__VALUE_MASK >>
- SDMA0_PHASE0_QUANTUM__VALUE__SHIFT);
- unit = (SDMA0_PHASE0_QUANTUM__UNIT_MASK >>
- SDMA0_PHASE0_QUANTUM__UNIT__SHIFT);
- WARN_ONCE(1,
- "clamping sdma_phase_quantum to %uK clock cycles\n",
- value << unit);
- }
- phase_quantum =
- value << SDMA0_PHASE0_QUANTUM__VALUE__SHIFT |
- unit << SDMA0_PHASE0_QUANTUM__UNIT__SHIFT;
- }
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- if (enable && amdgpu_sdma_phase_quantum) {
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_PHASE0_QUANTUM),
- phase_quantum);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_PHASE1_QUANTUM),
- phase_quantum);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_PHASE2_QUANTUM),
- phase_quantum);
- }
-
- if (!amdgpu_sriov_vf(adev)) {
- f32_cntl = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CNTL));
- f32_cntl = REG_SET_FIELD(f32_cntl, SDMA0_CNTL,
- AUTO_CTXSW_ENABLE, enable ? 1 : 0);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CNTL), f32_cntl);
- }
- }
-
-}
-
-/**
- * sdma_v5_2_enable - stop the async dma engines
- *
- * @adev: amdgpu_device pointer
- * @enable: enable/disable the DMA MEs.
- *
- * Halt or unhalt the async dma engines.
- */
-static void sdma_v5_2_enable(struct amdgpu_device *adev, bool enable)
-{
- u32 f32_cntl;
- int i;
-
- if (!enable) {
- sdma_v5_2_gfx_stop(adev);
- sdma_v5_2_rlc_stop(adev);
- }
-
- if (!amdgpu_sriov_vf(adev)) {
- for (i = 0; i < adev->sdma.num_instances; i++) {
- f32_cntl = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_F32_CNTL));
- f32_cntl = REG_SET_FIELD(f32_cntl, SDMA0_F32_CNTL, HALT, enable ? 0 : 1);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_F32_CNTL), f32_cntl);
- }
- }
-}
-
-/**
- * sdma_v5_2_gfx_resume - setup and start the async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Set up the gfx DMA ring buffers and enable them.
- * Returns 0 for success, error for failure.
- */
-static int sdma_v5_2_gfx_resume(struct amdgpu_device *adev)
-{
- struct amdgpu_ring *ring;
- u32 rb_cntl, ib_cntl;
- u32 rb_bufsz;
- u32 doorbell;
- u32 doorbell_offset;
- u32 temp;
- u32 wptr_poll_cntl;
- u64 wptr_gpu_addr;
- int i, r;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- ring = &adev->sdma.instance[i].ring;
-
- if (!amdgpu_sriov_vf(adev))
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_SEM_WAIT_FAIL_TIMER_CNTL), 0);
-
- /* Set ring buffer size in dwords */
- rb_bufsz = order_base_2(ring->ring_size / 4);
- rb_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL));
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_SIZE, rb_bufsz);
-#ifdef __BIG_ENDIAN
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_SWAP_ENABLE, 1);
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL,
- RPTR_WRITEBACK_SWAP_ENABLE, 1);
-#endif
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL), rb_cntl);
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_RPTR), 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_RPTR_HI), 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR), 0);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_HI), 0);
-
- /* setup the wptr shadow polling */
- wptr_gpu_addr = ring->wptr_gpu_addr;
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_POLL_ADDR_LO),
- lower_32_bits(wptr_gpu_addr));
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_POLL_ADDR_HI),
- upper_32_bits(wptr_gpu_addr));
- wptr_poll_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i,
- mmSDMA0_GFX_RB_WPTR_POLL_CNTL));
- wptr_poll_cntl = REG_SET_FIELD(wptr_poll_cntl,
- SDMA0_GFX_RB_WPTR_POLL_CNTL,
- F32_POLL_ENABLE, 1);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_POLL_CNTL),
- wptr_poll_cntl);
-
- /* set the wb address whether it's enabled or not */
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_RPTR_ADDR_HI),
- upper_32_bits(ring->rptr_gpu_addr) & 0xFFFFFFFF);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_RPTR_ADDR_LO),
- lower_32_bits(ring->rptr_gpu_addr) & 0xFFFFFFFC);
-
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RPTR_WRITEBACK_ENABLE, 1);
-
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_BASE), ring->gpu_addr >> 8);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_BASE_HI), ring->gpu_addr >> 40);
-
- ring->wptr = 0;
-
- /* before programing wptr to a less value, need set minor_ptr_update first */
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_MINOR_PTR_UPDATE), 1);
-
- if (!amdgpu_sriov_vf(adev)) { /* only bare-metal use register write for wptr */
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR), lower_32_bits(ring->wptr << 2));
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_WPTR_HI), upper_32_bits(ring->wptr << 2));
- }
-
- doorbell = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_DOORBELL));
- doorbell_offset = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_DOORBELL_OFFSET));
-
- if (ring->use_doorbell) {
- doorbell = REG_SET_FIELD(doorbell, SDMA0_GFX_DOORBELL, ENABLE, 1);
- doorbell_offset = REG_SET_FIELD(doorbell_offset, SDMA0_GFX_DOORBELL_OFFSET,
- OFFSET, ring->doorbell_index);
- } else {
- doorbell = REG_SET_FIELD(doorbell, SDMA0_GFX_DOORBELL, ENABLE, 0);
- }
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_DOORBELL), doorbell);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_DOORBELL_OFFSET), doorbell_offset);
-
- adev->nbio.funcs->sdma_doorbell_range(adev, i, ring->use_doorbell,
- ring->doorbell_index,
- adev->doorbell_index.sdma_doorbell_range);
-
- if (amdgpu_sriov_vf(adev))
- sdma_v5_2_ring_set_wptr(ring);
-
- /* set minor_ptr_update to 0 after wptr programed */
-
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_MINOR_PTR_UPDATE), 0);
-
- /* SRIOV VF has no control of any of registers below */
- if (!amdgpu_sriov_vf(adev)) {
- /* set utc l1 enable flag always to 1 */
- temp = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CNTL));
- temp = REG_SET_FIELD(temp, SDMA0_CNTL, UTC_L1_ENABLE, 1);
-
- /* enable MCBP */
- temp = REG_SET_FIELD(temp, SDMA0_CNTL, MIDCMD_PREEMPT_ENABLE, 1);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CNTL), temp);
-
- /* Set up RESP_MODE to non-copy addresses */
- temp = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UTCL1_CNTL));
- temp = REG_SET_FIELD(temp, SDMA0_UTCL1_CNTL, RESP_MODE, 3);
- temp = REG_SET_FIELD(temp, SDMA0_UTCL1_CNTL, REDO_DELAY, 9);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UTCL1_CNTL), temp);
-
- /* program default cache read and write policy */
- temp = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UTCL1_PAGE));
- /* clean read policy and write policy bits */
- temp &= 0xFF0FFF;
- temp |= ((CACHE_READ_POLICY_L2__DEFAULT << 12) |
- (CACHE_WRITE_POLICY_L2__DEFAULT << 14) |
- SDMA0_UTCL1_PAGE__LLC_NOALLOC_MASK);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UTCL1_PAGE), temp);
-
- /* unhalt engine */
- temp = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_F32_CNTL));
- temp = REG_SET_FIELD(temp, SDMA0_F32_CNTL, HALT, 0);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_F32_CNTL), temp);
- }
-
- /* enable DMA RB */
- rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_ENABLE, 1);
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_RB_CNTL), rb_cntl);
-
- ib_cntl = RREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_IB_CNTL));
- ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_ENABLE, 1);
-#ifdef __BIG_ENDIAN
- ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_SWAP_ENABLE, 1);
-#endif
- /* enable DMA IBs */
- WREG32_SOC15_IP(GC, sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_GFX_IB_CNTL), ib_cntl);
-
- if (amdgpu_sriov_vf(adev)) { /* bare-metal sequence doesn't need below to lines */
- sdma_v5_2_ctx_switch_enable(adev, true);
- sdma_v5_2_enable(adev, true);
- }
-
- r = amdgpu_ring_test_helper(ring);
- if (r)
- return r;
- }
-
- return 0;
-}
-
-/**
- * sdma_v5_2_rlc_resume - setup and start the async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Set up the compute DMA queues and enable them.
- * Returns 0 for success, error for failure.
- */
-static int sdma_v5_2_rlc_resume(struct amdgpu_device *adev)
-{
- return 0;
-}
-
-/**
- * sdma_v5_2_load_microcode - load the sDMA ME ucode
- *
- * @adev: amdgpu_device pointer
- *
- * Loads the sDMA0/1/2/3 ucode.
- * Returns 0 for success, -EINVAL if the ucode is not available.
- */
-static int sdma_v5_2_load_microcode(struct amdgpu_device *adev)
-{
- const struct sdma_firmware_header_v1_0 *hdr;
- const __le32 *fw_data;
- u32 fw_size;
- int i, j;
-
- /* halt the MEs */
- sdma_v5_2_enable(adev, false);
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- if (!adev->sdma.instance[i].fw)
- return -EINVAL;
-
- hdr = (const struct sdma_firmware_header_v1_0 *)adev->sdma.instance[i].fw->data;
- amdgpu_ucode_print_sdma_hdr(&hdr->header);
- fw_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
-
- fw_data = (const __le32 *)
- (adev->sdma.instance[i].fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes));
-
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UCODE_ADDR), 0);
-
- for (j = 0; j < fw_size; j++) {
- if (amdgpu_emu_mode == 1 && j % 500 == 0)
- msleep(1);
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UCODE_DATA), le32_to_cpup(fw_data++));
- }
-
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_UCODE_ADDR), adev->sdma.instance[i].fw_version);
- }
-
- return 0;
-}
-
-static int sdma_v5_2_soft_reset(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- u32 grbm_soft_reset;
- u32 tmp;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- grbm_soft_reset = REG_SET_FIELD(0,
- GRBM_SOFT_RESET, SOFT_RESET_SDMA0,
- 1);
- grbm_soft_reset <<= i;
-
- tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET);
- tmp |= grbm_soft_reset;
- DRM_DEBUG("GRBM_SOFT_RESET=0x%08X\n", tmp);
- WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp);
- tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET);
-
- udelay(50);
-
- tmp &= ~grbm_soft_reset;
- WREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET, tmp);
- tmp = RREG32_SOC15(GC, 0, mmGRBM_SOFT_RESET);
-
- udelay(50);
- }
-
- return 0;
-}
-
-/**
- * sdma_v5_2_start - setup and start the async dma engines
- *
- * @adev: amdgpu_device pointer
- *
- * Set up the DMA engines and enable them.
- * Returns 0 for success, error for failure.
- */
-static int sdma_v5_2_start(struct amdgpu_device *adev)
-{
- int r = 0;
-
- if (amdgpu_sriov_vf(adev)) {
- sdma_v5_2_ctx_switch_enable(adev, false);
- sdma_v5_2_enable(adev, false);
-
- /* set RB registers */
- r = sdma_v5_2_gfx_resume(adev);
- return r;
- }
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT) {
- r = sdma_v5_2_load_microcode(adev);
- if (r)
- return r;
-
- /* The value of mmSDMA_F32_CNTL is invalid the moment after loading fw */
- if (amdgpu_emu_mode == 1)
- msleep(1000);
- }
-
- sdma_v5_2_soft_reset(adev);
- /* unhalt the MEs */
- sdma_v5_2_enable(adev, true);
- /* enable sdma ring preemption */
- sdma_v5_2_ctx_switch_enable(adev, true);
-
- /* start the gfx rings and rlc compute queues */
- r = sdma_v5_2_gfx_resume(adev);
- if (r)
- return r;
- r = sdma_v5_2_rlc_resume(adev);
-
- return r;
-}
-
-static int sdma_v5_2_mqd_init(struct amdgpu_device *adev, void *mqd,
- struct amdgpu_mqd_prop *prop)
-{
- struct v10_sdma_mqd *m = mqd;
- uint64_t wb_gpu_addr;
-
- m->sdmax_rlcx_rb_cntl =
- order_base_2(prop->queue_size / 4) << SDMA0_RLC0_RB_CNTL__RB_SIZE__SHIFT |
- 1 << SDMA0_RLC0_RB_CNTL__RPTR_WRITEBACK_ENABLE__SHIFT |
- 6 << SDMA0_RLC0_RB_CNTL__RPTR_WRITEBACK_TIMER__SHIFT |
- 1 << SDMA0_RLC0_RB_CNTL__RB_PRIV__SHIFT;
-
- m->sdmax_rlcx_rb_base = lower_32_bits(prop->hqd_base_gpu_addr >> 8);
- m->sdmax_rlcx_rb_base_hi = upper_32_bits(prop->hqd_base_gpu_addr >> 8);
-
- m->sdmax_rlcx_rb_wptr_poll_cntl = RREG32(sdma_v5_2_get_reg_offset(adev, 0,
- mmSDMA0_GFX_RB_WPTR_POLL_CNTL));
-
- wb_gpu_addr = prop->wptr_gpu_addr;
- m->sdmax_rlcx_rb_wptr_poll_addr_lo = lower_32_bits(wb_gpu_addr);
- m->sdmax_rlcx_rb_wptr_poll_addr_hi = upper_32_bits(wb_gpu_addr);
-
- wb_gpu_addr = prop->rptr_gpu_addr;
- m->sdmax_rlcx_rb_rptr_addr_lo = lower_32_bits(wb_gpu_addr);
- m->sdmax_rlcx_rb_rptr_addr_hi = upper_32_bits(wb_gpu_addr);
-
- m->sdmax_rlcx_ib_cntl = RREG32(sdma_v5_2_get_reg_offset(adev, 0,
- mmSDMA0_GFX_IB_CNTL));
-
- m->sdmax_rlcx_doorbell_offset =
- prop->doorbell_index << SDMA0_RLC0_DOORBELL_OFFSET__OFFSET__SHIFT;
-
- m->sdmax_rlcx_doorbell = REG_SET_FIELD(0, SDMA0_RLC0_DOORBELL, ENABLE, 1);
-
- return 0;
-}
-
-static void sdma_v5_2_set_mqd_funcs(struct amdgpu_device *adev)
-{
- adev->mqds[AMDGPU_HW_IP_DMA].mqd_size = sizeof(struct v10_sdma_mqd);
- adev->mqds[AMDGPU_HW_IP_DMA].init_mqd = sdma_v5_2_mqd_init;
-}
-
-/**
- * sdma_v5_2_ring_test_ring - simple async dma engine test
- *
- * @ring: amdgpu_ring structure holding ring information
- *
- * Test the DMA engine by writing using it to write an
- * value to memory.
- * Returns 0 for success, error for failure.
- */
-static int sdma_v5_2_ring_test_ring(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- unsigned i;
- unsigned index;
- int r;
- u32 tmp;
- u64 gpu_addr;
- volatile uint32_t *cpu_ptr = NULL;
-
- tmp = 0xCAFEDEAD;
-
- if (ring->is_mes_queue) {
- uint32_t offset = 0;
- offset = amdgpu_mes_ctx_get_offs(ring,
- AMDGPU_MES_CTX_PADDING_OFFS);
- gpu_addr = amdgpu_mes_ctx_get_offs_gpu_addr(ring, offset);
- cpu_ptr = amdgpu_mes_ctx_get_offs_cpu_addr(ring, offset);
- *cpu_ptr = tmp;
- } else {
- r = amdgpu_device_wb_get(adev, &index);
- if (r) {
- dev_err(adev->dev, "(%d) failed to allocate wb slot\n", r);
- return r;
- }
-
- gpu_addr = adev->wb.gpu_addr + (index * 4);
- adev->wb.wb[index] = cpu_to_le32(tmp);
- }
-
- r = amdgpu_ring_alloc(ring, 20);
- if (r) {
- DRM_ERROR("amdgpu: dma failed to lock ring %d (%d).\n", ring->idx, r);
- if (!ring->is_mes_queue)
- amdgpu_device_wb_free(adev, index);
- return r;
- }
-
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR));
- amdgpu_ring_write(ring, lower_32_bits(gpu_addr));
- amdgpu_ring_write(ring, upper_32_bits(gpu_addr));
- amdgpu_ring_write(ring, SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(0));
- amdgpu_ring_write(ring, 0xDEADBEEF);
- amdgpu_ring_commit(ring);
-
- for (i = 0; i < adev->usec_timeout; i++) {
- if (ring->is_mes_queue)
- tmp = le32_to_cpu(*cpu_ptr);
- else
- tmp = le32_to_cpu(adev->wb.wb[index]);
- if (tmp == 0xDEADBEEF)
- break;
- if (amdgpu_emu_mode == 1)
- msleep(1);
- else
- udelay(1);
- }
-
- if (i >= adev->usec_timeout)
- r = -ETIMEDOUT;
-
- if (!ring->is_mes_queue)
- amdgpu_device_wb_free(adev, index);
-
- return r;
-}
-
-/**
- * sdma_v5_2_ring_test_ib - test an IB on the DMA engine
- *
- * @ring: amdgpu_ring structure holding ring information
- * @timeout: timeout value in jiffies, or MAX_SCHEDULE_TIMEOUT
- *
- * Test a simple IB in the DMA ring.
- * Returns 0 on success, error on failure.
- */
-static int sdma_v5_2_ring_test_ib(struct amdgpu_ring *ring, long timeout)
-{
- struct amdgpu_device *adev = ring->adev;
- struct amdgpu_ib ib;
- struct dma_fence *f = NULL;
- unsigned index;
- long r;
- u32 tmp = 0;
- u64 gpu_addr;
- volatile uint32_t *cpu_ptr = NULL;
-
- tmp = 0xCAFEDEAD;
- memset(&ib, 0, sizeof(ib));
-
- if (ring->is_mes_queue) {
- uint32_t offset = 0;
- offset = amdgpu_mes_ctx_get_offs(ring, AMDGPU_MES_CTX_IB_OFFS);
- ib.gpu_addr = amdgpu_mes_ctx_get_offs_gpu_addr(ring, offset);
- ib.ptr = (void *)amdgpu_mes_ctx_get_offs_cpu_addr(ring, offset);
-
- offset = amdgpu_mes_ctx_get_offs(ring,
- AMDGPU_MES_CTX_PADDING_OFFS);
- gpu_addr = amdgpu_mes_ctx_get_offs_gpu_addr(ring, offset);
- cpu_ptr = amdgpu_mes_ctx_get_offs_cpu_addr(ring, offset);
- *cpu_ptr = tmp;
- } else {
- r = amdgpu_device_wb_get(adev, &index);
- if (r) {
- dev_err(adev->dev, "(%ld) failed to allocate wb slot\n", r);
- return r;
- }
-
- gpu_addr = adev->wb.gpu_addr + (index * 4);
- adev->wb.wb[index] = cpu_to_le32(tmp);
-
- r = amdgpu_ib_get(adev, NULL, 256, AMDGPU_IB_POOL_DIRECT, &ib);
- if (r) {
- DRM_ERROR("amdgpu: failed to get ib (%ld).\n", r);
- goto err0;
- }
- }
-
- ib.ptr[0] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR);
- ib.ptr[1] = lower_32_bits(gpu_addr);
- ib.ptr[2] = upper_32_bits(gpu_addr);
- ib.ptr[3] = SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(0);
- ib.ptr[4] = 0xDEADBEEF;
- ib.ptr[5] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
- ib.ptr[6] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
- ib.ptr[7] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
- ib.length_dw = 8;
-
- r = amdgpu_ib_schedule(ring, 1, &ib, NULL, &f);
- if (r)
- goto err1;
-
- r = dma_fence_wait_timeout(f, false, timeout);
- if (r == 0) {
- DRM_ERROR("amdgpu: IB test timed out\n");
- r = -ETIMEDOUT;
- goto err1;
- } else if (r < 0) {
- DRM_ERROR("amdgpu: fence wait failed (%ld).\n", r);
- goto err1;
- }
-
- if (ring->is_mes_queue)
- tmp = le32_to_cpu(*cpu_ptr);
- else
- tmp = le32_to_cpu(adev->wb.wb[index]);
-
- if (tmp == 0xDEADBEEF)
- r = 0;
- else
- r = -EINVAL;
-
-err1:
- amdgpu_ib_free(adev, &ib, NULL);
- dma_fence_put(f);
-err0:
- if (!ring->is_mes_queue)
- amdgpu_device_wb_free(adev, index);
- return r;
-}
-
-
-/**
- * sdma_v5_2_vm_copy_pte - update PTEs by copying them from the GART
- *
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @src: src addr to copy from
- * @count: number of page entries to update
- *
- * Update PTEs by copying them from the GART using sDMA.
- */
-static void sdma_v5_2_vm_copy_pte(struct amdgpu_ib *ib,
- uint64_t pe, uint64_t src,
- unsigned count)
-{
- unsigned bytes = count * 8;
-
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
- ib->ptr[ib->length_dw++] = bytes - 1;
- ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
- ib->ptr[ib->length_dw++] = lower_32_bits(src);
- ib->ptr[ib->length_dw++] = upper_32_bits(src);
- ib->ptr[ib->length_dw++] = lower_32_bits(pe);
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-
-}
-
-/**
- * sdma_v5_2_vm_write_pte - update PTEs by writing them manually
- *
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @value: dst addr to write into pe
- * @count: number of page entries to update
- * @incr: increase next addr by incr bytes
- *
- * Update PTEs by writing them manually using sDMA.
- */
-static void sdma_v5_2_vm_write_pte(struct amdgpu_ib *ib, uint64_t pe,
- uint64_t value, unsigned count,
- uint32_t incr)
-{
- unsigned ndw = count * 2;
-
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR);
- ib->ptr[ib->length_dw++] = lower_32_bits(pe);
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
- ib->ptr[ib->length_dw++] = ndw - 1;
- for (; ndw > 0; ndw -= 2) {
- ib->ptr[ib->length_dw++] = lower_32_bits(value);
- ib->ptr[ib->length_dw++] = upper_32_bits(value);
- value += incr;
- }
-}
-
-/**
- * sdma_v5_2_vm_set_pte_pde - update the page tables using sDMA
- *
- * @ib: indirect buffer to fill with commands
- * @pe: addr of the page entry
- * @addr: dst addr to write into pe
- * @count: number of page entries to update
- * @incr: increase next addr by incr bytes
- * @flags: access flags
- *
- * Update the page tables using sDMA.
- */
-static void sdma_v5_2_vm_set_pte_pde(struct amdgpu_ib *ib,
- uint64_t pe,
- uint64_t addr, unsigned count,
- uint32_t incr, uint64_t flags)
-{
- /* for physically contiguous pages (vram) */
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_PTEPDE);
- ib->ptr[ib->length_dw++] = lower_32_bits(pe); /* dst addr */
- ib->ptr[ib->length_dw++] = upper_32_bits(pe);
- ib->ptr[ib->length_dw++] = lower_32_bits(flags); /* mask */
- ib->ptr[ib->length_dw++] = upper_32_bits(flags);
- ib->ptr[ib->length_dw++] = lower_32_bits(addr); /* value */
- ib->ptr[ib->length_dw++] = upper_32_bits(addr);
- ib->ptr[ib->length_dw++] = incr; /* increment size */
- ib->ptr[ib->length_dw++] = 0;
- ib->ptr[ib->length_dw++] = count - 1; /* number of entries */
-}
-
-/**
- * sdma_v5_2_ring_pad_ib - pad the IB
- *
- * @ib: indirect buffer to fill with padding
- * @ring: amdgpu_ring structure holding ring information
- *
- * Pad the IB with NOPs to a boundary multiple of 8.
- */
-static void sdma_v5_2_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib)
-{
- struct amdgpu_sdma_instance *sdma = amdgpu_sdma_get_instance_from_ring(ring);
- u32 pad_count;
- int i;
-
- pad_count = (-ib->length_dw) & 0x7;
- for (i = 0; i < pad_count; i++)
- if (sdma && sdma->burst_nop && (i == 0))
- ib->ptr[ib->length_dw++] =
- SDMA_PKT_HEADER_OP(SDMA_OP_NOP) |
- SDMA_PKT_NOP_HEADER_COUNT(pad_count - 1);
- else
- ib->ptr[ib->length_dw++] =
- SDMA_PKT_HEADER_OP(SDMA_OP_NOP);
-}
-
-
-/**
- * sdma_v5_2_ring_emit_pipeline_sync - sync the pipeline
- *
- * @ring: amdgpu_ring pointer
- *
- * Make sure all previous operations are completed (CIK).
- */
-static void sdma_v5_2_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
-{
- uint32_t seq = ring->fence_drv.sync_seq;
- uint64_t addr = ring->fence_drv.gpu_addr;
-
- /* wait for idle */
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
- SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) |
- SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3) | /* equal */
- SDMA_PKT_POLL_REGMEM_HEADER_MEM_POLL(1));
- amdgpu_ring_write(ring, addr & 0xfffffffc);
- amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
- amdgpu_ring_write(ring, seq); /* reference */
- amdgpu_ring_write(ring, 0xffffffff); /* mask */
- amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
- SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(4)); /* retry count, poll interval */
-}
-
-
-/**
- * sdma_v5_2_ring_emit_vm_flush - vm flush using sDMA
- *
- * @ring: amdgpu_ring pointer
- * @vmid: vmid number to use
- * @pd_addr: address
- *
- * Update the page table base and flush the VM TLB
- * using sDMA.
- */
-static void sdma_v5_2_ring_emit_vm_flush(struct amdgpu_ring *ring,
- unsigned vmid, uint64_t pd_addr)
-{
- amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
-}
-
-static void sdma_v5_2_ring_emit_wreg(struct amdgpu_ring *ring,
- uint32_t reg, uint32_t val)
-{
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
- SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
- amdgpu_ring_write(ring, reg);
- amdgpu_ring_write(ring, val);
-}
-
-static void sdma_v5_2_ring_emit_reg_wait(struct amdgpu_ring *ring, uint32_t reg,
- uint32_t val, uint32_t mask)
-{
- amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
- SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) |
- SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3)); /* equal */
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, 0);
- amdgpu_ring_write(ring, val); /* reference */
- amdgpu_ring_write(ring, mask); /* mask */
- amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
- SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10));
-}
-
-static void sdma_v5_2_ring_emit_reg_write_reg_wait(struct amdgpu_ring *ring,
- uint32_t reg0, uint32_t reg1,
- uint32_t ref, uint32_t mask)
-{
- amdgpu_ring_emit_wreg(ring, reg0, ref);
- /* wait for a cycle to reset vm_inv_eng*_ack */
- amdgpu_ring_emit_reg_wait(ring, reg0, 0, 0);
- amdgpu_ring_emit_reg_wait(ring, reg1, mask, mask);
-}
-
-static int sdma_v5_2_early_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = amdgpu_sdma_init_microcode(adev, 0, true);
- if (r)
- return r;
-
- sdma_v5_2_set_ring_funcs(adev);
- sdma_v5_2_set_buffer_funcs(adev);
- sdma_v5_2_set_vm_pte_funcs(adev);
- sdma_v5_2_set_irq_funcs(adev);
- sdma_v5_2_set_mqd_funcs(adev);
-
- return 0;
-}
-
-static unsigned sdma_v5_2_seq_to_irq_id(int seq_num)
-{
- switch (seq_num) {
- case 0:
- return SOC15_IH_CLIENTID_SDMA0;
- case 1:
- return SOC15_IH_CLIENTID_SDMA1;
- case 2:
- return SOC15_IH_CLIENTID_SDMA2;
- case 3:
- return SOC15_IH_CLIENTID_SDMA3_Sienna_Cichlid;
- default:
- break;
- }
- return -EINVAL;
-}
-
-static unsigned sdma_v5_2_seq_to_trap_id(int seq_num)
-{
- switch (seq_num) {
- case 0:
- return SDMA0_5_0__SRCID__SDMA_TRAP;
- case 1:
- return SDMA1_5_0__SRCID__SDMA_TRAP;
- case 2:
- return SDMA2_5_0__SRCID__SDMA_TRAP;
- case 3:
- return SDMA3_5_0__SRCID__SDMA_TRAP;
- default:
- break;
- }
- return -EINVAL;
-}
-
-static int sdma_v5_2_sw_init(void *handle)
-{
- struct amdgpu_ring *ring;
- int r, i;
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- uint32_t reg_count = ARRAY_SIZE(sdma_reg_list_5_2);
- uint32_t *ptr;
-
- /* SDMA trap event */
- for (i = 0; i < adev->sdma.num_instances; i++) {
- r = amdgpu_irq_add_id(adev, sdma_v5_2_seq_to_irq_id(i),
- sdma_v5_2_seq_to_trap_id(i),
- &adev->sdma.trap_irq);
- if (r)
- return r;
- }
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- ring = &adev->sdma.instance[i].ring;
- ring->ring_obj = NULL;
- ring->use_doorbell = true;
- ring->me = i;
-
- DRM_INFO("use_doorbell being set to: [%s]\n",
- ring->use_doorbell?"true":"false");
-
- ring->doorbell_index =
- (adev->doorbell_index.sdma_engine[i] << 1); //get DWORD offset
-
- ring->vm_hub = AMDGPU_GFXHUB(0);
- sprintf(ring->name, "sdma%d", i);
- r = amdgpu_ring_init(adev, ring, 1024, &adev->sdma.trap_irq,
- AMDGPU_SDMA_IRQ_INSTANCE0 + i,
- AMDGPU_RING_PRIO_DEFAULT, NULL);
- if (r)
- return r;
- }
-
- /* Allocate memory for SDMA IP Dump buffer */
- ptr = kcalloc(adev->sdma.num_instances * reg_count, sizeof(uint32_t), GFP_KERNEL);
- if (ptr)
- adev->sdma.ip_dump = ptr;
- else
- DRM_ERROR("Failed to allocated memory for SDMA IP Dump\n");
-
- return r;
-}
-
-static int sdma_v5_2_sw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++)
- amdgpu_ring_fini(&adev->sdma.instance[i].ring);
-
- amdgpu_sdma_destroy_inst_ctx(adev, true);
-
- kfree(adev->sdma.ip_dump);
-
- return 0;
-}
-
-static int sdma_v5_2_hw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- return sdma_v5_2_start(adev);
-}
-
-static int sdma_v5_2_hw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- if (amdgpu_sriov_vf(adev))
- return 0;
-
- sdma_v5_2_ctx_switch_enable(adev, false);
- sdma_v5_2_enable(adev, false);
-
- return 0;
-}
-
-static int sdma_v5_2_suspend(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- return sdma_v5_2_hw_fini(adev);
-}
-
-static int sdma_v5_2_resume(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- return sdma_v5_2_hw_init(adev);
-}
-
-static bool sdma_v5_2_is_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- u32 i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- u32 tmp = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_STATUS_REG));
-
- if (!(tmp & SDMA0_STATUS_REG__IDLE_MASK))
- return false;
- }
-
- return true;
-}
-
-static int sdma_v5_2_wait_for_idle(void *handle)
-{
- unsigned i;
- u32 sdma0, sdma1, sdma2, sdma3;
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- for (i = 0; i < adev->usec_timeout; i++) {
- sdma0 = RREG32(sdma_v5_2_get_reg_offset(adev, 0, mmSDMA0_STATUS_REG));
- sdma1 = RREG32(sdma_v5_2_get_reg_offset(adev, 1, mmSDMA0_STATUS_REG));
- sdma2 = RREG32(sdma_v5_2_get_reg_offset(adev, 2, mmSDMA0_STATUS_REG));
- sdma3 = RREG32(sdma_v5_2_get_reg_offset(adev, 3, mmSDMA0_STATUS_REG));
-
- if (sdma0 & sdma1 & sdma2 & sdma3 & SDMA0_STATUS_REG__IDLE_MASK)
- return 0;
- udelay(1);
- }
- return -ETIMEDOUT;
-}
-
-static int sdma_v5_2_ring_preempt_ib(struct amdgpu_ring *ring)
-{
- int i, r = 0;
- struct amdgpu_device *adev = ring->adev;
- u32 index = 0;
- u64 sdma_gfx_preempt;
-
- amdgpu_sdma_get_index_from_ring(ring, &index);
- sdma_gfx_preempt =
- sdma_v5_2_get_reg_offset(adev, index, mmSDMA0_GFX_PREEMPT);
-
- /* assert preemption condition */
- amdgpu_ring_set_preempt_cond_exec(ring, false);
-
- /* emit the trailing fence */
- ring->trail_seq += 1;
- amdgpu_ring_alloc(ring, 10);
- sdma_v5_2_ring_emit_fence(ring, ring->trail_fence_gpu_addr,
- ring->trail_seq, 0);
- amdgpu_ring_commit(ring);
-
- /* assert IB preemption */
- WREG32(sdma_gfx_preempt, 1);
-
- /* poll the trailing fence */
- for (i = 0; i < adev->usec_timeout; i++) {
- if (ring->trail_seq ==
- le32_to_cpu(*(ring->trail_fence_cpu_addr)))
- break;
- udelay(1);
- }
-
- if (i >= adev->usec_timeout) {
- r = -EINVAL;
- DRM_ERROR("ring %d failed to be preempted\n", ring->idx);
- }
-
- /* deassert IB preemption */
- WREG32(sdma_gfx_preempt, 0);
-
- /* deassert the preemption condition */
- amdgpu_ring_set_preempt_cond_exec(ring, true);
- return r;
-}
-
-static int sdma_v5_2_set_trap_irq_state(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- unsigned type,
- enum amdgpu_interrupt_state state)
-{
- u32 sdma_cntl;
- u32 reg_offset = sdma_v5_2_get_reg_offset(adev, type, mmSDMA0_CNTL);
-
- if (!amdgpu_sriov_vf(adev)) {
- sdma_cntl = RREG32(reg_offset);
- sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE,
- state == AMDGPU_IRQ_STATE_ENABLE ? 1 : 0);
- WREG32(reg_offset, sdma_cntl);
- }
-
- return 0;
-}
-
-static int sdma_v5_2_process_trap_irq(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- struct amdgpu_iv_entry *entry)
-{
- uint32_t mes_queue_id = entry->src_data[0];
-
- DRM_DEBUG("IH: SDMA trap\n");
-
- if (adev->enable_mes && (mes_queue_id & AMDGPU_FENCE_MES_QUEUE_FLAG)) {
- struct amdgpu_mes_queue *queue;
-
- mes_queue_id &= AMDGPU_FENCE_MES_QUEUE_ID_MASK;
-
- spin_lock(&adev->mes.queue_id_lock);
- queue = idr_find(&adev->mes.queue_id_idr, mes_queue_id);
- if (queue) {
- DRM_DEBUG("process smda queue id = %d\n", mes_queue_id);
- amdgpu_fence_process(queue->ring);
- }
- spin_unlock(&adev->mes.queue_id_lock);
- return 0;
- }
-
- switch (entry->client_id) {
- case SOC15_IH_CLIENTID_SDMA0:
- switch (entry->ring_id) {
- case 0:
- amdgpu_fence_process(&adev->sdma.instance[0].ring);
- break;
- case 1:
- /* XXX compute */
- break;
- case 2:
- /* XXX compute */
- break;
- case 3:
- /* XXX page queue*/
- break;
- }
- break;
- case SOC15_IH_CLIENTID_SDMA1:
- switch (entry->ring_id) {
- case 0:
- amdgpu_fence_process(&adev->sdma.instance[1].ring);
- break;
- case 1:
- /* XXX compute */
- break;
- case 2:
- /* XXX compute */
- break;
- case 3:
- /* XXX page queue*/
- break;
- }
- break;
- case SOC15_IH_CLIENTID_SDMA2:
- switch (entry->ring_id) {
- case 0:
- amdgpu_fence_process(&adev->sdma.instance[2].ring);
- break;
- case 1:
- /* XXX compute */
- break;
- case 2:
- /* XXX compute */
- break;
- case 3:
- /* XXX page queue*/
- break;
- }
- break;
- case SOC15_IH_CLIENTID_SDMA3_Sienna_Cichlid:
- switch (entry->ring_id) {
- case 0:
- amdgpu_fence_process(&adev->sdma.instance[3].ring);
- break;
- case 1:
- /* XXX compute */
- break;
- case 2:
- /* XXX compute */
- break;
- case 3:
- /* XXX page queue*/
- break;
- }
- break;
- }
- return 0;
-}
-
-static int sdma_v5_2_process_illegal_inst_irq(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- struct amdgpu_iv_entry *entry)
-{
- return 0;
-}
-
-static bool sdma_v5_2_firmware_mgcg_support(struct amdgpu_device *adev,
- int i)
-{
- switch (amdgpu_ip_version(adev, SDMA0_HWIP, 0)) {
- case IP_VERSION(5, 2, 1):
- if (adev->sdma.instance[i].fw_version < 70)
- return false;
- break;
- case IP_VERSION(5, 2, 3):
- if (adev->sdma.instance[i].fw_version < 47)
- return false;
- break;
- case IP_VERSION(5, 2, 7):
- if (adev->sdma.instance[i].fw_version < 9)
- return false;
- break;
- default:
- return true;
- }
-
- return true;
-
-}
-
-static void sdma_v5_2_update_medium_grain_clock_gating(struct amdgpu_device *adev,
- bool enable)
-{
- uint32_t data, def;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
-
- if (!sdma_v5_2_firmware_mgcg_support(adev, i))
- adev->cg_flags &= ~AMD_CG_SUPPORT_SDMA_MGCG;
-
- if (enable && (adev->cg_flags & AMD_CG_SUPPORT_SDMA_MGCG)) {
- /* Enable sdma clock gating */
- def = data = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CLK_CTRL));
- data &= ~(SDMA0_CLK_CTRL__SOFT_OVERRIDE4_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE3_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE2_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE1_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE0_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDER_REG_MASK);
- if (def != data)
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CLK_CTRL), data);
- } else {
- /* Disable sdma clock gating */
- def = data = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CLK_CTRL));
- data |= (SDMA0_CLK_CTRL__SOFT_OVERRIDE4_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE3_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE2_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE1_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDE0_MASK |
- SDMA0_CLK_CTRL__SOFT_OVERRIDER_REG_MASK);
- if (def != data)
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_CLK_CTRL), data);
- }
- }
-}
-
-static void sdma_v5_2_update_medium_grain_light_sleep(struct amdgpu_device *adev,
- bool enable)
-{
- uint32_t data, def;
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- if (adev->sdma.instance[i].fw_version < 70 &&
- amdgpu_ip_version(adev, SDMA0_HWIP, 0) ==
- IP_VERSION(5, 2, 1))
- adev->cg_flags &= ~AMD_CG_SUPPORT_SDMA_LS;
-
- if (enable && (adev->cg_flags & AMD_CG_SUPPORT_SDMA_LS)) {
- /* Enable sdma mem light sleep */
- def = data = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_POWER_CNTL));
- data |= SDMA0_POWER_CNTL__MEM_POWER_OVERRIDE_MASK;
- if (def != data)
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_POWER_CNTL), data);
-
- } else {
- /* Disable sdma mem light sleep */
- def = data = RREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_POWER_CNTL));
- data &= ~SDMA0_POWER_CNTL__MEM_POWER_OVERRIDE_MASK;
- if (def != data)
- WREG32(sdma_v5_2_get_reg_offset(adev, i, mmSDMA0_POWER_CNTL), data);
-
- }
- }
-}
-
-static int sdma_v5_2_set_clockgating_state(void *handle,
- enum amd_clockgating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- if (amdgpu_sriov_vf(adev))
- return 0;
-
- switch (amdgpu_ip_version(adev, SDMA0_HWIP, 0)) {
- case IP_VERSION(5, 2, 0):
- case IP_VERSION(5, 2, 2):
- case IP_VERSION(5, 2, 1):
- case IP_VERSION(5, 2, 4):
- case IP_VERSION(5, 2, 5):
- case IP_VERSION(5, 2, 6):
- case IP_VERSION(5, 2, 3):
- case IP_VERSION(5, 2, 7):
- sdma_v5_2_update_medium_grain_clock_gating(adev,
- state == AMD_CG_STATE_GATE);
- sdma_v5_2_update_medium_grain_light_sleep(adev,
- state == AMD_CG_STATE_GATE);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int sdma_v5_2_set_powergating_state(void *handle,
- enum amd_powergating_state state)
-{
- return 0;
-}
-
-static void sdma_v5_2_get_clockgating_state(void *handle, u64 *flags)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int data;
-
- if (amdgpu_sriov_vf(adev))
- *flags = 0;
-
- /* AMD_CG_SUPPORT_SDMA_MGCG */
- data = RREG32(sdma_v5_2_get_reg_offset(adev, 0, mmSDMA0_CLK_CTRL));
- if (!(data & SDMA0_CLK_CTRL__CGCG_EN_OVERRIDE_MASK))
- *flags |= AMD_CG_SUPPORT_SDMA_MGCG;
-
- /* AMD_CG_SUPPORT_SDMA_LS */
- data = RREG32_KIQ(sdma_v5_2_get_reg_offset(adev, 0, mmSDMA0_POWER_CNTL));
- if (data & SDMA0_POWER_CNTL__MEM_POWER_OVERRIDE_MASK)
- *flags |= AMD_CG_SUPPORT_SDMA_LS;
-}
-
-static void sdma_v5_2_ring_begin_use(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- /* SDMA 5.2.3 (RMB) FW doesn't seem to properly
- * disallow GFXOFF in some cases leading to
- * hangs in SDMA. Disallow GFXOFF while SDMA is active.
- * We can probably just limit this to 5.2.3,
- * but it shouldn't hurt for other parts since
- * this GFXOFF will be disallowed anyway when SDMA is
- * active, this just makes it explicit.
- * sdma_v5_2_ring_set_wptr() takes advantage of this
- * to update the wptr because sometimes SDMA seems to miss
- * doorbells when entering PG. If you remove this, update
- * sdma_v5_2_ring_set_wptr() as well!
- */
- amdgpu_gfx_off_ctrl(adev, false);
-}
-
-static void sdma_v5_2_ring_end_use(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- /* SDMA 5.2.3 (RMB) FW doesn't seem to properly
- * disallow GFXOFF in some cases leading to
- * hangs in SDMA. Allow GFXOFF when SDMA is complete.
- */
- amdgpu_gfx_off_ctrl(adev, true);
-}
-
-static void sdma_v5_2_print_ip_state(void *handle, struct drm_printer *p)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- uint32_t reg_count = ARRAY_SIZE(sdma_reg_list_5_2);
- uint32_t instance_offset;
-
- if (!adev->sdma.ip_dump)
- return;
-
- drm_printf(p, "num_instances:%d\n", adev->sdma.num_instances);
- for (i = 0; i < adev->sdma.num_instances; i++) {
- instance_offset = i * reg_count;
- drm_printf(p, "\nInstance:%d\n", i);
-
- for (j = 0; j < reg_count; j++)
- drm_printf(p, "%-50s \t 0x%08x\n", sdma_reg_list_5_2[j].reg_name,
- adev->sdma.ip_dump[instance_offset + j]);
- }
-}
-
-static void sdma_v5_2_dump_ip_state(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- uint32_t instance_offset;
- uint32_t reg_count = ARRAY_SIZE(sdma_reg_list_5_2);
-
- if (!adev->sdma.ip_dump)
- return;
-
- amdgpu_gfx_off_ctrl(adev, false);
- for (i = 0; i < adev->sdma.num_instances; i++) {
- instance_offset = i * reg_count;
- for (j = 0; j < reg_count; j++)
- adev->sdma.ip_dump[instance_offset + j] =
- RREG32(sdma_v5_2_get_reg_offset(adev, i,
- sdma_reg_list_5_2[j].reg_offset));
- }
- amdgpu_gfx_off_ctrl(adev, true);
-}
-
-static const struct amd_ip_funcs sdma_v5_2_ip_funcs = {
- .name = "sdma_v5_2",
- .early_init = sdma_v5_2_early_init,
- .late_init = NULL,
- .sw_init = sdma_v5_2_sw_init,
- .sw_fini = sdma_v5_2_sw_fini,
- .hw_init = sdma_v5_2_hw_init,
- .hw_fini = sdma_v5_2_hw_fini,
- .suspend = sdma_v5_2_suspend,
- .resume = sdma_v5_2_resume,
- .is_idle = sdma_v5_2_is_idle,
- .wait_for_idle = sdma_v5_2_wait_for_idle,
- .soft_reset = sdma_v5_2_soft_reset,
- .set_clockgating_state = sdma_v5_2_set_clockgating_state,
- .set_powergating_state = sdma_v5_2_set_powergating_state,
- .get_clockgating_state = sdma_v5_2_get_clockgating_state,
- .dump_ip_state = sdma_v5_2_dump_ip_state,
- .print_ip_state = sdma_v5_2_print_ip_state,
-};
-
-static const struct amdgpu_ring_funcs sdma_v5_2_ring_funcs = {
- .type = AMDGPU_RING_TYPE_SDMA,
- .align_mask = 0xf,
- .nop = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP),
- .support_64bit_ptrs = true,
- .secure_submission_supported = true,
- .get_rptr = sdma_v5_2_ring_get_rptr,
- .get_wptr = sdma_v5_2_ring_get_wptr,
- .set_wptr = sdma_v5_2_ring_set_wptr,
- .emit_frame_size =
- 5 + /* sdma_v5_2_ring_init_cond_exec */
- 6 + /* sdma_v5_2_ring_emit_hdp_flush */
- 3 + /* hdp_invalidate */
- 6 + /* sdma_v5_2_ring_emit_pipeline_sync */
- /* sdma_v5_2_ring_emit_vm_flush */
- SOC15_FLUSH_GPU_TLB_NUM_WREG * 3 +
- SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 6 +
- 10 + 10 + 10, /* sdma_v5_2_ring_emit_fence x3 for user fence, vm fence */
- .emit_ib_size = 7 + 6, /* sdma_v5_2_ring_emit_ib */
- .emit_ib = sdma_v5_2_ring_emit_ib,
- .emit_mem_sync = sdma_v5_2_ring_emit_mem_sync,
- .emit_fence = sdma_v5_2_ring_emit_fence,
- .emit_pipeline_sync = sdma_v5_2_ring_emit_pipeline_sync,
- .emit_vm_flush = sdma_v5_2_ring_emit_vm_flush,
- .emit_hdp_flush = sdma_v5_2_ring_emit_hdp_flush,
- .test_ring = sdma_v5_2_ring_test_ring,
- .test_ib = sdma_v5_2_ring_test_ib,
- .insert_nop = sdma_v5_2_ring_insert_nop,
- .pad_ib = sdma_v5_2_ring_pad_ib,
- .begin_use = sdma_v5_2_ring_begin_use,
- .end_use = sdma_v5_2_ring_end_use,
- .emit_wreg = sdma_v5_2_ring_emit_wreg,
- .emit_reg_wait = sdma_v5_2_ring_emit_reg_wait,
- .emit_reg_write_reg_wait = sdma_v5_2_ring_emit_reg_write_reg_wait,
- .init_cond_exec = sdma_v5_2_ring_init_cond_exec,
- .preempt_ib = sdma_v5_2_ring_preempt_ib,
-};
-
-static void sdma_v5_2_set_ring_funcs(struct amdgpu_device *adev)
-{
- int i;
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- adev->sdma.instance[i].ring.funcs = &sdma_v5_2_ring_funcs;
- adev->sdma.instance[i].ring.me = i;
- }
-}
-
-static const struct amdgpu_irq_src_funcs sdma_v5_2_trap_irq_funcs = {
- .set = sdma_v5_2_set_trap_irq_state,
- .process = sdma_v5_2_process_trap_irq,
-};
-
-static const struct amdgpu_irq_src_funcs sdma_v5_2_illegal_inst_irq_funcs = {
- .process = sdma_v5_2_process_illegal_inst_irq,
-};
-
-static void sdma_v5_2_set_irq_funcs(struct amdgpu_device *adev)
-{
- adev->sdma.trap_irq.num_types = AMDGPU_SDMA_IRQ_INSTANCE0 +
- adev->sdma.num_instances;
- adev->sdma.trap_irq.funcs = &sdma_v5_2_trap_irq_funcs;
- adev->sdma.illegal_inst_irq.funcs = &sdma_v5_2_illegal_inst_irq_funcs;
-}
-
-/**
- * sdma_v5_2_emit_copy_buffer - copy buffer using the sDMA engine
- *
- * @ib: indirect buffer to copy to
- * @src_offset: src GPU address
- * @dst_offset: dst GPU address
- * @byte_count: number of bytes to xfer
- * @copy_flags: copy flags for the buffers
- *
- * Copy GPU buffers using the DMA engine.
- * Used by the amdgpu ttm implementation to move pages if
- * registered as the asic copy callback.
- */
-static void sdma_v5_2_emit_copy_buffer(struct amdgpu_ib *ib,
- uint64_t src_offset,
- uint64_t dst_offset,
- uint32_t byte_count,
- uint32_t copy_flags)
-{
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
- SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR) |
- SDMA_PKT_COPY_LINEAR_HEADER_TMZ((copy_flags & AMDGPU_COPY_FLAGS_TMZ) ? 1 : 0);
- ib->ptr[ib->length_dw++] = byte_count - 1;
- ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
- ib->ptr[ib->length_dw++] = lower_32_bits(src_offset);
- ib->ptr[ib->length_dw++] = upper_32_bits(src_offset);
- ib->ptr[ib->length_dw++] = lower_32_bits(dst_offset);
- ib->ptr[ib->length_dw++] = upper_32_bits(dst_offset);
-}
-
-/**
- * sdma_v5_2_emit_fill_buffer - fill buffer using the sDMA engine
- *
- * @ib: indirect buffer to fill
- * @src_data: value to write to buffer
- * @dst_offset: dst GPU address
- * @byte_count: number of bytes to xfer
- *
- * Fill GPU buffers using the DMA engine.
- */
-static void sdma_v5_2_emit_fill_buffer(struct amdgpu_ib *ib,
- uint32_t src_data,
- uint64_t dst_offset,
- uint32_t byte_count)
-{
- ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_CONST_FILL);
- ib->ptr[ib->length_dw++] = lower_32_bits(dst_offset);
- ib->ptr[ib->length_dw++] = upper_32_bits(dst_offset);
- ib->ptr[ib->length_dw++] = src_data;
- ib->ptr[ib->length_dw++] = byte_count - 1;
-}
-
-static const struct amdgpu_buffer_funcs sdma_v5_2_buffer_funcs = {
- .copy_max_bytes = 0x400000,
- .copy_num_dw = 7,
- .emit_copy_buffer = sdma_v5_2_emit_copy_buffer,
-
- .fill_max_bytes = 0x400000,
- .fill_num_dw = 5,
- .emit_fill_buffer = sdma_v5_2_emit_fill_buffer,
-};
-
-static void sdma_v5_2_set_buffer_funcs(struct amdgpu_device *adev)
-{
- if (adev->mman.buffer_funcs == NULL) {
- adev->mman.buffer_funcs = &sdma_v5_2_buffer_funcs;
- adev->mman.buffer_funcs_ring = &adev->sdma.instance[0].ring;
- }
-}
-
-static const struct amdgpu_vm_pte_funcs sdma_v5_2_vm_pte_funcs = {
- .copy_pte_num_dw = 7,
- .copy_pte = sdma_v5_2_vm_copy_pte,
- .write_pte = sdma_v5_2_vm_write_pte,
- .set_pte_pde = sdma_v5_2_vm_set_pte_pde,
-};
-
-static void sdma_v5_2_set_vm_pte_funcs(struct amdgpu_device *adev)
-{
- unsigned i;
-
- if (adev->vm_manager.vm_pte_funcs == NULL) {
- adev->vm_manager.vm_pte_funcs = &sdma_v5_2_vm_pte_funcs;
- for (i = 0; i < adev->sdma.num_instances; i++) {
- adev->vm_manager.vm_pte_scheds[i] =
- &adev->sdma.instance[i].ring.sched;
- }
- adev->vm_manager.vm_pte_num_scheds = adev->sdma.num_instances;
- }
-}
-
-const struct amdgpu_ip_block_version sdma_v5_2_ip_block = {
- .type = AMD_IP_BLOCK_TYPE_SDMA,
- .major = 5,
- .minor = 2,
- .rev = 0,
- .funcs = &sdma_v5_2_ip_funcs,
-};
diff --git a/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/postimage.2 b/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/postimage.2
deleted file mode 100644
index 0fda70336300..000000000000
--- a/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/postimage.2
+++ /dev/null
@@ -1,1927 +0,0 @@
-/*
- * Copyright 2022 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/firmware.h>
-#include <drm/drm_drv.h>
-
-#include "amdgpu.h"
-#include "amdgpu_vcn.h"
-#include "amdgpu_pm.h"
-#include "soc15.h"
-#include "soc15d.h"
-#include "soc15_hw_ip.h"
-#include "vcn_v2_0.h"
-#include "mmsch_v4_0_3.h"
-
-#include "vcn/vcn_4_0_3_offset.h"
-#include "vcn/vcn_4_0_3_sh_mask.h"
-#include "ivsrcid/vcn/irqsrcs_vcn_4_0.h"
-
-#define mmUVD_DPG_LMA_CTL regUVD_DPG_LMA_CTL
-#define mmUVD_DPG_LMA_CTL_BASE_IDX regUVD_DPG_LMA_CTL_BASE_IDX
-#define mmUVD_DPG_LMA_DATA regUVD_DPG_LMA_DATA
-#define mmUVD_DPG_LMA_DATA_BASE_IDX regUVD_DPG_LMA_DATA_BASE_IDX
-
-#define VCN_VID_SOC_ADDRESS_2_0 0x1fb00
-#define VCN1_VID_SOC_ADDRESS_3_0 0x48300
-
-static const struct amdgpu_hwip_reg_entry vcn_reg_list_4_0_3[] = {
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_POWER_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA0),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA1),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_CMD),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_PGFSM_CONFIG),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_PGFSM_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_CTL),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_DATA),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_MASK),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_PAUSE)
-};
-
-#define NORMALIZE_VCN_REG_OFFSET(offset) \
- (offset & 0x1FFFF)
-
-static int vcn_v4_0_3_start_sriov(struct amdgpu_device *adev);
-static void vcn_v4_0_3_set_unified_ring_funcs(struct amdgpu_device *adev);
-static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev);
-static int vcn_v4_0_3_set_powergating_state(void *handle,
- enum amd_powergating_state state);
-static int vcn_v4_0_3_pause_dpg_mode(struct amdgpu_device *adev,
- int inst_idx, struct dpg_pause_state *new_state);
-static void vcn_v4_0_3_unified_ring_set_wptr(struct amdgpu_ring *ring);
-static void vcn_v4_0_3_set_ras_funcs(struct amdgpu_device *adev);
-static void vcn_v4_0_3_enable_ras(struct amdgpu_device *adev,
- int inst_idx, bool indirect);
-/**
- * vcn_v4_0_3_early_init - set function pointers
- *
- * @handle: amdgpu_device pointer
- *
- * Set ring and irq function pointers
- */
-static int vcn_v4_0_3_early_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- /* re-use enc ring as unified ring */
- adev->vcn.num_enc_rings = 1;
-
- vcn_v4_0_3_set_unified_ring_funcs(adev);
- vcn_v4_0_3_set_irq_funcs(adev);
- vcn_v4_0_3_set_ras_funcs(adev);
-
- return amdgpu_vcn_early_init(adev);
-}
-
-/**
- * vcn_v4_0_3_sw_init - sw init for VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Load firmware and sw initialization
- */
-static int vcn_v4_0_3_sw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_ring *ring;
- int i, r, vcn_inst;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
- uint32_t *ptr;
-
- r = amdgpu_vcn_sw_init(adev);
- if (r)
- return r;
-
- amdgpu_vcn_setup_ucode(adev);
-
- r = amdgpu_vcn_resume(adev);
- if (r)
- return r;
-
- /* VCN DEC TRAP */
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_VCN,
- VCN_4_0__SRCID__UVD_ENC_GENERAL_PURPOSE, &adev->vcn.inst->irq);
- if (r)
- return r;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
-
- vcn_inst = GET_INST(VCN, i);
-
- ring = &adev->vcn.inst[i].ring_enc[0];
- ring->use_doorbell = true;
-
- if (!amdgpu_sriov_vf(adev))
- ring->doorbell_index =
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 9 * vcn_inst;
- else
- ring->doorbell_index =
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 32 * vcn_inst;
-
- ring->vm_hub = AMDGPU_MMHUB0(adev->vcn.inst[i].aid_id);
- sprintf(ring->name, "vcn_unified_%d", adev->vcn.inst[i].aid_id);
- r = amdgpu_ring_init(adev, ring, 512, &adev->vcn.inst->irq, 0,
- AMDGPU_RING_PRIO_DEFAULT,
- &adev->vcn.inst[i].sched_score);
- if (r)
- return r;
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->present_flag_0 = cpu_to_le32(AMDGPU_FW_SHARED_FLAG_0_UNIFIED_QUEUE);
- fw_shared->sq.is_enabled = true;
-
- if (amdgpu_vcnfw_log)
- amdgpu_vcn_fwlog_init(&adev->vcn.inst[i]);
- }
-
- if (amdgpu_sriov_vf(adev)) {
- r = amdgpu_virt_alloc_mm_table(adev);
- if (r)
- return r;
- }
-
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)
- adev->vcn.pause_dpg_mode = vcn_v4_0_3_pause_dpg_mode;
-
- if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- r = amdgpu_vcn_ras_sw_init(adev);
- if (r) {
- dev_err(adev->dev, "Failed to initialize vcn ras block!\n");
- return r;
- }
- }
-
- /* Allocate memory for VCN IP Dump buffer */
- ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL);
- if (!ptr) {
- DRM_ERROR("Failed to allocate memory for VCN IP Dump\n");
- adev->vcn.ip_dump = NULL;
- } else {
- adev->vcn.ip_dump = ptr;
- }
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_sw_fini - sw fini for VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * VCN suspend and free up sw allocation
- */
-static int vcn_v4_0_3_sw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, r, idx;
-
- if (drm_dev_enter(&adev->ddev, &idx)) {
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->present_flag_0 = 0;
- fw_shared->sq.is_enabled = cpu_to_le32(false);
- }
- drm_dev_exit(idx);
- }
-
- if (amdgpu_sriov_vf(adev))
- amdgpu_virt_free_mm_table(adev);
-
- r = amdgpu_vcn_suspend(adev);
- if (r)
- return r;
-
- r = amdgpu_vcn_sw_fini(adev);
-
- kfree(adev->vcn.ip_dump);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_hw_init - start and test VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Initialize the hardware, boot up the VCPU and do some testing
- */
-static int vcn_v4_0_3_hw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_ring *ring;
- int i, r, vcn_inst;
-
- if (amdgpu_sriov_vf(adev)) {
- r = vcn_v4_0_3_start_sriov(adev);
- if (r)
- return r;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ring = &adev->vcn.inst[i].ring_enc[0];
- ring->wptr = 0;
- ring->wptr_old = 0;
- vcn_v4_0_3_unified_ring_set_wptr(ring);
- ring->sched.ready = true;
- }
- } else {
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- vcn_inst = GET_INST(VCN, i);
- ring = &adev->vcn.inst[i].ring_enc[0];
-
- if (ring->use_doorbell) {
- adev->nbio.funcs->vcn_doorbell_range(
- adev, ring->use_doorbell,
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 9 * vcn_inst,
- adev->vcn.inst[i].aid_id);
-
- WREG32_SOC15(
- VCN, GET_INST(VCN, ring->me),
- regVCN_RB1_DB_CTRL,
- ring->doorbell_index
- << VCN_RB1_DB_CTRL__OFFSET__SHIFT |
- VCN_RB1_DB_CTRL__EN_MASK);
-
- /* Read DB_CTRL to flush the write DB_CTRL command. */
- RREG32_SOC15(
- VCN, GET_INST(VCN, ring->me),
- regVCN_RB1_DB_CTRL);
- }
-
- r = amdgpu_ring_test_helper(ring);
- if (r)
- return r;
- }
- }
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_hw_fini - stop the hardware block
- *
- * @handle: amdgpu_device pointer
- *
- * Stop the VCN block, mark ring as not ready any more
- */
-static int vcn_v4_0_3_hw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- cancel_delayed_work_sync(&adev->vcn.idle_work);
-
- if (adev->vcn.cur_state != AMD_PG_STATE_GATE)
- vcn_v4_0_3_set_powergating_state(adev, AMD_PG_STATE_GATE);
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_suspend - suspend VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * HW fini and suspend VCN block
- */
-static int vcn_v4_0_3_suspend(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = vcn_v4_0_3_hw_fini(adev);
- if (r)
- return r;
-
- r = amdgpu_vcn_suspend(adev);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_resume - resume VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Resume firmware and hw init VCN block
- */
-static int vcn_v4_0_3_resume(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = amdgpu_vcn_resume(adev);
- if (r)
- return r;
-
- r = vcn_v4_0_3_hw_init(adev);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_mc_resume - memory controller programming
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Let the VCN memory controller know it's offsets
- */
-static void vcn_v4_0_3_mc_resume(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t offset, size, vcn_inst;
- const struct common_firmware_header *hdr;
-
- hdr = (const struct common_firmware_header *)adev->vcn.fw[inst_idx]->data;
- size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);
-
- vcn_inst = GET_INST(VCN, inst_idx);
- /* cache window 0: fw */
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + inst_idx]
- .tmr_mc_addr_lo));
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + inst_idx]
- .tmr_mc_addr_hi));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET0, 0);
- offset = 0;
- } else {
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr));
- WREG32_SOC15(VCN, vcn_inst,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr));
- offset = size;
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET0,
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
- }
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE0, size);
-
- /* cache window 1: stack */
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset));
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET1, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE1,
- AMDGPU_VCN_STACK_SIZE);
-
- /* cache window 2: context */
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE));
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET2, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE2,
- AMDGPU_VCN_CONTEXT_SIZE);
-
- /* non-cache window */
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr));
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_NONCACHE_OFFSET0, 0);
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_VCPU_NONCACHE_SIZE0,
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)));
-}
-
-/**
- * vcn_v4_0_3_mc_resume_dpg_mode - memory controller programming for dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Let the VCN memory controller know it's offsets with dpg mode
- */
-static void vcn_v4_0_3_mc_resume_dpg_mode(struct amdgpu_device *adev, int inst_idx, bool indirect)
-{
- uint32_t offset, size;
- const struct common_firmware_header *hdr;
-
- hdr = (const struct common_firmware_header *)adev->vcn.fw[inst_idx]->data;
- size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);
-
- /* cache window 0: fw */
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- if (!indirect) {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN +
- inst_idx].tmr_mc_addr_lo), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN +
- inst_idx].tmr_mc_addr_hi), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0), 0, 0, indirect);
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0), 0, 0, indirect);
- }
- offset = 0;
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr), 0, indirect);
- offset = size;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0),
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3, 0, indirect);
- }
-
- if (!indirect)
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE0), size, 0, indirect);
- else
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE0), 0, 0, indirect);
-
- /* cache window 1: stack */
- if (!indirect) {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET1), 0, 0, indirect);
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET1), 0, 0, indirect);
- }
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE1), AMDGPU_VCN_STACK_SIZE, 0, indirect);
-
- /* cache window 2: context */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET2), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE2), AMDGPU_VCN_CONTEXT_SIZE, 0, indirect);
-
- /* non-cache window */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_NONCACHE_OFFSET0), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_NONCACHE_SIZE0),
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)), 0, indirect);
-
- /* VCN global tiling registers */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_GFX8_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_GFX10_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect);
-}
-
-/**
- * vcn_v4_0_3_disable_clock_gating - disable VCN clock gating
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Disable clock gating for VCN block
- */
-static void vcn_v4_0_3_disable_clock_gating(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t data;
- int vcn_inst;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* VCN disable CGC */
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK;
- data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_GATE);
- data &= ~(UVD_CGC_GATE__SYS_MASK
- | UVD_CGC_GATE__MPEG2_MASK
- | UVD_CGC_GATE__REGS_MASK
- | UVD_CGC_GATE__RBC_MASK
- | UVD_CGC_GATE__LMI_MC_MASK
- | UVD_CGC_GATE__LMI_UMC_MASK
- | UVD_CGC_GATE__MPC_MASK
- | UVD_CGC_GATE__LBSI_MASK
- | UVD_CGC_GATE__LRBBM_MASK
- | UVD_CGC_GATE__WCB_MASK
- | UVD_CGC_GATE__VCPU_MASK
- | UVD_CGC_GATE__MMSCH_MASK);
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_GATE, data);
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_CGC_GATE, 0, 0xFFFFFFFF);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data &= ~(UVD_CGC_CTRL__SYS_MODE_MASK
- | UVD_CGC_CTRL__MPEG2_MODE_MASK
- | UVD_CGC_CTRL__REGS_MODE_MASK
- | UVD_CGC_CTRL__RBC_MODE_MASK
- | UVD_CGC_CTRL__LMI_MC_MODE_MASK
- | UVD_CGC_CTRL__LMI_UMC_MODE_MASK
- | UVD_CGC_CTRL__MPC_MODE_MASK
- | UVD_CGC_CTRL__LBSI_MODE_MASK
- | UVD_CGC_CTRL__LRBBM_MODE_MASK
- | UVD_CGC_CTRL__WCB_MODE_MASK
- | UVD_CGC_CTRL__VCPU_MODE_MASK
- | UVD_CGC_CTRL__MMSCH_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_GATE);
- data |= (UVD_SUVD_CGC_GATE__SRE_MASK
- | UVD_SUVD_CGC_GATE__SIT_MASK
- | UVD_SUVD_CGC_GATE__SMP_MASK
- | UVD_SUVD_CGC_GATE__SCM_MASK
- | UVD_SUVD_CGC_GATE__SDB_MASK
- | UVD_SUVD_CGC_GATE__SRE_H264_MASK
- | UVD_SUVD_CGC_GATE__SRE_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SIT_H264_MASK
- | UVD_SUVD_CGC_GATE__SIT_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SCM_H264_MASK
- | UVD_SUVD_CGC_GATE__SCM_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SDB_H264_MASK
- | UVD_SUVD_CGC_GATE__SDB_HEVC_MASK
- | UVD_SUVD_CGC_GATE__ENT_MASK
- | UVD_SUVD_CGC_GATE__SIT_HEVC_DEC_MASK
- | UVD_SUVD_CGC_GATE__SITE_MASK
- | UVD_SUVD_CGC_GATE__SRE_VP9_MASK
- | UVD_SUVD_CGC_GATE__SCM_VP9_MASK
- | UVD_SUVD_CGC_GATE__SIT_VP9_DEC_MASK
- | UVD_SUVD_CGC_GATE__SDB_VP9_MASK
- | UVD_SUVD_CGC_GATE__IME_HEVC_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_GATE, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL);
- data &= ~(UVD_SUVD_CGC_CTRL__SRE_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SIT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SMP_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK
- | UVD_SUVD_CGC_CTRL__ENT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__IME_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SITE_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL, data);
-}
-
-/**
- * vcn_v4_0_3_disable_clock_gating_dpg_mode - disable VCN clock gating dpg mode
- *
- * @adev: amdgpu_device pointer
- * @sram_sel: sram select
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Disable clock gating for VCN block with dpg mode
- */
-static void vcn_v4_0_3_disable_clock_gating_dpg_mode(struct amdgpu_device *adev, uint8_t sram_sel,
- int inst_idx, uint8_t indirect)
-{
- uint32_t reg_data = 0;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- /* enable sw clock gating control */
- reg_data = 0 << UVD_CGC_CTRL__DYN_CLOCK_MODE__SHIFT;
- reg_data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- reg_data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- reg_data &= ~(UVD_CGC_CTRL__SYS_MODE_MASK |
- UVD_CGC_CTRL__MPEG2_MODE_MASK |
- UVD_CGC_CTRL__REGS_MODE_MASK |
- UVD_CGC_CTRL__RBC_MODE_MASK |
- UVD_CGC_CTRL__LMI_MC_MODE_MASK |
- UVD_CGC_CTRL__LMI_UMC_MODE_MASK |
- UVD_CGC_CTRL__IDCT_MODE_MASK |
- UVD_CGC_CTRL__MPRD_MODE_MASK |
- UVD_CGC_CTRL__MPC_MODE_MASK |
- UVD_CGC_CTRL__LBSI_MODE_MASK |
- UVD_CGC_CTRL__LRBBM_MODE_MASK |
- UVD_CGC_CTRL__WCB_MODE_MASK |
- UVD_CGC_CTRL__VCPU_MODE_MASK);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_CGC_CTRL), reg_data, sram_sel, indirect);
-
- /* turn off clock gating */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_CGC_GATE), 0, sram_sel, indirect);
-
- /* turn on SUVD clock gating */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_SUVD_CGC_GATE), 1, sram_sel, indirect);
-
- /* turn on sw mode in UVD_SUVD_CGC_CTRL */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_SUVD_CGC_CTRL), 0, sram_sel, indirect);
-}
-
-/**
- * vcn_v4_0_3_enable_clock_gating - enable VCN clock gating
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Enable clock gating for VCN block
- */
-static void vcn_v4_0_3_enable_clock_gating(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t data;
- int vcn_inst;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* enable VCN CGC */
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data |= 0 << UVD_CGC_CTRL__DYN_CLOCK_MODE__SHIFT;
- data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data |= (UVD_CGC_CTRL__SYS_MODE_MASK
- | UVD_CGC_CTRL__MPEG2_MODE_MASK
- | UVD_CGC_CTRL__REGS_MODE_MASK
- | UVD_CGC_CTRL__RBC_MODE_MASK
- | UVD_CGC_CTRL__LMI_MC_MODE_MASK
- | UVD_CGC_CTRL__LMI_UMC_MODE_MASK
- | UVD_CGC_CTRL__MPC_MODE_MASK
- | UVD_CGC_CTRL__LBSI_MODE_MASK
- | UVD_CGC_CTRL__LRBBM_MODE_MASK
- | UVD_CGC_CTRL__WCB_MODE_MASK
- | UVD_CGC_CTRL__VCPU_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL);
- data |= (UVD_SUVD_CGC_CTRL__SRE_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SIT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SMP_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK
- | UVD_SUVD_CGC_CTRL__ENT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__IME_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SITE_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL, data);
-}
-
-/**
- * vcn_v4_0_3_start_dpg_mode - VCN start with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Start VCN block with dpg mode
- */
-static int vcn_v4_0_3_start_dpg_mode(struct amdgpu_device *adev, int inst_idx, bool indirect)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared =
- adev->vcn.inst[inst_idx].fw_shared.cpu_addr;
- struct amdgpu_ring *ring;
- int vcn_inst;
- uint32_t tmp;
-
- vcn_inst = GET_INST(VCN, inst_idx);
- /* disable register anti-hang mechanism */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_POWER_STATUS), 1,
- ~UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
- /* enable dynamic power gating mode */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_POWER_STATUS);
- tmp |= UVD_POWER_STATUS__UVD_PG_MODE_MASK;
- tmp |= UVD_POWER_STATUS__UVD_PG_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_POWER_STATUS, tmp);
-
- if (indirect) {
- DRM_DEV_DEBUG(adev->dev, "VCN %d start: on AID %d",
- inst_idx, adev->vcn.inst[inst_idx].aid_id);
- adev->vcn.inst[inst_idx].dpg_sram_curr_addr =
- (uint32_t *)adev->vcn.inst[inst_idx].dpg_sram_cpu_addr;
- /* Use dummy register 0xDEADBEEF passing AID selection to PSP FW */
- WREG32_SOC15_DPG_MODE(inst_idx, 0xDEADBEEF,
- adev->vcn.inst[inst_idx].aid_id, 0, true);
- }
-
- /* enable clock gating */
- vcn_v4_0_3_disable_clock_gating_dpg_mode(adev, 0, inst_idx, indirect);
-
- /* enable VCPU clock */
- tmp = (0xFF << UVD_VCPU_CNTL__PRB_TIMEOUT_VAL__SHIFT);
- tmp |= UVD_VCPU_CNTL__CLK_EN_MASK;
- tmp |= UVD_VCPU_CNTL__BLK_RST_MASK;
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CNTL), tmp, 0, indirect);
-
- /* disable master interrupt */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MASTINT_EN), 0, 0, indirect);
-
- /* setup regUVD_LMI_CTRL */
- tmp = (UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
- UVD_LMI_CTRL__REQ_MODE_MASK |
- UVD_LMI_CTRL__CRC_RESET_MASK |
- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK |
- (8 << UVD_LMI_CTRL__WRITE_CLEAN_TIMER__SHIFT) |
- 0x00100000L);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_CTRL), tmp, 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_CNTL),
- 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT, 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUXA0),
- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)), 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUXB0),
- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)), 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUX),
- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)), 0, indirect);
-
- vcn_v4_0_3_mc_resume_dpg_mode(adev, inst_idx, indirect);
-
- tmp = (0xFF << UVD_VCPU_CNTL__PRB_TIMEOUT_VAL__SHIFT);
- tmp |= UVD_VCPU_CNTL__CLK_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CNTL), tmp, 0, indirect);
-
- /* enable LMI MC and UMC channels */
- tmp = 0x1f << UVD_LMI_CTRL2__RE_OFLD_MIF_WR_REQ_NUM__SHIFT;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_CTRL2), tmp, 0, indirect);
-
- vcn_v4_0_3_enable_ras(adev, inst_idx, indirect);
-
- /* enable master interrupt */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MASTINT_EN),
- UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect);
-
- if (indirect)
- amdgpu_vcn_psp_update_sram(adev, inst_idx, AMDGPU_UCODE_ID_VCN0_RAM);
-
- ring = &adev->vcn.inst[inst_idx].ring_enc[0];
-
- /* program the RB_BASE for ring buffer */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_LO,
- lower_32_bits(ring->gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_HI,
- upper_32_bits(ring->gpu_addr));
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_SIZE,
- ring->ring_size / sizeof(uint32_t));
-
- /* resetting ring, fw should not check RB ring */
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp &= ~(VCN_RB_ENABLE__RB_EN_MASK);
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
- fw_shared->sq.queue_mode |= FW_QUEUE_RING_RESET;
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_RPTR, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR, 0);
- ring->wptr = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp |= VCN_RB_ENABLE__RB_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
- fw_shared->sq.queue_mode &= ~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF);
-
- /*resetting done, fw can check RB ring */
- fw_shared->sq.queue_mode &= cpu_to_le32(~FW_QUEUE_RING_RESET);
-
- return 0;
-}
-
-static int vcn_v4_0_3_start_sriov(struct amdgpu_device *adev)
-{
- int i, vcn_inst;
- struct amdgpu_ring *ring_enc;
- uint64_t cache_addr;
- uint64_t rb_enc_addr;
- uint64_t ctx_addr;
- uint32_t param, resp, expected;
- uint32_t offset, cache_size;
- uint32_t tmp, timeout;
-
- struct amdgpu_mm_table *table = &adev->virt.mm_table;
- uint32_t *table_loc;
- uint32_t table_size;
- uint32_t size, size_dw;
- uint32_t init_status;
- uint32_t enabled_vcn;
-
- struct mmsch_v4_0_cmd_direct_write
- direct_wt = { {0} };
- struct mmsch_v4_0_cmd_direct_read_modify_write
- direct_rd_mod_wt = { {0} };
- struct mmsch_v4_0_cmd_end end = { {0} };
- struct mmsch_v4_0_3_init_header header;
-
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- volatile struct amdgpu_fw_shared_rb_setup *rb_setup;
-
- direct_wt.cmd_header.command_type =
- MMSCH_COMMAND__DIRECT_REG_WRITE;
- direct_rd_mod_wt.cmd_header.command_type =
- MMSCH_COMMAND__DIRECT_REG_READ_MODIFY_WRITE;
- end.cmd_header.command_type = MMSCH_COMMAND__END;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- vcn_inst = GET_INST(VCN, i);
-
- memset(&header, 0, sizeof(struct mmsch_v4_0_3_init_header));
- header.version = MMSCH_VERSION;
- header.total_size = sizeof(struct mmsch_v4_0_3_init_header) >> 2;
-
- table_loc = (uint32_t *)table->cpu_addr;
- table_loc += header.total_size;
-
- table_size = 0;
-
- MMSCH_V4_0_INSERT_DIRECT_RD_MOD_WT(SOC15_REG_OFFSET(VCN, 0, regUVD_STATUS),
- ~UVD_STATUS__UVD_BUSY, UVD_STATUS__UVD_BUSY);
-
- cache_size = AMDGPU_GPU_PAGE_ALIGN(adev->vcn.fw[i]->size + 4);
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + i].tmr_mc_addr_lo);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + i].tmr_mc_addr_hi);
-
- offset = 0;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET0), 0);
- } else {
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[i].gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[i].gpu_addr));
- offset = cache_size;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET0),
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
- }
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE0),
- cache_size);
-
- cache_addr = adev->vcn.inst[vcn_inst].gpu_addr + offset;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW), lower_32_bits(cache_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH), upper_32_bits(cache_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET1), 0);
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE1), AMDGPU_VCN_STACK_SIZE);
-
- cache_addr = adev->vcn.inst[vcn_inst].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE;
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW), lower_32_bits(cache_addr));
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH), upper_32_bits(cache_addr));
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET2), 0);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE2), AMDGPU_VCN_CONTEXT_SIZE);
-
- fw_shared = adev->vcn.inst[vcn_inst].fw_shared.cpu_addr;
- rb_setup = &fw_shared->rb_setup;
-
- ring_enc = &adev->vcn.inst[vcn_inst].ring_enc[0];
- ring_enc->wptr = 0;
- rb_enc_addr = ring_enc->gpu_addr;
-
- rb_setup->is_rb_enabled_flags |= RB_ENABLED;
- rb_setup->rb_addr_lo = lower_32_bits(rb_enc_addr);
- rb_setup->rb_addr_hi = upper_32_bits(rb_enc_addr);
- rb_setup->rb_size = ring_enc->ring_size / 4;
- fw_shared->present_flag_0 |= cpu_to_le32(AMDGPU_VCN_VF_RB_SETUP_FLAG);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[vcn_inst].fw_shared.gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[vcn_inst].fw_shared.gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_NONCACHE_SIZE0),
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)));
- MMSCH_V4_0_INSERT_END();
-
- header.vcn0.init_status = 0;
- header.vcn0.table_offset = header.total_size;
- header.vcn0.table_size = table_size;
- header.total_size += table_size;
-
- /* Send init table to mmsch */
- size = sizeof(struct mmsch_v4_0_3_init_header);
- table_loc = (uint32_t *)table->cpu_addr;
- memcpy((void *)table_loc, &header, size);
-
- ctx_addr = table->gpu_addr;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_ADDR_LO, lower_32_bits(ctx_addr));
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_ADDR_HI, upper_32_bits(ctx_addr));
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_VMID);
- tmp &= ~MMSCH_VF_VMID__VF_CTX_VMID_MASK;
- tmp |= (0 << MMSCH_VF_VMID__VF_CTX_VMID__SHIFT);
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_VMID, tmp);
-
- size = header.total_size;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_SIZE, size);
-
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_RESP, 0);
-
- param = 0x00000001;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_HOST, param);
- tmp = 0;
- timeout = 1000;
- resp = 0;
- expected = MMSCH_VF_MAILBOX_RESP__OK;
- while (resp != expected) {
- resp = RREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_RESP);
- if (resp != 0)
- break;
-
- udelay(10);
- tmp = tmp + 10;
- if (tmp >= timeout) {
- DRM_ERROR("failed to init MMSCH. TIME-OUT after %d usec"\
- " waiting for regMMSCH_VF_MAILBOX_RESP "\
- "(expected=0x%08x, readback=0x%08x)\n",
- tmp, expected, resp);
- return -EBUSY;
- }
- }
-
- enabled_vcn = amdgpu_vcn_is_disabled_vcn(adev, VCN_DECODE_RING, 0) ? 1 : 0;
- init_status = ((struct mmsch_v4_0_3_init_header *)(table_loc))->vcn0.init_status;
- if (resp != expected && resp != MMSCH_VF_MAILBOX_RESP__INCOMPLETE
- && init_status != MMSCH_VF_ENGINE_STATUS__PASS) {
- DRM_ERROR("MMSCH init status is incorrect! readback=0x%08x, header init "\
- "status for VCN%x: 0x%x\n", resp, enabled_vcn, init_status);
- }
- }
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_start - VCN start
- *
- * @adev: amdgpu_device pointer
- *
- * Start VCN block
- */
-static int vcn_v4_0_3_start(struct amdgpu_device *adev)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- struct amdgpu_ring *ring;
- int i, j, k, r, vcn_inst;
- uint32_t tmp;
-
- if (adev->pm.dpm_enabled)
- amdgpu_dpm_enable_uvd(adev, true);
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
- r = vcn_v4_0_3_start_dpg_mode(adev, i, adev->vcn.indirect_sram);
- continue;
- }
-
- vcn_inst = GET_INST(VCN, i);
- /* set VCN status busy */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_STATUS) |
- UVD_STATUS__UVD_BUSY;
- WREG32_SOC15(VCN, vcn_inst, regUVD_STATUS, tmp);
-
- /*SW clock gating */
- vcn_v4_0_3_disable_clock_gating(adev, i);
-
- /* enable VCPU clock */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__CLK_EN_MASK,
- ~UVD_VCPU_CNTL__CLK_EN_MASK);
-
- /* disable master interrupt */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_MASTINT_EN), 0,
- ~UVD_MASTINT_EN__VCPU_EN_MASK);
-
- /* enable LMI MC and UMC channels */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_LMI_CTRL2), 0,
- ~UVD_LMI_CTRL2__STALL_ARB_UMC_MASK);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp &= ~UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
- tmp &= ~UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- /* setup regUVD_LMI_CTRL */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL);
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL,
- tmp | UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK);
-
- /* setup regUVD_MPC_CNTL */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_MPC_CNTL);
- tmp &= ~UVD_MPC_CNTL__REPLACEMENT_MODE_MASK;
- tmp |= 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_CNTL, tmp);
-
- /* setup UVD_MPC_SET_MUXA0 */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUXA0,
- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)));
-
- /* setup UVD_MPC_SET_MUXB0 */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUXB0,
- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)));
-
- /* setup UVD_MPC_SET_MUX */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUX,
- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)));
-
- vcn_v4_0_3_mc_resume(adev, i);
-
- /* VCN global tiling registers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_GFX8_ADDR_CONFIG,
- adev->gfx.config.gb_addr_config);
- WREG32_SOC15(VCN, vcn_inst, regUVD_GFX10_ADDR_CONFIG,
- adev->gfx.config.gb_addr_config);
-
- /* unblock VCPU register access */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_RB_ARB_CTRL), 0,
- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
-
- /* release VCPU reset to boot */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL), 0,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- for (j = 0; j < 10; ++j) {
- uint32_t status;
-
- for (k = 0; k < 100; ++k) {
- status = RREG32_SOC15(VCN, vcn_inst,
- regUVD_STATUS);
- if (status & 2)
- break;
- mdelay(10);
- }
- r = 0;
- if (status & 2)
- break;
-
- DRM_DEV_ERROR(adev->dev,
- "VCN decode not responding, trying to reset the VCPU!!!\n");
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst,
- regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__BLK_RST_MASK,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
- mdelay(10);
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst,
- regUVD_VCPU_CNTL),
- 0, ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- mdelay(10);
- r = -1;
- }
-
- if (r) {
- DRM_DEV_ERROR(adev->dev, "VCN decode not responding, giving up!!!\n");
- return r;
- }
-
- /* enable master interrupt */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_MASTINT_EN),
- UVD_MASTINT_EN__VCPU_EN_MASK,
- ~UVD_MASTINT_EN__VCPU_EN_MASK);
-
- /* clear the busy bit of VCN_STATUS */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_STATUS), 0,
- ~(2 << UVD_STATUS__VCPU_REPORT__SHIFT));
-
- ring = &adev->vcn.inst[i].ring_enc[0];
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
-
- /* program the RB_BASE for ring buffer */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_LO,
- lower_32_bits(ring->gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_HI,
- upper_32_bits(ring->gpu_addr));
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_SIZE,
- ring->ring_size / sizeof(uint32_t));
-
- /* resetting ring, fw should not check RB ring */
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp &= ~(VCN_RB_ENABLE__RB_EN_MASK);
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_RPTR, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR, 0);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp |= VCN_RB_ENABLE__RB_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
-
- ring->wptr = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
- fw_shared->sq.queue_mode &=
- cpu_to_le32(~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF));
-
- }
- return 0;
-}
-
-/**
- * vcn_v4_0_3_stop_dpg_mode - VCN stop with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- *
- * Stop VCN block with dpg mode
- */
-static int vcn_v4_0_3_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t tmp;
- int vcn_inst;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* Wait for power status to be 1 */
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_POWER_STATUS, 1,
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
-
- /* wait for read ptr to be equal to write ptr */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_RB_RPTR, tmp, 0xFFFFFFFF);
-
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_POWER_STATUS, 1,
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
-
- /* disable dynamic power gating mode */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_POWER_STATUS), 0,
- ~UVD_POWER_STATUS__UVD_PG_MODE_MASK);
- return 0;
-}
-
-/**
- * vcn_v4_0_3_stop - VCN stop
- *
- * @adev: amdgpu_device pointer
- *
- * Stop VCN block
- */
-static int vcn_v4_0_3_stop(struct amdgpu_device *adev)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- int i, r = 0, vcn_inst;
- uint32_t tmp;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- vcn_inst = GET_INST(VCN, i);
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->sq.queue_mode |= FW_QUEUE_DPG_HOLD_OFF;
-
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
- vcn_v4_0_3_stop_dpg_mode(adev, i);
- continue;
- }
-
- /* wait for vcn idle */
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_STATUS,
- UVD_STATUS__IDLE, 0x7);
- if (r)
- goto Done;
-
- tmp = UVD_LMI_STATUS__VCPU_LMI_WRITE_CLEAN_MASK |
- UVD_LMI_STATUS__READ_CLEAN_MASK |
- UVD_LMI_STATUS__WRITE_CLEAN_MASK |
- UVD_LMI_STATUS__WRITE_CLEAN_RAW_MASK;
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_LMI_STATUS, tmp,
- tmp);
- if (r)
- goto Done;
-
- /* stall UMC channel */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL2);
- tmp |= UVD_LMI_CTRL2__STALL_ARB_UMC_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL2, tmp);
- tmp = UVD_LMI_STATUS__UMC_READ_CLEAN_RAW_MASK |
- UVD_LMI_STATUS__UMC_WRITE_CLEAN_RAW_MASK;
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_LMI_STATUS, tmp,
- tmp);
- if (r)
- goto Done;
-
- /* Unblock VCPU Register access */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_RB_ARB_CTRL),
- UVD_RB_ARB_CTRL__VCPU_DIS_MASK,
- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
-
- /* release VCPU reset to boot */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__BLK_RST_MASK,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- /* disable VCPU clock */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL), 0,
- ~(UVD_VCPU_CNTL__CLK_EN_MASK));
-
- /* reset LMI UMC/LMI/VCPU */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp |= UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp |= UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- /* clear VCN status */
- WREG32_SOC15(VCN, vcn_inst, regUVD_STATUS, 0);
-
- /* apply HW clock gating */
- vcn_v4_0_3_enable_clock_gating(adev, i);
- }
-Done:
- if (adev->pm.dpm_enabled)
- amdgpu_dpm_enable_uvd(adev, false);
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_pause_dpg_mode - VCN pause with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @new_state: pause state
- *
- * Pause dpg mode for VCN block
- */
-static int vcn_v4_0_3_pause_dpg_mode(struct amdgpu_device *adev, int inst_idx,
- struct dpg_pause_state *new_state)
-{
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_unified_ring_get_rptr - get unified read pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Returns the current hardware unified read pointer
- */
-static uint64_t vcn_v4_0_3_unified_ring_get_rptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- return RREG32_SOC15(VCN, GET_INST(VCN, ring->me), regUVD_RB_RPTR);
-}
-
-/**
- * vcn_v4_0_3_unified_ring_get_wptr - get unified write pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Returns the current hardware unified write pointer
- */
-static uint64_t vcn_v4_0_3_unified_ring_get_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- if (ring->use_doorbell)
- return *ring->wptr_cpu_addr;
- else
- return RREG32_SOC15(VCN, GET_INST(VCN, ring->me),
- regUVD_RB_WPTR);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_reg_wait(struct amdgpu_ring *ring, uint32_t reg,
- uint32_t val, uint32_t mask)
-{
- /* For VF, only local offsets should be used */
- if (amdgpu_sriov_vf(ring->adev))
- reg = NORMALIZE_VCN_REG_OFFSET(reg);
-
- amdgpu_ring_write(ring, VCN_ENC_CMD_REG_WAIT);
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, mask);
- amdgpu_ring_write(ring, val);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg, uint32_t val)
-{
- /* For VF, only local offsets should be used */
- if (amdgpu_sriov_vf(ring->adev))
- reg = NORMALIZE_VCN_REG_OFFSET(reg);
-
- amdgpu_ring_write(ring, VCN_ENC_CMD_REG_WRITE);
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, val);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_vm_flush(struct amdgpu_ring *ring,
- unsigned int vmid, uint64_t pd_addr)
-{
- struct amdgpu_vmhub *hub = &ring->adev->vmhub[ring->vm_hub];
-
- pd_addr = amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
-
- /* wait for reg writes */
- vcn_v4_0_3_enc_ring_emit_reg_wait(ring, hub->ctx0_ptb_addr_lo32 +
- vmid * hub->ctx_addr_distance,
- lower_32_bits(pd_addr), 0xffffffff);
-}
-
-static void vcn_v4_0_3_ring_emit_hdp_flush(struct amdgpu_ring *ring)
-{
- /* VCN engine access for HDP flush doesn't work when RRMT is enabled.
- * This is a workaround to avoid any HDP flush through VCN ring.
- */
-}
-
-/**
- * vcn_v4_0_3_unified_ring_set_wptr - set enc write pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Commits the enc write pointer to the hardware
- */
-static void vcn_v4_0_3_unified_ring_set_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- if (ring->use_doorbell) {
- *ring->wptr_cpu_addr = lower_32_bits(ring->wptr);
- WDOORBELL32(ring->doorbell_index, lower_32_bits(ring->wptr));
- } else {
- WREG32_SOC15(VCN, GET_INST(VCN, ring->me), regUVD_RB_WPTR,
- lower_32_bits(ring->wptr));
- }
-}
-
-static const struct amdgpu_ring_funcs vcn_v4_0_3_unified_ring_vm_funcs = {
- .type = AMDGPU_RING_TYPE_VCN_ENC,
- .align_mask = 0x3f,
- .nop = VCN_ENC_CMD_NO_OP,
- .get_rptr = vcn_v4_0_3_unified_ring_get_rptr,
- .get_wptr = vcn_v4_0_3_unified_ring_get_wptr,
- .set_wptr = vcn_v4_0_3_unified_ring_set_wptr,
- .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_v4_0_3_enc_ring_emit_vm_flush,
- .emit_hdp_flush = vcn_v4_0_3_ring_emit_hdp_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_v4_0_3_enc_ring_emit_wreg,
- .emit_reg_wait = vcn_v4_0_3_enc_ring_emit_reg_wait,
- .emit_reg_write_reg_wait = amdgpu_ring_emit_reg_write_reg_wait_helper,
-};
-
-/**
- * vcn_v4_0_3_set_unified_ring_funcs - set unified ring functions
- *
- * @adev: amdgpu_device pointer
- *
- * Set unified ring functions
- */
-static void vcn_v4_0_3_set_unified_ring_funcs(struct amdgpu_device *adev)
-{
- int i, vcn_inst;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- adev->vcn.inst[i].ring_enc[0].funcs = &vcn_v4_0_3_unified_ring_vm_funcs;
- adev->vcn.inst[i].ring_enc[0].me = i;
- vcn_inst = GET_INST(VCN, i);
- adev->vcn.inst[i].aid_id =
- vcn_inst / adev->vcn.num_inst_per_aid;
- }
-}
-
-/**
- * vcn_v4_0_3_is_idle - check VCN block is idle
- *
- * @handle: amdgpu_device pointer
- *
- * Check whether VCN block is idle
- */
-static bool vcn_v4_0_3_is_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, ret = 1;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ret &= (RREG32_SOC15(VCN, GET_INST(VCN, i), regUVD_STATUS) ==
- UVD_STATUS__IDLE);
- }
-
- return ret;
-}
-
-/**
- * vcn_v4_0_3_wait_for_idle - wait for VCN block idle
- *
- * @handle: amdgpu_device pointer
- *
- * Wait for VCN block idle
- */
-static int vcn_v4_0_3_wait_for_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, ret = 0;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ret = SOC15_WAIT_ON_RREG(VCN, GET_INST(VCN, i), regUVD_STATUS,
- UVD_STATUS__IDLE, UVD_STATUS__IDLE);
- if (ret)
- return ret;
- }
-
- return ret;
-}
-
-/* vcn_v4_0_3_set_clockgating_state - set VCN block clockgating state
- *
- * @handle: amdgpu_device pointer
- * @state: clock gating state
- *
- * Set VCN block clockgating state
- */
-static int vcn_v4_0_3_set_clockgating_state(void *handle,
- enum amd_clockgating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- bool enable = state == AMD_CG_STATE_GATE;
- int i;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- if (enable) {
- if (RREG32_SOC15(VCN, GET_INST(VCN, i),
- regUVD_STATUS) != UVD_STATUS__IDLE)
- return -EBUSY;
- vcn_v4_0_3_enable_clock_gating(adev, i);
- } else {
- vcn_v4_0_3_disable_clock_gating(adev, i);
- }
- }
- return 0;
-}
-
-/**
- * vcn_v4_0_3_set_powergating_state - set VCN block powergating state
- *
- * @handle: amdgpu_device pointer
- * @state: power gating state
- *
- * Set VCN block powergating state
- */
-static int vcn_v4_0_3_set_powergating_state(void *handle,
- enum amd_powergating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int ret;
-
- /* for SRIOV, guest should not control VCN Power-gating
- * MMSCH FW should control Power-gating and clock-gating
- * guest should avoid touching CGC and PG
- */
- if (amdgpu_sriov_vf(adev)) {
- adev->vcn.cur_state = AMD_PG_STATE_UNGATE;
- return 0;
- }
-
- if (state == adev->vcn.cur_state)
- return 0;
-
- if (state == AMD_PG_STATE_GATE)
- ret = vcn_v4_0_3_stop(adev);
- else
- ret = vcn_v4_0_3_start(adev);
-
- if (!ret)
- adev->vcn.cur_state = state;
-
- return ret;
-}
-
-/**
- * vcn_v4_0_3_set_interrupt_state - set VCN block interrupt state
- *
- * @adev: amdgpu_device pointer
- * @source: interrupt sources
- * @type: interrupt types
- * @state: interrupt states
- *
- * Set VCN block interrupt state
- */
-static int vcn_v4_0_3_set_interrupt_state(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- unsigned int type,
- enum amdgpu_interrupt_state state)
-{
- return 0;
-}
-
-/**
- * vcn_v4_0_3_process_interrupt - process VCN block interrupt
- *
- * @adev: amdgpu_device pointer
- * @source: interrupt sources
- * @entry: interrupt entry from clients and sources
- *
- * Process VCN block interrupt
- */
-static int vcn_v4_0_3_process_interrupt(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- struct amdgpu_iv_entry *entry)
-{
- uint32_t i, inst;
-
- i = node_id_to_phys_map[entry->node_id];
-
- DRM_DEV_DEBUG(adev->dev, "IH: VCN TRAP\n");
-
- for (inst = 0; inst < adev->vcn.num_vcn_inst; ++inst)
- if (adev->vcn.inst[inst].aid_id == i)
- break;
-
- if (inst >= adev->vcn.num_vcn_inst) {
- dev_WARN_ONCE(adev->dev, 1,
- "Interrupt received for unknown VCN instance %d",
- entry->node_id);
- return 0;
- }
-
- switch (entry->src_id) {
- case VCN_4_0__SRCID__UVD_ENC_GENERAL_PURPOSE:
- amdgpu_fence_process(&adev->vcn.inst[inst].ring_enc[0]);
- break;
- default:
- DRM_DEV_ERROR(adev->dev, "Unhandled interrupt: %d %d\n",
- entry->src_id, entry->src_data[0]);
- break;
- }
-
- return 0;
-}
-
-static const struct amdgpu_irq_src_funcs vcn_v4_0_3_irq_funcs = {
- .set = vcn_v4_0_3_set_interrupt_state,
- .process = vcn_v4_0_3_process_interrupt,
-};
-
-/**
- * vcn_v4_0_3_set_irq_funcs - set VCN block interrupt irq functions
- *
- * @adev: amdgpu_device pointer
- *
- * Set VCN block interrupt irq functions
- */
-static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev)
-{
- int i;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- adev->vcn.inst->irq.num_types++;
- }
- adev->vcn.inst->irq.funcs = &vcn_v4_0_3_irq_funcs;
-}
-
-static void vcn_v4_0_3_print_ip_state(void *handle, struct drm_printer *p)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
- uint32_t inst_off, is_powered;
-
- if (!adev->vcn.ip_dump)
- return;
-
- drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst);
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- if (adev->vcn.harvest_config & (1 << i)) {
- drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i);
- continue;
- }
-
- inst_off = i * reg_count;
- is_powered = (adev->vcn.ip_dump[inst_off] &
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1;
-
- if (is_powered) {
- drm_printf(p, "\nActive Instance:VCN%d\n", i);
- for (j = 0; j < reg_count; j++)
- drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_4_0_3[j].reg_name,
- adev->vcn.ip_dump[inst_off + j]);
- } else {
- drm_printf(p, "\nInactive Instance:VCN%d\n", i);
- }
- }
-}
-
-static void vcn_v4_0_3_dump_ip_state(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- bool is_powered;
- uint32_t inst_off, inst_id;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
-
- if (!adev->vcn.ip_dump)
- return;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- if (adev->vcn.harvest_config & (1 << i))
- continue;
-
- inst_id = GET_INST(VCN, i);
- inst_off = i * reg_count;
- /* mmUVD_POWER_STATUS is always readable and is first element of the array */
- adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, inst_id, regUVD_POWER_STATUS);
- is_powered = (adev->vcn.ip_dump[inst_off] &
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1;
-
- if (is_powered)
- for (j = 1; j < reg_count; j++)
- adev->vcn.ip_dump[inst_off + j] =
- RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_4_0_3[j],
- inst_id));
- }
-}
-
-static const struct amd_ip_funcs vcn_v4_0_3_ip_funcs = {
- .name = "vcn_v4_0_3",
- .early_init = vcn_v4_0_3_early_init,
- .late_init = NULL,
- .sw_init = vcn_v4_0_3_sw_init,
- .sw_fini = vcn_v4_0_3_sw_fini,
- .hw_init = vcn_v4_0_3_hw_init,
- .hw_fini = vcn_v4_0_3_hw_fini,
- .suspend = vcn_v4_0_3_suspend,
- .resume = vcn_v4_0_3_resume,
- .is_idle = vcn_v4_0_3_is_idle,
- .wait_for_idle = vcn_v4_0_3_wait_for_idle,
- .check_soft_reset = NULL,
- .pre_soft_reset = NULL,
- .soft_reset = NULL,
- .post_soft_reset = NULL,
- .set_clockgating_state = vcn_v4_0_3_set_clockgating_state,
- .set_powergating_state = vcn_v4_0_3_set_powergating_state,
- .dump_ip_state = vcn_v4_0_3_dump_ip_state,
- .print_ip_state = vcn_v4_0_3_print_ip_state,
-};
-
-const struct amdgpu_ip_block_version vcn_v4_0_3_ip_block = {
- .type = AMD_IP_BLOCK_TYPE_VCN,
- .major = 4,
- .minor = 0,
- .rev = 3,
- .funcs = &vcn_v4_0_3_ip_funcs,
-};
-
-static const struct amdgpu_ras_err_status_reg_entry vcn_v4_0_3_ue_reg_list[] = {
- {AMDGPU_RAS_REG_ENTRY(VCN, 0, regVCN_UE_ERR_STATUS_LO_VIDD, regVCN_UE_ERR_STATUS_HI_VIDD),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "VIDD"},
- {AMDGPU_RAS_REG_ENTRY(VCN, 0, regVCN_UE_ERR_STATUS_LO_VIDV, regVCN_UE_ERR_STATUS_HI_VIDV),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "VIDV"},
-};
-
-static void vcn_v4_0_3_inst_query_ras_error_count(struct amdgpu_device *adev,
- uint32_t vcn_inst,
- void *ras_err_status)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_err_status;
-
- /* vcn v4_0_3 only support query uncorrectable errors */
- amdgpu_ras_inst_query_ras_error_count(adev,
- vcn_v4_0_3_ue_reg_list,
- ARRAY_SIZE(vcn_v4_0_3_ue_reg_list),
- NULL, 0, GET_INST(VCN, vcn_inst),
- AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE,
- &err_data->ue_count);
-}
-
-static void vcn_v4_0_3_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_err_status)
-{
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- dev_warn(adev->dev, "VCN RAS is not supported\n");
- return;
- }
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++)
- vcn_v4_0_3_inst_query_ras_error_count(adev, i, ras_err_status);
-}
-
-static void vcn_v4_0_3_inst_reset_ras_error_count(struct amdgpu_device *adev,
- uint32_t vcn_inst)
-{
- amdgpu_ras_inst_reset_ras_error_count(adev,
- vcn_v4_0_3_ue_reg_list,
- ARRAY_SIZE(vcn_v4_0_3_ue_reg_list),
- GET_INST(VCN, vcn_inst));
-}
-
-static void vcn_v4_0_3_reset_ras_error_count(struct amdgpu_device *adev)
-{
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- dev_warn(adev->dev, "VCN RAS is not supported\n");
- return;
- }
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++)
- vcn_v4_0_3_inst_reset_ras_error_count(adev, i);
-}
-
-static const struct amdgpu_ras_block_hw_ops vcn_v4_0_3_ras_hw_ops = {
- .query_ras_error_count = vcn_v4_0_3_query_ras_error_count,
- .reset_ras_error_count = vcn_v4_0_3_reset_ras_error_count,
-};
-
-static struct amdgpu_vcn_ras vcn_v4_0_3_ras = {
- .ras_block = {
- .hw_ops = &vcn_v4_0_3_ras_hw_ops,
- },
-};
-
-static void vcn_v4_0_3_set_ras_funcs(struct amdgpu_device *adev)
-{
- adev->vcn.ras = &vcn_v4_0_3_ras;
-}
-
-static void vcn_v4_0_3_enable_ras(struct amdgpu_device *adev,
- int inst_idx, bool indirect)
-{
- uint32_t tmp;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN))
- return;
-
- tmp = VCN_RAS_CNTL__VCPU_VCODEC_REARM_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_IH_EN_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_PMI_EN_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_STALL_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regVCN_RAS_CNTL),
- tmp, 0, indirect);
-
- tmp = UVD_VCPU_INT_EN2__RASCNTL_VCPU_VCODEC_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regUVD_VCPU_INT_EN2),
- tmp, 0, indirect);
-
- tmp = UVD_SYS_INT_EN__RASCNTL_VCPU_VCODEC_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regUVD_SYS_INT_EN),
- tmp, 0, indirect);
-}
diff --git a/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage b/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage
deleted file mode 100644
index c55dabb2d37f..000000000000
--- a/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage
+++ /dev/null
@@ -1,1930 +0,0 @@
-/*
- * Copyright 2022 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/firmware.h>
-#include <drm/drm_drv.h>
-
-#include "amdgpu.h"
-#include "amdgpu_vcn.h"
-#include "amdgpu_pm.h"
-#include "soc15.h"
-#include "soc15d.h"
-#include "soc15_hw_ip.h"
-#include "vcn_v2_0.h"
-#include "mmsch_v4_0_3.h"
-
-#include "vcn/vcn_4_0_3_offset.h"
-#include "vcn/vcn_4_0_3_sh_mask.h"
-#include "ivsrcid/vcn/irqsrcs_vcn_4_0.h"
-
-#define mmUVD_DPG_LMA_CTL regUVD_DPG_LMA_CTL
-#define mmUVD_DPG_LMA_CTL_BASE_IDX regUVD_DPG_LMA_CTL_BASE_IDX
-#define mmUVD_DPG_LMA_DATA regUVD_DPG_LMA_DATA
-#define mmUVD_DPG_LMA_DATA_BASE_IDX regUVD_DPG_LMA_DATA_BASE_IDX
-
-#define VCN_VID_SOC_ADDRESS_2_0 0x1fb00
-#define VCN1_VID_SOC_ADDRESS_3_0 0x48300
-
-<<<<<<<
-=======
-static const struct amdgpu_hwip_reg_entry vcn_reg_list_4_0_3[] = {
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_POWER_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA0),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA1),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_CMD),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_PGFSM_CONFIG),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_PGFSM_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_CTL),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_DATA),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_MASK),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_PAUSE)
-};
-
->>>>>>>
-#define NORMALIZE_VCN_REG_OFFSET(offset) \
- (offset & 0x1FFFF)
-
-static int vcn_v4_0_3_start_sriov(struct amdgpu_device *adev);
-static void vcn_v4_0_3_set_unified_ring_funcs(struct amdgpu_device *adev);
-static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev);
-static int vcn_v4_0_3_set_powergating_state(void *handle,
- enum amd_powergating_state state);
-static int vcn_v4_0_3_pause_dpg_mode(struct amdgpu_device *adev,
- int inst_idx, struct dpg_pause_state *new_state);
-static void vcn_v4_0_3_unified_ring_set_wptr(struct amdgpu_ring *ring);
-static void vcn_v4_0_3_set_ras_funcs(struct amdgpu_device *adev);
-static void vcn_v4_0_3_enable_ras(struct amdgpu_device *adev,
- int inst_idx, bool indirect);
-/**
- * vcn_v4_0_3_early_init - set function pointers
- *
- * @handle: amdgpu_device pointer
- *
- * Set ring and irq function pointers
- */
-static int vcn_v4_0_3_early_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- /* re-use enc ring as unified ring */
- adev->vcn.num_enc_rings = 1;
-
- vcn_v4_0_3_set_unified_ring_funcs(adev);
- vcn_v4_0_3_set_irq_funcs(adev);
- vcn_v4_0_3_set_ras_funcs(adev);
-
- return amdgpu_vcn_early_init(adev);
-}
-
-/**
- * vcn_v4_0_3_sw_init - sw init for VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Load firmware and sw initialization
- */
-static int vcn_v4_0_3_sw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_ring *ring;
- int i, r, vcn_inst;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
- uint32_t *ptr;
-
- r = amdgpu_vcn_sw_init(adev);
- if (r)
- return r;
-
- amdgpu_vcn_setup_ucode(adev);
-
- r = amdgpu_vcn_resume(adev);
- if (r)
- return r;
-
- /* VCN DEC TRAP */
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_VCN,
- VCN_4_0__SRCID__UVD_ENC_GENERAL_PURPOSE, &adev->vcn.inst->irq);
- if (r)
- return r;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
-
- vcn_inst = GET_INST(VCN, i);
-
- ring = &adev->vcn.inst[i].ring_enc[0];
- ring->use_doorbell = true;
-
- if (!amdgpu_sriov_vf(adev))
- ring->doorbell_index =
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 9 * vcn_inst;
- else
- ring->doorbell_index =
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 32 * vcn_inst;
-
- ring->vm_hub = AMDGPU_MMHUB0(adev->vcn.inst[i].aid_id);
- sprintf(ring->name, "vcn_unified_%d", adev->vcn.inst[i].aid_id);
- r = amdgpu_ring_init(adev, ring, 512, &adev->vcn.inst->irq, 0,
- AMDGPU_RING_PRIO_DEFAULT,
- &adev->vcn.inst[i].sched_score);
- if (r)
- return r;
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->present_flag_0 = cpu_to_le32(AMDGPU_FW_SHARED_FLAG_0_UNIFIED_QUEUE);
- fw_shared->sq.is_enabled = true;
-
- if (amdgpu_vcnfw_log)
- amdgpu_vcn_fwlog_init(&adev->vcn.inst[i]);
- }
-
- if (amdgpu_sriov_vf(adev)) {
- r = amdgpu_virt_alloc_mm_table(adev);
- if (r)
- return r;
- }
-
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)
- adev->vcn.pause_dpg_mode = vcn_v4_0_3_pause_dpg_mode;
-
- if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- r = amdgpu_vcn_ras_sw_init(adev);
- if (r) {
- dev_err(adev->dev, "Failed to initialize vcn ras block!\n");
- return r;
- }
- }
-
- /* Allocate memory for VCN IP Dump buffer */
- ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL);
- if (!ptr) {
- DRM_ERROR("Failed to allocate memory for VCN IP Dump\n");
- adev->vcn.ip_dump = NULL;
- } else {
- adev->vcn.ip_dump = ptr;
- }
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_sw_fini - sw fini for VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * VCN suspend and free up sw allocation
- */
-static int vcn_v4_0_3_sw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, r, idx;
-
- if (drm_dev_enter(&adev->ddev, &idx)) {
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->present_flag_0 = 0;
- fw_shared->sq.is_enabled = cpu_to_le32(false);
- }
- drm_dev_exit(idx);
- }
-
- if (amdgpu_sriov_vf(adev))
- amdgpu_virt_free_mm_table(adev);
-
- r = amdgpu_vcn_suspend(adev);
- if (r)
- return r;
-
- r = amdgpu_vcn_sw_fini(adev);
-
- kfree(adev->vcn.ip_dump);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_hw_init - start and test VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Initialize the hardware, boot up the VCPU and do some testing
- */
-static int vcn_v4_0_3_hw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_ring *ring;
- int i, r, vcn_inst;
-
- if (amdgpu_sriov_vf(adev)) {
- r = vcn_v4_0_3_start_sriov(adev);
- if (r)
- return r;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ring = &adev->vcn.inst[i].ring_enc[0];
- ring->wptr = 0;
- ring->wptr_old = 0;
- vcn_v4_0_3_unified_ring_set_wptr(ring);
- ring->sched.ready = true;
- }
- } else {
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- vcn_inst = GET_INST(VCN, i);
- ring = &adev->vcn.inst[i].ring_enc[0];
-
- if (ring->use_doorbell) {
- adev->nbio.funcs->vcn_doorbell_range(
- adev, ring->use_doorbell,
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 9 * vcn_inst,
- adev->vcn.inst[i].aid_id);
-
- WREG32_SOC15(
- VCN, GET_INST(VCN, ring->me),
- regVCN_RB1_DB_CTRL,
- ring->doorbell_index
- << VCN_RB1_DB_CTRL__OFFSET__SHIFT |
- VCN_RB1_DB_CTRL__EN_MASK);
-
- /* Read DB_CTRL to flush the write DB_CTRL command. */
- RREG32_SOC15(
- VCN, GET_INST(VCN, ring->me),
- regVCN_RB1_DB_CTRL);
- }
-
- r = amdgpu_ring_test_helper(ring);
- if (r)
- return r;
- }
- }
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_hw_fini - stop the hardware block
- *
- * @handle: amdgpu_device pointer
- *
- * Stop the VCN block, mark ring as not ready any more
- */
-static int vcn_v4_0_3_hw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- cancel_delayed_work_sync(&adev->vcn.idle_work);
-
- if (adev->vcn.cur_state != AMD_PG_STATE_GATE)
- vcn_v4_0_3_set_powergating_state(adev, AMD_PG_STATE_GATE);
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_suspend - suspend VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * HW fini and suspend VCN block
- */
-static int vcn_v4_0_3_suspend(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = vcn_v4_0_3_hw_fini(adev);
- if (r)
- return r;
-
- r = amdgpu_vcn_suspend(adev);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_resume - resume VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Resume firmware and hw init VCN block
- */
-static int vcn_v4_0_3_resume(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = amdgpu_vcn_resume(adev);
- if (r)
- return r;
-
- r = vcn_v4_0_3_hw_init(adev);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_mc_resume - memory controller programming
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Let the VCN memory controller know it's offsets
- */
-static void vcn_v4_0_3_mc_resume(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t offset, size, vcn_inst;
- const struct common_firmware_header *hdr;
-
- hdr = (const struct common_firmware_header *)adev->vcn.fw[inst_idx]->data;
- size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);
-
- vcn_inst = GET_INST(VCN, inst_idx);
- /* cache window 0: fw */
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + inst_idx]
- .tmr_mc_addr_lo));
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + inst_idx]
- .tmr_mc_addr_hi));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET0, 0);
- offset = 0;
- } else {
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr));
- WREG32_SOC15(VCN, vcn_inst,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr));
- offset = size;
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET0,
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
- }
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE0, size);
-
- /* cache window 1: stack */
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset));
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET1, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE1,
- AMDGPU_VCN_STACK_SIZE);
-
- /* cache window 2: context */
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE));
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET2, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE2,
- AMDGPU_VCN_CONTEXT_SIZE);
-
- /* non-cache window */
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr));
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_NONCACHE_OFFSET0, 0);
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_VCPU_NONCACHE_SIZE0,
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)));
-}
-
-/**
- * vcn_v4_0_3_mc_resume_dpg_mode - memory controller programming for dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Let the VCN memory controller know it's offsets with dpg mode
- */
-static void vcn_v4_0_3_mc_resume_dpg_mode(struct amdgpu_device *adev, int inst_idx, bool indirect)
-{
- uint32_t offset, size;
- const struct common_firmware_header *hdr;
-
- hdr = (const struct common_firmware_header *)adev->vcn.fw[inst_idx]->data;
- size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);
-
- /* cache window 0: fw */
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- if (!indirect) {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN +
- inst_idx].tmr_mc_addr_lo), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN +
- inst_idx].tmr_mc_addr_hi), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0), 0, 0, indirect);
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0), 0, 0, indirect);
- }
- offset = 0;
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr), 0, indirect);
- offset = size;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0),
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3, 0, indirect);
- }
-
- if (!indirect)
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE0), size, 0, indirect);
- else
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE0), 0, 0, indirect);
-
- /* cache window 1: stack */
- if (!indirect) {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET1), 0, 0, indirect);
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET1), 0, 0, indirect);
- }
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE1), AMDGPU_VCN_STACK_SIZE, 0, indirect);
-
- /* cache window 2: context */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET2), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE2), AMDGPU_VCN_CONTEXT_SIZE, 0, indirect);
-
- /* non-cache window */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_NONCACHE_OFFSET0), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_NONCACHE_SIZE0),
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)), 0, indirect);
-
- /* VCN global tiling registers */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_GFX8_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_GFX10_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect);
-}
-
-/**
- * vcn_v4_0_3_disable_clock_gating - disable VCN clock gating
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Disable clock gating for VCN block
- */
-static void vcn_v4_0_3_disable_clock_gating(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t data;
- int vcn_inst;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* VCN disable CGC */
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK;
- data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_GATE);
- data &= ~(UVD_CGC_GATE__SYS_MASK
- | UVD_CGC_GATE__MPEG2_MASK
- | UVD_CGC_GATE__REGS_MASK
- | UVD_CGC_GATE__RBC_MASK
- | UVD_CGC_GATE__LMI_MC_MASK
- | UVD_CGC_GATE__LMI_UMC_MASK
- | UVD_CGC_GATE__MPC_MASK
- | UVD_CGC_GATE__LBSI_MASK
- | UVD_CGC_GATE__LRBBM_MASK
- | UVD_CGC_GATE__WCB_MASK
- | UVD_CGC_GATE__VCPU_MASK
- | UVD_CGC_GATE__MMSCH_MASK);
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_GATE, data);
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_CGC_GATE, 0, 0xFFFFFFFF);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data &= ~(UVD_CGC_CTRL__SYS_MODE_MASK
- | UVD_CGC_CTRL__MPEG2_MODE_MASK
- | UVD_CGC_CTRL__REGS_MODE_MASK
- | UVD_CGC_CTRL__RBC_MODE_MASK
- | UVD_CGC_CTRL__LMI_MC_MODE_MASK
- | UVD_CGC_CTRL__LMI_UMC_MODE_MASK
- | UVD_CGC_CTRL__MPC_MODE_MASK
- | UVD_CGC_CTRL__LBSI_MODE_MASK
- | UVD_CGC_CTRL__LRBBM_MODE_MASK
- | UVD_CGC_CTRL__WCB_MODE_MASK
- | UVD_CGC_CTRL__VCPU_MODE_MASK
- | UVD_CGC_CTRL__MMSCH_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_GATE);
- data |= (UVD_SUVD_CGC_GATE__SRE_MASK
- | UVD_SUVD_CGC_GATE__SIT_MASK
- | UVD_SUVD_CGC_GATE__SMP_MASK
- | UVD_SUVD_CGC_GATE__SCM_MASK
- | UVD_SUVD_CGC_GATE__SDB_MASK
- | UVD_SUVD_CGC_GATE__SRE_H264_MASK
- | UVD_SUVD_CGC_GATE__SRE_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SIT_H264_MASK
- | UVD_SUVD_CGC_GATE__SIT_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SCM_H264_MASK
- | UVD_SUVD_CGC_GATE__SCM_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SDB_H264_MASK
- | UVD_SUVD_CGC_GATE__SDB_HEVC_MASK
- | UVD_SUVD_CGC_GATE__ENT_MASK
- | UVD_SUVD_CGC_GATE__SIT_HEVC_DEC_MASK
- | UVD_SUVD_CGC_GATE__SITE_MASK
- | UVD_SUVD_CGC_GATE__SRE_VP9_MASK
- | UVD_SUVD_CGC_GATE__SCM_VP9_MASK
- | UVD_SUVD_CGC_GATE__SIT_VP9_DEC_MASK
- | UVD_SUVD_CGC_GATE__SDB_VP9_MASK
- | UVD_SUVD_CGC_GATE__IME_HEVC_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_GATE, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL);
- data &= ~(UVD_SUVD_CGC_CTRL__SRE_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SIT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SMP_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK
- | UVD_SUVD_CGC_CTRL__ENT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__IME_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SITE_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL, data);
-}
-
-/**
- * vcn_v4_0_3_disable_clock_gating_dpg_mode - disable VCN clock gating dpg mode
- *
- * @adev: amdgpu_device pointer
- * @sram_sel: sram select
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Disable clock gating for VCN block with dpg mode
- */
-static void vcn_v4_0_3_disable_clock_gating_dpg_mode(struct amdgpu_device *adev, uint8_t sram_sel,
- int inst_idx, uint8_t indirect)
-{
- uint32_t reg_data = 0;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- /* enable sw clock gating control */
- reg_data = 0 << UVD_CGC_CTRL__DYN_CLOCK_MODE__SHIFT;
- reg_data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- reg_data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- reg_data &= ~(UVD_CGC_CTRL__SYS_MODE_MASK |
- UVD_CGC_CTRL__MPEG2_MODE_MASK |
- UVD_CGC_CTRL__REGS_MODE_MASK |
- UVD_CGC_CTRL__RBC_MODE_MASK |
- UVD_CGC_CTRL__LMI_MC_MODE_MASK |
- UVD_CGC_CTRL__LMI_UMC_MODE_MASK |
- UVD_CGC_CTRL__IDCT_MODE_MASK |
- UVD_CGC_CTRL__MPRD_MODE_MASK |
- UVD_CGC_CTRL__MPC_MODE_MASK |
- UVD_CGC_CTRL__LBSI_MODE_MASK |
- UVD_CGC_CTRL__LRBBM_MODE_MASK |
- UVD_CGC_CTRL__WCB_MODE_MASK |
- UVD_CGC_CTRL__VCPU_MODE_MASK);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_CGC_CTRL), reg_data, sram_sel, indirect);
-
- /* turn off clock gating */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_CGC_GATE), 0, sram_sel, indirect);
-
- /* turn on SUVD clock gating */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_SUVD_CGC_GATE), 1, sram_sel, indirect);
-
- /* turn on sw mode in UVD_SUVD_CGC_CTRL */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_SUVD_CGC_CTRL), 0, sram_sel, indirect);
-}
-
-/**
- * vcn_v4_0_3_enable_clock_gating - enable VCN clock gating
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Enable clock gating for VCN block
- */
-static void vcn_v4_0_3_enable_clock_gating(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t data;
- int vcn_inst;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* enable VCN CGC */
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data |= 0 << UVD_CGC_CTRL__DYN_CLOCK_MODE__SHIFT;
- data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data |= (UVD_CGC_CTRL__SYS_MODE_MASK
- | UVD_CGC_CTRL__MPEG2_MODE_MASK
- | UVD_CGC_CTRL__REGS_MODE_MASK
- | UVD_CGC_CTRL__RBC_MODE_MASK
- | UVD_CGC_CTRL__LMI_MC_MODE_MASK
- | UVD_CGC_CTRL__LMI_UMC_MODE_MASK
- | UVD_CGC_CTRL__MPC_MODE_MASK
- | UVD_CGC_CTRL__LBSI_MODE_MASK
- | UVD_CGC_CTRL__LRBBM_MODE_MASK
- | UVD_CGC_CTRL__WCB_MODE_MASK
- | UVD_CGC_CTRL__VCPU_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL);
- data |= (UVD_SUVD_CGC_CTRL__SRE_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SIT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SMP_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK
- | UVD_SUVD_CGC_CTRL__ENT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__IME_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SITE_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL, data);
-}
-
-/**
- * vcn_v4_0_3_start_dpg_mode - VCN start with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Start VCN block with dpg mode
- */
-static int vcn_v4_0_3_start_dpg_mode(struct amdgpu_device *adev, int inst_idx, bool indirect)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared =
- adev->vcn.inst[inst_idx].fw_shared.cpu_addr;
- struct amdgpu_ring *ring;
- int vcn_inst;
- uint32_t tmp;
-
- vcn_inst = GET_INST(VCN, inst_idx);
- /* disable register anti-hang mechanism */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_POWER_STATUS), 1,
- ~UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
- /* enable dynamic power gating mode */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_POWER_STATUS);
- tmp |= UVD_POWER_STATUS__UVD_PG_MODE_MASK;
- tmp |= UVD_POWER_STATUS__UVD_PG_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_POWER_STATUS, tmp);
-
- if (indirect) {
- DRM_DEV_DEBUG(adev->dev, "VCN %d start: on AID %d",
- inst_idx, adev->vcn.inst[inst_idx].aid_id);
- adev->vcn.inst[inst_idx].dpg_sram_curr_addr =
- (uint32_t *)adev->vcn.inst[inst_idx].dpg_sram_cpu_addr;
- /* Use dummy register 0xDEADBEEF passing AID selection to PSP FW */
- WREG32_SOC15_DPG_MODE(inst_idx, 0xDEADBEEF,
- adev->vcn.inst[inst_idx].aid_id, 0, true);
- }
-
- /* enable clock gating */
- vcn_v4_0_3_disable_clock_gating_dpg_mode(adev, 0, inst_idx, indirect);
-
- /* enable VCPU clock */
- tmp = (0xFF << UVD_VCPU_CNTL__PRB_TIMEOUT_VAL__SHIFT);
- tmp |= UVD_VCPU_CNTL__CLK_EN_MASK;
- tmp |= UVD_VCPU_CNTL__BLK_RST_MASK;
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CNTL), tmp, 0, indirect);
-
- /* disable master interrupt */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MASTINT_EN), 0, 0, indirect);
-
- /* setup regUVD_LMI_CTRL */
- tmp = (UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
- UVD_LMI_CTRL__REQ_MODE_MASK |
- UVD_LMI_CTRL__CRC_RESET_MASK |
- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK |
- (8 << UVD_LMI_CTRL__WRITE_CLEAN_TIMER__SHIFT) |
- 0x00100000L);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_CTRL), tmp, 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_CNTL),
- 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT, 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUXA0),
- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)), 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUXB0),
- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)), 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUX),
- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)), 0, indirect);
-
- vcn_v4_0_3_mc_resume_dpg_mode(adev, inst_idx, indirect);
-
- tmp = (0xFF << UVD_VCPU_CNTL__PRB_TIMEOUT_VAL__SHIFT);
- tmp |= UVD_VCPU_CNTL__CLK_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CNTL), tmp, 0, indirect);
-
- /* enable LMI MC and UMC channels */
- tmp = 0x1f << UVD_LMI_CTRL2__RE_OFLD_MIF_WR_REQ_NUM__SHIFT;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_CTRL2), tmp, 0, indirect);
-
- vcn_v4_0_3_enable_ras(adev, inst_idx, indirect);
-
- /* enable master interrupt */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MASTINT_EN),
- UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect);
-
- if (indirect)
- amdgpu_vcn_psp_update_sram(adev, inst_idx, AMDGPU_UCODE_ID_VCN0_RAM);
-
- ring = &adev->vcn.inst[inst_idx].ring_enc[0];
-
- /* program the RB_BASE for ring buffer */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_LO,
- lower_32_bits(ring->gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_HI,
- upper_32_bits(ring->gpu_addr));
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_SIZE,
- ring->ring_size / sizeof(uint32_t));
-
- /* resetting ring, fw should not check RB ring */
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp &= ~(VCN_RB_ENABLE__RB_EN_MASK);
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
- fw_shared->sq.queue_mode |= FW_QUEUE_RING_RESET;
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_RPTR, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR, 0);
- ring->wptr = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp |= VCN_RB_ENABLE__RB_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
- fw_shared->sq.queue_mode &= ~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF);
-
- /*resetting done, fw can check RB ring */
- fw_shared->sq.queue_mode &= cpu_to_le32(~FW_QUEUE_RING_RESET);
-
- return 0;
-}
-
-static int vcn_v4_0_3_start_sriov(struct amdgpu_device *adev)
-{
- int i, vcn_inst;
- struct amdgpu_ring *ring_enc;
- uint64_t cache_addr;
- uint64_t rb_enc_addr;
- uint64_t ctx_addr;
- uint32_t param, resp, expected;
- uint32_t offset, cache_size;
- uint32_t tmp, timeout;
-
- struct amdgpu_mm_table *table = &adev->virt.mm_table;
- uint32_t *table_loc;
- uint32_t table_size;
- uint32_t size, size_dw;
- uint32_t init_status;
- uint32_t enabled_vcn;
-
- struct mmsch_v4_0_cmd_direct_write
- direct_wt = { {0} };
- struct mmsch_v4_0_cmd_direct_read_modify_write
- direct_rd_mod_wt = { {0} };
- struct mmsch_v4_0_cmd_end end = { {0} };
- struct mmsch_v4_0_3_init_header header;
-
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- volatile struct amdgpu_fw_shared_rb_setup *rb_setup;
-
- direct_wt.cmd_header.command_type =
- MMSCH_COMMAND__DIRECT_REG_WRITE;
- direct_rd_mod_wt.cmd_header.command_type =
- MMSCH_COMMAND__DIRECT_REG_READ_MODIFY_WRITE;
- end.cmd_header.command_type = MMSCH_COMMAND__END;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- vcn_inst = GET_INST(VCN, i);
-
- memset(&header, 0, sizeof(struct mmsch_v4_0_3_init_header));
- header.version = MMSCH_VERSION;
- header.total_size = sizeof(struct mmsch_v4_0_3_init_header) >> 2;
-
- table_loc = (uint32_t *)table->cpu_addr;
- table_loc += header.total_size;
-
- table_size = 0;
-
- MMSCH_V4_0_INSERT_DIRECT_RD_MOD_WT(SOC15_REG_OFFSET(VCN, 0, regUVD_STATUS),
- ~UVD_STATUS__UVD_BUSY, UVD_STATUS__UVD_BUSY);
-
- cache_size = AMDGPU_GPU_PAGE_ALIGN(adev->vcn.fw[i]->size + 4);
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + i].tmr_mc_addr_lo);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + i].tmr_mc_addr_hi);
-
- offset = 0;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET0), 0);
- } else {
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[i].gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[i].gpu_addr));
- offset = cache_size;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET0),
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
- }
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE0),
- cache_size);
-
- cache_addr = adev->vcn.inst[vcn_inst].gpu_addr + offset;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW), lower_32_bits(cache_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH), upper_32_bits(cache_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET1), 0);
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE1), AMDGPU_VCN_STACK_SIZE);
-
- cache_addr = adev->vcn.inst[vcn_inst].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE;
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW), lower_32_bits(cache_addr));
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH), upper_32_bits(cache_addr));
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET2), 0);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE2), AMDGPU_VCN_CONTEXT_SIZE);
-
- fw_shared = adev->vcn.inst[vcn_inst].fw_shared.cpu_addr;
- rb_setup = &fw_shared->rb_setup;
-
- ring_enc = &adev->vcn.inst[vcn_inst].ring_enc[0];
- ring_enc->wptr = 0;
- rb_enc_addr = ring_enc->gpu_addr;
-
- rb_setup->is_rb_enabled_flags |= RB_ENABLED;
- rb_setup->rb_addr_lo = lower_32_bits(rb_enc_addr);
- rb_setup->rb_addr_hi = upper_32_bits(rb_enc_addr);
- rb_setup->rb_size = ring_enc->ring_size / 4;
- fw_shared->present_flag_0 |= cpu_to_le32(AMDGPU_VCN_VF_RB_SETUP_FLAG);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[vcn_inst].fw_shared.gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[vcn_inst].fw_shared.gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_NONCACHE_SIZE0),
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)));
- MMSCH_V4_0_INSERT_END();
-
- header.vcn0.init_status = 0;
- header.vcn0.table_offset = header.total_size;
- header.vcn0.table_size = table_size;
- header.total_size += table_size;
-
- /* Send init table to mmsch */
- size = sizeof(struct mmsch_v4_0_3_init_header);
- table_loc = (uint32_t *)table->cpu_addr;
- memcpy((void *)table_loc, &header, size);
-
- ctx_addr = table->gpu_addr;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_ADDR_LO, lower_32_bits(ctx_addr));
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_ADDR_HI, upper_32_bits(ctx_addr));
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_VMID);
- tmp &= ~MMSCH_VF_VMID__VF_CTX_VMID_MASK;
- tmp |= (0 << MMSCH_VF_VMID__VF_CTX_VMID__SHIFT);
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_VMID, tmp);
-
- size = header.total_size;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_SIZE, size);
-
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_RESP, 0);
-
- param = 0x00000001;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_HOST, param);
- tmp = 0;
- timeout = 1000;
- resp = 0;
- expected = MMSCH_VF_MAILBOX_RESP__OK;
- while (resp != expected) {
- resp = RREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_RESP);
- if (resp != 0)
- break;
-
- udelay(10);
- tmp = tmp + 10;
- if (tmp >= timeout) {
- DRM_ERROR("failed to init MMSCH. TIME-OUT after %d usec"\
- " waiting for regMMSCH_VF_MAILBOX_RESP "\
- "(expected=0x%08x, readback=0x%08x)\n",
- tmp, expected, resp);
- return -EBUSY;
- }
- }
-
- enabled_vcn = amdgpu_vcn_is_disabled_vcn(adev, VCN_DECODE_RING, 0) ? 1 : 0;
- init_status = ((struct mmsch_v4_0_3_init_header *)(table_loc))->vcn0.init_status;
- if (resp != expected && resp != MMSCH_VF_MAILBOX_RESP__INCOMPLETE
- && init_status != MMSCH_VF_ENGINE_STATUS__PASS) {
- DRM_ERROR("MMSCH init status is incorrect! readback=0x%08x, header init "\
- "status for VCN%x: 0x%x\n", resp, enabled_vcn, init_status);
- }
- }
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_start - VCN start
- *
- * @adev: amdgpu_device pointer
- *
- * Start VCN block
- */
-static int vcn_v4_0_3_start(struct amdgpu_device *adev)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- struct amdgpu_ring *ring;
- int i, j, k, r, vcn_inst;
- uint32_t tmp;
-
- if (adev->pm.dpm_enabled)
- amdgpu_dpm_enable_uvd(adev, true);
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
- r = vcn_v4_0_3_start_dpg_mode(adev, i, adev->vcn.indirect_sram);
- continue;
- }
-
- vcn_inst = GET_INST(VCN, i);
- /* set VCN status busy */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_STATUS) |
- UVD_STATUS__UVD_BUSY;
- WREG32_SOC15(VCN, vcn_inst, regUVD_STATUS, tmp);
-
- /*SW clock gating */
- vcn_v4_0_3_disable_clock_gating(adev, i);
-
- /* enable VCPU clock */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__CLK_EN_MASK,
- ~UVD_VCPU_CNTL__CLK_EN_MASK);
-
- /* disable master interrupt */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_MASTINT_EN), 0,
- ~UVD_MASTINT_EN__VCPU_EN_MASK);
-
- /* enable LMI MC and UMC channels */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_LMI_CTRL2), 0,
- ~UVD_LMI_CTRL2__STALL_ARB_UMC_MASK);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp &= ~UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
- tmp &= ~UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- /* setup regUVD_LMI_CTRL */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL);
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL,
- tmp | UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK);
-
- /* setup regUVD_MPC_CNTL */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_MPC_CNTL);
- tmp &= ~UVD_MPC_CNTL__REPLACEMENT_MODE_MASK;
- tmp |= 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_CNTL, tmp);
-
- /* setup UVD_MPC_SET_MUXA0 */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUXA0,
- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)));
-
- /* setup UVD_MPC_SET_MUXB0 */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUXB0,
- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)));
-
- /* setup UVD_MPC_SET_MUX */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUX,
- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)));
-
- vcn_v4_0_3_mc_resume(adev, i);
-
- /* VCN global tiling registers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_GFX8_ADDR_CONFIG,
- adev->gfx.config.gb_addr_config);
- WREG32_SOC15(VCN, vcn_inst, regUVD_GFX10_ADDR_CONFIG,
- adev->gfx.config.gb_addr_config);
-
- /* unblock VCPU register access */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_RB_ARB_CTRL), 0,
- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
-
- /* release VCPU reset to boot */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL), 0,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- for (j = 0; j < 10; ++j) {
- uint32_t status;
-
- for (k = 0; k < 100; ++k) {
- status = RREG32_SOC15(VCN, vcn_inst,
- regUVD_STATUS);
- if (status & 2)
- break;
- mdelay(10);
- }
- r = 0;
- if (status & 2)
- break;
-
- DRM_DEV_ERROR(adev->dev,
- "VCN decode not responding, trying to reset the VCPU!!!\n");
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst,
- regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__BLK_RST_MASK,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
- mdelay(10);
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst,
- regUVD_VCPU_CNTL),
- 0, ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- mdelay(10);
- r = -1;
- }
-
- if (r) {
- DRM_DEV_ERROR(adev->dev, "VCN decode not responding, giving up!!!\n");
- return r;
- }
-
- /* enable master interrupt */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_MASTINT_EN),
- UVD_MASTINT_EN__VCPU_EN_MASK,
- ~UVD_MASTINT_EN__VCPU_EN_MASK);
-
- /* clear the busy bit of VCN_STATUS */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_STATUS), 0,
- ~(2 << UVD_STATUS__VCPU_REPORT__SHIFT));
-
- ring = &adev->vcn.inst[i].ring_enc[0];
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
-
- /* program the RB_BASE for ring buffer */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_LO,
- lower_32_bits(ring->gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_HI,
- upper_32_bits(ring->gpu_addr));
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_SIZE,
- ring->ring_size / sizeof(uint32_t));
-
- /* resetting ring, fw should not check RB ring */
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp &= ~(VCN_RB_ENABLE__RB_EN_MASK);
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_RPTR, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR, 0);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp |= VCN_RB_ENABLE__RB_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
-
- ring->wptr = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
- fw_shared->sq.queue_mode &=
- cpu_to_le32(~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF));
-
- }
- return 0;
-}
-
-/**
- * vcn_v4_0_3_stop_dpg_mode - VCN stop with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- *
- * Stop VCN block with dpg mode
- */
-static int vcn_v4_0_3_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t tmp;
- int vcn_inst;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* Wait for power status to be 1 */
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_POWER_STATUS, 1,
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
-
- /* wait for read ptr to be equal to write ptr */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_RB_RPTR, tmp, 0xFFFFFFFF);
-
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_POWER_STATUS, 1,
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
-
- /* disable dynamic power gating mode */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_POWER_STATUS), 0,
- ~UVD_POWER_STATUS__UVD_PG_MODE_MASK);
- return 0;
-}
-
-/**
- * vcn_v4_0_3_stop - VCN stop
- *
- * @adev: amdgpu_device pointer
- *
- * Stop VCN block
- */
-static int vcn_v4_0_3_stop(struct amdgpu_device *adev)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- int i, r = 0, vcn_inst;
- uint32_t tmp;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- vcn_inst = GET_INST(VCN, i);
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->sq.queue_mode |= FW_QUEUE_DPG_HOLD_OFF;
-
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
- vcn_v4_0_3_stop_dpg_mode(adev, i);
- continue;
- }
-
- /* wait for vcn idle */
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_STATUS,
- UVD_STATUS__IDLE, 0x7);
- if (r)
- goto Done;
-
- tmp = UVD_LMI_STATUS__VCPU_LMI_WRITE_CLEAN_MASK |
- UVD_LMI_STATUS__READ_CLEAN_MASK |
- UVD_LMI_STATUS__WRITE_CLEAN_MASK |
- UVD_LMI_STATUS__WRITE_CLEAN_RAW_MASK;
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_LMI_STATUS, tmp,
- tmp);
- if (r)
- goto Done;
-
- /* stall UMC channel */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL2);
- tmp |= UVD_LMI_CTRL2__STALL_ARB_UMC_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL2, tmp);
- tmp = UVD_LMI_STATUS__UMC_READ_CLEAN_RAW_MASK |
- UVD_LMI_STATUS__UMC_WRITE_CLEAN_RAW_MASK;
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_LMI_STATUS, tmp,
- tmp);
- if (r)
- goto Done;
-
- /* Unblock VCPU Register access */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_RB_ARB_CTRL),
- UVD_RB_ARB_CTRL__VCPU_DIS_MASK,
- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
-
- /* release VCPU reset to boot */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__BLK_RST_MASK,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- /* disable VCPU clock */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL), 0,
- ~(UVD_VCPU_CNTL__CLK_EN_MASK));
-
- /* reset LMI UMC/LMI/VCPU */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp |= UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp |= UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- /* clear VCN status */
- WREG32_SOC15(VCN, vcn_inst, regUVD_STATUS, 0);
-
- /* apply HW clock gating */
- vcn_v4_0_3_enable_clock_gating(adev, i);
- }
-Done:
- if (adev->pm.dpm_enabled)
- amdgpu_dpm_enable_uvd(adev, false);
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_pause_dpg_mode - VCN pause with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @new_state: pause state
- *
- * Pause dpg mode for VCN block
- */
-static int vcn_v4_0_3_pause_dpg_mode(struct amdgpu_device *adev, int inst_idx,
- struct dpg_pause_state *new_state)
-{
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_unified_ring_get_rptr - get unified read pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Returns the current hardware unified read pointer
- */
-static uint64_t vcn_v4_0_3_unified_ring_get_rptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- return RREG32_SOC15(VCN, GET_INST(VCN, ring->me), regUVD_RB_RPTR);
-}
-
-/**
- * vcn_v4_0_3_unified_ring_get_wptr - get unified write pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Returns the current hardware unified write pointer
- */
-static uint64_t vcn_v4_0_3_unified_ring_get_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- if (ring->use_doorbell)
- return *ring->wptr_cpu_addr;
- else
- return RREG32_SOC15(VCN, GET_INST(VCN, ring->me),
- regUVD_RB_WPTR);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_reg_wait(struct amdgpu_ring *ring, uint32_t reg,
- uint32_t val, uint32_t mask)
-{
- /* For VF, only local offsets should be used */
- if (amdgpu_sriov_vf(ring->adev))
- reg = NORMALIZE_VCN_REG_OFFSET(reg);
-
- amdgpu_ring_write(ring, VCN_ENC_CMD_REG_WAIT);
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, mask);
- amdgpu_ring_write(ring, val);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg, uint32_t val)
-{
- /* For VF, only local offsets should be used */
- if (amdgpu_sriov_vf(ring->adev))
- reg = NORMALIZE_VCN_REG_OFFSET(reg);
-
- amdgpu_ring_write(ring, VCN_ENC_CMD_REG_WRITE);
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, val);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_vm_flush(struct amdgpu_ring *ring,
- unsigned int vmid, uint64_t pd_addr)
-{
- struct amdgpu_vmhub *hub = &ring->adev->vmhub[ring->vm_hub];
-
- pd_addr = amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
-
- /* wait for reg writes */
- vcn_v4_0_3_enc_ring_emit_reg_wait(ring, hub->ctx0_ptb_addr_lo32 +
- vmid * hub->ctx_addr_distance,
- lower_32_bits(pd_addr), 0xffffffff);
-}
-
-static void vcn_v4_0_3_ring_emit_hdp_flush(struct amdgpu_ring *ring)
-{
- /* VCN engine access for HDP flush doesn't work when RRMT is enabled.
- * This is a workaround to avoid any HDP flush through VCN ring.
- */
-}
-
-/**
- * vcn_v4_0_3_unified_ring_set_wptr - set enc write pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Commits the enc write pointer to the hardware
- */
-static void vcn_v4_0_3_unified_ring_set_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- if (ring->use_doorbell) {
- *ring->wptr_cpu_addr = lower_32_bits(ring->wptr);
- WDOORBELL32(ring->doorbell_index, lower_32_bits(ring->wptr));
- } else {
- WREG32_SOC15(VCN, GET_INST(VCN, ring->me), regUVD_RB_WPTR,
- lower_32_bits(ring->wptr));
- }
-}
-
-static const struct amdgpu_ring_funcs vcn_v4_0_3_unified_ring_vm_funcs = {
- .type = AMDGPU_RING_TYPE_VCN_ENC,
- .align_mask = 0x3f,
- .nop = VCN_ENC_CMD_NO_OP,
- .get_rptr = vcn_v4_0_3_unified_ring_get_rptr,
- .get_wptr = vcn_v4_0_3_unified_ring_get_wptr,
- .set_wptr = vcn_v4_0_3_unified_ring_set_wptr,
- .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_v4_0_3_enc_ring_emit_vm_flush,
- .emit_hdp_flush = vcn_v4_0_3_ring_emit_hdp_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_v4_0_3_enc_ring_emit_wreg,
- .emit_reg_wait = vcn_v4_0_3_enc_ring_emit_reg_wait,
- .emit_reg_write_reg_wait = amdgpu_ring_emit_reg_write_reg_wait_helper,
-};
-
-/**
- * vcn_v4_0_3_set_unified_ring_funcs - set unified ring functions
- *
- * @adev: amdgpu_device pointer
- *
- * Set unified ring functions
- */
-static void vcn_v4_0_3_set_unified_ring_funcs(struct amdgpu_device *adev)
-{
- int i, vcn_inst;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- adev->vcn.inst[i].ring_enc[0].funcs = &vcn_v4_0_3_unified_ring_vm_funcs;
- adev->vcn.inst[i].ring_enc[0].me = i;
- vcn_inst = GET_INST(VCN, i);
- adev->vcn.inst[i].aid_id =
- vcn_inst / adev->vcn.num_inst_per_aid;
- }
-}
-
-/**
- * vcn_v4_0_3_is_idle - check VCN block is idle
- *
- * @handle: amdgpu_device pointer
- *
- * Check whether VCN block is idle
- */
-static bool vcn_v4_0_3_is_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, ret = 1;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ret &= (RREG32_SOC15(VCN, GET_INST(VCN, i), regUVD_STATUS) ==
- UVD_STATUS__IDLE);
- }
-
- return ret;
-}
-
-/**
- * vcn_v4_0_3_wait_for_idle - wait for VCN block idle
- *
- * @handle: amdgpu_device pointer
- *
- * Wait for VCN block idle
- */
-static int vcn_v4_0_3_wait_for_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, ret = 0;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ret = SOC15_WAIT_ON_RREG(VCN, GET_INST(VCN, i), regUVD_STATUS,
- UVD_STATUS__IDLE, UVD_STATUS__IDLE);
- if (ret)
- return ret;
- }
-
- return ret;
-}
-
-/* vcn_v4_0_3_set_clockgating_state - set VCN block clockgating state
- *
- * @handle: amdgpu_device pointer
- * @state: clock gating state
- *
- * Set VCN block clockgating state
- */
-static int vcn_v4_0_3_set_clockgating_state(void *handle,
- enum amd_clockgating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- bool enable = state == AMD_CG_STATE_GATE;
- int i;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- if (enable) {
- if (RREG32_SOC15(VCN, GET_INST(VCN, i),
- regUVD_STATUS) != UVD_STATUS__IDLE)
- return -EBUSY;
- vcn_v4_0_3_enable_clock_gating(adev, i);
- } else {
- vcn_v4_0_3_disable_clock_gating(adev, i);
- }
- }
- return 0;
-}
-
-/**
- * vcn_v4_0_3_set_powergating_state - set VCN block powergating state
- *
- * @handle: amdgpu_device pointer
- * @state: power gating state
- *
- * Set VCN block powergating state
- */
-static int vcn_v4_0_3_set_powergating_state(void *handle,
- enum amd_powergating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int ret;
-
- /* for SRIOV, guest should not control VCN Power-gating
- * MMSCH FW should control Power-gating and clock-gating
- * guest should avoid touching CGC and PG
- */
- if (amdgpu_sriov_vf(adev)) {
- adev->vcn.cur_state = AMD_PG_STATE_UNGATE;
- return 0;
- }
-
- if (state == adev->vcn.cur_state)
- return 0;
-
- if (state == AMD_PG_STATE_GATE)
- ret = vcn_v4_0_3_stop(adev);
- else
- ret = vcn_v4_0_3_start(adev);
-
- if (!ret)
- adev->vcn.cur_state = state;
-
- return ret;
-}
-
-/**
- * vcn_v4_0_3_set_interrupt_state - set VCN block interrupt state
- *
- * @adev: amdgpu_device pointer
- * @source: interrupt sources
- * @type: interrupt types
- * @state: interrupt states
- *
- * Set VCN block interrupt state
- */
-static int vcn_v4_0_3_set_interrupt_state(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- unsigned int type,
- enum amdgpu_interrupt_state state)
-{
- return 0;
-}
-
-/**
- * vcn_v4_0_3_process_interrupt - process VCN block interrupt
- *
- * @adev: amdgpu_device pointer
- * @source: interrupt sources
- * @entry: interrupt entry from clients and sources
- *
- * Process VCN block interrupt
- */
-static int vcn_v4_0_3_process_interrupt(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- struct amdgpu_iv_entry *entry)
-{
- uint32_t i, inst;
-
- i = node_id_to_phys_map[entry->node_id];
-
- DRM_DEV_DEBUG(adev->dev, "IH: VCN TRAP\n");
-
- for (inst = 0; inst < adev->vcn.num_vcn_inst; ++inst)
- if (adev->vcn.inst[inst].aid_id == i)
- break;
-
- if (inst >= adev->vcn.num_vcn_inst) {
- dev_WARN_ONCE(adev->dev, 1,
- "Interrupt received for unknown VCN instance %d",
- entry->node_id);
- return 0;
- }
-
- switch (entry->src_id) {
- case VCN_4_0__SRCID__UVD_ENC_GENERAL_PURPOSE:
- amdgpu_fence_process(&adev->vcn.inst[inst].ring_enc[0]);
- break;
- default:
- DRM_DEV_ERROR(adev->dev, "Unhandled interrupt: %d %d\n",
- entry->src_id, entry->src_data[0]);
- break;
- }
-
- return 0;
-}
-
-static const struct amdgpu_irq_src_funcs vcn_v4_0_3_irq_funcs = {
- .set = vcn_v4_0_3_set_interrupt_state,
- .process = vcn_v4_0_3_process_interrupt,
-};
-
-/**
- * vcn_v4_0_3_set_irq_funcs - set VCN block interrupt irq functions
- *
- * @adev: amdgpu_device pointer
- *
- * Set VCN block interrupt irq functions
- */
-static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev)
-{
- int i;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- adev->vcn.inst->irq.num_types++;
- }
- adev->vcn.inst->irq.funcs = &vcn_v4_0_3_irq_funcs;
-}
-
-static void vcn_v4_0_3_print_ip_state(void *handle, struct drm_printer *p)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
- uint32_t inst_off, is_powered;
-
- if (!adev->vcn.ip_dump)
- return;
-
- drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst);
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- if (adev->vcn.harvest_config & (1 << i)) {
- drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i);
- continue;
- }
-
- inst_off = i * reg_count;
- is_powered = (adev->vcn.ip_dump[inst_off] &
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1;
-
- if (is_powered) {
- drm_printf(p, "\nActive Instance:VCN%d\n", i);
- for (j = 0; j < reg_count; j++)
- drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_4_0_3[j].reg_name,
- adev->vcn.ip_dump[inst_off + j]);
- } else {
- drm_printf(p, "\nInactive Instance:VCN%d\n", i);
- }
- }
-}
-
-static void vcn_v4_0_3_dump_ip_state(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- bool is_powered;
- uint32_t inst_off, inst_id;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
-
- if (!adev->vcn.ip_dump)
- return;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- if (adev->vcn.harvest_config & (1 << i))
- continue;
-
- inst_id = GET_INST(VCN, i);
- inst_off = i * reg_count;
- /* mmUVD_POWER_STATUS is always readable and is first element of the array */
- adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, inst_id, regUVD_POWER_STATUS);
- is_powered = (adev->vcn.ip_dump[inst_off] &
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1;
-
- if (is_powered)
- for (j = 1; j < reg_count; j++)
- adev->vcn.ip_dump[inst_off + j] =
- RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_4_0_3[j],
- inst_id));
- }
-}
-
-static const struct amd_ip_funcs vcn_v4_0_3_ip_funcs = {
- .name = "vcn_v4_0_3",
- .early_init = vcn_v4_0_3_early_init,
- .late_init = NULL,
- .sw_init = vcn_v4_0_3_sw_init,
- .sw_fini = vcn_v4_0_3_sw_fini,
- .hw_init = vcn_v4_0_3_hw_init,
- .hw_fini = vcn_v4_0_3_hw_fini,
- .suspend = vcn_v4_0_3_suspend,
- .resume = vcn_v4_0_3_resume,
- .is_idle = vcn_v4_0_3_is_idle,
- .wait_for_idle = vcn_v4_0_3_wait_for_idle,
- .check_soft_reset = NULL,
- .pre_soft_reset = NULL,
- .soft_reset = NULL,
- .post_soft_reset = NULL,
- .set_clockgating_state = vcn_v4_0_3_set_clockgating_state,
- .set_powergating_state = vcn_v4_0_3_set_powergating_state,
- .dump_ip_state = vcn_v4_0_3_dump_ip_state,
- .print_ip_state = vcn_v4_0_3_print_ip_state,
-};
-
-const struct amdgpu_ip_block_version vcn_v4_0_3_ip_block = {
- .type = AMD_IP_BLOCK_TYPE_VCN,
- .major = 4,
- .minor = 0,
- .rev = 3,
- .funcs = &vcn_v4_0_3_ip_funcs,
-};
-
-static const struct amdgpu_ras_err_status_reg_entry vcn_v4_0_3_ue_reg_list[] = {
- {AMDGPU_RAS_REG_ENTRY(VCN, 0, regVCN_UE_ERR_STATUS_LO_VIDD, regVCN_UE_ERR_STATUS_HI_VIDD),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "VIDD"},
- {AMDGPU_RAS_REG_ENTRY(VCN, 0, regVCN_UE_ERR_STATUS_LO_VIDV, regVCN_UE_ERR_STATUS_HI_VIDV),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "VIDV"},
-};
-
-static void vcn_v4_0_3_inst_query_ras_error_count(struct amdgpu_device *adev,
- uint32_t vcn_inst,
- void *ras_err_status)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_err_status;
-
- /* vcn v4_0_3 only support query uncorrectable errors */
- amdgpu_ras_inst_query_ras_error_count(adev,
- vcn_v4_0_3_ue_reg_list,
- ARRAY_SIZE(vcn_v4_0_3_ue_reg_list),
- NULL, 0, GET_INST(VCN, vcn_inst),
- AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE,
- &err_data->ue_count);
-}
-
-static void vcn_v4_0_3_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_err_status)
-{
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- dev_warn(adev->dev, "VCN RAS is not supported\n");
- return;
- }
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++)
- vcn_v4_0_3_inst_query_ras_error_count(adev, i, ras_err_status);
-}
-
-static void vcn_v4_0_3_inst_reset_ras_error_count(struct amdgpu_device *adev,
- uint32_t vcn_inst)
-{
- amdgpu_ras_inst_reset_ras_error_count(adev,
- vcn_v4_0_3_ue_reg_list,
- ARRAY_SIZE(vcn_v4_0_3_ue_reg_list),
- GET_INST(VCN, vcn_inst));
-}
-
-static void vcn_v4_0_3_reset_ras_error_count(struct amdgpu_device *adev)
-{
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- dev_warn(adev->dev, "VCN RAS is not supported\n");
- return;
- }
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++)
- vcn_v4_0_3_inst_reset_ras_error_count(adev, i);
-}
-
-static const struct amdgpu_ras_block_hw_ops vcn_v4_0_3_ras_hw_ops = {
- .query_ras_error_count = vcn_v4_0_3_query_ras_error_count,
- .reset_ras_error_count = vcn_v4_0_3_reset_ras_error_count,
-};
-
-static struct amdgpu_vcn_ras vcn_v4_0_3_ras = {
- .ras_block = {
- .hw_ops = &vcn_v4_0_3_ras_hw_ops,
- },
-};
-
-static void vcn_v4_0_3_set_ras_funcs(struct amdgpu_device *adev)
-{
- adev->vcn.ras = &vcn_v4_0_3_ras;
-}
-
-static void vcn_v4_0_3_enable_ras(struct amdgpu_device *adev,
- int inst_idx, bool indirect)
-{
- uint32_t tmp;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN))
- return;
-
- tmp = VCN_RAS_CNTL__VCPU_VCODEC_REARM_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_IH_EN_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_PMI_EN_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_STALL_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regVCN_RAS_CNTL),
- tmp, 0, indirect);
-
- tmp = UVD_VCPU_INT_EN2__RASCNTL_VCPU_VCODEC_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regUVD_VCPU_INT_EN2),
- tmp, 0, indirect);
-
- tmp = UVD_SYS_INT_EN__RASCNTL_VCPU_VCODEC_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regUVD_SYS_INT_EN),
- tmp, 0, indirect);
-}
diff --git a/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage.1 b/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage.1
deleted file mode 100644
index c55dabb2d37f..000000000000
--- a/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage.1
+++ /dev/null
@@ -1,1930 +0,0 @@
-/*
- * Copyright 2022 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/firmware.h>
-#include <drm/drm_drv.h>
-
-#include "amdgpu.h"
-#include "amdgpu_vcn.h"
-#include "amdgpu_pm.h"
-#include "soc15.h"
-#include "soc15d.h"
-#include "soc15_hw_ip.h"
-#include "vcn_v2_0.h"
-#include "mmsch_v4_0_3.h"
-
-#include "vcn/vcn_4_0_3_offset.h"
-#include "vcn/vcn_4_0_3_sh_mask.h"
-#include "ivsrcid/vcn/irqsrcs_vcn_4_0.h"
-
-#define mmUVD_DPG_LMA_CTL regUVD_DPG_LMA_CTL
-#define mmUVD_DPG_LMA_CTL_BASE_IDX regUVD_DPG_LMA_CTL_BASE_IDX
-#define mmUVD_DPG_LMA_DATA regUVD_DPG_LMA_DATA
-#define mmUVD_DPG_LMA_DATA_BASE_IDX regUVD_DPG_LMA_DATA_BASE_IDX
-
-#define VCN_VID_SOC_ADDRESS_2_0 0x1fb00
-#define VCN1_VID_SOC_ADDRESS_3_0 0x48300
-
-<<<<<<<
-=======
-static const struct amdgpu_hwip_reg_entry vcn_reg_list_4_0_3[] = {
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_POWER_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA0),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA1),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_CMD),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_PGFSM_CONFIG),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_PGFSM_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_CTL),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_DATA),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_MASK),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_PAUSE)
-};
-
->>>>>>>
-#define NORMALIZE_VCN_REG_OFFSET(offset) \
- (offset & 0x1FFFF)
-
-static int vcn_v4_0_3_start_sriov(struct amdgpu_device *adev);
-static void vcn_v4_0_3_set_unified_ring_funcs(struct amdgpu_device *adev);
-static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev);
-static int vcn_v4_0_3_set_powergating_state(void *handle,
- enum amd_powergating_state state);
-static int vcn_v4_0_3_pause_dpg_mode(struct amdgpu_device *adev,
- int inst_idx, struct dpg_pause_state *new_state);
-static void vcn_v4_0_3_unified_ring_set_wptr(struct amdgpu_ring *ring);
-static void vcn_v4_0_3_set_ras_funcs(struct amdgpu_device *adev);
-static void vcn_v4_0_3_enable_ras(struct amdgpu_device *adev,
- int inst_idx, bool indirect);
-/**
- * vcn_v4_0_3_early_init - set function pointers
- *
- * @handle: amdgpu_device pointer
- *
- * Set ring and irq function pointers
- */
-static int vcn_v4_0_3_early_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- /* re-use enc ring as unified ring */
- adev->vcn.num_enc_rings = 1;
-
- vcn_v4_0_3_set_unified_ring_funcs(adev);
- vcn_v4_0_3_set_irq_funcs(adev);
- vcn_v4_0_3_set_ras_funcs(adev);
-
- return amdgpu_vcn_early_init(adev);
-}
-
-/**
- * vcn_v4_0_3_sw_init - sw init for VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Load firmware and sw initialization
- */
-static int vcn_v4_0_3_sw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_ring *ring;
- int i, r, vcn_inst;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
- uint32_t *ptr;
-
- r = amdgpu_vcn_sw_init(adev);
- if (r)
- return r;
-
- amdgpu_vcn_setup_ucode(adev);
-
- r = amdgpu_vcn_resume(adev);
- if (r)
- return r;
-
- /* VCN DEC TRAP */
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_VCN,
- VCN_4_0__SRCID__UVD_ENC_GENERAL_PURPOSE, &adev->vcn.inst->irq);
- if (r)
- return r;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
-
- vcn_inst = GET_INST(VCN, i);
-
- ring = &adev->vcn.inst[i].ring_enc[0];
- ring->use_doorbell = true;
-
- if (!amdgpu_sriov_vf(adev))
- ring->doorbell_index =
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 9 * vcn_inst;
- else
- ring->doorbell_index =
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 32 * vcn_inst;
-
- ring->vm_hub = AMDGPU_MMHUB0(adev->vcn.inst[i].aid_id);
- sprintf(ring->name, "vcn_unified_%d", adev->vcn.inst[i].aid_id);
- r = amdgpu_ring_init(adev, ring, 512, &adev->vcn.inst->irq, 0,
- AMDGPU_RING_PRIO_DEFAULT,
- &adev->vcn.inst[i].sched_score);
- if (r)
- return r;
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->present_flag_0 = cpu_to_le32(AMDGPU_FW_SHARED_FLAG_0_UNIFIED_QUEUE);
- fw_shared->sq.is_enabled = true;
-
- if (amdgpu_vcnfw_log)
- amdgpu_vcn_fwlog_init(&adev->vcn.inst[i]);
- }
-
- if (amdgpu_sriov_vf(adev)) {
- r = amdgpu_virt_alloc_mm_table(adev);
- if (r)
- return r;
- }
-
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)
- adev->vcn.pause_dpg_mode = vcn_v4_0_3_pause_dpg_mode;
-
- if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- r = amdgpu_vcn_ras_sw_init(adev);
- if (r) {
- dev_err(adev->dev, "Failed to initialize vcn ras block!\n");
- return r;
- }
- }
-
- /* Allocate memory for VCN IP Dump buffer */
- ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL);
- if (!ptr) {
- DRM_ERROR("Failed to allocate memory for VCN IP Dump\n");
- adev->vcn.ip_dump = NULL;
- } else {
- adev->vcn.ip_dump = ptr;
- }
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_sw_fini - sw fini for VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * VCN suspend and free up sw allocation
- */
-static int vcn_v4_0_3_sw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, r, idx;
-
- if (drm_dev_enter(&adev->ddev, &idx)) {
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->present_flag_0 = 0;
- fw_shared->sq.is_enabled = cpu_to_le32(false);
- }
- drm_dev_exit(idx);
- }
-
- if (amdgpu_sriov_vf(adev))
- amdgpu_virt_free_mm_table(adev);
-
- r = amdgpu_vcn_suspend(adev);
- if (r)
- return r;
-
- r = amdgpu_vcn_sw_fini(adev);
-
- kfree(adev->vcn.ip_dump);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_hw_init - start and test VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Initialize the hardware, boot up the VCPU and do some testing
- */
-static int vcn_v4_0_3_hw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_ring *ring;
- int i, r, vcn_inst;
-
- if (amdgpu_sriov_vf(adev)) {
- r = vcn_v4_0_3_start_sriov(adev);
- if (r)
- return r;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ring = &adev->vcn.inst[i].ring_enc[0];
- ring->wptr = 0;
- ring->wptr_old = 0;
- vcn_v4_0_3_unified_ring_set_wptr(ring);
- ring->sched.ready = true;
- }
- } else {
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- vcn_inst = GET_INST(VCN, i);
- ring = &adev->vcn.inst[i].ring_enc[0];
-
- if (ring->use_doorbell) {
- adev->nbio.funcs->vcn_doorbell_range(
- adev, ring->use_doorbell,
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 9 * vcn_inst,
- adev->vcn.inst[i].aid_id);
-
- WREG32_SOC15(
- VCN, GET_INST(VCN, ring->me),
- regVCN_RB1_DB_CTRL,
- ring->doorbell_index
- << VCN_RB1_DB_CTRL__OFFSET__SHIFT |
- VCN_RB1_DB_CTRL__EN_MASK);
-
- /* Read DB_CTRL to flush the write DB_CTRL command. */
- RREG32_SOC15(
- VCN, GET_INST(VCN, ring->me),
- regVCN_RB1_DB_CTRL);
- }
-
- r = amdgpu_ring_test_helper(ring);
- if (r)
- return r;
- }
- }
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_hw_fini - stop the hardware block
- *
- * @handle: amdgpu_device pointer
- *
- * Stop the VCN block, mark ring as not ready any more
- */
-static int vcn_v4_0_3_hw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- cancel_delayed_work_sync(&adev->vcn.idle_work);
-
- if (adev->vcn.cur_state != AMD_PG_STATE_GATE)
- vcn_v4_0_3_set_powergating_state(adev, AMD_PG_STATE_GATE);
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_suspend - suspend VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * HW fini and suspend VCN block
- */
-static int vcn_v4_0_3_suspend(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = vcn_v4_0_3_hw_fini(adev);
- if (r)
- return r;
-
- r = amdgpu_vcn_suspend(adev);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_resume - resume VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Resume firmware and hw init VCN block
- */
-static int vcn_v4_0_3_resume(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = amdgpu_vcn_resume(adev);
- if (r)
- return r;
-
- r = vcn_v4_0_3_hw_init(adev);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_mc_resume - memory controller programming
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Let the VCN memory controller know it's offsets
- */
-static void vcn_v4_0_3_mc_resume(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t offset, size, vcn_inst;
- const struct common_firmware_header *hdr;
-
- hdr = (const struct common_firmware_header *)adev->vcn.fw[inst_idx]->data;
- size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);
-
- vcn_inst = GET_INST(VCN, inst_idx);
- /* cache window 0: fw */
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + inst_idx]
- .tmr_mc_addr_lo));
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + inst_idx]
- .tmr_mc_addr_hi));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET0, 0);
- offset = 0;
- } else {
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr));
- WREG32_SOC15(VCN, vcn_inst,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr));
- offset = size;
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET0,
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
- }
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE0, size);
-
- /* cache window 1: stack */
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset));
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET1, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE1,
- AMDGPU_VCN_STACK_SIZE);
-
- /* cache window 2: context */
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE));
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET2, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE2,
- AMDGPU_VCN_CONTEXT_SIZE);
-
- /* non-cache window */
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr));
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_NONCACHE_OFFSET0, 0);
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_VCPU_NONCACHE_SIZE0,
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)));
-}
-
-/**
- * vcn_v4_0_3_mc_resume_dpg_mode - memory controller programming for dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Let the VCN memory controller know it's offsets with dpg mode
- */
-static void vcn_v4_0_3_mc_resume_dpg_mode(struct amdgpu_device *adev, int inst_idx, bool indirect)
-{
- uint32_t offset, size;
- const struct common_firmware_header *hdr;
-
- hdr = (const struct common_firmware_header *)adev->vcn.fw[inst_idx]->data;
- size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);
-
- /* cache window 0: fw */
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- if (!indirect) {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN +
- inst_idx].tmr_mc_addr_lo), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN +
- inst_idx].tmr_mc_addr_hi), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0), 0, 0, indirect);
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0), 0, 0, indirect);
- }
- offset = 0;
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr), 0, indirect);
- offset = size;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0),
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3, 0, indirect);
- }
-
- if (!indirect)
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE0), size, 0, indirect);
- else
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE0), 0, 0, indirect);
-
- /* cache window 1: stack */
- if (!indirect) {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET1), 0, 0, indirect);
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET1), 0, 0, indirect);
- }
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE1), AMDGPU_VCN_STACK_SIZE, 0, indirect);
-
- /* cache window 2: context */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET2), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE2), AMDGPU_VCN_CONTEXT_SIZE, 0, indirect);
-
- /* non-cache window */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_NONCACHE_OFFSET0), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_NONCACHE_SIZE0),
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)), 0, indirect);
-
- /* VCN global tiling registers */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_GFX8_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_GFX10_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect);
-}
-
-/**
- * vcn_v4_0_3_disable_clock_gating - disable VCN clock gating
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Disable clock gating for VCN block
- */
-static void vcn_v4_0_3_disable_clock_gating(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t data;
- int vcn_inst;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* VCN disable CGC */
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK;
- data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_GATE);
- data &= ~(UVD_CGC_GATE__SYS_MASK
- | UVD_CGC_GATE__MPEG2_MASK
- | UVD_CGC_GATE__REGS_MASK
- | UVD_CGC_GATE__RBC_MASK
- | UVD_CGC_GATE__LMI_MC_MASK
- | UVD_CGC_GATE__LMI_UMC_MASK
- | UVD_CGC_GATE__MPC_MASK
- | UVD_CGC_GATE__LBSI_MASK
- | UVD_CGC_GATE__LRBBM_MASK
- | UVD_CGC_GATE__WCB_MASK
- | UVD_CGC_GATE__VCPU_MASK
- | UVD_CGC_GATE__MMSCH_MASK);
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_GATE, data);
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_CGC_GATE, 0, 0xFFFFFFFF);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data &= ~(UVD_CGC_CTRL__SYS_MODE_MASK
- | UVD_CGC_CTRL__MPEG2_MODE_MASK
- | UVD_CGC_CTRL__REGS_MODE_MASK
- | UVD_CGC_CTRL__RBC_MODE_MASK
- | UVD_CGC_CTRL__LMI_MC_MODE_MASK
- | UVD_CGC_CTRL__LMI_UMC_MODE_MASK
- | UVD_CGC_CTRL__MPC_MODE_MASK
- | UVD_CGC_CTRL__LBSI_MODE_MASK
- | UVD_CGC_CTRL__LRBBM_MODE_MASK
- | UVD_CGC_CTRL__WCB_MODE_MASK
- | UVD_CGC_CTRL__VCPU_MODE_MASK
- | UVD_CGC_CTRL__MMSCH_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_GATE);
- data |= (UVD_SUVD_CGC_GATE__SRE_MASK
- | UVD_SUVD_CGC_GATE__SIT_MASK
- | UVD_SUVD_CGC_GATE__SMP_MASK
- | UVD_SUVD_CGC_GATE__SCM_MASK
- | UVD_SUVD_CGC_GATE__SDB_MASK
- | UVD_SUVD_CGC_GATE__SRE_H264_MASK
- | UVD_SUVD_CGC_GATE__SRE_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SIT_H264_MASK
- | UVD_SUVD_CGC_GATE__SIT_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SCM_H264_MASK
- | UVD_SUVD_CGC_GATE__SCM_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SDB_H264_MASK
- | UVD_SUVD_CGC_GATE__SDB_HEVC_MASK
- | UVD_SUVD_CGC_GATE__ENT_MASK
- | UVD_SUVD_CGC_GATE__SIT_HEVC_DEC_MASK
- | UVD_SUVD_CGC_GATE__SITE_MASK
- | UVD_SUVD_CGC_GATE__SRE_VP9_MASK
- | UVD_SUVD_CGC_GATE__SCM_VP9_MASK
- | UVD_SUVD_CGC_GATE__SIT_VP9_DEC_MASK
- | UVD_SUVD_CGC_GATE__SDB_VP9_MASK
- | UVD_SUVD_CGC_GATE__IME_HEVC_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_GATE, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL);
- data &= ~(UVD_SUVD_CGC_CTRL__SRE_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SIT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SMP_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK
- | UVD_SUVD_CGC_CTRL__ENT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__IME_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SITE_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL, data);
-}
-
-/**
- * vcn_v4_0_3_disable_clock_gating_dpg_mode - disable VCN clock gating dpg mode
- *
- * @adev: amdgpu_device pointer
- * @sram_sel: sram select
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Disable clock gating for VCN block with dpg mode
- */
-static void vcn_v4_0_3_disable_clock_gating_dpg_mode(struct amdgpu_device *adev, uint8_t sram_sel,
- int inst_idx, uint8_t indirect)
-{
- uint32_t reg_data = 0;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- /* enable sw clock gating control */
- reg_data = 0 << UVD_CGC_CTRL__DYN_CLOCK_MODE__SHIFT;
- reg_data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- reg_data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- reg_data &= ~(UVD_CGC_CTRL__SYS_MODE_MASK |
- UVD_CGC_CTRL__MPEG2_MODE_MASK |
- UVD_CGC_CTRL__REGS_MODE_MASK |
- UVD_CGC_CTRL__RBC_MODE_MASK |
- UVD_CGC_CTRL__LMI_MC_MODE_MASK |
- UVD_CGC_CTRL__LMI_UMC_MODE_MASK |
- UVD_CGC_CTRL__IDCT_MODE_MASK |
- UVD_CGC_CTRL__MPRD_MODE_MASK |
- UVD_CGC_CTRL__MPC_MODE_MASK |
- UVD_CGC_CTRL__LBSI_MODE_MASK |
- UVD_CGC_CTRL__LRBBM_MODE_MASK |
- UVD_CGC_CTRL__WCB_MODE_MASK |
- UVD_CGC_CTRL__VCPU_MODE_MASK);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_CGC_CTRL), reg_data, sram_sel, indirect);
-
- /* turn off clock gating */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_CGC_GATE), 0, sram_sel, indirect);
-
- /* turn on SUVD clock gating */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_SUVD_CGC_GATE), 1, sram_sel, indirect);
-
- /* turn on sw mode in UVD_SUVD_CGC_CTRL */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_SUVD_CGC_CTRL), 0, sram_sel, indirect);
-}
-
-/**
- * vcn_v4_0_3_enable_clock_gating - enable VCN clock gating
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Enable clock gating for VCN block
- */
-static void vcn_v4_0_3_enable_clock_gating(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t data;
- int vcn_inst;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* enable VCN CGC */
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data |= 0 << UVD_CGC_CTRL__DYN_CLOCK_MODE__SHIFT;
- data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data |= (UVD_CGC_CTRL__SYS_MODE_MASK
- | UVD_CGC_CTRL__MPEG2_MODE_MASK
- | UVD_CGC_CTRL__REGS_MODE_MASK
- | UVD_CGC_CTRL__RBC_MODE_MASK
- | UVD_CGC_CTRL__LMI_MC_MODE_MASK
- | UVD_CGC_CTRL__LMI_UMC_MODE_MASK
- | UVD_CGC_CTRL__MPC_MODE_MASK
- | UVD_CGC_CTRL__LBSI_MODE_MASK
- | UVD_CGC_CTRL__LRBBM_MODE_MASK
- | UVD_CGC_CTRL__WCB_MODE_MASK
- | UVD_CGC_CTRL__VCPU_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL);
- data |= (UVD_SUVD_CGC_CTRL__SRE_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SIT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SMP_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK
- | UVD_SUVD_CGC_CTRL__ENT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__IME_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SITE_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL, data);
-}
-
-/**
- * vcn_v4_0_3_start_dpg_mode - VCN start with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Start VCN block with dpg mode
- */
-static int vcn_v4_0_3_start_dpg_mode(struct amdgpu_device *adev, int inst_idx, bool indirect)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared =
- adev->vcn.inst[inst_idx].fw_shared.cpu_addr;
- struct amdgpu_ring *ring;
- int vcn_inst;
- uint32_t tmp;
-
- vcn_inst = GET_INST(VCN, inst_idx);
- /* disable register anti-hang mechanism */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_POWER_STATUS), 1,
- ~UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
- /* enable dynamic power gating mode */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_POWER_STATUS);
- tmp |= UVD_POWER_STATUS__UVD_PG_MODE_MASK;
- tmp |= UVD_POWER_STATUS__UVD_PG_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_POWER_STATUS, tmp);
-
- if (indirect) {
- DRM_DEV_DEBUG(adev->dev, "VCN %d start: on AID %d",
- inst_idx, adev->vcn.inst[inst_idx].aid_id);
- adev->vcn.inst[inst_idx].dpg_sram_curr_addr =
- (uint32_t *)adev->vcn.inst[inst_idx].dpg_sram_cpu_addr;
- /* Use dummy register 0xDEADBEEF passing AID selection to PSP FW */
- WREG32_SOC15_DPG_MODE(inst_idx, 0xDEADBEEF,
- adev->vcn.inst[inst_idx].aid_id, 0, true);
- }
-
- /* enable clock gating */
- vcn_v4_0_3_disable_clock_gating_dpg_mode(adev, 0, inst_idx, indirect);
-
- /* enable VCPU clock */
- tmp = (0xFF << UVD_VCPU_CNTL__PRB_TIMEOUT_VAL__SHIFT);
- tmp |= UVD_VCPU_CNTL__CLK_EN_MASK;
- tmp |= UVD_VCPU_CNTL__BLK_RST_MASK;
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CNTL), tmp, 0, indirect);
-
- /* disable master interrupt */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MASTINT_EN), 0, 0, indirect);
-
- /* setup regUVD_LMI_CTRL */
- tmp = (UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
- UVD_LMI_CTRL__REQ_MODE_MASK |
- UVD_LMI_CTRL__CRC_RESET_MASK |
- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK |
- (8 << UVD_LMI_CTRL__WRITE_CLEAN_TIMER__SHIFT) |
- 0x00100000L);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_CTRL), tmp, 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_CNTL),
- 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT, 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUXA0),
- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)), 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUXB0),
- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)), 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUX),
- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)), 0, indirect);
-
- vcn_v4_0_3_mc_resume_dpg_mode(adev, inst_idx, indirect);
-
- tmp = (0xFF << UVD_VCPU_CNTL__PRB_TIMEOUT_VAL__SHIFT);
- tmp |= UVD_VCPU_CNTL__CLK_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CNTL), tmp, 0, indirect);
-
- /* enable LMI MC and UMC channels */
- tmp = 0x1f << UVD_LMI_CTRL2__RE_OFLD_MIF_WR_REQ_NUM__SHIFT;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_CTRL2), tmp, 0, indirect);
-
- vcn_v4_0_3_enable_ras(adev, inst_idx, indirect);
-
- /* enable master interrupt */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MASTINT_EN),
- UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect);
-
- if (indirect)
- amdgpu_vcn_psp_update_sram(adev, inst_idx, AMDGPU_UCODE_ID_VCN0_RAM);
-
- ring = &adev->vcn.inst[inst_idx].ring_enc[0];
-
- /* program the RB_BASE for ring buffer */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_LO,
- lower_32_bits(ring->gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_HI,
- upper_32_bits(ring->gpu_addr));
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_SIZE,
- ring->ring_size / sizeof(uint32_t));
-
- /* resetting ring, fw should not check RB ring */
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp &= ~(VCN_RB_ENABLE__RB_EN_MASK);
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
- fw_shared->sq.queue_mode |= FW_QUEUE_RING_RESET;
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_RPTR, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR, 0);
- ring->wptr = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp |= VCN_RB_ENABLE__RB_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
- fw_shared->sq.queue_mode &= ~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF);
-
- /*resetting done, fw can check RB ring */
- fw_shared->sq.queue_mode &= cpu_to_le32(~FW_QUEUE_RING_RESET);
-
- return 0;
-}
-
-static int vcn_v4_0_3_start_sriov(struct amdgpu_device *adev)
-{
- int i, vcn_inst;
- struct amdgpu_ring *ring_enc;
- uint64_t cache_addr;
- uint64_t rb_enc_addr;
- uint64_t ctx_addr;
- uint32_t param, resp, expected;
- uint32_t offset, cache_size;
- uint32_t tmp, timeout;
-
- struct amdgpu_mm_table *table = &adev->virt.mm_table;
- uint32_t *table_loc;
- uint32_t table_size;
- uint32_t size, size_dw;
- uint32_t init_status;
- uint32_t enabled_vcn;
-
- struct mmsch_v4_0_cmd_direct_write
- direct_wt = { {0} };
- struct mmsch_v4_0_cmd_direct_read_modify_write
- direct_rd_mod_wt = { {0} };
- struct mmsch_v4_0_cmd_end end = { {0} };
- struct mmsch_v4_0_3_init_header header;
-
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- volatile struct amdgpu_fw_shared_rb_setup *rb_setup;
-
- direct_wt.cmd_header.command_type =
- MMSCH_COMMAND__DIRECT_REG_WRITE;
- direct_rd_mod_wt.cmd_header.command_type =
- MMSCH_COMMAND__DIRECT_REG_READ_MODIFY_WRITE;
- end.cmd_header.command_type = MMSCH_COMMAND__END;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- vcn_inst = GET_INST(VCN, i);
-
- memset(&header, 0, sizeof(struct mmsch_v4_0_3_init_header));
- header.version = MMSCH_VERSION;
- header.total_size = sizeof(struct mmsch_v4_0_3_init_header) >> 2;
-
- table_loc = (uint32_t *)table->cpu_addr;
- table_loc += header.total_size;
-
- table_size = 0;
-
- MMSCH_V4_0_INSERT_DIRECT_RD_MOD_WT(SOC15_REG_OFFSET(VCN, 0, regUVD_STATUS),
- ~UVD_STATUS__UVD_BUSY, UVD_STATUS__UVD_BUSY);
-
- cache_size = AMDGPU_GPU_PAGE_ALIGN(adev->vcn.fw[i]->size + 4);
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + i].tmr_mc_addr_lo);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + i].tmr_mc_addr_hi);
-
- offset = 0;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET0), 0);
- } else {
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[i].gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[i].gpu_addr));
- offset = cache_size;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET0),
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
- }
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE0),
- cache_size);
-
- cache_addr = adev->vcn.inst[vcn_inst].gpu_addr + offset;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW), lower_32_bits(cache_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH), upper_32_bits(cache_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET1), 0);
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE1), AMDGPU_VCN_STACK_SIZE);
-
- cache_addr = adev->vcn.inst[vcn_inst].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE;
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW), lower_32_bits(cache_addr));
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH), upper_32_bits(cache_addr));
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET2), 0);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE2), AMDGPU_VCN_CONTEXT_SIZE);
-
- fw_shared = adev->vcn.inst[vcn_inst].fw_shared.cpu_addr;
- rb_setup = &fw_shared->rb_setup;
-
- ring_enc = &adev->vcn.inst[vcn_inst].ring_enc[0];
- ring_enc->wptr = 0;
- rb_enc_addr = ring_enc->gpu_addr;
-
- rb_setup->is_rb_enabled_flags |= RB_ENABLED;
- rb_setup->rb_addr_lo = lower_32_bits(rb_enc_addr);
- rb_setup->rb_addr_hi = upper_32_bits(rb_enc_addr);
- rb_setup->rb_size = ring_enc->ring_size / 4;
- fw_shared->present_flag_0 |= cpu_to_le32(AMDGPU_VCN_VF_RB_SETUP_FLAG);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[vcn_inst].fw_shared.gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[vcn_inst].fw_shared.gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_NONCACHE_SIZE0),
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)));
- MMSCH_V4_0_INSERT_END();
-
- header.vcn0.init_status = 0;
- header.vcn0.table_offset = header.total_size;
- header.vcn0.table_size = table_size;
- header.total_size += table_size;
-
- /* Send init table to mmsch */
- size = sizeof(struct mmsch_v4_0_3_init_header);
- table_loc = (uint32_t *)table->cpu_addr;
- memcpy((void *)table_loc, &header, size);
-
- ctx_addr = table->gpu_addr;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_ADDR_LO, lower_32_bits(ctx_addr));
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_ADDR_HI, upper_32_bits(ctx_addr));
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_VMID);
- tmp &= ~MMSCH_VF_VMID__VF_CTX_VMID_MASK;
- tmp |= (0 << MMSCH_VF_VMID__VF_CTX_VMID__SHIFT);
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_VMID, tmp);
-
- size = header.total_size;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_SIZE, size);
-
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_RESP, 0);
-
- param = 0x00000001;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_HOST, param);
- tmp = 0;
- timeout = 1000;
- resp = 0;
- expected = MMSCH_VF_MAILBOX_RESP__OK;
- while (resp != expected) {
- resp = RREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_RESP);
- if (resp != 0)
- break;
-
- udelay(10);
- tmp = tmp + 10;
- if (tmp >= timeout) {
- DRM_ERROR("failed to init MMSCH. TIME-OUT after %d usec"\
- " waiting for regMMSCH_VF_MAILBOX_RESP "\
- "(expected=0x%08x, readback=0x%08x)\n",
- tmp, expected, resp);
- return -EBUSY;
- }
- }
-
- enabled_vcn = amdgpu_vcn_is_disabled_vcn(adev, VCN_DECODE_RING, 0) ? 1 : 0;
- init_status = ((struct mmsch_v4_0_3_init_header *)(table_loc))->vcn0.init_status;
- if (resp != expected && resp != MMSCH_VF_MAILBOX_RESP__INCOMPLETE
- && init_status != MMSCH_VF_ENGINE_STATUS__PASS) {
- DRM_ERROR("MMSCH init status is incorrect! readback=0x%08x, header init "\
- "status for VCN%x: 0x%x\n", resp, enabled_vcn, init_status);
- }
- }
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_start - VCN start
- *
- * @adev: amdgpu_device pointer
- *
- * Start VCN block
- */
-static int vcn_v4_0_3_start(struct amdgpu_device *adev)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- struct amdgpu_ring *ring;
- int i, j, k, r, vcn_inst;
- uint32_t tmp;
-
- if (adev->pm.dpm_enabled)
- amdgpu_dpm_enable_uvd(adev, true);
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
- r = vcn_v4_0_3_start_dpg_mode(adev, i, adev->vcn.indirect_sram);
- continue;
- }
-
- vcn_inst = GET_INST(VCN, i);
- /* set VCN status busy */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_STATUS) |
- UVD_STATUS__UVD_BUSY;
- WREG32_SOC15(VCN, vcn_inst, regUVD_STATUS, tmp);
-
- /*SW clock gating */
- vcn_v4_0_3_disable_clock_gating(adev, i);
-
- /* enable VCPU clock */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__CLK_EN_MASK,
- ~UVD_VCPU_CNTL__CLK_EN_MASK);
-
- /* disable master interrupt */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_MASTINT_EN), 0,
- ~UVD_MASTINT_EN__VCPU_EN_MASK);
-
- /* enable LMI MC and UMC channels */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_LMI_CTRL2), 0,
- ~UVD_LMI_CTRL2__STALL_ARB_UMC_MASK);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp &= ~UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
- tmp &= ~UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- /* setup regUVD_LMI_CTRL */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL);
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL,
- tmp | UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK);
-
- /* setup regUVD_MPC_CNTL */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_MPC_CNTL);
- tmp &= ~UVD_MPC_CNTL__REPLACEMENT_MODE_MASK;
- tmp |= 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_CNTL, tmp);
-
- /* setup UVD_MPC_SET_MUXA0 */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUXA0,
- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)));
-
- /* setup UVD_MPC_SET_MUXB0 */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUXB0,
- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)));
-
- /* setup UVD_MPC_SET_MUX */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUX,
- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)));
-
- vcn_v4_0_3_mc_resume(adev, i);
-
- /* VCN global tiling registers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_GFX8_ADDR_CONFIG,
- adev->gfx.config.gb_addr_config);
- WREG32_SOC15(VCN, vcn_inst, regUVD_GFX10_ADDR_CONFIG,
- adev->gfx.config.gb_addr_config);
-
- /* unblock VCPU register access */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_RB_ARB_CTRL), 0,
- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
-
- /* release VCPU reset to boot */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL), 0,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- for (j = 0; j < 10; ++j) {
- uint32_t status;
-
- for (k = 0; k < 100; ++k) {
- status = RREG32_SOC15(VCN, vcn_inst,
- regUVD_STATUS);
- if (status & 2)
- break;
- mdelay(10);
- }
- r = 0;
- if (status & 2)
- break;
-
- DRM_DEV_ERROR(adev->dev,
- "VCN decode not responding, trying to reset the VCPU!!!\n");
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst,
- regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__BLK_RST_MASK,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
- mdelay(10);
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst,
- regUVD_VCPU_CNTL),
- 0, ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- mdelay(10);
- r = -1;
- }
-
- if (r) {
- DRM_DEV_ERROR(adev->dev, "VCN decode not responding, giving up!!!\n");
- return r;
- }
-
- /* enable master interrupt */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_MASTINT_EN),
- UVD_MASTINT_EN__VCPU_EN_MASK,
- ~UVD_MASTINT_EN__VCPU_EN_MASK);
-
- /* clear the busy bit of VCN_STATUS */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_STATUS), 0,
- ~(2 << UVD_STATUS__VCPU_REPORT__SHIFT));
-
- ring = &adev->vcn.inst[i].ring_enc[0];
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
-
- /* program the RB_BASE for ring buffer */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_LO,
- lower_32_bits(ring->gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_HI,
- upper_32_bits(ring->gpu_addr));
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_SIZE,
- ring->ring_size / sizeof(uint32_t));
-
- /* resetting ring, fw should not check RB ring */
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp &= ~(VCN_RB_ENABLE__RB_EN_MASK);
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_RPTR, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR, 0);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp |= VCN_RB_ENABLE__RB_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
-
- ring->wptr = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
- fw_shared->sq.queue_mode &=
- cpu_to_le32(~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF));
-
- }
- return 0;
-}
-
-/**
- * vcn_v4_0_3_stop_dpg_mode - VCN stop with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- *
- * Stop VCN block with dpg mode
- */
-static int vcn_v4_0_3_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t tmp;
- int vcn_inst;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* Wait for power status to be 1 */
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_POWER_STATUS, 1,
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
-
- /* wait for read ptr to be equal to write ptr */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_RB_RPTR, tmp, 0xFFFFFFFF);
-
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_POWER_STATUS, 1,
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
-
- /* disable dynamic power gating mode */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_POWER_STATUS), 0,
- ~UVD_POWER_STATUS__UVD_PG_MODE_MASK);
- return 0;
-}
-
-/**
- * vcn_v4_0_3_stop - VCN stop
- *
- * @adev: amdgpu_device pointer
- *
- * Stop VCN block
- */
-static int vcn_v4_0_3_stop(struct amdgpu_device *adev)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- int i, r = 0, vcn_inst;
- uint32_t tmp;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- vcn_inst = GET_INST(VCN, i);
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->sq.queue_mode |= FW_QUEUE_DPG_HOLD_OFF;
-
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
- vcn_v4_0_3_stop_dpg_mode(adev, i);
- continue;
- }
-
- /* wait for vcn idle */
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_STATUS,
- UVD_STATUS__IDLE, 0x7);
- if (r)
- goto Done;
-
- tmp = UVD_LMI_STATUS__VCPU_LMI_WRITE_CLEAN_MASK |
- UVD_LMI_STATUS__READ_CLEAN_MASK |
- UVD_LMI_STATUS__WRITE_CLEAN_MASK |
- UVD_LMI_STATUS__WRITE_CLEAN_RAW_MASK;
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_LMI_STATUS, tmp,
- tmp);
- if (r)
- goto Done;
-
- /* stall UMC channel */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL2);
- tmp |= UVD_LMI_CTRL2__STALL_ARB_UMC_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL2, tmp);
- tmp = UVD_LMI_STATUS__UMC_READ_CLEAN_RAW_MASK |
- UVD_LMI_STATUS__UMC_WRITE_CLEAN_RAW_MASK;
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_LMI_STATUS, tmp,
- tmp);
- if (r)
- goto Done;
-
- /* Unblock VCPU Register access */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_RB_ARB_CTRL),
- UVD_RB_ARB_CTRL__VCPU_DIS_MASK,
- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
-
- /* release VCPU reset to boot */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__BLK_RST_MASK,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- /* disable VCPU clock */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL), 0,
- ~(UVD_VCPU_CNTL__CLK_EN_MASK));
-
- /* reset LMI UMC/LMI/VCPU */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp |= UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp |= UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- /* clear VCN status */
- WREG32_SOC15(VCN, vcn_inst, regUVD_STATUS, 0);
-
- /* apply HW clock gating */
- vcn_v4_0_3_enable_clock_gating(adev, i);
- }
-Done:
- if (adev->pm.dpm_enabled)
- amdgpu_dpm_enable_uvd(adev, false);
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_pause_dpg_mode - VCN pause with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @new_state: pause state
- *
- * Pause dpg mode for VCN block
- */
-static int vcn_v4_0_3_pause_dpg_mode(struct amdgpu_device *adev, int inst_idx,
- struct dpg_pause_state *new_state)
-{
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_unified_ring_get_rptr - get unified read pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Returns the current hardware unified read pointer
- */
-static uint64_t vcn_v4_0_3_unified_ring_get_rptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- return RREG32_SOC15(VCN, GET_INST(VCN, ring->me), regUVD_RB_RPTR);
-}
-
-/**
- * vcn_v4_0_3_unified_ring_get_wptr - get unified write pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Returns the current hardware unified write pointer
- */
-static uint64_t vcn_v4_0_3_unified_ring_get_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- if (ring->use_doorbell)
- return *ring->wptr_cpu_addr;
- else
- return RREG32_SOC15(VCN, GET_INST(VCN, ring->me),
- regUVD_RB_WPTR);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_reg_wait(struct amdgpu_ring *ring, uint32_t reg,
- uint32_t val, uint32_t mask)
-{
- /* For VF, only local offsets should be used */
- if (amdgpu_sriov_vf(ring->adev))
- reg = NORMALIZE_VCN_REG_OFFSET(reg);
-
- amdgpu_ring_write(ring, VCN_ENC_CMD_REG_WAIT);
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, mask);
- amdgpu_ring_write(ring, val);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg, uint32_t val)
-{
- /* For VF, only local offsets should be used */
- if (amdgpu_sriov_vf(ring->adev))
- reg = NORMALIZE_VCN_REG_OFFSET(reg);
-
- amdgpu_ring_write(ring, VCN_ENC_CMD_REG_WRITE);
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, val);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_vm_flush(struct amdgpu_ring *ring,
- unsigned int vmid, uint64_t pd_addr)
-{
- struct amdgpu_vmhub *hub = &ring->adev->vmhub[ring->vm_hub];
-
- pd_addr = amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
-
- /* wait for reg writes */
- vcn_v4_0_3_enc_ring_emit_reg_wait(ring, hub->ctx0_ptb_addr_lo32 +
- vmid * hub->ctx_addr_distance,
- lower_32_bits(pd_addr), 0xffffffff);
-}
-
-static void vcn_v4_0_3_ring_emit_hdp_flush(struct amdgpu_ring *ring)
-{
- /* VCN engine access for HDP flush doesn't work when RRMT is enabled.
- * This is a workaround to avoid any HDP flush through VCN ring.
- */
-}
-
-/**
- * vcn_v4_0_3_unified_ring_set_wptr - set enc write pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Commits the enc write pointer to the hardware
- */
-static void vcn_v4_0_3_unified_ring_set_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- if (ring->use_doorbell) {
- *ring->wptr_cpu_addr = lower_32_bits(ring->wptr);
- WDOORBELL32(ring->doorbell_index, lower_32_bits(ring->wptr));
- } else {
- WREG32_SOC15(VCN, GET_INST(VCN, ring->me), regUVD_RB_WPTR,
- lower_32_bits(ring->wptr));
- }
-}
-
-static const struct amdgpu_ring_funcs vcn_v4_0_3_unified_ring_vm_funcs = {
- .type = AMDGPU_RING_TYPE_VCN_ENC,
- .align_mask = 0x3f,
- .nop = VCN_ENC_CMD_NO_OP,
- .get_rptr = vcn_v4_0_3_unified_ring_get_rptr,
- .get_wptr = vcn_v4_0_3_unified_ring_get_wptr,
- .set_wptr = vcn_v4_0_3_unified_ring_set_wptr,
- .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_v4_0_3_enc_ring_emit_vm_flush,
- .emit_hdp_flush = vcn_v4_0_3_ring_emit_hdp_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_v4_0_3_enc_ring_emit_wreg,
- .emit_reg_wait = vcn_v4_0_3_enc_ring_emit_reg_wait,
- .emit_reg_write_reg_wait = amdgpu_ring_emit_reg_write_reg_wait_helper,
-};
-
-/**
- * vcn_v4_0_3_set_unified_ring_funcs - set unified ring functions
- *
- * @adev: amdgpu_device pointer
- *
- * Set unified ring functions
- */
-static void vcn_v4_0_3_set_unified_ring_funcs(struct amdgpu_device *adev)
-{
- int i, vcn_inst;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- adev->vcn.inst[i].ring_enc[0].funcs = &vcn_v4_0_3_unified_ring_vm_funcs;
- adev->vcn.inst[i].ring_enc[0].me = i;
- vcn_inst = GET_INST(VCN, i);
- adev->vcn.inst[i].aid_id =
- vcn_inst / adev->vcn.num_inst_per_aid;
- }
-}
-
-/**
- * vcn_v4_0_3_is_idle - check VCN block is idle
- *
- * @handle: amdgpu_device pointer
- *
- * Check whether VCN block is idle
- */
-static bool vcn_v4_0_3_is_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, ret = 1;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ret &= (RREG32_SOC15(VCN, GET_INST(VCN, i), regUVD_STATUS) ==
- UVD_STATUS__IDLE);
- }
-
- return ret;
-}
-
-/**
- * vcn_v4_0_3_wait_for_idle - wait for VCN block idle
- *
- * @handle: amdgpu_device pointer
- *
- * Wait for VCN block idle
- */
-static int vcn_v4_0_3_wait_for_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, ret = 0;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ret = SOC15_WAIT_ON_RREG(VCN, GET_INST(VCN, i), regUVD_STATUS,
- UVD_STATUS__IDLE, UVD_STATUS__IDLE);
- if (ret)
- return ret;
- }
-
- return ret;
-}
-
-/* vcn_v4_0_3_set_clockgating_state - set VCN block clockgating state
- *
- * @handle: amdgpu_device pointer
- * @state: clock gating state
- *
- * Set VCN block clockgating state
- */
-static int vcn_v4_0_3_set_clockgating_state(void *handle,
- enum amd_clockgating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- bool enable = state == AMD_CG_STATE_GATE;
- int i;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- if (enable) {
- if (RREG32_SOC15(VCN, GET_INST(VCN, i),
- regUVD_STATUS) != UVD_STATUS__IDLE)
- return -EBUSY;
- vcn_v4_0_3_enable_clock_gating(adev, i);
- } else {
- vcn_v4_0_3_disable_clock_gating(adev, i);
- }
- }
- return 0;
-}
-
-/**
- * vcn_v4_0_3_set_powergating_state - set VCN block powergating state
- *
- * @handle: amdgpu_device pointer
- * @state: power gating state
- *
- * Set VCN block powergating state
- */
-static int vcn_v4_0_3_set_powergating_state(void *handle,
- enum amd_powergating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int ret;
-
- /* for SRIOV, guest should not control VCN Power-gating
- * MMSCH FW should control Power-gating and clock-gating
- * guest should avoid touching CGC and PG
- */
- if (amdgpu_sriov_vf(adev)) {
- adev->vcn.cur_state = AMD_PG_STATE_UNGATE;
- return 0;
- }
-
- if (state == adev->vcn.cur_state)
- return 0;
-
- if (state == AMD_PG_STATE_GATE)
- ret = vcn_v4_0_3_stop(adev);
- else
- ret = vcn_v4_0_3_start(adev);
-
- if (!ret)
- adev->vcn.cur_state = state;
-
- return ret;
-}
-
-/**
- * vcn_v4_0_3_set_interrupt_state - set VCN block interrupt state
- *
- * @adev: amdgpu_device pointer
- * @source: interrupt sources
- * @type: interrupt types
- * @state: interrupt states
- *
- * Set VCN block interrupt state
- */
-static int vcn_v4_0_3_set_interrupt_state(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- unsigned int type,
- enum amdgpu_interrupt_state state)
-{
- return 0;
-}
-
-/**
- * vcn_v4_0_3_process_interrupt - process VCN block interrupt
- *
- * @adev: amdgpu_device pointer
- * @source: interrupt sources
- * @entry: interrupt entry from clients and sources
- *
- * Process VCN block interrupt
- */
-static int vcn_v4_0_3_process_interrupt(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- struct amdgpu_iv_entry *entry)
-{
- uint32_t i, inst;
-
- i = node_id_to_phys_map[entry->node_id];
-
- DRM_DEV_DEBUG(adev->dev, "IH: VCN TRAP\n");
-
- for (inst = 0; inst < adev->vcn.num_vcn_inst; ++inst)
- if (adev->vcn.inst[inst].aid_id == i)
- break;
-
- if (inst >= adev->vcn.num_vcn_inst) {
- dev_WARN_ONCE(adev->dev, 1,
- "Interrupt received for unknown VCN instance %d",
- entry->node_id);
- return 0;
- }
-
- switch (entry->src_id) {
- case VCN_4_0__SRCID__UVD_ENC_GENERAL_PURPOSE:
- amdgpu_fence_process(&adev->vcn.inst[inst].ring_enc[0]);
- break;
- default:
- DRM_DEV_ERROR(adev->dev, "Unhandled interrupt: %d %d\n",
- entry->src_id, entry->src_data[0]);
- break;
- }
-
- return 0;
-}
-
-static const struct amdgpu_irq_src_funcs vcn_v4_0_3_irq_funcs = {
- .set = vcn_v4_0_3_set_interrupt_state,
- .process = vcn_v4_0_3_process_interrupt,
-};
-
-/**
- * vcn_v4_0_3_set_irq_funcs - set VCN block interrupt irq functions
- *
- * @adev: amdgpu_device pointer
- *
- * Set VCN block interrupt irq functions
- */
-static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev)
-{
- int i;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- adev->vcn.inst->irq.num_types++;
- }
- adev->vcn.inst->irq.funcs = &vcn_v4_0_3_irq_funcs;
-}
-
-static void vcn_v4_0_3_print_ip_state(void *handle, struct drm_printer *p)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
- uint32_t inst_off, is_powered;
-
- if (!adev->vcn.ip_dump)
- return;
-
- drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst);
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- if (adev->vcn.harvest_config & (1 << i)) {
- drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i);
- continue;
- }
-
- inst_off = i * reg_count;
- is_powered = (adev->vcn.ip_dump[inst_off] &
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1;
-
- if (is_powered) {
- drm_printf(p, "\nActive Instance:VCN%d\n", i);
- for (j = 0; j < reg_count; j++)
- drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_4_0_3[j].reg_name,
- adev->vcn.ip_dump[inst_off + j]);
- } else {
- drm_printf(p, "\nInactive Instance:VCN%d\n", i);
- }
- }
-}
-
-static void vcn_v4_0_3_dump_ip_state(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- bool is_powered;
- uint32_t inst_off, inst_id;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
-
- if (!adev->vcn.ip_dump)
- return;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- if (adev->vcn.harvest_config & (1 << i))
- continue;
-
- inst_id = GET_INST(VCN, i);
- inst_off = i * reg_count;
- /* mmUVD_POWER_STATUS is always readable and is first element of the array */
- adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, inst_id, regUVD_POWER_STATUS);
- is_powered = (adev->vcn.ip_dump[inst_off] &
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1;
-
- if (is_powered)
- for (j = 1; j < reg_count; j++)
- adev->vcn.ip_dump[inst_off + j] =
- RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_4_0_3[j],
- inst_id));
- }
-}
-
-static const struct amd_ip_funcs vcn_v4_0_3_ip_funcs = {
- .name = "vcn_v4_0_3",
- .early_init = vcn_v4_0_3_early_init,
- .late_init = NULL,
- .sw_init = vcn_v4_0_3_sw_init,
- .sw_fini = vcn_v4_0_3_sw_fini,
- .hw_init = vcn_v4_0_3_hw_init,
- .hw_fini = vcn_v4_0_3_hw_fini,
- .suspend = vcn_v4_0_3_suspend,
- .resume = vcn_v4_0_3_resume,
- .is_idle = vcn_v4_0_3_is_idle,
- .wait_for_idle = vcn_v4_0_3_wait_for_idle,
- .check_soft_reset = NULL,
- .pre_soft_reset = NULL,
- .soft_reset = NULL,
- .post_soft_reset = NULL,
- .set_clockgating_state = vcn_v4_0_3_set_clockgating_state,
- .set_powergating_state = vcn_v4_0_3_set_powergating_state,
- .dump_ip_state = vcn_v4_0_3_dump_ip_state,
- .print_ip_state = vcn_v4_0_3_print_ip_state,
-};
-
-const struct amdgpu_ip_block_version vcn_v4_0_3_ip_block = {
- .type = AMD_IP_BLOCK_TYPE_VCN,
- .major = 4,
- .minor = 0,
- .rev = 3,
- .funcs = &vcn_v4_0_3_ip_funcs,
-};
-
-static const struct amdgpu_ras_err_status_reg_entry vcn_v4_0_3_ue_reg_list[] = {
- {AMDGPU_RAS_REG_ENTRY(VCN, 0, regVCN_UE_ERR_STATUS_LO_VIDD, regVCN_UE_ERR_STATUS_HI_VIDD),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "VIDD"},
- {AMDGPU_RAS_REG_ENTRY(VCN, 0, regVCN_UE_ERR_STATUS_LO_VIDV, regVCN_UE_ERR_STATUS_HI_VIDV),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "VIDV"},
-};
-
-static void vcn_v4_0_3_inst_query_ras_error_count(struct amdgpu_device *adev,
- uint32_t vcn_inst,
- void *ras_err_status)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_err_status;
-
- /* vcn v4_0_3 only support query uncorrectable errors */
- amdgpu_ras_inst_query_ras_error_count(adev,
- vcn_v4_0_3_ue_reg_list,
- ARRAY_SIZE(vcn_v4_0_3_ue_reg_list),
- NULL, 0, GET_INST(VCN, vcn_inst),
- AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE,
- &err_data->ue_count);
-}
-
-static void vcn_v4_0_3_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_err_status)
-{
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- dev_warn(adev->dev, "VCN RAS is not supported\n");
- return;
- }
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++)
- vcn_v4_0_3_inst_query_ras_error_count(adev, i, ras_err_status);
-}
-
-static void vcn_v4_0_3_inst_reset_ras_error_count(struct amdgpu_device *adev,
- uint32_t vcn_inst)
-{
- amdgpu_ras_inst_reset_ras_error_count(adev,
- vcn_v4_0_3_ue_reg_list,
- ARRAY_SIZE(vcn_v4_0_3_ue_reg_list),
- GET_INST(VCN, vcn_inst));
-}
-
-static void vcn_v4_0_3_reset_ras_error_count(struct amdgpu_device *adev)
-{
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- dev_warn(adev->dev, "VCN RAS is not supported\n");
- return;
- }
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++)
- vcn_v4_0_3_inst_reset_ras_error_count(adev, i);
-}
-
-static const struct amdgpu_ras_block_hw_ops vcn_v4_0_3_ras_hw_ops = {
- .query_ras_error_count = vcn_v4_0_3_query_ras_error_count,
- .reset_ras_error_count = vcn_v4_0_3_reset_ras_error_count,
-};
-
-static struct amdgpu_vcn_ras vcn_v4_0_3_ras = {
- .ras_block = {
- .hw_ops = &vcn_v4_0_3_ras_hw_ops,
- },
-};
-
-static void vcn_v4_0_3_set_ras_funcs(struct amdgpu_device *adev)
-{
- adev->vcn.ras = &vcn_v4_0_3_ras;
-}
-
-static void vcn_v4_0_3_enable_ras(struct amdgpu_device *adev,
- int inst_idx, bool indirect)
-{
- uint32_t tmp;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN))
- return;
-
- tmp = VCN_RAS_CNTL__VCPU_VCODEC_REARM_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_IH_EN_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_PMI_EN_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_STALL_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regVCN_RAS_CNTL),
- tmp, 0, indirect);
-
- tmp = UVD_VCPU_INT_EN2__RASCNTL_VCPU_VCODEC_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regUVD_VCPU_INT_EN2),
- tmp, 0, indirect);
-
- tmp = UVD_SYS_INT_EN__RASCNTL_VCPU_VCODEC_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regUVD_SYS_INT_EN),
- tmp, 0, indirect);
-}
diff --git a/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage.2 b/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage.2
deleted file mode 100644
index c55dabb2d37f..000000000000
--- a/rr-cache/feb901dea4bef5048493327b0af8cf88d7a586fb/preimage.2
+++ /dev/null
@@ -1,1930 +0,0 @@
-/*
- * Copyright 2022 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/firmware.h>
-#include <drm/drm_drv.h>
-
-#include "amdgpu.h"
-#include "amdgpu_vcn.h"
-#include "amdgpu_pm.h"
-#include "soc15.h"
-#include "soc15d.h"
-#include "soc15_hw_ip.h"
-#include "vcn_v2_0.h"
-#include "mmsch_v4_0_3.h"
-
-#include "vcn/vcn_4_0_3_offset.h"
-#include "vcn/vcn_4_0_3_sh_mask.h"
-#include "ivsrcid/vcn/irqsrcs_vcn_4_0.h"
-
-#define mmUVD_DPG_LMA_CTL regUVD_DPG_LMA_CTL
-#define mmUVD_DPG_LMA_CTL_BASE_IDX regUVD_DPG_LMA_CTL_BASE_IDX
-#define mmUVD_DPG_LMA_DATA regUVD_DPG_LMA_DATA
-#define mmUVD_DPG_LMA_DATA_BASE_IDX regUVD_DPG_LMA_DATA_BASE_IDX
-
-#define VCN_VID_SOC_ADDRESS_2_0 0x1fb00
-#define VCN1_VID_SOC_ADDRESS_3_0 0x48300
-
-<<<<<<<
-=======
-static const struct amdgpu_hwip_reg_entry vcn_reg_list_4_0_3[] = {
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_POWER_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_CONTEXT_ID2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA0),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_DATA1),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_GPCOM_VCPU_CMD),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_HI4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_BASE_LO4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_RPTR4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_WPTR4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE2),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE3),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_RB_SIZE4),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_PGFSM_CONFIG),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_PGFSM_STATUS),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_CTL),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_DATA),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_LMA_MASK),
- SOC15_REG_ENTRY_STR(VCN, 0, regUVD_DPG_PAUSE)
-};
-
->>>>>>>
-#define NORMALIZE_VCN_REG_OFFSET(offset) \
- (offset & 0x1FFFF)
-
-static int vcn_v4_0_3_start_sriov(struct amdgpu_device *adev);
-static void vcn_v4_0_3_set_unified_ring_funcs(struct amdgpu_device *adev);
-static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev);
-static int vcn_v4_0_3_set_powergating_state(void *handle,
- enum amd_powergating_state state);
-static int vcn_v4_0_3_pause_dpg_mode(struct amdgpu_device *adev,
- int inst_idx, struct dpg_pause_state *new_state);
-static void vcn_v4_0_3_unified_ring_set_wptr(struct amdgpu_ring *ring);
-static void vcn_v4_0_3_set_ras_funcs(struct amdgpu_device *adev);
-static void vcn_v4_0_3_enable_ras(struct amdgpu_device *adev,
- int inst_idx, bool indirect);
-/**
- * vcn_v4_0_3_early_init - set function pointers
- *
- * @handle: amdgpu_device pointer
- *
- * Set ring and irq function pointers
- */
-static int vcn_v4_0_3_early_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- /* re-use enc ring as unified ring */
- adev->vcn.num_enc_rings = 1;
-
- vcn_v4_0_3_set_unified_ring_funcs(adev);
- vcn_v4_0_3_set_irq_funcs(adev);
- vcn_v4_0_3_set_ras_funcs(adev);
-
- return amdgpu_vcn_early_init(adev);
-}
-
-/**
- * vcn_v4_0_3_sw_init - sw init for VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Load firmware and sw initialization
- */
-static int vcn_v4_0_3_sw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_ring *ring;
- int i, r, vcn_inst;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
- uint32_t *ptr;
-
- r = amdgpu_vcn_sw_init(adev);
- if (r)
- return r;
-
- amdgpu_vcn_setup_ucode(adev);
-
- r = amdgpu_vcn_resume(adev);
- if (r)
- return r;
-
- /* VCN DEC TRAP */
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_VCN,
- VCN_4_0__SRCID__UVD_ENC_GENERAL_PURPOSE, &adev->vcn.inst->irq);
- if (r)
- return r;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
-
- vcn_inst = GET_INST(VCN, i);
-
- ring = &adev->vcn.inst[i].ring_enc[0];
- ring->use_doorbell = true;
-
- if (!amdgpu_sriov_vf(adev))
- ring->doorbell_index =
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 9 * vcn_inst;
- else
- ring->doorbell_index =
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 32 * vcn_inst;
-
- ring->vm_hub = AMDGPU_MMHUB0(adev->vcn.inst[i].aid_id);
- sprintf(ring->name, "vcn_unified_%d", adev->vcn.inst[i].aid_id);
- r = amdgpu_ring_init(adev, ring, 512, &adev->vcn.inst->irq, 0,
- AMDGPU_RING_PRIO_DEFAULT,
- &adev->vcn.inst[i].sched_score);
- if (r)
- return r;
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->present_flag_0 = cpu_to_le32(AMDGPU_FW_SHARED_FLAG_0_UNIFIED_QUEUE);
- fw_shared->sq.is_enabled = true;
-
- if (amdgpu_vcnfw_log)
- amdgpu_vcn_fwlog_init(&adev->vcn.inst[i]);
- }
-
- if (amdgpu_sriov_vf(adev)) {
- r = amdgpu_virt_alloc_mm_table(adev);
- if (r)
- return r;
- }
-
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)
- adev->vcn.pause_dpg_mode = vcn_v4_0_3_pause_dpg_mode;
-
- if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- r = amdgpu_vcn_ras_sw_init(adev);
- if (r) {
- dev_err(adev->dev, "Failed to initialize vcn ras block!\n");
- return r;
- }
- }
-
- /* Allocate memory for VCN IP Dump buffer */
- ptr = kcalloc(adev->vcn.num_vcn_inst * reg_count, sizeof(uint32_t), GFP_KERNEL);
- if (!ptr) {
- DRM_ERROR("Failed to allocate memory for VCN IP Dump\n");
- adev->vcn.ip_dump = NULL;
- } else {
- adev->vcn.ip_dump = ptr;
- }
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_sw_fini - sw fini for VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * VCN suspend and free up sw allocation
- */
-static int vcn_v4_0_3_sw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, r, idx;
-
- if (drm_dev_enter(&adev->ddev, &idx)) {
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->present_flag_0 = 0;
- fw_shared->sq.is_enabled = cpu_to_le32(false);
- }
- drm_dev_exit(idx);
- }
-
- if (amdgpu_sriov_vf(adev))
- amdgpu_virt_free_mm_table(adev);
-
- r = amdgpu_vcn_suspend(adev);
- if (r)
- return r;
-
- r = amdgpu_vcn_sw_fini(adev);
-
- kfree(adev->vcn.ip_dump);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_hw_init - start and test VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Initialize the hardware, boot up the VCPU and do some testing
- */
-static int vcn_v4_0_3_hw_init(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- struct amdgpu_ring *ring;
- int i, r, vcn_inst;
-
- if (amdgpu_sriov_vf(adev)) {
- r = vcn_v4_0_3_start_sriov(adev);
- if (r)
- return r;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ring = &adev->vcn.inst[i].ring_enc[0];
- ring->wptr = 0;
- ring->wptr_old = 0;
- vcn_v4_0_3_unified_ring_set_wptr(ring);
- ring->sched.ready = true;
- }
- } else {
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- vcn_inst = GET_INST(VCN, i);
- ring = &adev->vcn.inst[i].ring_enc[0];
-
- if (ring->use_doorbell) {
- adev->nbio.funcs->vcn_doorbell_range(
- adev, ring->use_doorbell,
- (adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
- 9 * vcn_inst,
- adev->vcn.inst[i].aid_id);
-
- WREG32_SOC15(
- VCN, GET_INST(VCN, ring->me),
- regVCN_RB1_DB_CTRL,
- ring->doorbell_index
- << VCN_RB1_DB_CTRL__OFFSET__SHIFT |
- VCN_RB1_DB_CTRL__EN_MASK);
-
- /* Read DB_CTRL to flush the write DB_CTRL command. */
- RREG32_SOC15(
- VCN, GET_INST(VCN, ring->me),
- regVCN_RB1_DB_CTRL);
- }
-
- r = amdgpu_ring_test_helper(ring);
- if (r)
- return r;
- }
- }
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_hw_fini - stop the hardware block
- *
- * @handle: amdgpu_device pointer
- *
- * Stop the VCN block, mark ring as not ready any more
- */
-static int vcn_v4_0_3_hw_fini(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
- cancel_delayed_work_sync(&adev->vcn.idle_work);
-
- if (adev->vcn.cur_state != AMD_PG_STATE_GATE)
- vcn_v4_0_3_set_powergating_state(adev, AMD_PG_STATE_GATE);
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_suspend - suspend VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * HW fini and suspend VCN block
- */
-static int vcn_v4_0_3_suspend(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = vcn_v4_0_3_hw_fini(adev);
- if (r)
- return r;
-
- r = amdgpu_vcn_suspend(adev);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_resume - resume VCN block
- *
- * @handle: amdgpu_device pointer
- *
- * Resume firmware and hw init VCN block
- */
-static int vcn_v4_0_3_resume(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int r;
-
- r = amdgpu_vcn_resume(adev);
- if (r)
- return r;
-
- r = vcn_v4_0_3_hw_init(adev);
-
- return r;
-}
-
-/**
- * vcn_v4_0_3_mc_resume - memory controller programming
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Let the VCN memory controller know it's offsets
- */
-static void vcn_v4_0_3_mc_resume(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t offset, size, vcn_inst;
- const struct common_firmware_header *hdr;
-
- hdr = (const struct common_firmware_header *)adev->vcn.fw[inst_idx]->data;
- size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);
-
- vcn_inst = GET_INST(VCN, inst_idx);
- /* cache window 0: fw */
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + inst_idx]
- .tmr_mc_addr_lo));
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + inst_idx]
- .tmr_mc_addr_hi));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET0, 0);
- offset = 0;
- } else {
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr));
- WREG32_SOC15(VCN, vcn_inst,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr));
- offset = size;
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET0,
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
- }
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE0, size);
-
- /* cache window 1: stack */
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset));
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET1, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE1,
- AMDGPU_VCN_STACK_SIZE);
-
- /* cache window 2: context */
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE));
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_OFFSET2, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_CACHE_SIZE2,
- AMDGPU_VCN_CONTEXT_SIZE);
-
- /* non-cache window */
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW,
- lower_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr));
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH,
- upper_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_VCPU_NONCACHE_OFFSET0, 0);
- WREG32_SOC15(
- VCN, vcn_inst, regUVD_VCPU_NONCACHE_SIZE0,
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)));
-}
-
-/**
- * vcn_v4_0_3_mc_resume_dpg_mode - memory controller programming for dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Let the VCN memory controller know it's offsets with dpg mode
- */
-static void vcn_v4_0_3_mc_resume_dpg_mode(struct amdgpu_device *adev, int inst_idx, bool indirect)
-{
- uint32_t offset, size;
- const struct common_firmware_header *hdr;
-
- hdr = (const struct common_firmware_header *)adev->vcn.fw[inst_idx]->data;
- size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8);
-
- /* cache window 0: fw */
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- if (!indirect) {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN +
- inst_idx].tmr_mc_addr_lo), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- (adev->firmware.ucode[AMDGPU_UCODE_ID_VCN +
- inst_idx].tmr_mc_addr_hi), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0), 0, 0, indirect);
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0), 0, 0, indirect);
- }
- offset = 0;
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr), 0, indirect);
- offset = size;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET0),
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3, 0, indirect);
- }
-
- if (!indirect)
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE0), size, 0, indirect);
- else
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE0), 0, 0, indirect);
-
- /* cache window 1: stack */
- if (!indirect) {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET1), 0, 0, indirect);
- } else {
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET1), 0, 0, indirect);
- }
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE1), AMDGPU_VCN_STACK_SIZE, 0, indirect);
-
- /* cache window 2: context */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_OFFSET2), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CACHE_SIZE2), AMDGPU_VCN_CONTEXT_SIZE, 0, indirect);
-
- /* non-cache window */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[inst_idx].fw_shared.gpu_addr), 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_NONCACHE_OFFSET0), 0, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_NONCACHE_SIZE0),
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)), 0, indirect);
-
- /* VCN global tiling registers */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_GFX8_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_GFX10_ADDR_CONFIG), adev->gfx.config.gb_addr_config, 0, indirect);
-}
-
-/**
- * vcn_v4_0_3_disable_clock_gating - disable VCN clock gating
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Disable clock gating for VCN block
- */
-static void vcn_v4_0_3_disable_clock_gating(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t data;
- int vcn_inst;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* VCN disable CGC */
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK;
- data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_GATE);
- data &= ~(UVD_CGC_GATE__SYS_MASK
- | UVD_CGC_GATE__MPEG2_MASK
- | UVD_CGC_GATE__REGS_MASK
- | UVD_CGC_GATE__RBC_MASK
- | UVD_CGC_GATE__LMI_MC_MASK
- | UVD_CGC_GATE__LMI_UMC_MASK
- | UVD_CGC_GATE__MPC_MASK
- | UVD_CGC_GATE__LBSI_MASK
- | UVD_CGC_GATE__LRBBM_MASK
- | UVD_CGC_GATE__WCB_MASK
- | UVD_CGC_GATE__VCPU_MASK
- | UVD_CGC_GATE__MMSCH_MASK);
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_GATE, data);
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_CGC_GATE, 0, 0xFFFFFFFF);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data &= ~(UVD_CGC_CTRL__SYS_MODE_MASK
- | UVD_CGC_CTRL__MPEG2_MODE_MASK
- | UVD_CGC_CTRL__REGS_MODE_MASK
- | UVD_CGC_CTRL__RBC_MODE_MASK
- | UVD_CGC_CTRL__LMI_MC_MODE_MASK
- | UVD_CGC_CTRL__LMI_UMC_MODE_MASK
- | UVD_CGC_CTRL__MPC_MODE_MASK
- | UVD_CGC_CTRL__LBSI_MODE_MASK
- | UVD_CGC_CTRL__LRBBM_MODE_MASK
- | UVD_CGC_CTRL__WCB_MODE_MASK
- | UVD_CGC_CTRL__VCPU_MODE_MASK
- | UVD_CGC_CTRL__MMSCH_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_GATE);
- data |= (UVD_SUVD_CGC_GATE__SRE_MASK
- | UVD_SUVD_CGC_GATE__SIT_MASK
- | UVD_SUVD_CGC_GATE__SMP_MASK
- | UVD_SUVD_CGC_GATE__SCM_MASK
- | UVD_SUVD_CGC_GATE__SDB_MASK
- | UVD_SUVD_CGC_GATE__SRE_H264_MASK
- | UVD_SUVD_CGC_GATE__SRE_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SIT_H264_MASK
- | UVD_SUVD_CGC_GATE__SIT_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SCM_H264_MASK
- | UVD_SUVD_CGC_GATE__SCM_HEVC_MASK
- | UVD_SUVD_CGC_GATE__SDB_H264_MASK
- | UVD_SUVD_CGC_GATE__SDB_HEVC_MASK
- | UVD_SUVD_CGC_GATE__ENT_MASK
- | UVD_SUVD_CGC_GATE__SIT_HEVC_DEC_MASK
- | UVD_SUVD_CGC_GATE__SITE_MASK
- | UVD_SUVD_CGC_GATE__SRE_VP9_MASK
- | UVD_SUVD_CGC_GATE__SCM_VP9_MASK
- | UVD_SUVD_CGC_GATE__SIT_VP9_DEC_MASK
- | UVD_SUVD_CGC_GATE__SDB_VP9_MASK
- | UVD_SUVD_CGC_GATE__IME_HEVC_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_GATE, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL);
- data &= ~(UVD_SUVD_CGC_CTRL__SRE_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SIT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SMP_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK
- | UVD_SUVD_CGC_CTRL__ENT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__IME_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SITE_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL, data);
-}
-
-/**
- * vcn_v4_0_3_disable_clock_gating_dpg_mode - disable VCN clock gating dpg mode
- *
- * @adev: amdgpu_device pointer
- * @sram_sel: sram select
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Disable clock gating for VCN block with dpg mode
- */
-static void vcn_v4_0_3_disable_clock_gating_dpg_mode(struct amdgpu_device *adev, uint8_t sram_sel,
- int inst_idx, uint8_t indirect)
-{
- uint32_t reg_data = 0;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- /* enable sw clock gating control */
- reg_data = 0 << UVD_CGC_CTRL__DYN_CLOCK_MODE__SHIFT;
- reg_data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- reg_data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- reg_data &= ~(UVD_CGC_CTRL__SYS_MODE_MASK |
- UVD_CGC_CTRL__MPEG2_MODE_MASK |
- UVD_CGC_CTRL__REGS_MODE_MASK |
- UVD_CGC_CTRL__RBC_MODE_MASK |
- UVD_CGC_CTRL__LMI_MC_MODE_MASK |
- UVD_CGC_CTRL__LMI_UMC_MODE_MASK |
- UVD_CGC_CTRL__IDCT_MODE_MASK |
- UVD_CGC_CTRL__MPRD_MODE_MASK |
- UVD_CGC_CTRL__MPC_MODE_MASK |
- UVD_CGC_CTRL__LBSI_MODE_MASK |
- UVD_CGC_CTRL__LRBBM_MODE_MASK |
- UVD_CGC_CTRL__WCB_MODE_MASK |
- UVD_CGC_CTRL__VCPU_MODE_MASK);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_CGC_CTRL), reg_data, sram_sel, indirect);
-
- /* turn off clock gating */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_CGC_GATE), 0, sram_sel, indirect);
-
- /* turn on SUVD clock gating */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_SUVD_CGC_GATE), 1, sram_sel, indirect);
-
- /* turn on sw mode in UVD_SUVD_CGC_CTRL */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_SUVD_CGC_CTRL), 0, sram_sel, indirect);
-}
-
-/**
- * vcn_v4_0_3_enable_clock_gating - enable VCN clock gating
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number
- *
- * Enable clock gating for VCN block
- */
-static void vcn_v4_0_3_enable_clock_gating(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t data;
- int vcn_inst;
-
- if (adev->cg_flags & AMD_CG_SUPPORT_VCN_MGCG)
- return;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* enable VCN CGC */
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data |= 0 << UVD_CGC_CTRL__DYN_CLOCK_MODE__SHIFT;
- data |= 1 << UVD_CGC_CTRL__CLK_GATE_DLY_TIMER__SHIFT;
- data |= 4 << UVD_CGC_CTRL__CLK_OFF_DELAY__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL);
- data |= (UVD_CGC_CTRL__SYS_MODE_MASK
- | UVD_CGC_CTRL__MPEG2_MODE_MASK
- | UVD_CGC_CTRL__REGS_MODE_MASK
- | UVD_CGC_CTRL__RBC_MODE_MASK
- | UVD_CGC_CTRL__LMI_MC_MODE_MASK
- | UVD_CGC_CTRL__LMI_UMC_MODE_MASK
- | UVD_CGC_CTRL__MPC_MODE_MASK
- | UVD_CGC_CTRL__LBSI_MODE_MASK
- | UVD_CGC_CTRL__LRBBM_MODE_MASK
- | UVD_CGC_CTRL__WCB_MODE_MASK
- | UVD_CGC_CTRL__VCPU_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_CGC_CTRL, data);
-
- data = RREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL);
- data |= (UVD_SUVD_CGC_CTRL__SRE_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SIT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SMP_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK
- | UVD_SUVD_CGC_CTRL__ENT_MODE_MASK
- | UVD_SUVD_CGC_CTRL__IME_MODE_MASK
- | UVD_SUVD_CGC_CTRL__SITE_MODE_MASK);
- WREG32_SOC15(VCN, vcn_inst, regUVD_SUVD_CGC_CTRL, data);
-}
-
-/**
- * vcn_v4_0_3_start_dpg_mode - VCN start with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @indirect: indirectly write sram
- *
- * Start VCN block with dpg mode
- */
-static int vcn_v4_0_3_start_dpg_mode(struct amdgpu_device *adev, int inst_idx, bool indirect)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared =
- adev->vcn.inst[inst_idx].fw_shared.cpu_addr;
- struct amdgpu_ring *ring;
- int vcn_inst;
- uint32_t tmp;
-
- vcn_inst = GET_INST(VCN, inst_idx);
- /* disable register anti-hang mechanism */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_POWER_STATUS), 1,
- ~UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
- /* enable dynamic power gating mode */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_POWER_STATUS);
- tmp |= UVD_POWER_STATUS__UVD_PG_MODE_MASK;
- tmp |= UVD_POWER_STATUS__UVD_PG_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_POWER_STATUS, tmp);
-
- if (indirect) {
- DRM_DEV_DEBUG(adev->dev, "VCN %d start: on AID %d",
- inst_idx, adev->vcn.inst[inst_idx].aid_id);
- adev->vcn.inst[inst_idx].dpg_sram_curr_addr =
- (uint32_t *)adev->vcn.inst[inst_idx].dpg_sram_cpu_addr;
- /* Use dummy register 0xDEADBEEF passing AID selection to PSP FW */
- WREG32_SOC15_DPG_MODE(inst_idx, 0xDEADBEEF,
- adev->vcn.inst[inst_idx].aid_id, 0, true);
- }
-
- /* enable clock gating */
- vcn_v4_0_3_disable_clock_gating_dpg_mode(adev, 0, inst_idx, indirect);
-
- /* enable VCPU clock */
- tmp = (0xFF << UVD_VCPU_CNTL__PRB_TIMEOUT_VAL__SHIFT);
- tmp |= UVD_VCPU_CNTL__CLK_EN_MASK;
- tmp |= UVD_VCPU_CNTL__BLK_RST_MASK;
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CNTL), tmp, 0, indirect);
-
- /* disable master interrupt */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MASTINT_EN), 0, 0, indirect);
-
- /* setup regUVD_LMI_CTRL */
- tmp = (UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
- UVD_LMI_CTRL__REQ_MODE_MASK |
- UVD_LMI_CTRL__CRC_RESET_MASK |
- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK |
- (8 << UVD_LMI_CTRL__WRITE_CLEAN_TIMER__SHIFT) |
- 0x00100000L);
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_CTRL), tmp, 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_CNTL),
- 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT, 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUXA0),
- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)), 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUXB0),
- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)), 0, indirect);
-
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MPC_SET_MUX),
- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)), 0, indirect);
-
- vcn_v4_0_3_mc_resume_dpg_mode(adev, inst_idx, indirect);
-
- tmp = (0xFF << UVD_VCPU_CNTL__PRB_TIMEOUT_VAL__SHIFT);
- tmp |= UVD_VCPU_CNTL__CLK_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_VCPU_CNTL), tmp, 0, indirect);
-
- /* enable LMI MC and UMC channels */
- tmp = 0x1f << UVD_LMI_CTRL2__RE_OFLD_MIF_WR_REQ_NUM__SHIFT;
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_LMI_CTRL2), tmp, 0, indirect);
-
- vcn_v4_0_3_enable_ras(adev, inst_idx, indirect);
-
- /* enable master interrupt */
- WREG32_SOC15_DPG_MODE(inst_idx, SOC15_DPG_MODE_OFFSET(
- VCN, 0, regUVD_MASTINT_EN),
- UVD_MASTINT_EN__VCPU_EN_MASK, 0, indirect);
-
- if (indirect)
- amdgpu_vcn_psp_update_sram(adev, inst_idx, AMDGPU_UCODE_ID_VCN0_RAM);
-
- ring = &adev->vcn.inst[inst_idx].ring_enc[0];
-
- /* program the RB_BASE for ring buffer */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_LO,
- lower_32_bits(ring->gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_HI,
- upper_32_bits(ring->gpu_addr));
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_SIZE,
- ring->ring_size / sizeof(uint32_t));
-
- /* resetting ring, fw should not check RB ring */
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp &= ~(VCN_RB_ENABLE__RB_EN_MASK);
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
- fw_shared->sq.queue_mode |= FW_QUEUE_RING_RESET;
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_RPTR, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR, 0);
- ring->wptr = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp |= VCN_RB_ENABLE__RB_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
- fw_shared->sq.queue_mode &= ~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF);
-
- /*resetting done, fw can check RB ring */
- fw_shared->sq.queue_mode &= cpu_to_le32(~FW_QUEUE_RING_RESET);
-
- return 0;
-}
-
-static int vcn_v4_0_3_start_sriov(struct amdgpu_device *adev)
-{
- int i, vcn_inst;
- struct amdgpu_ring *ring_enc;
- uint64_t cache_addr;
- uint64_t rb_enc_addr;
- uint64_t ctx_addr;
- uint32_t param, resp, expected;
- uint32_t offset, cache_size;
- uint32_t tmp, timeout;
-
- struct amdgpu_mm_table *table = &adev->virt.mm_table;
- uint32_t *table_loc;
- uint32_t table_size;
- uint32_t size, size_dw;
- uint32_t init_status;
- uint32_t enabled_vcn;
-
- struct mmsch_v4_0_cmd_direct_write
- direct_wt = { {0} };
- struct mmsch_v4_0_cmd_direct_read_modify_write
- direct_rd_mod_wt = { {0} };
- struct mmsch_v4_0_cmd_end end = { {0} };
- struct mmsch_v4_0_3_init_header header;
-
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- volatile struct amdgpu_fw_shared_rb_setup *rb_setup;
-
- direct_wt.cmd_header.command_type =
- MMSCH_COMMAND__DIRECT_REG_WRITE;
- direct_rd_mod_wt.cmd_header.command_type =
- MMSCH_COMMAND__DIRECT_REG_READ_MODIFY_WRITE;
- end.cmd_header.command_type = MMSCH_COMMAND__END;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- vcn_inst = GET_INST(VCN, i);
-
- memset(&header, 0, sizeof(struct mmsch_v4_0_3_init_header));
- header.version = MMSCH_VERSION;
- header.total_size = sizeof(struct mmsch_v4_0_3_init_header) >> 2;
-
- table_loc = (uint32_t *)table->cpu_addr;
- table_loc += header.total_size;
-
- table_size = 0;
-
- MMSCH_V4_0_INSERT_DIRECT_RD_MOD_WT(SOC15_REG_OFFSET(VCN, 0, regUVD_STATUS),
- ~UVD_STATUS__UVD_BUSY, UVD_STATUS__UVD_BUSY);
-
- cache_size = AMDGPU_GPU_PAGE_ALIGN(adev->vcn.fw[i]->size + 4);
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + i].tmr_mc_addr_lo);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- adev->firmware.ucode[AMDGPU_UCODE_ID_VCN + i].tmr_mc_addr_hi);
-
- offset = 0;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET0), 0);
- } else {
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[i].gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[i].gpu_addr));
- offset = cache_size;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET0),
- AMDGPU_UVD_FIRMWARE_OFFSET >> 3);
- }
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE0),
- cache_size);
-
- cache_addr = adev->vcn.inst[vcn_inst].gpu_addr + offset;
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE1_64BIT_BAR_LOW), lower_32_bits(cache_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE1_64BIT_BAR_HIGH), upper_32_bits(cache_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET1), 0);
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE1), AMDGPU_VCN_STACK_SIZE);
-
- cache_addr = adev->vcn.inst[vcn_inst].gpu_addr + offset +
- AMDGPU_VCN_STACK_SIZE;
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE2_64BIT_BAR_LOW), lower_32_bits(cache_addr));
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_CACHE2_64BIT_BAR_HIGH), upper_32_bits(cache_addr));
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_OFFSET2), 0);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_CACHE_SIZE2), AMDGPU_VCN_CONTEXT_SIZE);
-
- fw_shared = adev->vcn.inst[vcn_inst].fw_shared.cpu_addr;
- rb_setup = &fw_shared->rb_setup;
-
- ring_enc = &adev->vcn.inst[vcn_inst].ring_enc[0];
- ring_enc->wptr = 0;
- rb_enc_addr = ring_enc->gpu_addr;
-
- rb_setup->is_rb_enabled_flags |= RB_ENABLED;
- rb_setup->rb_addr_lo = lower_32_bits(rb_enc_addr);
- rb_setup->rb_addr_hi = upper_32_bits(rb_enc_addr);
- rb_setup->rb_size = ring_enc->ring_size / 4;
- fw_shared->present_flag_0 |= cpu_to_le32(AMDGPU_VCN_VF_RB_SETUP_FLAG);
-
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_NC0_64BIT_BAR_LOW),
- lower_32_bits(adev->vcn.inst[vcn_inst].fw_shared.gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_LMI_VCPU_NC0_64BIT_BAR_HIGH),
- upper_32_bits(adev->vcn.inst[vcn_inst].fw_shared.gpu_addr));
- MMSCH_V4_0_INSERT_DIRECT_WT(SOC15_REG_OFFSET(VCN, 0,
- regUVD_VCPU_NONCACHE_SIZE0),
- AMDGPU_GPU_PAGE_ALIGN(sizeof(struct amdgpu_vcn4_fw_shared)));
- MMSCH_V4_0_INSERT_END();
-
- header.vcn0.init_status = 0;
- header.vcn0.table_offset = header.total_size;
- header.vcn0.table_size = table_size;
- header.total_size += table_size;
-
- /* Send init table to mmsch */
- size = sizeof(struct mmsch_v4_0_3_init_header);
- table_loc = (uint32_t *)table->cpu_addr;
- memcpy((void *)table_loc, &header, size);
-
- ctx_addr = table->gpu_addr;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_ADDR_LO, lower_32_bits(ctx_addr));
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_ADDR_HI, upper_32_bits(ctx_addr));
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_VMID);
- tmp &= ~MMSCH_VF_VMID__VF_CTX_VMID_MASK;
- tmp |= (0 << MMSCH_VF_VMID__VF_CTX_VMID__SHIFT);
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_VMID, tmp);
-
- size = header.total_size;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_CTX_SIZE, size);
-
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_RESP, 0);
-
- param = 0x00000001;
- WREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_HOST, param);
- tmp = 0;
- timeout = 1000;
- resp = 0;
- expected = MMSCH_VF_MAILBOX_RESP__OK;
- while (resp != expected) {
- resp = RREG32_SOC15(VCN, vcn_inst, regMMSCH_VF_MAILBOX_RESP);
- if (resp != 0)
- break;
-
- udelay(10);
- tmp = tmp + 10;
- if (tmp >= timeout) {
- DRM_ERROR("failed to init MMSCH. TIME-OUT after %d usec"\
- " waiting for regMMSCH_VF_MAILBOX_RESP "\
- "(expected=0x%08x, readback=0x%08x)\n",
- tmp, expected, resp);
- return -EBUSY;
- }
- }
-
- enabled_vcn = amdgpu_vcn_is_disabled_vcn(adev, VCN_DECODE_RING, 0) ? 1 : 0;
- init_status = ((struct mmsch_v4_0_3_init_header *)(table_loc))->vcn0.init_status;
- if (resp != expected && resp != MMSCH_VF_MAILBOX_RESP__INCOMPLETE
- && init_status != MMSCH_VF_ENGINE_STATUS__PASS) {
- DRM_ERROR("MMSCH init status is incorrect! readback=0x%08x, header init "\
- "status for VCN%x: 0x%x\n", resp, enabled_vcn, init_status);
- }
- }
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_start - VCN start
- *
- * @adev: amdgpu_device pointer
- *
- * Start VCN block
- */
-static int vcn_v4_0_3_start(struct amdgpu_device *adev)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- struct amdgpu_ring *ring;
- int i, j, k, r, vcn_inst;
- uint32_t tmp;
-
- if (adev->pm.dpm_enabled)
- amdgpu_dpm_enable_uvd(adev, true);
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
- r = vcn_v4_0_3_start_dpg_mode(adev, i, adev->vcn.indirect_sram);
- continue;
- }
-
- vcn_inst = GET_INST(VCN, i);
- /* set VCN status busy */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_STATUS) |
- UVD_STATUS__UVD_BUSY;
- WREG32_SOC15(VCN, vcn_inst, regUVD_STATUS, tmp);
-
- /*SW clock gating */
- vcn_v4_0_3_disable_clock_gating(adev, i);
-
- /* enable VCPU clock */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__CLK_EN_MASK,
- ~UVD_VCPU_CNTL__CLK_EN_MASK);
-
- /* disable master interrupt */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_MASTINT_EN), 0,
- ~UVD_MASTINT_EN__VCPU_EN_MASK);
-
- /* enable LMI MC and UMC channels */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_LMI_CTRL2), 0,
- ~UVD_LMI_CTRL2__STALL_ARB_UMC_MASK);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp &= ~UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
- tmp &= ~UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- /* setup regUVD_LMI_CTRL */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL);
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL,
- tmp | UVD_LMI_CTRL__WRITE_CLEAN_TIMER_EN_MASK |
- UVD_LMI_CTRL__MASK_MC_URGENT_MASK |
- UVD_LMI_CTRL__DATA_COHERENCY_EN_MASK |
- UVD_LMI_CTRL__VCPU_DATA_COHERENCY_EN_MASK);
-
- /* setup regUVD_MPC_CNTL */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_MPC_CNTL);
- tmp &= ~UVD_MPC_CNTL__REPLACEMENT_MODE_MASK;
- tmp |= 0x2 << UVD_MPC_CNTL__REPLACEMENT_MODE__SHIFT;
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_CNTL, tmp);
-
- /* setup UVD_MPC_SET_MUXA0 */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUXA0,
- ((0x1 << UVD_MPC_SET_MUXA0__VARA_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXA0__VARA_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXA0__VARA_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXA0__VARA_4__SHIFT)));
-
- /* setup UVD_MPC_SET_MUXB0 */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUXB0,
- ((0x1 << UVD_MPC_SET_MUXB0__VARB_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUXB0__VARB_2__SHIFT) |
- (0x3 << UVD_MPC_SET_MUXB0__VARB_3__SHIFT) |
- (0x4 << UVD_MPC_SET_MUXB0__VARB_4__SHIFT)));
-
- /* setup UVD_MPC_SET_MUX */
- WREG32_SOC15(VCN, vcn_inst, regUVD_MPC_SET_MUX,
- ((0x0 << UVD_MPC_SET_MUX__SET_0__SHIFT) |
- (0x1 << UVD_MPC_SET_MUX__SET_1__SHIFT) |
- (0x2 << UVD_MPC_SET_MUX__SET_2__SHIFT)));
-
- vcn_v4_0_3_mc_resume(adev, i);
-
- /* VCN global tiling registers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_GFX8_ADDR_CONFIG,
- adev->gfx.config.gb_addr_config);
- WREG32_SOC15(VCN, vcn_inst, regUVD_GFX10_ADDR_CONFIG,
- adev->gfx.config.gb_addr_config);
-
- /* unblock VCPU register access */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_RB_ARB_CTRL), 0,
- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
-
- /* release VCPU reset to boot */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL), 0,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- for (j = 0; j < 10; ++j) {
- uint32_t status;
-
- for (k = 0; k < 100; ++k) {
- status = RREG32_SOC15(VCN, vcn_inst,
- regUVD_STATUS);
- if (status & 2)
- break;
- mdelay(10);
- }
- r = 0;
- if (status & 2)
- break;
-
- DRM_DEV_ERROR(adev->dev,
- "VCN decode not responding, trying to reset the VCPU!!!\n");
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst,
- regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__BLK_RST_MASK,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
- mdelay(10);
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst,
- regUVD_VCPU_CNTL),
- 0, ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- mdelay(10);
- r = -1;
- }
-
- if (r) {
- DRM_DEV_ERROR(adev->dev, "VCN decode not responding, giving up!!!\n");
- return r;
- }
-
- /* enable master interrupt */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_MASTINT_EN),
- UVD_MASTINT_EN__VCPU_EN_MASK,
- ~UVD_MASTINT_EN__VCPU_EN_MASK);
-
- /* clear the busy bit of VCN_STATUS */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_STATUS), 0,
- ~(2 << UVD_STATUS__VCPU_REPORT__SHIFT));
-
- ring = &adev->vcn.inst[i].ring_enc[0];
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
-
- /* program the RB_BASE for ring buffer */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_LO,
- lower_32_bits(ring->gpu_addr));
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_BASE_HI,
- upper_32_bits(ring->gpu_addr));
-
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_SIZE,
- ring->ring_size / sizeof(uint32_t));
-
- /* resetting ring, fw should not check RB ring */
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp &= ~(VCN_RB_ENABLE__RB_EN_MASK);
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
-
- /* Initialize the ring buffer's read and write pointers */
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_RPTR, 0);
- WREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR, 0);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE);
- tmp |= VCN_RB_ENABLE__RB_EN_MASK;
- WREG32_SOC15(VCN, vcn_inst, regVCN_RB_ENABLE, tmp);
-
- ring->wptr = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
- fw_shared->sq.queue_mode &=
- cpu_to_le32(~(FW_QUEUE_RING_RESET | FW_QUEUE_DPG_HOLD_OFF));
-
- }
- return 0;
-}
-
-/**
- * vcn_v4_0_3_stop_dpg_mode - VCN stop with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- *
- * Stop VCN block with dpg mode
- */
-static int vcn_v4_0_3_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx)
-{
- uint32_t tmp;
- int vcn_inst;
-
- vcn_inst = GET_INST(VCN, inst_idx);
-
- /* Wait for power status to be 1 */
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_POWER_STATUS, 1,
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
-
- /* wait for read ptr to be equal to write ptr */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_RB_WPTR);
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_RB_RPTR, tmp, 0xFFFFFFFF);
-
- SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_POWER_STATUS, 1,
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK);
-
- /* disable dynamic power gating mode */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_POWER_STATUS), 0,
- ~UVD_POWER_STATUS__UVD_PG_MODE_MASK);
- return 0;
-}
-
-/**
- * vcn_v4_0_3_stop - VCN stop
- *
- * @adev: amdgpu_device pointer
- *
- * Stop VCN block
- */
-static int vcn_v4_0_3_stop(struct amdgpu_device *adev)
-{
- volatile struct amdgpu_vcn4_fw_shared *fw_shared;
- int i, r = 0, vcn_inst;
- uint32_t tmp;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- vcn_inst = GET_INST(VCN, i);
-
- fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
- fw_shared->sq.queue_mode |= FW_QUEUE_DPG_HOLD_OFF;
-
- if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) {
- vcn_v4_0_3_stop_dpg_mode(adev, i);
- continue;
- }
-
- /* wait for vcn idle */
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_STATUS,
- UVD_STATUS__IDLE, 0x7);
- if (r)
- goto Done;
-
- tmp = UVD_LMI_STATUS__VCPU_LMI_WRITE_CLEAN_MASK |
- UVD_LMI_STATUS__READ_CLEAN_MASK |
- UVD_LMI_STATUS__WRITE_CLEAN_MASK |
- UVD_LMI_STATUS__WRITE_CLEAN_RAW_MASK;
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_LMI_STATUS, tmp,
- tmp);
- if (r)
- goto Done;
-
- /* stall UMC channel */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL2);
- tmp |= UVD_LMI_CTRL2__STALL_ARB_UMC_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_LMI_CTRL2, tmp);
- tmp = UVD_LMI_STATUS__UMC_READ_CLEAN_RAW_MASK |
- UVD_LMI_STATUS__UMC_WRITE_CLEAN_RAW_MASK;
- r = SOC15_WAIT_ON_RREG(VCN, vcn_inst, regUVD_LMI_STATUS, tmp,
- tmp);
- if (r)
- goto Done;
-
- /* Unblock VCPU Register access */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_RB_ARB_CTRL),
- UVD_RB_ARB_CTRL__VCPU_DIS_MASK,
- ~UVD_RB_ARB_CTRL__VCPU_DIS_MASK);
-
- /* release VCPU reset to boot */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL),
- UVD_VCPU_CNTL__BLK_RST_MASK,
- ~UVD_VCPU_CNTL__BLK_RST_MASK);
-
- /* disable VCPU clock */
- WREG32_P(SOC15_REG_OFFSET(VCN, vcn_inst, regUVD_VCPU_CNTL), 0,
- ~(UVD_VCPU_CNTL__CLK_EN_MASK));
-
- /* reset LMI UMC/LMI/VCPU */
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp |= UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- tmp = RREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET);
- tmp |= UVD_SOFT_RESET__LMI_SOFT_RESET_MASK;
- WREG32_SOC15(VCN, vcn_inst, regUVD_SOFT_RESET, tmp);
-
- /* clear VCN status */
- WREG32_SOC15(VCN, vcn_inst, regUVD_STATUS, 0);
-
- /* apply HW clock gating */
- vcn_v4_0_3_enable_clock_gating(adev, i);
- }
-Done:
- if (adev->pm.dpm_enabled)
- amdgpu_dpm_enable_uvd(adev, false);
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_pause_dpg_mode - VCN pause with dpg mode
- *
- * @adev: amdgpu_device pointer
- * @inst_idx: instance number index
- * @new_state: pause state
- *
- * Pause dpg mode for VCN block
- */
-static int vcn_v4_0_3_pause_dpg_mode(struct amdgpu_device *adev, int inst_idx,
- struct dpg_pause_state *new_state)
-{
-
- return 0;
-}
-
-/**
- * vcn_v4_0_3_unified_ring_get_rptr - get unified read pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Returns the current hardware unified read pointer
- */
-static uint64_t vcn_v4_0_3_unified_ring_get_rptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- return RREG32_SOC15(VCN, GET_INST(VCN, ring->me), regUVD_RB_RPTR);
-}
-
-/**
- * vcn_v4_0_3_unified_ring_get_wptr - get unified write pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Returns the current hardware unified write pointer
- */
-static uint64_t vcn_v4_0_3_unified_ring_get_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- if (ring->use_doorbell)
- return *ring->wptr_cpu_addr;
- else
- return RREG32_SOC15(VCN, GET_INST(VCN, ring->me),
- regUVD_RB_WPTR);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_reg_wait(struct amdgpu_ring *ring, uint32_t reg,
- uint32_t val, uint32_t mask)
-{
- /* For VF, only local offsets should be used */
- if (amdgpu_sriov_vf(ring->adev))
- reg = NORMALIZE_VCN_REG_OFFSET(reg);
-
- amdgpu_ring_write(ring, VCN_ENC_CMD_REG_WAIT);
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, mask);
- amdgpu_ring_write(ring, val);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_wreg(struct amdgpu_ring *ring, uint32_t reg, uint32_t val)
-{
- /* For VF, only local offsets should be used */
- if (amdgpu_sriov_vf(ring->adev))
- reg = NORMALIZE_VCN_REG_OFFSET(reg);
-
- amdgpu_ring_write(ring, VCN_ENC_CMD_REG_WRITE);
- amdgpu_ring_write(ring, reg << 2);
- amdgpu_ring_write(ring, val);
-}
-
-static void vcn_v4_0_3_enc_ring_emit_vm_flush(struct amdgpu_ring *ring,
- unsigned int vmid, uint64_t pd_addr)
-{
- struct amdgpu_vmhub *hub = &ring->adev->vmhub[ring->vm_hub];
-
- pd_addr = amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
-
- /* wait for reg writes */
- vcn_v4_0_3_enc_ring_emit_reg_wait(ring, hub->ctx0_ptb_addr_lo32 +
- vmid * hub->ctx_addr_distance,
- lower_32_bits(pd_addr), 0xffffffff);
-}
-
-static void vcn_v4_0_3_ring_emit_hdp_flush(struct amdgpu_ring *ring)
-{
- /* VCN engine access for HDP flush doesn't work when RRMT is enabled.
- * This is a workaround to avoid any HDP flush through VCN ring.
- */
-}
-
-/**
- * vcn_v4_0_3_unified_ring_set_wptr - set enc write pointer
- *
- * @ring: amdgpu_ring pointer
- *
- * Commits the enc write pointer to the hardware
- */
-static void vcn_v4_0_3_unified_ring_set_wptr(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
-
- if (ring != &adev->vcn.inst[ring->me].ring_enc[0])
- DRM_ERROR("wrong ring id is identified in %s", __func__);
-
- if (ring->use_doorbell) {
- *ring->wptr_cpu_addr = lower_32_bits(ring->wptr);
- WDOORBELL32(ring->doorbell_index, lower_32_bits(ring->wptr));
- } else {
- WREG32_SOC15(VCN, GET_INST(VCN, ring->me), regUVD_RB_WPTR,
- lower_32_bits(ring->wptr));
- }
-}
-
-static const struct amdgpu_ring_funcs vcn_v4_0_3_unified_ring_vm_funcs = {
- .type = AMDGPU_RING_TYPE_VCN_ENC,
- .align_mask = 0x3f,
- .nop = VCN_ENC_CMD_NO_OP,
- .get_rptr = vcn_v4_0_3_unified_ring_get_rptr,
- .get_wptr = vcn_v4_0_3_unified_ring_get_wptr,
- .set_wptr = vcn_v4_0_3_unified_ring_set_wptr,
- .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_v4_0_3_enc_ring_emit_vm_flush,
- .emit_hdp_flush = vcn_v4_0_3_ring_emit_hdp_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_v4_0_3_enc_ring_emit_wreg,
- .emit_reg_wait = vcn_v4_0_3_enc_ring_emit_reg_wait,
- .emit_reg_write_reg_wait = amdgpu_ring_emit_reg_write_reg_wait_helper,
-};
-
-/**
- * vcn_v4_0_3_set_unified_ring_funcs - set unified ring functions
- *
- * @adev: amdgpu_device pointer
- *
- * Set unified ring functions
- */
-static void vcn_v4_0_3_set_unified_ring_funcs(struct amdgpu_device *adev)
-{
- int i, vcn_inst;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- adev->vcn.inst[i].ring_enc[0].funcs = &vcn_v4_0_3_unified_ring_vm_funcs;
- adev->vcn.inst[i].ring_enc[0].me = i;
- vcn_inst = GET_INST(VCN, i);
- adev->vcn.inst[i].aid_id =
- vcn_inst / adev->vcn.num_inst_per_aid;
- }
-}
-
-/**
- * vcn_v4_0_3_is_idle - check VCN block is idle
- *
- * @handle: amdgpu_device pointer
- *
- * Check whether VCN block is idle
- */
-static bool vcn_v4_0_3_is_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, ret = 1;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ret &= (RREG32_SOC15(VCN, GET_INST(VCN, i), regUVD_STATUS) ==
- UVD_STATUS__IDLE);
- }
-
- return ret;
-}
-
-/**
- * vcn_v4_0_3_wait_for_idle - wait for VCN block idle
- *
- * @handle: amdgpu_device pointer
- *
- * Wait for VCN block idle
- */
-static int vcn_v4_0_3_wait_for_idle(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, ret = 0;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- ret = SOC15_WAIT_ON_RREG(VCN, GET_INST(VCN, i), regUVD_STATUS,
- UVD_STATUS__IDLE, UVD_STATUS__IDLE);
- if (ret)
- return ret;
- }
-
- return ret;
-}
-
-/* vcn_v4_0_3_set_clockgating_state - set VCN block clockgating state
- *
- * @handle: amdgpu_device pointer
- * @state: clock gating state
- *
- * Set VCN block clockgating state
- */
-static int vcn_v4_0_3_set_clockgating_state(void *handle,
- enum amd_clockgating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- bool enable = state == AMD_CG_STATE_GATE;
- int i;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- if (enable) {
- if (RREG32_SOC15(VCN, GET_INST(VCN, i),
- regUVD_STATUS) != UVD_STATUS__IDLE)
- return -EBUSY;
- vcn_v4_0_3_enable_clock_gating(adev, i);
- } else {
- vcn_v4_0_3_disable_clock_gating(adev, i);
- }
- }
- return 0;
-}
-
-/**
- * vcn_v4_0_3_set_powergating_state - set VCN block powergating state
- *
- * @handle: amdgpu_device pointer
- * @state: power gating state
- *
- * Set VCN block powergating state
- */
-static int vcn_v4_0_3_set_powergating_state(void *handle,
- enum amd_powergating_state state)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int ret;
-
- /* for SRIOV, guest should not control VCN Power-gating
- * MMSCH FW should control Power-gating and clock-gating
- * guest should avoid touching CGC and PG
- */
- if (amdgpu_sriov_vf(adev)) {
- adev->vcn.cur_state = AMD_PG_STATE_UNGATE;
- return 0;
- }
-
- if (state == adev->vcn.cur_state)
- return 0;
-
- if (state == AMD_PG_STATE_GATE)
- ret = vcn_v4_0_3_stop(adev);
- else
- ret = vcn_v4_0_3_start(adev);
-
- if (!ret)
- adev->vcn.cur_state = state;
-
- return ret;
-}
-
-/**
- * vcn_v4_0_3_set_interrupt_state - set VCN block interrupt state
- *
- * @adev: amdgpu_device pointer
- * @source: interrupt sources
- * @type: interrupt types
- * @state: interrupt states
- *
- * Set VCN block interrupt state
- */
-static int vcn_v4_0_3_set_interrupt_state(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- unsigned int type,
- enum amdgpu_interrupt_state state)
-{
- return 0;
-}
-
-/**
- * vcn_v4_0_3_process_interrupt - process VCN block interrupt
- *
- * @adev: amdgpu_device pointer
- * @source: interrupt sources
- * @entry: interrupt entry from clients and sources
- *
- * Process VCN block interrupt
- */
-static int vcn_v4_0_3_process_interrupt(struct amdgpu_device *adev,
- struct amdgpu_irq_src *source,
- struct amdgpu_iv_entry *entry)
-{
- uint32_t i, inst;
-
- i = node_id_to_phys_map[entry->node_id];
-
- DRM_DEV_DEBUG(adev->dev, "IH: VCN TRAP\n");
-
- for (inst = 0; inst < adev->vcn.num_vcn_inst; ++inst)
- if (adev->vcn.inst[inst].aid_id == i)
- break;
-
- if (inst >= adev->vcn.num_vcn_inst) {
- dev_WARN_ONCE(adev->dev, 1,
- "Interrupt received for unknown VCN instance %d",
- entry->node_id);
- return 0;
- }
-
- switch (entry->src_id) {
- case VCN_4_0__SRCID__UVD_ENC_GENERAL_PURPOSE:
- amdgpu_fence_process(&adev->vcn.inst[inst].ring_enc[0]);
- break;
- default:
- DRM_DEV_ERROR(adev->dev, "Unhandled interrupt: %d %d\n",
- entry->src_id, entry->src_data[0]);
- break;
- }
-
- return 0;
-}
-
-static const struct amdgpu_irq_src_funcs vcn_v4_0_3_irq_funcs = {
- .set = vcn_v4_0_3_set_interrupt_state,
- .process = vcn_v4_0_3_process_interrupt,
-};
-
-/**
- * vcn_v4_0_3_set_irq_funcs - set VCN block interrupt irq functions
- *
- * @adev: amdgpu_device pointer
- *
- * Set VCN block interrupt irq functions
- */
-static void vcn_v4_0_3_set_irq_funcs(struct amdgpu_device *adev)
-{
- int i;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
- adev->vcn.inst->irq.num_types++;
- }
- adev->vcn.inst->irq.funcs = &vcn_v4_0_3_irq_funcs;
-}
-
-static void vcn_v4_0_3_print_ip_state(void *handle, struct drm_printer *p)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
- uint32_t inst_off, is_powered;
-
- if (!adev->vcn.ip_dump)
- return;
-
- drm_printf(p, "num_instances:%d\n", adev->vcn.num_vcn_inst);
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- if (adev->vcn.harvest_config & (1 << i)) {
- drm_printf(p, "\nHarvested Instance:VCN%d Skipping dump\n", i);
- continue;
- }
-
- inst_off = i * reg_count;
- is_powered = (adev->vcn.ip_dump[inst_off] &
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1;
-
- if (is_powered) {
- drm_printf(p, "\nActive Instance:VCN%d\n", i);
- for (j = 0; j < reg_count; j++)
- drm_printf(p, "%-50s \t 0x%08x\n", vcn_reg_list_4_0_3[j].reg_name,
- adev->vcn.ip_dump[inst_off + j]);
- } else {
- drm_printf(p, "\nInactive Instance:VCN%d\n", i);
- }
- }
-}
-
-static void vcn_v4_0_3_dump_ip_state(void *handle)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- int i, j;
- bool is_powered;
- uint32_t inst_off, inst_id;
- uint32_t reg_count = ARRAY_SIZE(vcn_reg_list_4_0_3);
-
- if (!adev->vcn.ip_dump)
- return;
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++) {
- if (adev->vcn.harvest_config & (1 << i))
- continue;
-
- inst_id = GET_INST(VCN, i);
- inst_off = i * reg_count;
- /* mmUVD_POWER_STATUS is always readable and is first element of the array */
- adev->vcn.ip_dump[inst_off] = RREG32_SOC15(VCN, inst_id, regUVD_POWER_STATUS);
- is_powered = (adev->vcn.ip_dump[inst_off] &
- UVD_POWER_STATUS__UVD_POWER_STATUS_MASK) != 1;
-
- if (is_powered)
- for (j = 1; j < reg_count; j++)
- adev->vcn.ip_dump[inst_off + j] =
- RREG32(SOC15_REG_ENTRY_OFFSET_INST(vcn_reg_list_4_0_3[j],
- inst_id));
- }
-}
-
-static const struct amd_ip_funcs vcn_v4_0_3_ip_funcs = {
- .name = "vcn_v4_0_3",
- .early_init = vcn_v4_0_3_early_init,
- .late_init = NULL,
- .sw_init = vcn_v4_0_3_sw_init,
- .sw_fini = vcn_v4_0_3_sw_fini,
- .hw_init = vcn_v4_0_3_hw_init,
- .hw_fini = vcn_v4_0_3_hw_fini,
- .suspend = vcn_v4_0_3_suspend,
- .resume = vcn_v4_0_3_resume,
- .is_idle = vcn_v4_0_3_is_idle,
- .wait_for_idle = vcn_v4_0_3_wait_for_idle,
- .check_soft_reset = NULL,
- .pre_soft_reset = NULL,
- .soft_reset = NULL,
- .post_soft_reset = NULL,
- .set_clockgating_state = vcn_v4_0_3_set_clockgating_state,
- .set_powergating_state = vcn_v4_0_3_set_powergating_state,
- .dump_ip_state = vcn_v4_0_3_dump_ip_state,
- .print_ip_state = vcn_v4_0_3_print_ip_state,
-};
-
-const struct amdgpu_ip_block_version vcn_v4_0_3_ip_block = {
- .type = AMD_IP_BLOCK_TYPE_VCN,
- .major = 4,
- .minor = 0,
- .rev = 3,
- .funcs = &vcn_v4_0_3_ip_funcs,
-};
-
-static const struct amdgpu_ras_err_status_reg_entry vcn_v4_0_3_ue_reg_list[] = {
- {AMDGPU_RAS_REG_ENTRY(VCN, 0, regVCN_UE_ERR_STATUS_LO_VIDD, regVCN_UE_ERR_STATUS_HI_VIDD),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "VIDD"},
- {AMDGPU_RAS_REG_ENTRY(VCN, 0, regVCN_UE_ERR_STATUS_LO_VIDV, regVCN_UE_ERR_STATUS_HI_VIDV),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "VIDV"},
-};
-
-static void vcn_v4_0_3_inst_query_ras_error_count(struct amdgpu_device *adev,
- uint32_t vcn_inst,
- void *ras_err_status)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_err_status;
-
- /* vcn v4_0_3 only support query uncorrectable errors */
- amdgpu_ras_inst_query_ras_error_count(adev,
- vcn_v4_0_3_ue_reg_list,
- ARRAY_SIZE(vcn_v4_0_3_ue_reg_list),
- NULL, 0, GET_INST(VCN, vcn_inst),
- AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE,
- &err_data->ue_count);
-}
-
-static void vcn_v4_0_3_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_err_status)
-{
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- dev_warn(adev->dev, "VCN RAS is not supported\n");
- return;
- }
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++)
- vcn_v4_0_3_inst_query_ras_error_count(adev, i, ras_err_status);
-}
-
-static void vcn_v4_0_3_inst_reset_ras_error_count(struct amdgpu_device *adev,
- uint32_t vcn_inst)
-{
- amdgpu_ras_inst_reset_ras_error_count(adev,
- vcn_v4_0_3_ue_reg_list,
- ARRAY_SIZE(vcn_v4_0_3_ue_reg_list),
- GET_INST(VCN, vcn_inst));
-}
-
-static void vcn_v4_0_3_reset_ras_error_count(struct amdgpu_device *adev)
-{
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN)) {
- dev_warn(adev->dev, "VCN RAS is not supported\n");
- return;
- }
-
- for (i = 0; i < adev->vcn.num_vcn_inst; i++)
- vcn_v4_0_3_inst_reset_ras_error_count(adev, i);
-}
-
-static const struct amdgpu_ras_block_hw_ops vcn_v4_0_3_ras_hw_ops = {
- .query_ras_error_count = vcn_v4_0_3_query_ras_error_count,
- .reset_ras_error_count = vcn_v4_0_3_reset_ras_error_count,
-};
-
-static struct amdgpu_vcn_ras vcn_v4_0_3_ras = {
- .ras_block = {
- .hw_ops = &vcn_v4_0_3_ras_hw_ops,
- },
-};
-
-static void vcn_v4_0_3_set_ras_funcs(struct amdgpu_device *adev)
-{
- adev->vcn.ras = &vcn_v4_0_3_ras;
-}
-
-static void vcn_v4_0_3_enable_ras(struct amdgpu_device *adev,
- int inst_idx, bool indirect)
-{
- uint32_t tmp;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__VCN))
- return;
-
- tmp = VCN_RAS_CNTL__VCPU_VCODEC_REARM_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_IH_EN_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_PMI_EN_MASK |
- VCN_RAS_CNTL__VCPU_VCODEC_STALL_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regVCN_RAS_CNTL),
- tmp, 0, indirect);
-
- tmp = UVD_VCPU_INT_EN2__RASCNTL_VCPU_VCODEC_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regUVD_VCPU_INT_EN2),
- tmp, 0, indirect);
-
- tmp = UVD_SYS_INT_EN__RASCNTL_VCPU_VCODEC_EN_MASK;
- WREG32_SOC15_DPG_MODE(inst_idx,
- SOC15_DPG_MODE_OFFSET(VCN, 0, regUVD_SYS_INT_EN),
- tmp, 0, indirect);
-}