amd-drm-next-7.2-2026-05-20:

amdgpu:
 - Userq fixes
 - VPE fix
 - SMU 15 fix
 - Misc fixes
 - VCE fixes
 - DC bios parsing fixes
 - DC aux fix
 - Mode1 reset fix
 - RAS fixes
 - SR-IOV fixes
 - Runtime PM clean up
 - DCN 4.2 updates
 - eDP fixes
 - PSR power module update
 - ISM fixes
 - DP MST fixes
 - Replay power module update
 - Auxless ALPM support
 - ACP memory leaks in error paths fixed
 - Restructure VM state machine
 
 amdkfd:
 - Misc fixes
 - MES 12.1 SDMA updates
 
 radeon:
 - CS parser fix
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQgO5Idg2tXNTSZAr293/aFa7yZ2AUCag38rgAKCRC93/aFa7yZ
 2IwbAQCIj6dqR+DWFC1hjfOuFEfpjlCYGjpi333f51qvC5ZOLwEAjidvjZr5qDQA
 GEDs8+Zagpqy7EUSvPBngfb9jyhoFwg=
 =mRgc
 -----END PGP SIGNATURE-----

Merge tag 'amd-drm-next-7.2-2026-05-20' of https://gitlab.freedesktop.org/agd5f/linux into drm-next

amd-drm-next-7.2-2026-05-20:

amdgpu:
- Userq fixes
- VPE fix
- SMU 15 fix
- Misc fixes
- VCE fixes
- DC bios parsing fixes
- DC aux fix
- Mode1 reset fix
- RAS fixes
- SR-IOV fixes
- Runtime PM clean up
- DCN 4.2 updates
- eDP fixes
- PSR power module update
- ISM fixes
- DP MST fixes
- Replay power module update
- Auxless ALPM support
- ACP memory leaks in error paths fixed
- Restructure VM state machine

amdkfd:
- Misc fixes
- MES 12.1 SDMA updates

radeon:
- CS parser fix

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Alex Deucher <alexander.deucher@amd.com>
Link: https://patch.msgid.link/20260520183200.28877-1-alexander.deucher@amd.com
This commit is contained in:
Dave Airlie 2026-05-21 16:55:55 +10:00
commit 4b18a9d44e
107 changed files with 2988 additions and 2729 deletions

View File

@ -3,7 +3,6 @@
config DRM_AMDGPU
tristate "AMD GPU"
depends on DRM && PCI
depends on !UML
select FW_LOADER
select DRM_CLIENT
select DRM_CLIENT_SELECTION

View File

@ -1074,6 +1074,7 @@ struct amdgpu_device {
long psp_timeout;
uint64_t unique_id;
uint8_t unitid;
uint64_t df_perfmon_config_assign_mask[AMDGPU_MAX_DF_PERFMONS];
/* enable runtime pm on the device */
@ -1153,7 +1154,6 @@ struct amdgpu_device {
bool apu_prefer_gtt;
bool userq_halt_for_enforce_isolation;
struct work_struct userq_reset_work;
struct amdgpu_uid *uid_info;
struct amdgpu_uma_carveout_info uma_info;

View File

@ -228,7 +228,6 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
u64 acp_base;
u32 val = 0;
u32 count = 0;
struct i2s_platform_data *i2s_pdata = NULL;
struct amdgpu_device *adev = ip_block->adev;
@ -272,18 +271,18 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
goto failure;
}
i2s_pdata = kzalloc_objs(struct i2s_platform_data, 1);
if (!i2s_pdata) {
adev->acp.i2s_pdata = kzalloc_objs(struct i2s_platform_data, 1);
if (!adev->acp.i2s_pdata) {
r = -ENOMEM;
goto failure;
}
i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
i2s_pdata[0].cap = DWC_I2S_PLAY | DWC_I2S_RECORD;
i2s_pdata[0].snd_rates = SNDRV_PCM_RATE_8000_96000;
i2s_pdata[0].i2s_reg_comp1 = ACP_I2S_COMP1_CAP_REG_OFFSET;
i2s_pdata[0].i2s_reg_comp2 = ACP_I2S_COMP2_CAP_REG_OFFSET;
adev->acp.i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
adev->acp.i2s_pdata[0].cap = DWC_I2S_PLAY | DWC_I2S_RECORD;
adev->acp.i2s_pdata[0].snd_rates = SNDRV_PCM_RATE_8000_96000;
adev->acp.i2s_pdata[0].i2s_reg_comp1 = ACP_I2S_COMP1_CAP_REG_OFFSET;
adev->acp.i2s_pdata[0].i2s_reg_comp2 = ACP_I2S_COMP2_CAP_REG_OFFSET;
adev->acp.acp_res[0].name = "acp2x_dma";
adev->acp.acp_res[0].flags = IORESOURCE_MEM;
@ -311,7 +310,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
adev->acp.acp_cell[1].id = 1;
adev->acp.acp_cell[1].num_resources = 1;
adev->acp.acp_cell[1].resources = &adev->acp.acp_res[1];
adev->acp.acp_cell[1].platform_data = &i2s_pdata[0];
adev->acp.acp_cell[1].platform_data = &adev->acp.i2s_pdata[0];
adev->acp.acp_cell[1].pdata_size = sizeof(struct i2s_platform_data);
r = mfd_add_devices(adev->acp.parent, 0, adev->acp.acp_cell, 2, NULL, 0, NULL);
if (r)
@ -336,53 +335,53 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
goto failure;
}
i2s_pdata = kzalloc_objs(struct i2s_platform_data, 3);
if (!i2s_pdata) {
adev->acp.i2s_pdata = kzalloc_objs(struct i2s_platform_data, 3);
if (!adev->acp.i2s_pdata) {
r = -ENOMEM;
goto failure;
}
switch (adev->asic_type) {
case CHIP_STONEY:
i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
adev->acp.i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
break;
default:
i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
adev->acp.i2s_pdata[0].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
}
i2s_pdata[0].cap = DWC_I2S_PLAY;
i2s_pdata[0].snd_rates = SNDRV_PCM_RATE_8000_96000;
i2s_pdata[0].i2s_reg_comp1 = ACP_I2S_COMP1_PLAY_REG_OFFSET;
i2s_pdata[0].i2s_reg_comp2 = ACP_I2S_COMP2_PLAY_REG_OFFSET;
adev->acp.i2s_pdata[0].cap = DWC_I2S_PLAY;
adev->acp.i2s_pdata[0].snd_rates = SNDRV_PCM_RATE_8000_96000;
adev->acp.i2s_pdata[0].i2s_reg_comp1 = ACP_I2S_COMP1_PLAY_REG_OFFSET;
adev->acp.i2s_pdata[0].i2s_reg_comp2 = ACP_I2S_COMP2_PLAY_REG_OFFSET;
switch (adev->asic_type) {
case CHIP_STONEY:
i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
DW_I2S_QUIRK_COMP_PARAM1 |
DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
adev->acp.i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
DW_I2S_QUIRK_COMP_PARAM1 |
DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
break;
default:
i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
DW_I2S_QUIRK_COMP_PARAM1;
adev->acp.i2s_pdata[1].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET |
DW_I2S_QUIRK_COMP_PARAM1;
}
i2s_pdata[1].cap = DWC_I2S_RECORD;
i2s_pdata[1].snd_rates = SNDRV_PCM_RATE_8000_96000;
i2s_pdata[1].i2s_reg_comp1 = ACP_I2S_COMP1_CAP_REG_OFFSET;
i2s_pdata[1].i2s_reg_comp2 = ACP_I2S_COMP2_CAP_REG_OFFSET;
adev->acp.i2s_pdata[1].cap = DWC_I2S_RECORD;
adev->acp.i2s_pdata[1].snd_rates = SNDRV_PCM_RATE_8000_96000;
adev->acp.i2s_pdata[1].i2s_reg_comp1 = ACP_I2S_COMP1_CAP_REG_OFFSET;
adev->acp.i2s_pdata[1].i2s_reg_comp2 = ACP_I2S_COMP2_CAP_REG_OFFSET;
i2s_pdata[2].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
adev->acp.i2s_pdata[2].quirks = DW_I2S_QUIRK_COMP_REG_OFFSET;
switch (adev->asic_type) {
case CHIP_STONEY:
i2s_pdata[2].quirks |= DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
adev->acp.i2s_pdata[2].quirks |= DW_I2S_QUIRK_16BIT_IDX_OVERRIDE;
break;
default:
break;
}
i2s_pdata[2].cap = DWC_I2S_PLAY | DWC_I2S_RECORD;
i2s_pdata[2].snd_rates = SNDRV_PCM_RATE_8000_96000;
i2s_pdata[2].i2s_reg_comp1 = ACP_BT_COMP1_REG_OFFSET;
i2s_pdata[2].i2s_reg_comp2 = ACP_BT_COMP2_REG_OFFSET;
adev->acp.i2s_pdata[2].cap = DWC_I2S_PLAY | DWC_I2S_RECORD;
adev->acp.i2s_pdata[2].snd_rates = SNDRV_PCM_RATE_8000_96000;
adev->acp.i2s_pdata[2].i2s_reg_comp1 = ACP_BT_COMP1_REG_OFFSET;
adev->acp.i2s_pdata[2].i2s_reg_comp2 = ACP_BT_COMP2_REG_OFFSET;
adev->acp.acp_res[0].name = "acp2x_dma";
adev->acp.acp_res[0].flags = IORESOURCE_MEM;
@ -420,21 +419,21 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
adev->acp.acp_cell[1].id = 1;
adev->acp.acp_cell[1].num_resources = 1;
adev->acp.acp_cell[1].resources = &adev->acp.acp_res[1];
adev->acp.acp_cell[1].platform_data = &i2s_pdata[0];
adev->acp.acp_cell[1].platform_data = &adev->acp.i2s_pdata[0];
adev->acp.acp_cell[1].pdata_size = sizeof(struct i2s_platform_data);
adev->acp.acp_cell[2].name = "designware-i2s";
adev->acp.acp_cell[2].id = 2;
adev->acp.acp_cell[2].num_resources = 1;
adev->acp.acp_cell[2].resources = &adev->acp.acp_res[2];
adev->acp.acp_cell[2].platform_data = &i2s_pdata[1];
adev->acp.acp_cell[2].platform_data = &adev->acp.i2s_pdata[1];
adev->acp.acp_cell[2].pdata_size = sizeof(struct i2s_platform_data);
adev->acp.acp_cell[3].name = "designware-i2s";
adev->acp.acp_cell[3].id = 3;
adev->acp.acp_cell[3].num_resources = 1;
adev->acp.acp_cell[3].resources = &adev->acp.acp_res[3];
adev->acp.acp_cell[3].platform_data = &i2s_pdata[2];
adev->acp.acp_cell[3].platform_data = &adev->acp.i2s_pdata[2];
adev->acp.acp_cell[3].pdata_size = sizeof(struct i2s_platform_data);
r = mfd_add_devices(adev->acp.parent, 0, adev->acp.acp_cell, ACP_DEVS, NULL, 0, NULL);
@ -491,7 +490,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
return 0;
failure:
kfree(i2s_pdata);
kfree(adev->acp.i2s_pdata);
kfree(adev->acp.acp_res);
kfree(adev->acp.acp_cell);
kfree(adev->acp.acp_genpd);
@ -556,6 +555,7 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block)
acp_genpd_remove_device);
mfd_remove_devices(adev->acp.parent);
kfree(adev->acp.i2s_pdata);
kfree(adev->acp.acp_res);
kfree(adev->acp.acp_genpd);
kfree(adev->acp.acp_cell);

View File

@ -35,6 +35,7 @@ struct amdgpu_acp {
struct mfd_cell *acp_cell;
struct resource *acp_res;
struct acp_pm_domain *acp_genpd;
struct i2s_platform_data *i2s_pdata;
};
extern const struct amdgpu_ip_block_version acp_ip_block;

View File

@ -36,6 +36,9 @@
#include "amdgpu_ras.h"
#include "amdgpu_umc.h"
#include "amdgpu_reset.h"
#if IS_ENABLED(CONFIG_HSA_AMD)
#include "kfd_priv.h"
#endif
/* Total memory size in system memory and all GPU VRAM. Used to
* estimate worst case amount of memory to reserve for page tables
@ -320,6 +323,28 @@ void amdgpu_amdkfd_gpu_reset(struct amdgpu_device *adev)
(void)amdgpu_reset_domain_schedule(adev->reset_domain, &adev->kfd.reset_work);
}
void amdgpu_amdkfd_clear_kfd_mapping(struct amdgpu_device *adev)
{
#if IS_ENABLED(CONFIG_HSA_AMD)
struct kfd_dev *kfd = adev->kfd.dev;
unsigned int i;
if (!kfd)
return;
for (i = 0; i < kfd->num_nodes; i++) {
struct kfd_node *node = kfd->nodes[i];
kfd_dev_unmap_mapping_range(KFD_MMAP_TYPE_DOORBELL |
KFD_MMAP_GPU_ID(node->id),
kfd_doorbell_process_slice(kfd));
kfd_dev_unmap_mapping_range(KFD_MMAP_TYPE_MMIO |
KFD_MMAP_GPU_ID(node->id),
PAGE_SIZE);
}
#endif
}
int amdgpu_amdkfd_alloc_kernel_mem(struct amdgpu_device *adev, size_t size,
u32 domain, void **mem_obj, uint64_t *gpu_addr,
void **cpu_ptr, bool cp_mqd_gfx9)

View File

@ -360,6 +360,7 @@ int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev,
uint64_t size, u32 alloc_flag, int8_t xcp_id);
void amdgpu_amdkfd_unreserve_mem_limit(struct amdgpu_device *adev,
uint64_t size, u32 alloc_flag, int8_t xcp_id);
void amdgpu_amdkfd_clear_kfd_mapping(struct amdgpu_device *adev);
u64 amdgpu_amdkfd_xcp_memory_size(struct amdgpu_device *adev, int xcp_id);

View File

@ -36,6 +36,21 @@
#include "atombios_encoders.h"
#include "bif/bif_4_1_d.h"
/* VBIOS-reported table size is unchecked against the image; cap iterations and
* adev->i2c_bus[] indexing to AMDGPU_MAX_I2C_BUS.
*/
static int amdgpu_atombios_gpio_i2c_num_entries(uint16_t size)
{
u32 bytes;
if (size < sizeof(ATOM_COMMON_TABLE_HEADER))
return 0;
bytes = size - sizeof(ATOM_COMMON_TABLE_HEADER);
return (int)min_t(u32, bytes / sizeof(ATOM_GPIO_I2C_ASSIGMENT),
AMDGPU_MAX_I2C_BUS);
}
static struct amdgpu_i2c_bus_rec amdgpu_atombios_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio)
{
struct amdgpu_i2c_bus_rec i2c;
@ -96,8 +111,7 @@ struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *
if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {
i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
sizeof(ATOM_GPIO_I2C_ASSIGMENT);
num_indices = amdgpu_atombios_gpio_i2c_num_entries(size);
gpio = &i2c_info->asGPIO_Info[0];
for (i = 0; i < num_indices; i++) {
@ -127,8 +141,7 @@ void amdgpu_atombios_i2c_init(struct amdgpu_device *adev)
if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {
i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
sizeof(ATOM_GPIO_I2C_ASSIGMENT);
num_indices = amdgpu_atombios_gpio_i2c_num_entries(size);
gpio = &i2c_info->asGPIO_Info[0];
for (i = 0; i < num_indices; i++) {
@ -158,8 +171,7 @@ void amdgpu_atombios_oem_i2c_init(struct amdgpu_device *adev, u8 i2c_id)
if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) {
i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) /
sizeof(ATOM_GPIO_I2C_ASSIGMENT);
num_indices = amdgpu_atombios_gpio_i2c_num_entries(size);
gpio = &i2c_info->asGPIO_Info[0];
for (i = 0; i < num_indices; i++) {

View File

@ -484,7 +484,7 @@ static u32 amdgpu_cper_ring_get_ent_sz(struct amdgpu_ring *ring, u64 pos)
void amdgpu_cper_ring_write(struct amdgpu_ring *ring, void *src, int count)
{
u64 pos, wptr_old, rptr;
u64 pos, wptr_old, rptr, next_rptr;
int rec_cnt_dw = count >> 2;
u32 chunk, ent_sz;
u8 *s = (u8 *)src;
@ -525,9 +525,19 @@ void amdgpu_cper_ring_write(struct amdgpu_ring *ring, void *src, int count)
do {
ent_sz = amdgpu_cper_ring_get_ent_sz(ring, pos);
next_rptr = rptr;
if (ent_sz >= sizeof(u32))
next_rptr = (rptr + (ent_sz >> 2)) & ring->ptr_mask;
rptr += (ent_sz >> 2);
rptr &= ring->ptr_mask;
if (next_rptr == rptr) {
/* Corrupt entry size, reset the ring to avoid an infinite loop. */
rptr = ring->wptr;
*ring->rptr_cpu_addr = rptr;
ring->count_dw = (ring->ring_size - 4) >> 2;
goto out_unlock;
}
rptr = next_rptr;
*ring->rptr_cpu_addr = rptr;
pos = rptr;
@ -536,6 +546,8 @@ void amdgpu_cper_ring_write(struct amdgpu_ring *ring, void *src, int count)
if (ring->count_dw >= rec_cnt_dw)
ring->count_dw -= rec_cnt_dw;
out_unlock:
mutex_unlock(&ring->adev->cper.ring_lock);
}

View File

@ -2126,6 +2126,9 @@ int amdgpu_debugfs_init(struct amdgpu_device *adev)
debugfs_create_x32("amdgpu_smu_debug", 0600, root,
&adev->pm.smu_debug_mask);
debugfs_create_x64("unique_id", 0444, root, &adev->unique_id);
debugfs_create_x8("unitid", 0444, root, &adev->unitid);
ent = debugfs_create_file("amdgpu_preempt_ib", 0600, root, adev,
&fops_ib_preempt);
if (IS_ERR(ent)) {

View File

@ -3789,7 +3789,6 @@ int amdgpu_device_init(struct amdgpu_device *adev,
}
INIT_WORK(&adev->xgmi_reset_work, amdgpu_device_xgmi_reset_func);
INIT_WORK(&adev->userq_reset_work, amdgpu_userq_reset_work);
amdgpu_coredump_init(adev);
@ -5485,7 +5484,7 @@ static inline void amdgpu_device_stop_pending_resets(struct amdgpu_device *adev)
if (!amdgpu_sriov_vf(adev))
cancel_work(&adev->reset_work);
#endif
cancel_work(&adev->userq_reset_work);
amdgpu_userq_mgr_cancel_reset_work(adev);
if (adev->kfd.dev)
cancel_work(&adev->kfd.reset_work);
@ -5843,6 +5842,12 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
/* We need to lock reset domain only once both for XGMI and single device */
amdgpu_device_recovery_get_reset_lock(adev, &device_list);
/* unmap all the mappings of doorbell and framebuffer to prevent user space from
* accessing them
*/
unmap_mapping_range(adev->ddev.anon_inode->i_mapping, 0, 0, 1);
amdgpu_amdkfd_clear_kfd_mapping(adev);
amdgpu_device_halt_activities(adev, job, reset_context, &device_list,
hive, need_emergency_restart);
if (need_emergency_restart)

View File

@ -305,7 +305,7 @@ static int amdgpu_discovery_get_tmr_info(struct amdgpu_device *adev,
adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_IPD_TABLE_ID].offset;
adev->discovery.size =
adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_IPD_TABLE_ID].size_kb << 10;
if (!adev->discovery.offset || !adev->discovery.size)
if (!adev->discovery.size)
return -EINVAL;
} else {
goto out;

View File

@ -2810,12 +2810,11 @@ static int amdgpu_runtime_idle_check_userq(struct device *dev)
return xa_empty(&adev->userq_doorbell_xa) ? 0 : -EBUSY;
}
static int amdgpu_pmops_runtime_suspend(struct device *dev)
static int amdgpu_pmops_runtime_checks(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(drm_dev);
int ret, i;
int ret;
if (adev->pm.rpm_mode == AMDGPU_RUNPM_NONE) {
pm_runtime_forbid(dev);
@ -2825,7 +2824,27 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
ret = amdgpu_runtime_idle_check_display(dev);
if (ret)
return ret;
ret = amdgpu_runtime_idle_check_userq(dev);
return amdgpu_runtime_idle_check_userq(dev);
}
static int amdgpu_pmops_runtime_idle(struct device *dev)
{
int ret;
ret = amdgpu_pmops_runtime_checks(dev);
pm_runtime_autosuspend(dev);
return ret;
}
static int amdgpu_pmops_runtime_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
struct amdgpu_device *adev = drm_to_adev(drm_dev);
int ret, i;
ret = amdgpu_pmops_runtime_checks(dev);
if (ret)
return ret;
@ -2937,27 +2956,6 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
return 0;
}
static int amdgpu_pmops_runtime_idle(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(drm_dev);
int ret;
if (adev->pm.rpm_mode == AMDGPU_RUNPM_NONE) {
pm_runtime_forbid(dev);
return -EBUSY;
}
ret = amdgpu_runtime_idle_check_display(dev);
if (ret)
goto done;
ret = amdgpu_runtime_idle_check_userq(dev);
done:
pm_runtime_autosuspend(dev);
return ret;
}
static int amdgpu_drm_release(struct inode *inode, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;

View File

@ -32,6 +32,7 @@
#include <linux/pci.h>
#include <linux/dma-buf.h>
#include <linux/dma-fence-unwrap.h>
#include <linux/uaccess.h>
#include <drm/amdgpu_drm.h>
#include <drm/drm_drv.h>
@ -509,6 +510,9 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data,
if (offset_in_page(args->addr | args->size))
return -EINVAL;
if (!access_ok((void __user *)(uintptr_t)args->addr, args->size))
return -EFAULT;
/* reject unknown flag values */
if (args->flags & ~(AMDGPU_GEM_USERPTR_READONLY |
AMDGPU_GEM_USERPTR_ANONONLY | AMDGPU_GEM_USERPTR_VALIDATE |
@ -822,7 +826,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
struct drm_syncobj *timeline_syncobj = NULL;
struct dma_fence_chain *timeline_chain = NULL;
struct drm_exec exec;
uint64_t vm_size;
uint64_t vm_size, tmp;
int r = 0;
/* Validate virtual address range against reserved regions. */
@ -846,7 +850,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
vm_size = adev->vm_manager.max_pfn * AMDGPU_GPU_PAGE_SIZE;
vm_size -= AMDGPU_VA_RESERVED_TOP;
if (args->va_address + args->map_size > vm_size) {
if (check_add_overflow(args->va_address, args->map_size, &tmp) || tmp > vm_size) {
dev_dbg(dev->dev,
"va_address 0x%llx is in top reserved area 0x%llx\n",
args->va_address + args->map_size, vm_size);

View File

@ -199,11 +199,18 @@ int amdgpu_gtt_mgr_alloc_entries(struct amdgpu_gtt_mgr *mgr,
enum drm_mm_insert_mode mode)
{
struct amdgpu_device *adev = container_of(mgr, typeof(*adev), mman.gtt_mgr);
u32 alignment = 0;
int r;
/* Align to TLB L2 cache entry size to work around "V bit HW bug" */
if (adev->asic_type == CHIP_TAHITI) {
alignment = 32 * 1024 / AMDGPU_GPU_PAGE_SIZE;
num_pages = ALIGN(num_pages, alignment);
}
spin_lock(&mgr->lock);
r = drm_mm_insert_node_in_range(&mgr->mm, mm_node, num_pages,
0, GART_ENTRY_WITHOUT_BO_COLOR, 0,
alignment, GART_ENTRY_WITHOUT_BO_COLOR, 0,
adev->gmc.gart_size >> PAGE_SHIFT,
mode);
spin_unlock(&mgr->lock);

View File

@ -312,6 +312,9 @@ int amdgpu_mes_suspend(struct amdgpu_device *adev, u32 xcc_id)
memset(&input, 0x0, sizeof(struct mes_suspend_gang_input));
input.suspend_all_gangs = 1;
input.xcc_id = xcc_id;
if ((amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 1, 0)) &&
((adev->mes.sched_version & AMDGPU_MES_VERSION_MASK) >= 0x71))
input.suspend_all_sdma_gangs = 1;
/*
* Avoid taking any other locks under MES lock to avoid circular

View File

@ -298,6 +298,7 @@ struct mes_unmap_legacy_queue_input {
struct mes_suspend_gang_input {
uint32_t xcc_id;
bool suspend_all_gangs;
bool suspend_all_sdma_gangs;
uint64_t gang_context_addr;
uint64_t suspend_fence_addr;
uint32_t suspend_fence_value;

View File

@ -96,7 +96,8 @@ struct amdgpu_bo_va {
* if non-zero, cannot unmap from GPU because user queues may still access it
*/
unsigned int queue_refcount;
atomic_t userq_va_mapped;
/* Indicates if this buffer is mapped for any user queue. Once set, never reset. */
bool userq_va_mapped;
};
struct amdgpu_bo {

View File

@ -857,7 +857,11 @@ static int psp_load_toc(struct psp_context *psp,
struct psp_gfx_cmd_resp *cmd = acquire_psp_cmd_buf(psp);
/* Copy toc to psp firmware private buffer */
psp_copy_fw(psp, psp->toc.start_addr, psp->toc.size_bytes);
ret = psp_copy_fw(psp, psp->toc.start_addr, psp->toc.size_bytes);
if (ret) {
release_psp_cmd_buf(psp);
return ret;
}
psp_prep_load_toc_cmd_buf(cmd, psp->fw_pri_mc_addr, psp->toc.size_bytes);
@ -1173,8 +1177,11 @@ static int psp_rl_load(struct amdgpu_device *adev)
cmd = acquire_psp_cmd_buf(psp);
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
memcpy(psp->fw_pri_buf, psp->rl.start_addr, psp->rl.size_bytes);
ret = psp_copy_fw(psp, psp->rl.start_addr, psp->rl.size_bytes);
if (ret) {
release_psp_cmd_buf(psp);
return ret;
}
cmd->cmd_id = GFX_CMD_ID_LOAD_IP_FW;
cmd->cmd.cmd_load_ip_fw.fw_phy_addr_lo = lower_32_bits(psp->fw_pri_mc_addr);
@ -1760,8 +1767,12 @@ int psp_ta_load(struct psp_context *psp, struct ta_context *context)
cmd = acquire_psp_cmd_buf(psp);
psp_copy_fw(psp, context->bin_desc.start_addr,
context->bin_desc.size_bytes);
ret = psp_copy_fw(psp, context->bin_desc.start_addr,
context->bin_desc.size_bytes);
if (ret) {
release_psp_cmd_buf(psp);
return ret;
}
if (amdgpu_virt_xgmi_migrate_enabled(psp->adev) &&
context->mem_context.shared_bo)
@ -4562,17 +4573,24 @@ static ssize_t psp_usbc_pd_fw_sysfs_write(struct device *dev,
return count;
}
void psp_copy_fw(struct psp_context *psp, uint8_t *start_addr, uint32_t bin_size)
int psp_copy_fw(struct psp_context *psp, uint8_t *start_addr, uint32_t bin_size)
{
int idx;
if (!drm_dev_enter(adev_to_drm(psp->adev), &idx))
return;
return -ENODEV;
if (!bin_size || bin_size > PSP_1_MEG) {
dev_err(psp->adev->dev, "PSP firmware is invalid\n");
drm_dev_exit(idx);
return -EINVAL;
}
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
memcpy(psp->fw_pri_buf, start_addr, bin_size);
drm_dev_exit(idx);
return 0;
}
/**

View File

@ -638,7 +638,7 @@ int psp_get_fw_attestation_records_addr(struct psp_context *psp,
int psp_update_fw_reservation(struct psp_context *psp);
int psp_load_fw_list(struct psp_context *psp,
struct amdgpu_firmware_info **ucode_list, int ucode_count);
void psp_copy_fw(struct psp_context *psp, uint8_t *start_addr, uint32_t bin_size);
int psp_copy_fw(struct psp_context *psp, uint8_t *start_addr, uint32_t bin_size);
int psp_spatial_partition(struct psp_context *psp, int mode);
int psp_memory_partition(struct psp_context *psp, int mode);

View File

@ -3093,6 +3093,25 @@ static int amdgpu_ras_mca2pa(struct amdgpu_device *adev,
return -EINVAL;
}
static bool __check_record_in_range(struct amdgpu_device *adev,
struct eeprom_table_record *bps, int count)
{
int i;
for (i = 0; i < count; i++) {
if (bps[i].retired_page >=
(adev->gmc.real_vram_size >> AMDGPU_GPU_PAGE_SHIFT)) {
dev_warn(adev->dev,
"Recorded address out of range: 0x%llx, 0x%llx, 0x%x, 0x%x\n",
bps[i].address, bps[i].retired_page,
bps[i].mem_channel, bps[i].mcumc_id);
return false;
}
}
return true;
}
static int __amdgpu_ras_restore_bad_pages(struct amdgpu_device *adev,
struct eeprom_table_record *bps, int count)
{
@ -3100,6 +3119,9 @@ static int __amdgpu_ras_restore_bad_pages(struct amdgpu_device *adev,
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct ras_err_handler_data *data = con->eh_data;
if (!__check_record_in_range(adev, bps, count))
return 0;
for (j = 0; j < count; j++) {
if (!data->space_left &&
amdgpu_ras_realloc_eh_data_space(adev, data, 256)) {
@ -5641,6 +5663,11 @@ int amdgpu_ras_reserve_page(struct amdgpu_device *adev, uint64_t pfn)
uint64_t start = pfn << AMDGPU_GPU_PAGE_SHIFT;
int ret = 0;
if (pfn >= (adev->gmc.real_vram_size >> AMDGPU_GPU_PAGE_SHIFT)) {
dev_warn(adev->dev, "Ignoring out-of-range bad page 0x%llx", start);
return 0;
}
if (amdgpu_ras_check_critical_address(adev, start))
return 0;

View File

@ -1051,6 +1051,7 @@ int amdgpu_ras_eeprom_read_idx(struct amdgpu_ras_eeprom_control *control,
uint64_t ts, end_idx;
int i, ret;
u64 mca, ipid;
u32 cu, mem_channel, mcumc_id;
if (!amdgpu_ras_smu_eeprom_supported(adev))
return 0;
@ -1079,9 +1080,10 @@ int amdgpu_ras_eeprom_read_idx(struct amdgpu_ras_eeprom_control *control,
record[i - rec_idx].err_type = AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE;
adev->umc.ras->mca_ipid_parse(adev, ipid,
(uint32_t *)&(record[i - rec_idx].cu),
(uint32_t *)&(record[i - rec_idx].mem_channel),
(uint32_t *)&(record[i - rec_idx].mcumc_id), NULL);
&cu, &mem_channel, &mcumc_id, NULL);
record[i - rec_idx].cu = (u8)cu;
record[i - rec_idx].mem_channel = (u8)mem_channel;
record[i - rec_idx].mcumc_id = (u8)mcumc_id;
}
return 0;
@ -1632,6 +1634,14 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
}
control->ras_fri = RAS_OFFSET_TO_INDEX(control, hdr->first_rec_offset);
if (hdr->first_rec_offset < control->ras_record_offset ||
control->ras_fri >= control->ras_max_record_count) {
dev_err(adev->dev,
"RAS header invalid, ras_fri: %u, first_rec_offset:0x%x",
control->ras_fri, hdr->first_rec_offset);
return -EINVAL;
}
control->ras_num_mca_recs = 0;
control->ras_num_pa_recs = 0;
return 0;

View File

@ -175,11 +175,14 @@ int amdgpu_seq64_alloc(struct amdgpu_device *adev, u64 *va,
{
unsigned long bit_pos;
bit_pos = find_first_zero_bit(adev->seq64.used, adev->seq64.num_sem);
if (bit_pos >= adev->seq64.num_sem)
return -ENOSPC;
for (;;) {
bit_pos = find_first_zero_bit(adev->seq64.used, adev->seq64.num_sem);
if (bit_pos >= adev->seq64.num_sem)
return -ENOSPC;
__set_bit(bit_pos, adev->seq64.used);
if (!test_and_set_bit(bit_pos, adev->seq64.used))
break;
}
*va = bit_pos * sizeof(u64) + amdgpu_seq64_get_va_base(adev);
@ -205,7 +208,7 @@ void amdgpu_seq64_free(struct amdgpu_device *adev, u64 va)
bit_pos = (va - amdgpu_seq64_get_va_base(adev)) / sizeof(u64);
if (bit_pos < adev->seq64.num_sem)
__clear_bit(bit_pos, adev->seq64.used);
clear_bit(bit_pos, adev->seq64.used);
}
/**

View File

@ -130,6 +130,7 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev,
if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
adev->umc.ras->ras_block.hw_ops->query_ras_error_address &&
adev->umc.max_ras_err_cnt_per_query) {
kfree(err_data->err_addr);
err_data->err_addr =
kzalloc_objs(struct eeprom_table_record,
adev->umc.max_ras_err_cnt_per_query);
@ -160,6 +161,7 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev,
if (adev->umc.ras &&
adev->umc.ras->ecc_info_query_ras_error_address &&
adev->umc.max_ras_err_cnt_per_query) {
kfree(err_data->err_addr);
err_data->err_addr =
kzalloc_objs(struct eeprom_table_record,
adev->umc.max_ras_err_cnt_per_query);

View File

@ -82,19 +82,11 @@ static bool amdgpu_userq_is_reset_type_supported(struct amdgpu_device *adev,
return false;
}
static void amdgpu_userq_gpu_reset(struct amdgpu_device *adev)
{
if (amdgpu_device_should_recover_gpu(adev)) {
amdgpu_reset_domain_schedule(adev->reset_domain,
&adev->userq_reset_work);
/* Wait for the reset job to complete */
flush_work(&adev->userq_reset_work);
}
}
static int
amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
static void amdgpu_userq_mgr_reset_work(struct work_struct *work)
{
struct amdgpu_userq_mgr *uq_mgr =
container_of(work, struct amdgpu_userq_mgr,
reset_work);
struct amdgpu_device *adev = uq_mgr->adev;
const int queue_types[] = {
AMDGPU_RING_TYPE_COMPUTE,
@ -103,12 +95,11 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
};
const int num_queue_types = ARRAY_SIZE(queue_types);
bool gpu_reset = false;
int r = 0;
int i;
int i, r;
if (unlikely(adev->debug_disable_gpu_ring_reset)) {
dev_err(adev->dev, "userq reset disabled by debug mask\n");
return 0;
return;
}
/*
@ -116,7 +107,7 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
* skip all reset detection logic
*/
if (!amdgpu_gpu_recovery)
return 0;
return;
/*
* Iterate through all queue types to detect and reset problematic queues
@ -141,10 +132,19 @@ amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
}
}
if (gpu_reset)
amdgpu_userq_gpu_reset(adev);
if (gpu_reset) {
struct amdgpu_reset_context reset_context;
return r;
memset(&reset_context, 0, sizeof(reset_context));
reset_context.method = AMD_RESET_METHOD_NONE;
reset_context.reset_req_dev = adev;
reset_context.src = AMDGPU_RESET_SRC_USERQ;
set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags);
/*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/
amdgpu_device_gpu_recover(adev, NULL, &reset_context);
}
}
static void amdgpu_userq_hang_detect_work(struct work_struct *work)
@ -153,7 +153,11 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work)
container_of(work, struct amdgpu_usermode_queue,
hang_detect_work.work);
amdgpu_userq_detect_and_reset_queues(queue->userq_mgr);
/*
* Don't schedule the work here! Scheduling or queue work from one reset
* handler to another is illegal if you don't take extra precautions!
*/
amdgpu_userq_mgr_reset_work(&queue->userq_mgr->reset_work);
}
/*
@ -182,8 +186,8 @@ void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue)
break;
}
schedule_delayed_work(&queue->hang_detect_work,
msecs_to_jiffies(timeout_ms));
queue_delayed_work(adev->reset_domain->wq, &queue->hang_detect_work,
msecs_to_jiffies(timeout_ms));
}
void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell)
@ -223,7 +227,7 @@ static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue,
INIT_LIST_HEAD(&va_cursor->list);
va_cursor->gpu_addr = addr;
atomic_set(&va_map->bo_va->userq_va_mapped, 1);
va_map->bo_va->userq_va_mapped = true;
list_add(&va_cursor->list, &queue->userq_va_list);
return 0;
@ -270,7 +274,7 @@ static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr)
dma_resv_assert_held(vm->root.bo->tbo.base.resv);
mapping = amdgpu_vm_bo_lookup_mapping(vm, addr);
if (!IS_ERR_OR_NULL(mapping) && atomic_read(&mapping->bo_va->userq_va_mapped))
if (!IS_ERR_OR_NULL(mapping) && mapping->bo_va->userq_va_mapped)
r = true;
else
r = false;
@ -296,16 +300,8 @@ static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue)
return false;
}
static void amdgpu_userq_buffer_va_list_del(struct amdgpu_bo_va_mapping *mapping,
struct amdgpu_userq_va_cursor *va_cursor)
{
atomic_set(&mapping->bo_va->userq_va_mapped, 0);
list_del(&va_cursor->list);
kfree(va_cursor);
}
static int amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev,
struct amdgpu_usermode_queue *queue)
static void amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev,
struct amdgpu_usermode_queue *queue)
{
struct amdgpu_userq_va_cursor *va_cursor, *tmp;
struct amdgpu_bo_va_mapping *mapping;
@ -315,15 +311,12 @@ static int amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev,
list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) {
mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, va_cursor->gpu_addr);
if (!mapping) {
return -EINVAL;
}
dev_dbg(adev->dev, "delete the userq:%p va:%llx\n",
queue, va_cursor->gpu_addr);
amdgpu_userq_buffer_va_list_del(mapping, va_cursor);
if (mapping)
dev_dbg(adev->dev, "delete the userq:%p va:%llx\n",
queue, va_cursor->gpu_addr);
list_del(&va_cursor->list);
kfree(va_cursor);
}
return 0;
}
static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue)
@ -504,16 +497,20 @@ int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr,
goto free_obj;
}
r = amdgpu_bo_pin(userq_obj->obj, AMDGPU_GEM_DOMAIN_GTT);
if (r)
goto unresv;
r = amdgpu_ttm_alloc_gart(&(userq_obj->obj)->tbo);
if (r) {
drm_file_err(uq_mgr->file, "Failed to alloc GART for userqueue object (%d)", r);
goto unresv;
goto unpin_bo;
}
r = amdgpu_bo_kmap(userq_obj->obj, &userq_obj->cpu_ptr);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map BO for userqueue (%d)", r);
goto unresv;
goto unpin_bo;
}
userq_obj->gpu_addr = amdgpu_bo_gpu_offset(userq_obj->obj);
@ -521,11 +518,13 @@ int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr,
memset(userq_obj->cpu_ptr, 0, size);
return 0;
unpin_bo:
amdgpu_bo_unpin(userq_obj->obj);
unresv:
amdgpu_bo_unreserve(userq_obj->obj);
free_obj:
amdgpu_bo_unref(&userq_obj->obj);
return r;
}
@ -533,6 +532,7 @@ void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_userq_obj *userq_obj)
{
amdgpu_bo_kunmap(userq_obj->obj);
amdgpu_bo_unpin(userq_obj->obj);
amdgpu_bo_unref(&userq_obj->obj);
}
@ -708,14 +708,14 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
const struct amdgpu_userq_funcs *uq_funcs;
struct amdgpu_usermode_queue *queue;
struct amdgpu_db_info db_info;
bool skip_map_queue;
u32 qid;
uint64_t index;
int r = 0;
int priority =
(args->in.flags & AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_MASK) >>
AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_SHIFT;
int priority;
u32 qid;
int r;
priority =
(args->in.flags & AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_MASK)
>> AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_SHIFT;
r = amdgpu_userq_priority_permit(filp, priority);
if (r)
return r;
@ -728,40 +728,43 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
uq_funcs = adev->userq_funcs[args->in.ip_type];
if (!uq_funcs) {
drm_file_err(uq_mgr->file, "Usermode queue is not supported for this IP (%u)\n",
args->in.ip_type);
r = -EINVAL;
goto err_pm_runtime;
}
queue = kzalloc_obj(struct amdgpu_usermode_queue);
if (!queue) {
drm_file_err(uq_mgr->file, "Failed to allocate memory for queue\n");
r = -ENOMEM;
goto err_pm_runtime;
}
kref_init(&queue->refcount);
INIT_LIST_HEAD(&queue->userq_va_list);
queue->doorbell_handle = args->in.doorbell_handle;
queue->queue_type = args->in.ip_type;
queue->vm = &fpriv->vm;
queue->priority = priority;
db_info.queue_type = queue->queue_type;
db_info.doorbell_handle = queue->doorbell_handle;
db_info.db_obj = &queue->db_obj;
db_info.doorbell_offset = args->in.doorbell_offset;
queue->userq_mgr = uq_mgr;
INIT_DELAYED_WORK(&queue->hang_detect_work,
amdgpu_userq_hang_detect_work);
/* Validate the userq virtual address.*/
r = amdgpu_bo_reserve(fpriv->vm.root.bo, false);
mutex_init(&queue->fence_drv_lock);
xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC);
r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv);
if (r)
goto free_queue;
if (amdgpu_userq_input_va_validate(adev, queue, args->in.queue_va, args->in.queue_size) ||
amdgpu_userq_input_va_validate(adev, queue, args->in.rptr_va, AMDGPU_GPU_PAGE_SIZE) ||
amdgpu_userq_input_va_validate(adev, queue, args->in.wptr_va, AMDGPU_GPU_PAGE_SIZE)) {
/* Make sure the queue can actually run with those virtual addresses. */
r = amdgpu_bo_reserve(fpriv->vm.root.bo, false);
if (r)
goto free_fence_drv;
if (amdgpu_userq_input_va_validate(adev, queue, args->in.queue_va,
args->in.queue_size) ||
amdgpu_userq_input_va_validate(adev, queue, args->in.rptr_va,
AMDGPU_GPU_PAGE_SIZE) ||
amdgpu_userq_input_va_validate(adev, queue, args->in.wptr_va,
AMDGPU_GPU_PAGE_SIZE)) {
r = -EINVAL;
amdgpu_bo_unreserve(fpriv->vm.root.bo);
goto clean_mapping;
@ -769,6 +772,10 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
amdgpu_bo_unreserve(fpriv->vm.root.bo);
/* Convert relative doorbell offset into absolute doorbell index */
db_info.queue_type = queue->queue_type;
db_info.doorbell_handle = queue->doorbell_handle;
db_info.db_obj = &queue->db_obj;
db_info.doorbell_offset = args->in.doorbell_offset;
index = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp);
if (index == (uint64_t)-EINVAL) {
drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n");
@ -777,82 +784,64 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
}
queue->doorbell_index = index;
mutex_init(&queue->fence_drv_lock);
xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC);
r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv);
if (r) {
drm_file_err(uq_mgr->file, "Failed to alloc fence driver\n");
goto clean_mapping;
}
r = uq_funcs->mqd_create(queue, &args->in);
if (r) {
drm_file_err(uq_mgr->file, "Failed to create Queue\n");
goto clean_fence_driver;
goto clean_mapping;
}
/* Update VM owner at userq submit-time for page-fault attribution. */
amdgpu_vm_set_task_info(&fpriv->vm);
r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue,
GFP_KERNEL));
if (r)
goto clean_mqd;
amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
/* don't map the queue if scheduling is halted */
if (adev->userq_halt_for_enforce_isolation &&
((queue->queue_type == AMDGPU_HW_IP_GFX) ||
(queue->queue_type == AMDGPU_HW_IP_COMPUTE)))
skip_map_queue = true;
else
skip_map_queue = false;
if (!skip_map_queue) {
if (!adev->userq_halt_for_enforce_isolation ||
((queue->queue_type != AMDGPU_HW_IP_GFX) &&
(queue->queue_type != AMDGPU_HW_IP_COMPUTE))) {
r = amdgpu_userq_map_helper(queue);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
goto clean_mqd;
mutex_unlock(&uq_mgr->userq_mutex);
goto clean_doorbell;
}
}
/* drop this refcount during queue destroy */
kref_init(&queue->refcount);
/* Wait for mode-1 reset to complete */
down_read(&adev->reset_domain->sem);
r = xa_alloc(&uq_mgr->userq_xa, &qid, queue,
XA_LIMIT(1, AMDGPU_MAX_USERQ_COUNT), GFP_KERNEL);
if (r) {
if (!skip_map_queue)
amdgpu_userq_unmap_helper(queue);
r = -ENOMEM;
goto clean_reset_domain;
}
r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue, GFP_KERNEL));
if (r) {
xa_erase(&uq_mgr->userq_xa, qid);
if (!skip_map_queue)
amdgpu_userq_unmap_helper(queue);
goto clean_reset_domain;
}
up_read(&adev->reset_domain->sem);
amdgpu_debugfs_userq_init(filp, queue, qid);
INIT_DELAYED_WORK(&queue->hang_detect_work,
amdgpu_userq_hang_detect_work);
args->out.queue_id = qid;
atomic_inc(&uq_mgr->userq_count[queue->queue_type]);
mutex_unlock(&uq_mgr->userq_mutex);
r = xa_alloc(&uq_mgr->userq_xa, &qid, queue,
XA_LIMIT(1, AMDGPU_MAX_USERQ_COUNT),
GFP_KERNEL);
if (r) {
/*
* This drops the last reference which should take care of
* all cleanup.
*/
amdgpu_userq_put(queue);
return r;
}
amdgpu_debugfs_userq_init(filp, queue, qid);
args->out.queue_id = qid;
return 0;
clean_reset_domain:
up_read(&adev->reset_domain->sem);
clean_doorbell:
xa_erase_irq(&adev->userq_doorbell_xa, index);
clean_mqd:
mutex_unlock(&uq_mgr->userq_mutex);
uq_funcs->mqd_destroy(queue);
clean_fence_driver:
amdgpu_userq_fence_driver_free(queue);
clean_mapping:
amdgpu_bo_reserve(fpriv->vm.root.bo, true);
amdgpu_userq_buffer_vas_list_cleanup(adev, queue);
amdgpu_bo_unreserve(fpriv->vm.root.bo);
mutex_destroy(&queue->fence_drv_lock);
free_fence_drv:
amdgpu_userq_fence_driver_free(queue);
free_queue:
kfree(queue);
err_pm_runtime:
@ -1039,12 +1028,12 @@ amdgpu_userq_bo_validate(struct amdgpu_device *adev, struct drm_exec *exec,
struct amdgpu_bo *bo;
int ret;
spin_lock(&vm->status_lock);
while (!list_empty(&vm->invalidated)) {
bo_va = list_first_entry(&vm->invalidated,
spin_lock(&vm->individual_lock);
while (!list_empty(&vm->always_valid.evicted)) {
bo_va = list_first_entry(&vm->always_valid.evicted,
struct amdgpu_bo_va,
base.vm_status);
spin_unlock(&vm->status_lock);
spin_unlock(&vm->individual_lock);
bo = bo_va->base.bo;
ret = drm_exec_prepare_obj(exec, &bo->tbo.base, 2);
@ -1056,14 +1045,14 @@ amdgpu_userq_bo_validate(struct amdgpu_device *adev, struct drm_exec *exec,
if (ret)
return ret;
/* This moves the bo_va to the done list */
/* This moves the bo_va to the idle list */
ret = amdgpu_vm_bo_update(adev, bo_va, false);
if (ret)
return ret;
spin_lock(&vm->status_lock);
spin_lock(&vm->individual_lock);
}
spin_unlock(&vm->status_lock);
spin_unlock(&vm->individual_lock);
return 0;
}
@ -1095,7 +1084,7 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
if (unlikely(ret))
goto unlock_all;
ret = amdgpu_vm_lock_done_list(vm, &exec, 1);
ret = amdgpu_vm_lock_individual(vm, &exec, 1);
drm_exec_retry_on_contention(&exec);
if (unlikely(ret))
goto unlock_all;
@ -1138,7 +1127,7 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
key = 0;
/* Validate User Ptr BOs */
list_for_each_entry(bo_va, &vm->done, base.vm_status) {
list_for_each_entry(bo_va, &vm->always_valid.idle, base.vm_status) {
bo = bo_va->base.bo;
if (!bo)
continue;
@ -1188,10 +1177,10 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
/*
* We need to wait for all VM updates to finish before restarting the
* queues. Using the done list like that is now ok since everything is
* queues. Using the idle list like that is now ok since everything is
* locked in place.
*/
list_for_each_entry(bo_va, &vm->done, base.vm_status)
list_for_each_entry(bo_va, &vm->always_valid.idle, base.vm_status)
dma_fence_wait(bo_va->last_pt_update, false);
dma_fence_wait(vm->last_update, false);
@ -1252,28 +1241,13 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
if (ret) {
drm_file_err(uq_mgr->file,
"Couldn't unmap all the queues, eviction failed ret=%d\n", ret);
amdgpu_userq_detect_and_reset_queues(uq_mgr);
amdgpu_reset_domain_schedule(uq_mgr->adev->reset_domain,
&uq_mgr->reset_work);
flush_work(&uq_mgr->reset_work);
}
return ret;
}
void amdgpu_userq_reset_work(struct work_struct *work)
{
struct amdgpu_device *adev = container_of(work, struct amdgpu_device,
userq_reset_work);
struct amdgpu_reset_context reset_context;
memset(&reset_context, 0, sizeof(reset_context));
reset_context.method = AMD_RESET_METHOD_NONE;
reset_context.reset_req_dev = adev;
reset_context.src = AMDGPU_RESET_SRC_USERQ;
set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags);
/*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/
amdgpu_device_gpu_recover(adev, NULL, &reset_context);
}
static void
amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
{
@ -1307,9 +1281,24 @@ int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *f
userq_mgr->file = file_priv;
INIT_DELAYED_WORK(&userq_mgr->resume_work, amdgpu_userq_restore_worker);
INIT_WORK(&userq_mgr->reset_work, amdgpu_userq_mgr_reset_work);
return 0;
}
void amdgpu_userq_mgr_cancel_reset_work(struct amdgpu_device *adev)
{
struct xarray *xa = &adev->userq_doorbell_xa;
struct amdgpu_usermode_queue *queue;
unsigned long flags, queue_id;
xa_lock_irqsave(xa, flags);
xa_for_each(xa, queue_id, queue) {
cancel_delayed_work(&queue->hang_detect_work);
cancel_work(&queue->userq_mgr->reset_work);
}
xa_unlock_irqrestore(xa, flags);
}
void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr)
{
cancel_delayed_work_sync(&userq_mgr->resume_work);
@ -1335,6 +1324,14 @@ void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr)
}
xa_destroy(&userq_mgr->userq_xa);
/*
* Drain any in-flight reset_work. By this point all queues are freed
* and userq_count is 0, so if reset_work starts now it exits early.
* We still need to wait in case it was already executing gpu_recover.
*/
cancel_work_sync(&userq_mgr->reset_work);
mutex_destroy(&userq_mgr->userq_mutex);
}

View File

@ -84,7 +84,13 @@ struct amdgpu_usermode_queue {
u32 xcp_id;
int priority;
struct dentry *debugfs_queue;
struct delayed_work hang_detect_work;
/**
* @hang_detect_work:
*
* Delayed work which runs when userq_fences time out.
*/
struct delayed_work hang_detect_work;
struct kref refcount;
struct list_head userq_va_list;
@ -116,6 +122,13 @@ struct amdgpu_userq_mgr {
struct amdgpu_device *adev;
struct delayed_work resume_work;
struct drm_file *file;
/**
* @reset_work:
*
* Reset work which is used when eviction fails.
*/
struct work_struct reset_work;
atomic_t userq_count[AMDGPU_RING_TYPE_MAX];
};
@ -134,6 +147,7 @@ int amdgpu_userq_ioctl(struct drm_device *dev, void *data, struct drm_file *filp
int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *file_priv,
struct amdgpu_device *adev);
void amdgpu_userq_mgr_cancel_reset_work(struct amdgpu_device *adev);
void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr);
void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);

View File

@ -370,51 +370,48 @@ static int amdgpu_userq_fence_read_wptr(struct amdgpu_device *adev,
{
struct amdgpu_bo_va_mapping *mapping;
struct amdgpu_bo *bo;
struct drm_exec exec;
u64 addr, *ptr;
int r;
r = amdgpu_bo_reserve(queue->vm->root.bo, false);
if (r)
return r;
int ret;
addr = queue->userq_prop->wptr_gpu_addr;
addr &= AMDGPU_GMC_HOLE_MASK;
mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, addr >> PAGE_SHIFT);
if (!mapping) {
amdgpu_bo_unreserve(queue->vm->root.bo);
DRM_ERROR("Failed to lookup amdgpu_bo_va_mapping\n");
return -EINVAL;
drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 2);
drm_exec_until_all_locked(&exec) {
ret = amdgpu_vm_lock_pd(queue->vm, &exec, 1);
drm_exec_retry_on_contention(&exec);
if (unlikely(ret))
goto lock_error;
mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, addr >> PAGE_SHIFT);
if (!mapping) {
ret = -EINVAL;
goto lock_error;
}
ret = drm_exec_lock_obj(&exec, &mapping->bo_va->base.bo->tbo.base);
drm_exec_retry_on_contention(&exec);
if (unlikely(ret))
goto lock_error;
}
bo = amdgpu_bo_ref(mapping->bo_va->base.bo);
amdgpu_bo_unreserve(queue->vm->root.bo);
r = amdgpu_bo_reserve(bo, true);
if (r) {
amdgpu_bo_unref(&bo);
DRM_ERROR("Failed to reserve userqueue wptr bo");
return r;
}
r = amdgpu_bo_kmap(bo, (void **)&ptr);
if (r) {
bo = mapping->bo_va->base.bo;
ret = amdgpu_bo_kmap(bo, (void **)&ptr);
if (ret) {
DRM_ERROR("Failed mapping the userqueue wptr bo");
goto map_error;
goto lock_error;
}
*wptr = le64_to_cpu(*ptr);
amdgpu_bo_kunmap(bo);
amdgpu_bo_unreserve(bo);
amdgpu_bo_unref(&bo);
drm_exec_fini(&exec);
return 0;
map_error:
amdgpu_bo_unreserve(bo);
amdgpu_bo_unref(&bo);
return r;
lock_error:
drm_exec_fini(&exec);
return ret;
}
static void

View File

@ -463,6 +463,9 @@ static void amdgpu_virt_add_bad_page(struct amdgpu_device *adev,
static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
{
struct amd_sriov_msg_pf2vf_info_header *pf2vf_info = adev->virt.fw_reserve.p_pf2vf;
struct amdgim_pf2vf_info_v1 *pf2vf_v1;
struct amd_sriov_msg_pf2vf_info *pf2vf;
uint32_t checksum;
uint32_t checkval;
@ -479,7 +482,8 @@ static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
switch (pf2vf_info->version) {
case 1:
checksum = ((struct amdgim_pf2vf_info_v1 *)pf2vf_info)->checksum;
pf2vf_v1 = (struct amdgim_pf2vf_info_v1 *)pf2vf_info;
checksum = pf2vf_v1->checksum;
checkval = amd_sriov_msg_checksum(
adev->virt.fw_reserve.p_pf2vf, pf2vf_info->size,
adev->virt.fw_reserve.checksum_key, checksum);
@ -490,12 +494,12 @@ static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
return -EINVAL;
}
adev->virt.gim_feature =
((struct amdgim_pf2vf_info_v1 *)pf2vf_info)->feature_flags;
adev->virt.gim_feature = pf2vf_v1->feature_flags;
break;
case 2:
/* TODO: missing key, need to add it later */
checksum = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->checksum;
pf2vf = (struct amd_sriov_msg_pf2vf_info *)pf2vf_info;
checksum = pf2vf->checksum;
checkval = amd_sriov_msg_checksum(
adev->virt.fw_reserve.p_pf2vf, pf2vf_info->size,
0, checksum);
@ -507,11 +511,9 @@ static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
}
adev->virt.vf2pf_update_interval_ms =
((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->vf2pf_update_interval_ms;
adev->virt.gim_feature =
((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->feature_flags.all;
adev->virt.reg_access =
((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->reg_access_flags.all;
pf2vf->vf2pf_update_interval_ms;
adev->virt.gim_feature = pf2vf->feature_flags.all;
adev->virt.reg_access = pf2vf->reg_access_flags.all;
adev->virt.decode_max_dimension_pixels = 0;
adev->virt.decode_max_frame_pixels = 0;
@ -519,26 +521,30 @@ static int amdgpu_virt_read_pf2vf_data(struct amdgpu_device *adev)
adev->virt.encode_max_frame_pixels = 0;
adev->virt.is_mm_bw_enabled = false;
for (i = 0; i < AMD_SRIOV_MSG_RESERVE_VCN_INST; i++) {
tmp = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->mm_bw_management[i].decode_max_dimension_pixels;
tmp = pf2vf->mm_bw_management[i].decode_max_dimension_pixels;
adev->virt.decode_max_dimension_pixels = max(tmp, adev->virt.decode_max_dimension_pixels);
tmp = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->mm_bw_management[i].decode_max_frame_pixels;
tmp = pf2vf->mm_bw_management[i].decode_max_frame_pixels;
adev->virt.decode_max_frame_pixels = max(tmp, adev->virt.decode_max_frame_pixels);
tmp = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->mm_bw_management[i].encode_max_dimension_pixels;
tmp = pf2vf->mm_bw_management[i].encode_max_dimension_pixels;
adev->virt.encode_max_dimension_pixels = max(tmp, adev->virt.encode_max_dimension_pixels);
tmp = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->mm_bw_management[i].encode_max_frame_pixels;
tmp = pf2vf->mm_bw_management[i].encode_max_frame_pixels;
adev->virt.encode_max_frame_pixels = max(tmp, adev->virt.encode_max_frame_pixels);
}
if ((adev->virt.decode_max_dimension_pixels > 0) || (adev->virt.encode_max_dimension_pixels > 0))
adev->virt.is_mm_bw_enabled = true;
adev->unique_id =
((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->uuid;
adev->virt.ras_en_caps.all = ((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->ras_en_caps.all;
adev->unique_id = pf2vf->uuid;
adev->unitid = 0;
if (amdgpu_sriov_is_unitid_support(adev))
adev->unitid = pf2vf->unitid;
adev->virt.ras_en_caps.all = pf2vf->ras_en_caps.all;
adev->virt.ras_telemetry_en_caps.all =
((struct amd_sriov_msg_pf2vf_info *)pf2vf_info)->ras_telemetry_en_caps.all;
pf2vf->ras_telemetry_en_caps.all;
break;
default:
dev_err(adev->dev, "invalid pf2vf version: 0x%x\n", pf2vf_info->version);

View File

@ -138,6 +138,47 @@ static void amdgpu_vm_assert_locked(struct amdgpu_vm *vm)
dma_resv_assert_held(vm->root.bo->tbo.base.resv);
}
/* Initialize the amdgpu_vm_bo_status object */
static void amdgpu_vm_bo_status_init(struct amdgpu_vm_bo_status *lists)
{
INIT_LIST_HEAD(&lists->evicted);
INIT_LIST_HEAD(&lists->moved);
INIT_LIST_HEAD(&lists->idle);
}
/*
* Make sure we have the lock to modify the vm_bo status and return the object
* with the status lists.
*/
static struct amdgpu_vm_bo_status *
amdgpu_vm_bo_lock_lists(struct amdgpu_vm_bo_base *vm_bo)
{
struct amdgpu_vm *vm = vm_bo->vm;
struct amdgpu_bo *bo = vm_bo->bo;
if (amdgpu_vm_is_bo_always_valid(vm, bo)) {
/* No extra locking needed, protected by the root PD resv lock */
amdgpu_vm_assert_locked(vm);
if (bo->tbo.type == ttm_bo_type_kernel)
return &vm->kernel;
return &vm->always_valid;
}
spin_lock(&vm_bo->vm->individual_lock);
return &vm->individual;
}
/* Eventually unlock the status list lock again */
static void amdgpu_vm_bo_unlock_lists(struct amdgpu_vm_bo_base *vm_bo)
{
if (amdgpu_vm_is_bo_always_valid(vm_bo->vm, vm_bo->bo))
amdgpu_vm_assert_locked(vm_bo->vm);
else
spin_unlock(&vm_bo->vm->individual_lock);
}
/**
* amdgpu_vm_is_bo_always_valid - check if the BO is VM always valid
*
@ -157,37 +198,44 @@ bool amdgpu_vm_is_bo_always_valid(struct amdgpu_vm *vm, struct amdgpu_bo *bo)
*
* @vm_bo: vm_bo which is evicted
*
* State for PDs/PTs and per VM BOs which are not at the location they should
* be.
* State for vm_bo objects meaning the underlying BO was evicted and need to
* move in place again.
*/
static void amdgpu_vm_bo_evicted(struct amdgpu_vm_bo_base *vm_bo)
{
struct amdgpu_vm *vm = vm_bo->vm;
struct amdgpu_bo *bo = vm_bo->bo;
struct amdgpu_vm_bo_status *lists;
lists = amdgpu_vm_bo_lock_lists(vm_bo);
vm_bo->moved = true;
amdgpu_vm_assert_locked(vm);
spin_lock(&vm_bo->vm->status_lock);
if (bo->tbo.type == ttm_bo_type_kernel)
list_move(&vm_bo->vm_status, &vm->evicted);
else
list_move_tail(&vm_bo->vm_status, &vm->evicted);
spin_unlock(&vm_bo->vm->status_lock);
list_move(&vm_bo->vm_status, &lists->evicted);
amdgpu_vm_bo_unlock_lists(vm_bo);
}
/**
* amdgpu_vm_bo_moved - vm_bo is moved
*
* @vm_bo: vm_bo which is moved
*
* State for per VM BOs which are moved, but that change is not yet reflected
* in the page tables.
* State for vm_bo objects meaning the underlying BO was moved but the new
* location not yet reflected in the page tables.
*/
static void amdgpu_vm_bo_moved(struct amdgpu_vm_bo_base *vm_bo)
{
amdgpu_vm_assert_locked(vm_bo->vm);
spin_lock(&vm_bo->vm->status_lock);
list_move(&vm_bo->vm_status, &vm_bo->vm->moved);
spin_unlock(&vm_bo->vm->status_lock);
struct amdgpu_vm_bo_status *lists;
struct amdgpu_bo *bo = vm_bo->bo;
/*
* The root PD doesn't have a parent PDE and goes directly into the
* idle state.
*/
lists = amdgpu_vm_bo_lock_lists(vm_bo);
if (bo && bo->tbo.type == ttm_bo_type_kernel && !bo->parent) {
vm_bo->moved = false;
list_move(&vm_bo->vm_status, &lists->idle);
} else {
vm_bo->moved = true;
list_move(&vm_bo->vm_status, &lists->moved);
}
amdgpu_vm_bo_unlock_lists(vm_bo);
}
/**
@ -195,120 +243,43 @@ static void amdgpu_vm_bo_moved(struct amdgpu_vm_bo_base *vm_bo)
*
* @vm_bo: vm_bo which is now idle
*
* State for PDs/PTs and per VM BOs which have gone through the state machine
* and are now idle.
* State for vm_bo objects meaning we are done with the state machine and no
* further action is necessary.
*/
static void amdgpu_vm_bo_idle(struct amdgpu_vm_bo_base *vm_bo)
{
amdgpu_vm_assert_locked(vm_bo->vm);
spin_lock(&vm_bo->vm->status_lock);
list_move(&vm_bo->vm_status, &vm_bo->vm->idle);
spin_unlock(&vm_bo->vm->status_lock);
vm_bo->moved = false;
}
struct amdgpu_vm_bo_status *lists;
/**
* amdgpu_vm_bo_invalidated - vm_bo is invalidated
*
* @vm_bo: vm_bo which is now invalidated
*
* State for normal BOs which are invalidated and that change not yet reflected
* in the PTs.
*/
static void amdgpu_vm_bo_invalidated(struct amdgpu_vm_bo_base *vm_bo)
{
spin_lock(&vm_bo->vm->status_lock);
list_move(&vm_bo->vm_status, &vm_bo->vm->invalidated);
spin_unlock(&vm_bo->vm->status_lock);
}
/**
* amdgpu_vm_bo_evicted_user - vm_bo is evicted
*
* @vm_bo: vm_bo which is evicted
*
* State for BOs used by user mode queues which are not at the location they
* should be.
*/
static void amdgpu_vm_bo_evicted_user(struct amdgpu_vm_bo_base *vm_bo)
{
vm_bo->moved = true;
spin_lock(&vm_bo->vm->status_lock);
list_move(&vm_bo->vm_status, &vm_bo->vm->evicted_user);
spin_unlock(&vm_bo->vm->status_lock);
}
/**
* amdgpu_vm_bo_relocated - vm_bo is reloacted
*
* @vm_bo: vm_bo which is relocated
*
* State for PDs/PTs which needs to update their parent PD.
* For the root PD, just move to idle state.
*/
static void amdgpu_vm_bo_relocated(struct amdgpu_vm_bo_base *vm_bo)
{
amdgpu_vm_assert_locked(vm_bo->vm);
if (vm_bo->bo->parent) {
spin_lock(&vm_bo->vm->status_lock);
list_move(&vm_bo->vm_status, &vm_bo->vm->relocated);
spin_unlock(&vm_bo->vm->status_lock);
} else {
amdgpu_vm_bo_idle(vm_bo);
}
}
/**
* amdgpu_vm_bo_done - vm_bo is done
*
* @vm_bo: vm_bo which is now done
*
* State for normal BOs which are invalidated and that change has been updated
* in the PTs.
*/
static void amdgpu_vm_bo_done(struct amdgpu_vm_bo_base *vm_bo)
{
amdgpu_vm_assert_locked(vm_bo->vm);
spin_lock(&vm_bo->vm->status_lock);
list_move(&vm_bo->vm_status, &vm_bo->vm->done);
spin_unlock(&vm_bo->vm->status_lock);
lists = amdgpu_vm_bo_lock_lists(vm_bo);
if (!amdgpu_vm_is_bo_always_valid(vm_bo->vm, vm_bo->bo))
vm_bo->moved = false;
list_move(&vm_bo->vm_status, &lists->idle);
amdgpu_vm_bo_unlock_lists(vm_bo);
}
/**
* amdgpu_vm_bo_reset_state_machine - reset the vm_bo state machine
* @vm: the VM which state machine to reset
*
* Move all vm_bo object in the VM into a state where they will be updated
* again during validation.
* Move all vm_bo object in the VM into a state where their location will be
* updated in the page tables again.
*/
static void amdgpu_vm_bo_reset_state_machine(struct amdgpu_vm *vm)
{
struct amdgpu_vm_bo_base *vm_bo, *tmp;
amdgpu_vm_assert_locked(vm);
list_splice_init(&vm->kernel.idle, &vm->kernel.moved);
list_splice_init(&vm->always_valid.idle, &vm->always_valid.moved);
spin_lock(&vm->status_lock);
list_splice_init(&vm->done, &vm->invalidated);
list_for_each_entry(vm_bo, &vm->invalidated, vm_status)
vm_bo->moved = true;
list_for_each_entry_safe(vm_bo, tmp, &vm->idle, vm_status) {
struct amdgpu_bo *bo = vm_bo->bo;
vm_bo->moved = true;
if (!bo || bo->tbo.type != ttm_bo_type_kernel)
list_move(&vm_bo->vm_status, &vm_bo->vm->moved);
else if (bo->parent)
list_move(&vm_bo->vm_status, &vm_bo->vm->relocated);
}
spin_unlock(&vm->status_lock);
spin_lock(&vm->individual_lock);
list_splice_init(&vm->individual.idle, &vm->individual.moved);
spin_unlock(&vm->individual_lock);
}
/**
* amdgpu_vm_update_shared - helper to update shared memory stat
* @base: base structure for tracking BO usage in a VM
*
* Takes the vm status_lock and updates the shared memory stat. If the basic
* Takes the vm stats_lock and updates the shared memory stat. If the basic
* stat changed (e.g. buffer was moved) amdgpu_vm_update_stats need to be called
* as well.
*/
@ -321,7 +292,7 @@ static void amdgpu_vm_update_shared(struct amdgpu_vm_bo_base *base)
bool shared;
dma_resv_assert_held(bo->tbo.base.resv);
spin_lock(&vm->status_lock);
spin_lock(&vm->stats_lock);
shared = drm_gem_object_is_shared_for_memory_stats(&bo->tbo.base);
if (base->shared != shared) {
base->shared = shared;
@ -333,7 +304,7 @@ static void amdgpu_vm_update_shared(struct amdgpu_vm_bo_base *base)
vm->stats[bo_memtype].drm.private += size;
}
}
spin_unlock(&vm->status_lock);
spin_unlock(&vm->stats_lock);
}
/**
@ -358,11 +329,11 @@ void amdgpu_vm_bo_update_shared(struct amdgpu_bo *bo)
* be bo->tbo.resource
* @sign: if we should add (+1) or subtract (-1) from the stat
*
* Caller need to have the vm status_lock held. Useful for when multiple update
* Caller need to have the vm stats_lock held. Useful for when multiple update
* need to happen at the same time.
*/
static void amdgpu_vm_update_stats_locked(struct amdgpu_vm_bo_base *base,
struct ttm_resource *res, int sign)
struct ttm_resource *res, int sign)
{
struct amdgpu_vm *vm = base->vm;
struct amdgpu_bo *bo = base->bo;
@ -386,7 +357,8 @@ static void amdgpu_vm_update_stats_locked(struct amdgpu_vm_bo_base *base,
*/
if (bo->flags & AMDGPU_GEM_CREATE_DISCARDABLE)
vm->stats[res_memtype].drm.purgeable += size;
if (!(bo->preferred_domains & amdgpu_mem_type_to_domain(res_memtype)))
if (!(bo->preferred_domains &
amdgpu_mem_type_to_domain(res_memtype)))
vm->stats[bo_memtype].evicted += size;
}
}
@ -405,9 +377,9 @@ void amdgpu_vm_update_stats(struct amdgpu_vm_bo_base *base,
{
struct amdgpu_vm *vm = base->vm;
spin_lock(&vm->status_lock);
spin_lock(&vm->stats_lock);
amdgpu_vm_update_stats_locked(base, res, sign);
spin_unlock(&vm->status_lock);
spin_unlock(&vm->stats_lock);
}
/**
@ -428,37 +400,34 @@ void amdgpu_vm_bo_base_init(struct amdgpu_vm_bo_base *base,
base->next = NULL;
INIT_LIST_HEAD(&base->vm_status);
dma_resv_assert_held(vm->root.bo->tbo.base.resv);
if (!bo)
return;
base->next = bo->vm_bo;
bo->vm_bo = base;
spin_lock(&vm->status_lock);
spin_lock(&vm->stats_lock);
base->shared = drm_gem_object_is_shared_for_memory_stats(&bo->tbo.base);
amdgpu_vm_update_stats_locked(base, bo->tbo.resource, +1);
spin_unlock(&vm->status_lock);
spin_unlock(&vm->stats_lock);
if (!amdgpu_vm_is_bo_always_valid(vm, bo))
if (!amdgpu_vm_is_bo_always_valid(vm, bo)) {
amdgpu_vm_bo_idle(base);
return;
dma_resv_assert_held(vm->root.bo->tbo.base.resv);
}
ttm_bo_set_bulk_move(&bo->tbo, &vm->lru_bulk_move);
if (bo->tbo.type == ttm_bo_type_kernel && bo->parent)
amdgpu_vm_bo_relocated(base);
else
amdgpu_vm_bo_idle(base);
if (bo->preferred_domains &
amdgpu_mem_type_to_domain(bo->tbo.resource->mem_type))
return;
/*
* we checked all the prerequisites, but it looks like this per vm bo
* is currently evicted. add the bo to the evicted list to make sure it
* is validated on next vm use to avoid fault.
* */
amdgpu_vm_bo_evicted(base);
* When a per VM isn't in the desired domain put it into the evicted
* state to make sure that it gets validated on the next best occasion.
*/
if (bo->preferred_domains &
amdgpu_mem_type_to_domain(bo->tbo.resource->mem_type))
amdgpu_vm_bo_moved(base);
else
amdgpu_vm_bo_evicted(base);
}
/**
@ -479,41 +448,41 @@ int amdgpu_vm_lock_pd(struct amdgpu_vm *vm, struct drm_exec *exec,
}
/**
* amdgpu_vm_lock_done_list - lock all BOs on the done list
* amdgpu_vm_lock_individual - lock all BOs on the individual idle list
* @vm: vm providing the BOs
* @exec: drm execution context
* @num_fences: number of extra fences to reserve
*
* Lock the BOs on the done list in the DRM execution context.
* Lock the BOs on the individual idle list in the DRM execution context.
*/
int amdgpu_vm_lock_done_list(struct amdgpu_vm *vm, struct drm_exec *exec,
unsigned int num_fences)
int amdgpu_vm_lock_individual(struct amdgpu_vm *vm, struct drm_exec *exec,
unsigned int num_fences)
{
struct list_head *prev = &vm->done;
struct list_head *prev = &vm->individual.idle;
struct amdgpu_bo_va *bo_va;
struct amdgpu_bo *bo;
int ret;
/* We can only trust prev->next while holding the lock */
spin_lock(&vm->status_lock);
while (!list_is_head(prev->next, &vm->done)) {
spin_lock(&vm->individual_lock);
while (!list_is_head(prev->next, &vm->individual.idle)) {
bo_va = list_entry(prev->next, typeof(*bo_va), base.vm_status);
bo = bo_va->base.bo;
if (bo) {
amdgpu_bo_ref(bo);
spin_unlock(&vm->status_lock);
spin_unlock(&vm->individual_lock);
ret = drm_exec_prepare_obj(exec, &bo->tbo.base, 1);
amdgpu_bo_unref(&bo);
if (unlikely(ret))
return ret;
spin_lock(&vm->status_lock);
spin_lock(&vm->individual_lock);
}
prev = prev->next;
}
spin_unlock(&vm->status_lock);
spin_unlock(&vm->individual_lock);
return 0;
}
@ -609,10 +578,10 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm,
void *param)
{
uint64_t new_vm_generation = amdgpu_vm_generation(adev, vm);
struct amdgpu_vm_bo_base *bo_base;
struct amdgpu_bo *bo;
struct amdgpu_vm_bo_base *bo_base, *tmp;
int r;
dma_resv_assert_held(vm->root.bo->tbo.base.resv);
if (vm->generation != new_vm_generation) {
vm->generation = new_vm_generation;
amdgpu_vm_bo_reset_state_machine(vm);
@ -622,50 +591,60 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm,
return r;
}
spin_lock(&vm->status_lock);
while (!list_empty(&vm->evicted)) {
bo_base = list_first_entry(&vm->evicted,
struct amdgpu_vm_bo_base,
vm_status);
spin_unlock(&vm->status_lock);
bo = bo_base->bo;
r = validate(param, bo);
list_for_each_entry_safe(bo_base, tmp, &vm->kernel.evicted, vm_status) {
r = validate(param, bo_base->bo);
if (r)
return r;
if (bo->tbo.type != ttm_bo_type_kernel) {
amdgpu_vm_bo_moved(bo_base);
} else {
vm->update_funcs->map_table(to_amdgpu_bo_vm(bo));
amdgpu_vm_bo_relocated(bo_base);
}
spin_lock(&vm->status_lock);
vm->update_funcs->map_table(to_amdgpu_bo_vm(bo_base->bo));
amdgpu_vm_bo_moved(bo_base);
}
while (ticket && !list_empty(&vm->evicted_user)) {
bo_base = list_first_entry(&vm->evicted_user,
struct amdgpu_vm_bo_base,
vm_status);
spin_unlock(&vm->status_lock);
bo = bo_base->bo;
dma_resv_assert_held(bo->tbo.base.resv);
r = validate(param, bo);
if (r)
return r;
amdgpu_vm_bo_invalidated(bo_base);
spin_lock(&vm->status_lock);
}
spin_unlock(&vm->status_lock);
/*
* As soon as all page tables are in place we can start updating them
* again.
*/
amdgpu_vm_eviction_lock(vm);
vm->evicting = false;
amdgpu_vm_eviction_unlock(vm);
list_for_each_entry_safe(bo_base, tmp, &vm->always_valid.evicted,
vm_status) {
r = validate(param, bo_base->bo);
if (r)
return r;
amdgpu_vm_bo_moved(bo_base);
}
if (!ticket)
return 0;
spin_lock(&vm->individual_lock);
restart:
list_for_each_entry(bo_base, &vm->individual.evicted, vm_status) {
struct amdgpu_bo *bo = bo_base->bo;
if (dma_resv_locking_ctx(bo->tbo.base.resv) != ticket)
continue;
spin_unlock(&vm->individual_lock);
r = validate(param, bo);
if (r)
return r;
amdgpu_vm_bo_moved(bo_base);
/* It's a bit inefficient to always jump back to the start, but
* we would need to re-structure the KFD for properly fixing
* that.
*/
spin_lock(&vm->individual_lock);
goto restart;
}
spin_unlock(&vm->individual_lock);
return 0;
}
@ -689,9 +668,7 @@ bool amdgpu_vm_ready(struct amdgpu_vm *vm)
ret = !vm->evicting;
amdgpu_vm_eviction_unlock(vm);
spin_lock(&vm->status_lock);
ret &= list_empty(&vm->evicted);
spin_unlock(&vm->status_lock);
ret &= list_empty(&vm->kernel.evicted);
spin_lock(&vm->immediate.lock);
ret &= !vm->immediate.stopped;
@ -985,18 +962,13 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev,
struct amdgpu_vm *vm, bool immediate)
{
struct amdgpu_vm_update_params params;
struct amdgpu_vm_bo_base *entry;
struct amdgpu_vm_bo_base *entry, *tmp;
bool flush_tlb_needed = false;
LIST_HEAD(relocated);
int r, idx;
amdgpu_vm_assert_locked(vm);
spin_lock(&vm->status_lock);
list_splice_init(&vm->relocated, &relocated);
spin_unlock(&vm->status_lock);
if (list_empty(&relocated))
if (list_empty(&vm->kernel.moved))
return 0;
if (!drm_dev_enter(adev_to_drm(adev), &idx))
@ -1012,7 +984,7 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev,
if (r)
goto error;
list_for_each_entry(entry, &relocated, vm_status) {
list_for_each_entry(entry, &vm->kernel.moved, vm_status) {
/* vm_flush_needed after updating moved PDEs */
flush_tlb_needed |= entry->moved;
@ -1028,11 +1000,8 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev,
if (flush_tlb_needed)
atomic64_inc(&vm->tlb_seq);
while (!list_empty(&relocated)) {
entry = list_first_entry(&relocated, struct amdgpu_vm_bo_base,
vm_status);
list_for_each_entry_safe(entry, tmp, &vm->kernel.moved, vm_status)
amdgpu_vm_bo_idle(entry);
}
error:
drm_dev_exit(idx);
@ -1260,9 +1229,9 @@ int amdgpu_vm_update_range(struct amdgpu_device *adev, struct amdgpu_vm *vm,
void amdgpu_vm_get_memory(struct amdgpu_vm *vm,
struct amdgpu_mem_stats stats[__AMDGPU_PL_NUM])
{
spin_lock(&vm->status_lock);
spin_lock(&vm->stats_lock);
memcpy(stats, vm->stats, sizeof(*stats) * __AMDGPU_PL_NUM);
spin_unlock(&vm->status_lock);
spin_unlock(&vm->stats_lock);
}
/**
@ -1406,7 +1375,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, struct amdgpu_bo_va *bo_va,
else
amdgpu_vm_bo_idle(&bo_va->base);
} else {
amdgpu_vm_bo_done(&bo_va->base);
amdgpu_vm_bo_idle(&bo_va->base);
}
list_splice_init(&bo_va->invalids, &bo_va->valids);
@ -1629,29 +1598,25 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev,
struct amdgpu_vm *vm,
struct ww_acquire_ctx *ticket)
{
struct amdgpu_bo_va *bo_va;
struct amdgpu_bo_va *bo_va, *tmp;
struct dma_resv *resv;
bool clear, unlock;
int r;
spin_lock(&vm->status_lock);
while (!list_empty(&vm->moved)) {
bo_va = list_first_entry(&vm->moved, struct amdgpu_bo_va,
base.vm_status);
spin_unlock(&vm->status_lock);
list_for_each_entry_safe(bo_va, tmp, &vm->always_valid.moved,
base.vm_status) {
/* Per VM BOs never need to bo cleared in the page tables */
r = amdgpu_vm_bo_update(adev, bo_va, false);
if (r)
return r;
spin_lock(&vm->status_lock);
}
while (!list_empty(&vm->invalidated)) {
bo_va = list_first_entry(&vm->invalidated, struct amdgpu_bo_va,
base.vm_status);
spin_lock(&vm->individual_lock);
while (!list_empty(&vm->individual.moved)) {
bo_va = list_first_entry(&vm->individual.moved,
typeof(*bo_va), base.vm_status);
resv = bo_va->base.bo->tbo.base.resv;
spin_unlock(&vm->status_lock);
spin_unlock(&vm->individual_lock);
/* Try to reserve the BO to avoid clearing its ptes */
if (!adev->debug_vm && dma_resv_trylock(resv)) {
@ -1681,11 +1646,11 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev,
drm_gem_is_imported(&bo_va->base.bo->tbo.base) &&
(!bo_va->base.bo->tbo.resource ||
bo_va->base.bo->tbo.resource->mem_type == TTM_PL_SYSTEM))
amdgpu_vm_bo_evicted_user(&bo_va->base);
amdgpu_vm_bo_evicted(&bo_va->base);
spin_lock(&vm->status_lock);
spin_lock(&vm->individual_lock);
}
spin_unlock(&vm->status_lock);
spin_unlock(&vm->individual_lock);
return 0;
}
@ -2002,7 +1967,7 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev,
* during user requests GEM unmap IOCTL except for forcing the unmap
* from user space.
*/
if (unlikely(atomic_read(&bo_va->userq_va_mapped) > 0))
if (unlikely(bo_va->userq_va_mapped))
amdgpu_userq_gem_va_unmap_validate(adev, mapping, saddr);
list_del(&mapping->list);
@ -2223,9 +2188,9 @@ void amdgpu_vm_bo_del(struct amdgpu_device *adev,
}
}
spin_lock(&vm->status_lock);
spin_lock(&vm->individual_lock);
list_del(&bo_va->base.vm_status);
spin_unlock(&vm->status_lock);
spin_unlock(&vm->individual_lock);
list_for_each_entry_safe(mapping, next, &bo_va->valids, list) {
list_del(&mapping->list);
@ -2305,14 +2270,7 @@ void amdgpu_vm_bo_invalidate(struct amdgpu_bo *bo, bool evicted)
if (bo_base->moved)
continue;
bo_base->moved = true;
if (bo->tbo.type == ttm_bo_type_kernel)
amdgpu_vm_bo_relocated(bo_base);
else if (amdgpu_vm_is_bo_always_valid(vm, bo))
amdgpu_vm_bo_moved(bo_base);
else
amdgpu_vm_bo_invalidated(bo_base);
amdgpu_vm_bo_moved(bo_base);
}
}
@ -2333,10 +2291,10 @@ void amdgpu_vm_bo_move(struct amdgpu_bo *bo, struct ttm_resource *new_mem,
for (bo_base = bo->vm_bo; bo_base; bo_base = bo_base->next) {
struct amdgpu_vm *vm = bo_base->vm;
spin_lock(&vm->status_lock);
spin_lock(&vm->stats_lock);
amdgpu_vm_update_stats_locked(bo_base, bo->tbo.resource, -1);
amdgpu_vm_update_stats_locked(bo_base, new_mem, +1);
spin_unlock(&vm->status_lock);
spin_unlock(&vm->stats_lock);
}
amdgpu_vm_bo_invalidate(bo, evicted);
@ -2600,16 +2558,14 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
vm->va = RB_ROOT_CACHED;
for (i = 0; i < AMDGPU_MAX_VMHUBS; i++)
vm->reserved_vmid[i] = NULL;
INIT_LIST_HEAD(&vm->evicted);
INIT_LIST_HEAD(&vm->evicted_user);
INIT_LIST_HEAD(&vm->relocated);
INIT_LIST_HEAD(&vm->moved);
INIT_LIST_HEAD(&vm->idle);
INIT_LIST_HEAD(&vm->invalidated);
spin_lock_init(&vm->status_lock);
amdgpu_vm_bo_status_init(&vm->kernel);
amdgpu_vm_bo_status_init(&vm->always_valid);
spin_lock_init(&vm->individual_lock);
amdgpu_vm_bo_status_init(&vm->individual);
INIT_LIST_HEAD(&vm->freed);
INIT_LIST_HEAD(&vm->done);
INIT_KFIFO(vm->faults);
spin_lock_init(&vm->stats_lock);
r = amdgpu_vm_init_entities(adev, vm);
if (r)
@ -3086,6 +3042,42 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid,
}
#if defined(CONFIG_DEBUG_FS)
/* print the debug info for a specific set of status lists */
static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m,
struct amdgpu_vm_bo_status *lists)
{
struct amdgpu_vm_bo_base *base;
unsigned int id;
id = 0;
seq_puts(m, "\tEvicted BOs:\n");
list_for_each_entry(base, &lists->evicted, vm_status) {
if (!base->bo)
continue;
amdgpu_bo_print_info(id++, base->bo, m);
}
id = 0;
seq_puts(m, "\tMoved BOs:\n");
list_for_each_entry(base, &lists->moved, vm_status) {
if (!base->bo)
continue;
amdgpu_bo_print_info(id++, base->bo, m);
}
id = 0;
seq_puts(m, "\tIdle BOs:\n");
list_for_each_entry(base, &lists->moved, vm_status) {
if (!base->bo)
continue;
amdgpu_bo_print_info(id++, base->bo, m);
}
}
/**
* amdgpu_debugfs_vm_bo_info - print BO info for the VM
*
@ -3096,90 +3088,18 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid,
*/
void amdgpu_debugfs_vm_bo_info(struct amdgpu_vm *vm, struct seq_file *m)
{
struct amdgpu_bo_va *bo_va, *tmp;
u64 total_idle = 0;
u64 total_evicted = 0;
u64 total_relocated = 0;
u64 total_moved = 0;
u64 total_invalidated = 0;
u64 total_done = 0;
unsigned int total_idle_objs = 0;
unsigned int total_evicted_objs = 0;
unsigned int total_relocated_objs = 0;
unsigned int total_moved_objs = 0;
unsigned int total_invalidated_objs = 0;
unsigned int total_done_objs = 0;
unsigned int id = 0;
amdgpu_vm_assert_locked(vm);
spin_lock(&vm->status_lock);
seq_puts(m, "\tIdle BOs:\n");
list_for_each_entry_safe(bo_va, tmp, &vm->idle, base.vm_status) {
if (!bo_va->base.bo)
continue;
total_idle += amdgpu_bo_print_info(id++, bo_va->base.bo, m);
}
total_idle_objs = id;
id = 0;
seq_puts(m, "\tKernel PT/PDs:\n");
amdgpu_debugfs_vm_bo_status_info(m, &vm->kernel);
seq_puts(m, "\tEvicted BOs:\n");
list_for_each_entry_safe(bo_va, tmp, &vm->evicted, base.vm_status) {
if (!bo_va->base.bo)
continue;
total_evicted += amdgpu_bo_print_info(id++, bo_va->base.bo, m);
}
total_evicted_objs = id;
id = 0;
seq_puts(m, "\tPer VM BOs:\n");
amdgpu_debugfs_vm_bo_status_info(m, &vm->always_valid);
seq_puts(m, "\tRelocated BOs:\n");
list_for_each_entry_safe(bo_va, tmp, &vm->relocated, base.vm_status) {
if (!bo_va->base.bo)
continue;
total_relocated += amdgpu_bo_print_info(id++, bo_va->base.bo, m);
}
total_relocated_objs = id;
id = 0;
seq_puts(m, "\tMoved BOs:\n");
list_for_each_entry_safe(bo_va, tmp, &vm->moved, base.vm_status) {
if (!bo_va->base.bo)
continue;
total_moved += amdgpu_bo_print_info(id++, bo_va->base.bo, m);
}
total_moved_objs = id;
id = 0;
seq_puts(m, "\tInvalidated BOs:\n");
list_for_each_entry_safe(bo_va, tmp, &vm->invalidated, base.vm_status) {
if (!bo_va->base.bo)
continue;
total_invalidated += amdgpu_bo_print_info(id++, bo_va->base.bo, m);
}
total_invalidated_objs = id;
id = 0;
seq_puts(m, "\tDone BOs:\n");
list_for_each_entry_safe(bo_va, tmp, &vm->done, base.vm_status) {
if (!bo_va->base.bo)
continue;
total_done += amdgpu_bo_print_info(id++, bo_va->base.bo, m);
}
spin_unlock(&vm->status_lock);
total_done_objs = id;
seq_printf(m, "\tTotal idle size: %12lld\tobjs:\t%d\n", total_idle,
total_idle_objs);
seq_printf(m, "\tTotal evicted size: %12lld\tobjs:\t%d\n", total_evicted,
total_evicted_objs);
seq_printf(m, "\tTotal relocated size: %12lld\tobjs:\t%d\n", total_relocated,
total_relocated_objs);
seq_printf(m, "\tTotal moved size: %12lld\tobjs:\t%d\n", total_moved,
total_moved_objs);
seq_printf(m, "\tTotal invalidated size: %12lld\tobjs:\t%d\n", total_invalidated,
total_invalidated_objs);
seq_printf(m, "\tTotal done size: %12lld\tobjs:\t%d\n", total_done,
total_done_objs);
seq_puts(m, "\tIndividual BOs:\n");
spin_lock(&vm->individual_lock);
amdgpu_debugfs_vm_bo_status_info(m, &vm->individual);
spin_unlock(&vm->individual_lock);
}
#endif

View File

@ -205,17 +205,34 @@ struct amdgpu_vm_bo_base {
/* protected by bo being reserved */
struct amdgpu_vm_bo_base *next;
/* protected by vm status_lock */
/* protected by vm reservation and invalidated_lock */
struct list_head vm_status;
/* if the bo is counted as shared in mem stats
* protected by vm status_lock */
* protected by vm BO being reserved */
bool shared;
/* protected by the BO being reserved */
bool moved;
};
/*
* The following status lists contain amdgpu_vm_bo_base objects for
* either PD/PTs, per VM BOs or BOs with individual resv object.
*
* The state transits are: evicted -> moved -> idle
*/
struct amdgpu_vm_bo_status {
/* BOs evicted which need to move into place again */
struct list_head evicted;
/* BOs which moved but new location hasn't been updated in the PDs/PTs */
struct list_head moved;
/* BOs done with the state machine and need no further action */
struct list_head idle;
};
/* provided by hw blocks that can write ptes, e.g., sdma */
struct amdgpu_vm_pte_funcs {
/* number of dw to reserve per operation */
@ -345,47 +362,29 @@ struct amdgpu_vm {
bool evicting;
unsigned int saved_flags;
/* Lock to protect vm_bo add/del/move on all lists of vm */
spinlock_t status_lock;
/* Memory statistics for this vm, protected by status_lock */
/* Memory statistics for this vm, protected by stats_lock */
spinlock_t stats_lock;
struct amdgpu_mem_stats stats[__AMDGPU_PL_NUM];
/* BO's belonging to PD/PT which are internal to the kernel. */
struct amdgpu_vm_bo_status kernel;
/*
* The following lists contain amdgpu_vm_bo_base objects for either
* PDs, PTs or per VM BOs. The state transits are:
*
* evicted -> relocated (PDs, PTs) or moved (per VM BOs) -> idle
* BOs allocated by userspace where the dma_resv is shared with the
* root PD
*/
/* Per-VM and PT BOs who needs a validation */
struct list_head evicted;
/* PT BOs which relocated and their parent need an update */
struct list_head relocated;
/* per VM BOs moved, but not yet updated in the PT */
struct list_head moved;
/* All BOs of this VM not currently in the state machine */
struct list_head idle;
struct amdgpu_vm_bo_status always_valid;
/*
* The following lists contain amdgpu_vm_bo_base objects for BOs which
* have their own dma_resv object and not depend on the root PD. Their
* state transits are:
* have their own dma_resv object and not depend on the root PD.
*
* evicted_user or invalidated -> done
* Lists are protected by the individual_lock.
*/
spinlock_t individual_lock;
/* BOs for user mode queues that need a validation */
struct list_head evicted_user;
/* regular invalidated BOs, but not yet updated in the PT */
struct list_head invalidated;
/* BOs which are invalidated, has been updated in the PTs */
struct list_head done;
/* Userspace BOs with individual resv object */
struct amdgpu_vm_bo_status individual;
/*
* This list contains amdgpu_bo_va_mapping objects which have been freed
@ -507,8 +506,8 @@ int amdgpu_vm_make_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm);
void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm);
int amdgpu_vm_lock_pd(struct amdgpu_vm *vm, struct drm_exec *exec,
unsigned int num_fences);
int amdgpu_vm_lock_done_list(struct amdgpu_vm *vm, struct drm_exec *exec,
unsigned int num_fences);
int amdgpu_vm_lock_individual(struct amdgpu_vm *vm, struct drm_exec *exec,
unsigned int num_fences);
bool amdgpu_vm_ready(struct amdgpu_vm *vm);
uint64_t amdgpu_vm_generation(struct amdgpu_device *adev, struct amdgpu_vm *vm);
int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm,

View File

@ -544,9 +544,7 @@ static void amdgpu_vm_pt_free(struct amdgpu_vm_bo_base *entry)
entry->bo->vm_bo = NULL;
ttm_bo_set_bulk_move(&entry->bo->tbo, NULL);
spin_lock(&entry->vm->status_lock);
list_del(&entry->vm_status);
spin_unlock(&entry->vm->status_lock);
amdgpu_bo_unref(&entry->bo);
}
@ -590,7 +588,6 @@ static void amdgpu_vm_pt_add_list(struct amdgpu_vm_update_params *params,
struct amdgpu_vm_pt_cursor seek;
struct amdgpu_vm_bo_base *entry;
spin_lock(&params->vm->status_lock);
for_each_amdgpu_vm_pt_dfs_safe(params->adev, params->vm, cursor, seek, entry) {
if (entry && entry->bo)
list_move(&entry->vm_status, &params->tlb_flush_waitlist);
@ -598,7 +595,6 @@ static void amdgpu_vm_pt_add_list(struct amdgpu_vm_update_params *params,
/* enter start node now */
list_move(&cursor->entry->vm_status, &params->tlb_flush_waitlist);
spin_unlock(&params->vm->status_lock);
}
/**

View File

@ -566,6 +566,11 @@ static void vpe_ring_emit_fence(struct amdgpu_ring *ring, uint64_t addr,
amdgpu_ring_write(ring, 0);
}
/* WA: Force sync after TRAP to avoid VPE1 fail to power off */
if (ring->adev->vpe.collaborate_mode) {
amdgpu_ring_write(ring, VPE_CMD_HEADER(VPE_CMD_OPCODE_COLLAB_SYNC, 0));
amdgpu_ring_write(ring, 0xabcd);
}
}
static void vpe_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
@ -972,7 +977,7 @@ static const struct amdgpu_ring_funcs vpe_ring_funcs = {
.emit_frame_size =
5 + /* vpe_ring_init_cond_exec */
6 + /* vpe_ring_emit_pipeline_sync */
10 + 10 + 10 + /* vpe_ring_emit_fence */
12 + 12 + 12 + /* vpe_ring_emit_fence */
/* vpe_ring_emit_vm_flush */
SOC15_FLUSH_GPU_TLB_NUM_WREG * 3 +
SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 6,

View File

@ -295,7 +295,7 @@ struct amd_sriov_msg_pf2vf_info {
uint32_t vf2pf_update_interval_ms;
/* identification in ROCm SMI */
uint64_t uuid;
uint32_t fcn_idx;
uint32_t pad;
/* flags to indicate which register access method VF should use */
union amd_sriov_reg_access_flags reg_access_flags;
/* MM BW management */

View File

@ -59,6 +59,9 @@
#define ATOM_CMD_TIMEOUT_SEC 20
/* Limit ATOM command table recursion (calltable) to avoid kernel stack overflow. */
#define ATOM_EXECUTE_MAX_DEPTH 32
typedef struct {
struct atom_context *ctx;
uint32_t *ps, *ws;
@ -1229,6 +1232,13 @@ static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index,
if (!base)
return -EINVAL;
if (ctx->execute_depth >= ATOM_EXECUTE_MAX_DEPTH) {
DRM_ERROR("atombios command table nesting exceeded limit (%u)\n",
ATOM_EXECUTE_MAX_DEPTH);
return -ELOOP;
}
ctx->execute_depth++;
len = CU16(base + ATOM_CT_SIZE_PTR);
ws = CU8(base + ATOM_CT_WS_PTR);
ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK;
@ -1285,6 +1295,7 @@ static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index,
free:
if (ws)
kfree(ectx.ws);
ctx->execute_depth--;
return ret;
}

View File

@ -153,6 +153,9 @@ struct atom_context {
uint8_t vbios_ver_str[STRLEN_NORMAL];
uint8_t date[STRLEN_NORMAL];
uint8_t build_num[STRLEN_NORMAL];
/* Nesting depth for ATOM_OP_CALLTABLE */
unsigned int execute_depth;
};
extern int amdgpu_atom_debug;

View File

@ -480,6 +480,7 @@ static int mes_v12_1_suspend_gang(struct amdgpu_mes *mes,
mes_suspend_gang_pkt.header.dwsize = API_FRAME_SIZE_IN_DWORDS;
mes_suspend_gang_pkt.suspend_all_gangs = input->suspend_all_gangs;
mes_suspend_gang_pkt.suspend_all_sdma_gangs = input->suspend_all_sdma_gangs;
mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr;
mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr;
mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value;

View File

@ -217,7 +217,9 @@ static int psp_v11_0_bootloader_load_component(struct psp_context *psp,
return ret;
/* Copy PSP System Driver binary to memory */
psp_copy_fw(psp, bin_desc->start_addr, bin_desc->size_bytes);
ret = psp_copy_fw(psp, bin_desc->start_addr, bin_desc->size_bytes);
if (ret)
return ret;
/* Provide the sys driver to bootloader */
WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_36,
@ -263,7 +265,9 @@ static int psp_v11_0_bootloader_load_sos(struct psp_context *psp)
return ret;
/* Copy Secure OS binary to PSP memory */
psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
ret = psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
if (ret)
return ret;
/* Provide the PSP secure OS to bootloader */
WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_36,

View File

@ -87,7 +87,9 @@ static int psp_v12_0_bootloader_load_sysdrv(struct psp_context *psp)
return ret;
/* Copy PSP System Driver binary to memory */
psp_copy_fw(psp, psp->sys.start_addr, psp->sys.size_bytes);
ret = psp_copy_fw(psp, psp->sys.start_addr, psp->sys.size_bytes);
if (ret)
return ret;
/* Provide the sys driver to bootloader */
WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_36,
@ -123,7 +125,9 @@ static int psp_v12_0_bootloader_load_sos(struct psp_context *psp)
return ret;
/* Copy Secure OS binary to PSP memory */
psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
ret = psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
if (ret)
return ret;
/* Provide the PSP secure OS to bootloader */
WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_36,

View File

@ -271,10 +271,9 @@ static int psp_v13_0_bootloader_load_component(struct psp_context *psp,
if (ret)
return ret;
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
/* Copy PSP KDB binary to memory */
memcpy(psp->fw_pri_buf, bin_desc->start_addr, bin_desc->size_bytes);
ret = psp_copy_fw(psp, bin_desc->start_addr, bin_desc->size_bytes);
if (ret)
return ret;
/* Provide the PSP KDB to bootloader */
WREG32_SOC15(MP0, 0, regMP0_SMN_C2PMSG_36,
@ -353,10 +352,9 @@ static int psp_v13_0_bootloader_load_sos(struct psp_context *psp)
if (ret)
return ret;
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
/* Copy Secure OS binary to PSP memory */
memcpy(psp->fw_pri_buf, psp->sos.start_addr, psp->sos.size_bytes);
ret = psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
if (ret)
return ret;
/* Provide the PSP secure OS to bootloader */
WREG32_SOC15(MP0, 0, regMP0_SMN_C2PMSG_36,

View File

@ -105,10 +105,9 @@ static int psp_v13_0_4_bootloader_load_component(struct psp_context *psp,
if (ret)
return ret;
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
/* Copy PSP KDB binary to memory */
memcpy(psp->fw_pri_buf, bin_desc->start_addr, bin_desc->size_bytes);
ret = psp_copy_fw(psp, bin_desc->start_addr, bin_desc->size_bytes);
if (ret)
return ret;
/* Provide the PSP KDB to bootloader */
WREG32_SOC15(MP0, 0, regMP0_SMN_C2PMSG_36,
@ -168,10 +167,9 @@ static int psp_v13_0_4_bootloader_load_sos(struct psp_context *psp)
if (ret)
return ret;
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
/* Copy Secure OS binary to PSP memory */
memcpy(psp->fw_pri_buf, psp->sos.start_addr, psp->sos.size_bytes);
ret = psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
if (ret)
return ret;
/* Provide the PSP secure OS to bootloader */
WREG32_SOC15(MP0, 0, regMP0_SMN_C2PMSG_36,

View File

@ -140,10 +140,9 @@ static int psp_v14_0_bootloader_load_component(struct psp_context *psp,
if (ret)
return ret;
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
/* Copy PSP KDB binary to memory */
memcpy(psp->fw_pri_buf, bin_desc->start_addr, bin_desc->size_bytes);
ret = psp_copy_fw(psp, bin_desc->start_addr, bin_desc->size_bytes);
if (ret)
return ret;
/* Provide the PSP KDB to bootloader */
WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_36,
@ -214,10 +213,9 @@ static int psp_v14_0_bootloader_load_sos(struct psp_context *psp)
if (ret)
return ret;
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
/* Copy Secure OS binary to PSP memory */
memcpy(psp->fw_pri_buf, psp->sos.start_addr, psp->sos.size_bytes);
ret = psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
if (ret)
return ret;
/* Provide the PSP secure OS to bootloader */
WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_36,

View File

@ -96,7 +96,9 @@ static int psp_v3_1_bootloader_load_sysdrv(struct psp_context *psp)
return ret;
/* Copy PSP System Driver binary to memory */
psp_copy_fw(psp, psp->sys.start_addr, psp->sys.size_bytes);
ret = psp_copy_fw(psp, psp->sys.start_addr, psp->sys.size_bytes);
if (ret)
return ret;
/* Provide the sys driver to bootloader */
WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_36,
@ -135,7 +137,9 @@ static int psp_v3_1_bootloader_load_sos(struct psp_context *psp)
return ret;
/* Copy Secure OS binary to PSP memory */
psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
ret = psp_copy_fw(psp, psp->sos.start_addr, psp->sos.size_bytes);
if (ret)
return ret;
/* Provide the PSP secure OS to bootloader */
WREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_36,

View File

@ -42,9 +42,10 @@
#include "oss/oss_1_0_d.h"
#include "oss/oss_1_0_sh_mask.h"
#define VCE_V1_0_ALIGNMENT (32 * 1024)
#define VCE_V1_0_FW_SIZE (256 * 1024)
#define VCE_V1_0_STACK_SIZE (64 * 1024)
#define VCE_V1_0_DATA_SIZE (7808 * (AMDGPU_MAX_VCE_HANDLES + 1))
#define VCE_V1_0_DATA_SIZE (ALIGN(7808 * (AMDGPU_MAX_VCE_HANDLES + 1), VCE_V1_0_ALIGNMENT))
#define VCE_STATUS_VCPU_REPORT_FW_LOADED_MASK 0x02
static void vce_v1_0_set_ring_funcs(struct amdgpu_device *adev);
@ -177,7 +178,7 @@ static void vce_v1_0_init_cg(struct amdgpu_device *adev)
}
/**
* vce_v1_0_load_fw_signature - load firmware signature into VCPU BO
* vce_v1_0_load_fw() - load firmware signature into VCPU BO
*
* @adev: amdgpu_device pointer
*
@ -185,21 +186,26 @@ static void vce_v1_0_init_cg(struct amdgpu_device *adev)
* This function finds the signature appropriate for the current
* ASIC and writes that into the VCPU BO.
*/
static int vce_v1_0_load_fw_signature(struct amdgpu_device *adev)
static int vce_v1_0_load_fw(struct amdgpu_device *adev)
{
const struct common_firmware_header *hdr;
struct vce_v1_0_fw_signature *sign;
unsigned int ucode_offset;
u32 ucode_offset;
u32 ucode_size;
uint32_t chip_id;
u32 *cpu_addr;
int i;
hdr = (const struct common_firmware_header *)adev->vce.fw->data;
ucode_offset = le32_to_cpu(hdr->ucode_array_offset_bytes);
ucode_size = hdr->ucode_size_bytes - sizeof(struct vce_v1_0_fw_signature *);
cpu_addr = adev->vce.cpu_addr;
sign = (void *)adev->vce.fw->data + ucode_offset;
if (ucode_size > VCE_V1_0_FW_SIZE - AMDGPU_VCE_FIRMWARE_OFFSET)
return -EINVAL;
switch (adev->asic_type) {
case CHIP_TAHITI:
chip_id = 0x01000014;
@ -226,12 +232,14 @@ static int vce_v1_0_load_fw_signature(struct amdgpu_device *adev)
return -EINVAL;
}
memset_io(&cpu_addr[0], 0, amdgpu_bo_size(adev->vce.vcpu_bo));
cpu_addr += (256 - 64) / 4;
memcpy_toio(&cpu_addr[0], &sign->val[i].nonce[0], 16);
cpu_addr[4] = cpu_to_le32(le32_to_cpu(sign->length) + 64);
memset_io(&cpu_addr[5], 0, 44);
memcpy_toio(&cpu_addr[16], &sign[1], hdr->ucode_size_bytes - sizeof(*sign));
memcpy_toio(&cpu_addr[16], &sign[1], ucode_size);
cpu_addr += (le32_to_cpu(sign->length) + 64) / 4;
memcpy_toio(&cpu_addr[0], &sign->val[i].sigval[0], 16);
@ -312,18 +320,23 @@ static int vce_v1_0_mc_resume(struct amdgpu_device *adev)
WREG32(mmVCE_VCPU_SCRATCH7, AMDGPU_MAX_VCE_HANDLES);
offset = adev->vce.gpu_addr + AMDGPU_VCE_FIRMWARE_OFFSET;
size = VCE_V1_0_FW_SIZE;
WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset & 0x7fffffff);
size = VCE_V1_0_FW_SIZE - AMDGPU_VCE_FIRMWARE_OFFSET;
WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset);
WREG32(mmVCE_VCPU_CACHE_SIZE0, size);
offset += size;
size = VCE_V1_0_STACK_SIZE;
WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset & 0x7fffffff);
WARN_ON(!IS_ALIGNED(offset, VCE_V1_0_ALIGNMENT));
WARN_ON(!IS_ALIGNED(size, VCE_V1_0_ALIGNMENT));
WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset);
WREG32(mmVCE_VCPU_CACHE_SIZE1, size);
offset += size;
size = VCE_V1_0_DATA_SIZE;
WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset & 0x7fffffff);
WARN_ON(!IS_ALIGNED(offset, VCE_V1_0_ALIGNMENT));
WARN_ON(!IS_ALIGNED(size, VCE_V1_0_ALIGNMENT));
WARN_ON((offset + size - adev->vce.gpu_addr) > amdgpu_bo_size(adev->vce.vcpu_bo));
WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset);
WREG32(mmVCE_VCPU_CACHE_SIZE2, size);
WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100);
@ -527,22 +540,31 @@ static int vce_v1_0_early_init(struct amdgpu_ip_block *ip_block)
* To accomodate that, we put GART to the LOW address range
* and reserve some GART pages where we map the VCPU BO,
* so that it gets a 32-bit address.
*
* The BAR address is zero and we can't change it
* due to the firmware validation mechanism.
* It seems that it fails to initialize if the address is >= 128 MiB.
*/
static int vce_v1_0_ensure_vcpu_bo_32bit_addr(struct amdgpu_device *adev)
{
u64 bo_size = amdgpu_bo_size(adev->vce.vcpu_bo);
u64 max_vcpu_bo_addr = 0xffffffff - bo_size;
u64 max_vcpu_bo_addr = 0x07ffffff - bo_size;
u64 num_pages = ALIGN(bo_size, AMDGPU_GPU_PAGE_SIZE) / AMDGPU_GPU_PAGE_SIZE;
u64 pa = amdgpu_gmc_vram_pa(adev, adev->vce.vcpu_bo);
u64 flags = AMDGPU_PTE_READABLE | AMDGPU_PTE_WRITEABLE | AMDGPU_PTE_VALID;
u64 vce_gart_start_offs;
int r;
r = amdgpu_gtt_mgr_alloc_entries(&adev->mman.gtt_mgr,
&adev->vce.gart_node, num_pages,
DRM_MM_INSERT_LOW);
if (r)
return r;
if (adev->gmc.vram_start < adev->gmc.gart_start)
return amdgpu_bo_gpu_offset(adev->vce.vcpu_bo) <= max_vcpu_bo_addr ? 0 : -EINVAL;
if (!drm_mm_node_allocated(&adev->vce.gart_node)) {
r = amdgpu_gtt_mgr_alloc_entries(&adev->mman.gtt_mgr,
&adev->vce.gart_node, num_pages,
DRM_MM_INSERT_LOW);
if (r)
return r;
}
vce_gart_start_offs = amdgpu_gtt_node_to_byte_offset(&adev->vce.gart_node);
@ -553,8 +575,6 @@ static int vce_v1_0_ensure_vcpu_bo_32bit_addr(struct amdgpu_device *adev)
amdgpu_gart_map_vram_range(adev, pa, adev->vce.gart_node.start,
num_pages, flags, adev->gart.ptr);
adev->vce.gpu_addr = adev->gmc.gart_start + vce_gart_start_offs;
if (adev->vce.gpu_addr > max_vcpu_bo_addr)
return -EINVAL;
return 0;
}
@ -574,10 +594,7 @@ static int vce_v1_0_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
r = amdgpu_vce_resume(adev);
if (r)
return r;
r = vce_v1_0_load_fw_signature(adev);
r = vce_v1_0_load_fw(adev);
if (r)
return r;
r = vce_v1_0_ensure_vcpu_bo_32bit_addr(adev);
@ -696,10 +713,7 @@ static int vce_v1_0_resume(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int r;
r = amdgpu_vce_resume(adev);
if (r)
return r;
r = vce_v1_0_load_fw_signature(adev);
r = vce_v1_0_load_fw(adev);
if (r)
return r;
r = vce_v1_0_ensure_vcpu_bo_32bit_addr(adev);

View File

@ -37,9 +37,14 @@
#include "oss/oss_2_0_d.h"
#include "oss/oss_2_0_sh_mask.h"
/* Use 24K to be safe. The FW supposedly only requires 23744 bytes. */
#define VCE_V2_0_DATA_ENTRY_SIZE (24 * 1024)
#define VCE_V2_0_FW_SIZE (256 * 1024)
#define VCE_V2_0_STACK_SIZE (64 * 1024)
#define VCE_V2_0_DATA_SIZE (23552 * AMDGPU_MAX_VCE_HANDLES)
#define VCE_V2_0_DATA_SIZE (VCE_V2_0_DATA_ENTRY_SIZE * (AMDGPU_MAX_VCE_HANDLES + 1))
#define VCE_STATUS_VCPU_REPORT_FW_LOADED_MASK 0x02
static void vce_v2_0_set_ring_funcs(struct amdgpu_device *adev);
@ -183,7 +188,7 @@ static void vce_v2_0_mc_resume(struct amdgpu_device *adev)
WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR, (adev->vce.gpu_addr >> 8));
offset = AMDGPU_VCE_FIRMWARE_OFFSET;
size = VCE_V2_0_FW_SIZE;
size = VCE_V2_0_FW_SIZE - AMDGPU_VCE_FIRMWARE_OFFSET;
WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset & 0x7fffffff);
WREG32(mmVCE_VCPU_CACHE_SIZE0, size);

View File

@ -574,7 +574,7 @@ static void vce_v3_0_mc_resume(struct amdgpu_device *adev, int idx)
} else
WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR, (adev->vce.gpu_addr >> 8));
offset = AMDGPU_VCE_FIRMWARE_OFFSET;
size = VCE_V3_0_FW_SIZE;
size = VCE_V3_0_FW_SIZE - AMDGPU_VCE_FIRMWARE_OFFSET;
WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset & 0x7fffffff);
WREG32(mmVCE_VCPU_CACHE_SIZE0, size);

View File

@ -69,6 +69,21 @@ static const struct class kfd_class = {
.name = kfd_dev_name,
};
/*
* Cache the address space of the chardev on first open so that the reset
* path can drop all userspace mappings of doorbell and MMIO ranges via
* unmap_mapping_range().
*/
static struct address_space *kfd_dev_mapping;
void kfd_dev_unmap_mapping_range(loff_t const holebegin, loff_t const holelen)
{
struct address_space *mapping = READ_ONCE(kfd_dev_mapping);
if (mapping)
unmap_mapping_range(mapping, holebegin, holelen, 1);
}
static inline struct kfd_process_device *kfd_lock_pdd_by_id(struct kfd_process *p, __u32 gpu_id)
{
struct kfd_process_device *pdd;
@ -135,6 +150,13 @@ static int kfd_open(struct inode *inode, struct file *filep)
if (iminor(inode) != 0)
return -ENODEV;
/*
* /dev/kfd is a single chardev so all opens share one inode. Cache
* its address_space on the first open for use by the reset path.
*/
if (!READ_ONCE(kfd_dev_mapping))
cmpxchg(&kfd_dev_mapping, NULL, inode->i_mapping);
is_32bit_user_mode = in_compat_syscall();
if (is_32bit_user_mode) {

View File

@ -612,6 +612,9 @@ static int allocate_doorbell(struct qcm_process_device *qpd,
} else {
/* For CP queues on SOC15 */
if (restore_id) {
if (*restore_id >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS)
return -EINVAL;
/* make sure that ID is free */
if (__test_and_set_bit(*restore_id, qpd->doorbell_bitmap))
return -EINVAL;
@ -1724,6 +1727,9 @@ static int allocate_sdma_queue(struct device_queue_manager *dqm,
}
if (restore_sdma_id) {
if (*restore_sdma_id >= get_num_sdma_queues(dqm))
return -EINVAL;
/* Re-use existing sdma_id */
if (!test_bit(*restore_sdma_id, dqm->sdma_bitmap)) {
dev_err(dev, "SDMA queue already in use\n");
@ -1750,6 +1756,9 @@ static int allocate_sdma_queue(struct device_queue_manager *dqm,
return -ENOMEM;
}
if (restore_sdma_id) {
if (*restore_sdma_id >= get_num_xgmi_sdma_queues(dqm))
return -EINVAL;
/* Re-use existing sdma_id */
if (!test_bit(*restore_sdma_id, dqm->xgmi_sdma_bitmap)) {
dev_err(dev, "SDMA queue already in use\n");

View File

@ -375,11 +375,15 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd,
{
struct v9_mqd *m;
struct kfd_context_save_area_header header;
u32 cntl_stack_size;
u32 cntl_stack_offset;
/* Control stack is located one page after MQD. */
void *mqd_ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE);
m = get_mqd(mqd);
cntl_stack_size = min_t(u32, m->cp_hqd_cntl_stack_size, q->ctl_stack_size);
cntl_stack_offset = min_t(u32, m->cp_hqd_cntl_stack_offset, cntl_stack_size);
*ctl_stack_used_size = m->cp_hqd_cntl_stack_size -
m->cp_hqd_cntl_stack_offset;
@ -395,9 +399,10 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd,
if (copy_to_user(ctl_stack, &header, sizeof(header.wave_state)))
return -EFAULT;
if (copy_to_user(ctl_stack + m->cp_hqd_cntl_stack_offset,
mqd_ctl_stack + m->cp_hqd_cntl_stack_offset,
*ctl_stack_used_size))
*ctl_stack_used_size = cntl_stack_size - cntl_stack_offset;
if (copy_to_user(ctl_stack + cntl_stack_offset, mqd_ctl_stack + cntl_stack_offset,
*ctl_stack_used_size))
return -EFAULT;
return 0;

View File

@ -399,6 +399,7 @@ enum kfd_mempool {
/* Character device interface */
int kfd_chardev_init(void);
void kfd_chardev_exit(void);
void kfd_dev_unmap_mapping_range(loff_t const holebegin, loff_t const holelen);
/**
* enum kfd_unmap_queues_filter - Enum for queue filters.

View File

@ -514,7 +514,8 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
dev->node_props.capability |=
HSA_CAP_AQL_QUEUE_DOUBLE_MAP;
if (KFD_GC_VERSION(dev->gpu) < IP_VERSION(10, 0, 0) &&
if ((KFD_GC_VERSION(dev->gpu) < IP_VERSION(10, 0, 0) ||
KFD_GC_VERSION(dev->gpu) == IP_VERSION(12, 1, 0)) &&
(dev->gpu->adev->sdma.supported_reset & AMDGPU_RESET_TYPE_PER_QUEUE))
dev->node_props.capability2 |= HSA_CAP2_PER_SDMA_QUEUE_RESET_SUPPORTED;

View File

@ -2330,9 +2330,16 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
adev->dm.idle_workqueue = NULL;
}
/* Disable ISM before dc_destroy() invalidates dm->dc */
/*
* Disable ISM before dc_destroy() invalidates dm->dc.
*
* Quiesce workers first without dc_lock (they take dc_lock
* themselves, so syncing under it would deadlock), then drive the
* FSM back to FULL_POWER_RUNNING under dc_lock.
*/
amdgpu_dm_ism_disable(&adev->dm);
scoped_guard(mutex, &adev->dm.dc_lock)
amdgpu_dm_ism_disable(&adev->dm);
amdgpu_dm_ism_force_full_power(&adev->dm);
amdgpu_dm_destroy_drm_device(&adev->dm);
@ -3364,9 +3371,14 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
if (amdgpu_in_reset(adev)) {
enum dc_status res;
/* Quiesce ISM workers before taking dc_lock (workers take
* dc_lock themselves; syncing under it would deadlock).
*/
amdgpu_dm_ism_disable(dm);
mutex_lock(&dm->dc_lock);
amdgpu_dm_ism_disable(dm);
amdgpu_dm_ism_force_full_power(dm);
dc_allow_idle_optimizations(adev->dm.dc, false);
dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state);
@ -3400,8 +3412,13 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
amdgpu_dm_irq_suspend(adev);
/*
* Quiesce ISM workers before taking dc_lock (workers take dc_lock
* themselves; syncing under it would deadlock).
*/
amdgpu_dm_ism_disable(dm);
scoped_guard(mutex, &dm->dc_lock)
amdgpu_dm_ism_disable(dm);
amdgpu_dm_ism_force_full_power(dm);
hpd_rx_irq_work_suspend(dm);

View File

@ -451,8 +451,8 @@ void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism,
struct amdgpu_display_manager *dm = &adev->dm;
struct dm_crtc_state *acrtc_state = to_dm_crtc_state(acrtc->base.state);
/* ISM transitions must be called with mutex acquired */
ASSERT(mutex_is_locked(&dm->dc_lock));
/* ISM transitions must be called with dc_lock held */
lockdep_assert_held(&dm->dc_lock);
/* ISM should not run after dc is destroyed */
ASSERT(dm->dc);
@ -516,13 +516,20 @@ static void dm_ism_sso_delayed_work_func(struct work_struct *work)
}
/**
* amdgpu_dm_ism_disable - Disable the ISM
* amdgpu_dm_ism_disable - Quiesce ISM workers
*
* @dm: The amdgpu display manager
*
* Disable the idle state manager by disabling any ISM work, canceling pending
* work, and waiting for in-progress work to finish. After disabling, the system
* is left in DM_ISM_STATE_FULL_POWER_RUNNING state.
* Cancels and disables any pending or in-flight ISM delayed work and waits
* for in-progress work to finish. After this returns, no ISM worker can run
* and subsequent mod_delayed_work() calls become no-ops via
* clear_pending_if_disabled().
*
* Must NOT be called with dc_lock held: the workers themselves take dc_lock,
* so a synchronous wait under dc_lock would deadlock.
*
* The caller is responsible for driving the FSM back to FULL_POWER_RUNNING
* (under dc_lock) by calling amdgpu_dm_ism_force_full_power().
*/
void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
{
@ -530,21 +537,54 @@ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
struct amdgpu_crtc *acrtc;
struct amdgpu_dm_ism *ism;
ASSERT(mutex_is_locked(&dm->dc_lock));
/*
* Caller must NOT hold dc_lock: the ISM delayed work handlers
* acquire dc_lock themselves, so waiting for them via
* disable_delayed_work_sync() while holding dc_lock would
* self-deadlock against an in-flight worker.
*/
lockdep_assert_not_held(&dm->dc_lock);
drm_for_each_crtc(crtc, dm->ddev) {
acrtc = to_amdgpu_crtc(crtc);
ism = &acrtc->ism;
/* Cancel and disable any pending work */
disable_delayed_work_sync(&ism->delayed_work);
disable_delayed_work_sync(&ism->sso_delayed_work);
}
}
/**
* amdgpu_dm_ism_force_full_power - Force every CRTC's ISM FSM to FULL_POWER
*
* @dm: The amdgpu display manager
*
* Sends DM_ISM_EVENT_EXIT_IDLE_REQUESTED to every CRTC's ISM, leaving each
* FSM in FULL_POWER_RUNNING. Intended to be paired with
* amdgpu_dm_ism_disable(): callers should first quiesce workers (without
* dc_lock), then take dc_lock and call this helper.
*
* Must be called with dc_lock held.
*/
void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm)
{
struct drm_crtc *crtc;
struct amdgpu_crtc *acrtc;
/*
* Caller must hold dc_lock: commit_event() drives the FSM and
* may touch dc state via dc_allow_idle_optimizations() etc.
*/
lockdep_assert_held(&dm->dc_lock);
drm_for_each_crtc(crtc, dm->ddev) {
acrtc = to_amdgpu_crtc(crtc);
/*
* When disabled, leave in FULL_POWER_RUNNING state.
* EXIT_IDLE will not queue any work
* EXIT_IDLE will not queue any work.
*/
amdgpu_dm_ism_commit_event(ism,
amdgpu_dm_ism_commit_event(&acrtc->ism,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED);
}
}

View File

@ -146,6 +146,7 @@ void amdgpu_dm_ism_fini(struct amdgpu_dm_ism *ism);
void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism,
enum amdgpu_dm_ism_event event);
void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm);
void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm);
void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm);
#endif

View File

@ -493,6 +493,10 @@ static enum bp_result get_gpio_i2c_info(
- sizeof(struct atom_common_table_header))
/ sizeof(struct atom_gpio_pin_assignment);
if (!bios_get_image(&bp->base, DATA_TABLES(gpio_pin_lut),
le16_to_cpu(header->table_header.structuresize)))
return BP_RESULT_BADBIOSTABLE;
pin = (struct atom_gpio_pin_assignment *) header->gpio_pin;
for (table_index = 0; table_index < count; table_index++) {
@ -681,6 +685,11 @@ static enum bp_result bios_parser_get_gpio_pin_info(
count = (le16_to_cpu(header->table_header.structuresize)
- sizeof(struct atom_common_table_header))
/ sizeof(struct atom_gpio_pin_assignment);
if (!bios_get_image(&bp->base, DATA_TABLES(gpio_pin_lut),
le16_to_cpu(header->table_header.structuresize)))
return BP_RESULT_BADBIOSTABLE;
for (i = 0; i < count; ++i) {
if (header->gpio_pin[i].gpio_id != gpio_id)
continue;

View File

@ -37,10 +37,13 @@ uint8_t *bios_get_image(struct dc_bios *bp,
uint32_t offset,
uint32_t size)
{
if (bp->bios && offset + size < bp->bios_size)
return bp->bios + offset;
else
if (!bp->bios)
return NULL;
if (offset > bp->bios_size || size > bp->bios_size - offset)
return NULL;
return bp->bios + offset;
}
#include "reg_helper.h"

View File

@ -309,6 +309,8 @@ void dcn42_update_clocks(struct clk_mgr *clk_mgr_base,
}
// workaround: Limit dppclk to 100Mhz to avoid lower eDP panel switch to plus 4K monitor underflow.
if (new_clocks->dppclk_khz < 100000)
new_clocks->dppclk_khz = 100000;
if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr->base.clks.dppclk_khz)) {
if (clk_mgr->base.clks.dppclk_khz > new_clocks->dppclk_khz)

View File

@ -6614,7 +6614,11 @@ bool dc_process_dmub_aux_transfer_async(struct dc *dc,
uint8_t action;
union dmub_rb_cmd cmd = {0};
ASSERT(payload->length <= 16);
if (link_index >= dc->link_count || !dc->links[link_index])
return false;
if (payload->length > sizeof(cmd.dp_aux_access.aux_control.dpaux.data))
return false;
cmd.dp_aux_access.header.type = DMUB_CMD__DP_AUX_ACCESS;
cmd.dp_aux_access.header.payload_bytes = 0;

View File

@ -63,7 +63,7 @@ struct dcn_dsc_reg_state;
struct dcn_optc_reg_state;
struct dcn_dccg_reg_state;
#define DC_VER "3.2.382"
#define DC_VER "3.2.383"
/**
* MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC
@ -1220,6 +1220,7 @@ struct dc_debug_options {
bool enable_otg_frame_sync_pwa;
unsigned int min_deep_sleep_dcfclk_khz;
unsigned int force_odm2to1_for_edp_pixclk_mhz;
bool enable_replay_esd_recovery;
};

View File

@ -346,7 +346,9 @@ enum swizzle_mode_addr3_values {
DC_ADDR3_SW_4KB_3D = 5,
DC_ADDR3_SW_64KB_3D = 6,
DC_ADDR3_SW_256KB_3D = 7,
DC_ADDR3_SW_MAX = 8,
DC_ADDR3_SW_64KB_2D_Z = 8,
DC_ADDR3_SW_256KB_2D_Z = 9,
DC_ADDR3_SW_MAX = 10,
DC_ADDR3_SW_UNKNOWN = DC_ADDR3_SW_MAX
};

View File

@ -603,6 +603,7 @@ static void dpp3_power_on_blnd_lut(
if (power_on) {
REG_UPDATE(CM_MEM_PWR_CTRL, BLNDGAM_MEM_PWR_FORCE, 0);
REG_WAIT(CM_MEM_PWR_STATUS, BLNDGAM_MEM_PWR_STATE, 0, 1, 5);
dpp_base->deferred_reg_writes.bits.disable_blnd_lut = false;
} else {
dpp_base->ctx->dc->optimized_required = true;
dpp_base->deferred_reg_writes.bits.disable_blnd_lut = true;
@ -620,6 +621,7 @@ static void dpp3_power_on_hdr3dlut(
if (power_on) {
REG_UPDATE(CM_MEM_PWR_CTRL2, HDR3DLUT_MEM_PWR_FORCE, 0);
REG_WAIT(CM_MEM_PWR_STATUS2, HDR3DLUT_MEM_PWR_STATE, 0, 1, 5);
dpp_base->deferred_reg_writes.bits.disable_3dlut = false;
} else {
dpp_base->ctx->dc->optimized_required = true;
dpp_base->deferred_reg_writes.bits.disable_3dlut = true;
@ -637,6 +639,7 @@ static void dpp3_power_on_shaper(
if (power_on) {
REG_UPDATE(CM_MEM_PWR_CTRL2, SHAPER_MEM_PWR_FORCE, 0);
REG_WAIT(CM_MEM_PWR_STATUS2, SHAPER_MEM_PWR_STATE, 0, 1, 5);
dpp_base->deferred_reg_writes.bits.disable_shaper = false;
} else {
dpp_base->ctx->dc->optimized_required = true;
dpp_base->deferred_reg_writes.bits.disable_shaper = true;

View File

@ -136,6 +136,7 @@ static void dpp3_power_on_gamcor_lut(
if (dpp_base->ctx->dc->caps.ips_v2_support)
REG_UPDATE(CM_MEM_PWR_CTRL, GAMCOR_MEM_PWR_DIS, 1);
REG_WAIT(CM_MEM_PWR_STATUS, GAMCOR_MEM_PWR_STATE, 0, 1, 5);
dpp_base->deferred_reg_writes.bits.disable_gamcor = false;
} else {
dpp_base->ctx->dc->optimized_required = true;
dpp_base->deferred_reg_writes.bits.disable_gamcor = true;

View File

@ -161,6 +161,7 @@ static void dpp401_power_on_dscl(
REG_WAIT(DSCL_MEM_PWR_STATUS, LUT_MEM_PWR_STATE, 0, 1, 100);
} else
REG_WAIT(DSCL_MEM_PWR_STATUS, LUT_MEM_PWR_STATE, 0, 1, 5);
dpp->base.deferred_reg_writes.bits.disable_dscl = false;
} else {
if (dpp->base.ctx->dc->debug.enable_mem_low_power.bits.dscl) {
dpp->base.ctx->dc->optimized_required = true;

View File

@ -617,7 +617,9 @@ bool hubbub401_dcc_support_swizzle(
swizzle_supported = true;
break;
case DC_ADDR3_SW_64KB_2D:
case DC_ADDR3_SW_64KB_2D_Z:
case DC_ADDR3_SW_256KB_2D:
case DC_ADDR3_SW_256KB_2D_Z:
swizzle_supported = true;
break;
default:

View File

@ -181,6 +181,7 @@ void dcn10_set_wait_for_update_needed_for_pipe(struct dc *dc, struct pipe_ctx *p
uint32_t vupdate_start, vupdate_end;
struct crtc_position position;
unsigned int vpos, cur_frame;
uint32_t max_frame_count;
if (!pipe_ctx->stream ||
!pipe_ctx->stream_res.tg ||
@ -197,7 +198,8 @@ void dcn10_set_wait_for_update_needed_for_pipe(struct dc *dc, struct pipe_ctx *p
struct optc *optc1 = DCN10TG_FROM_TG(tg);
ASSERT(optc1->max_frame_count != 0);
max_frame_count = optc1->tg_mask->OTG_FRAME_COUNT >> optc1->tg_shift->OTG_FRAME_COUNT;
ASSERT(max_frame_count != 0);
if (tg->funcs->is_tg_enabled && !tg->funcs->is_tg_enabled(tg))
return;
@ -209,8 +211,8 @@ void dcn10_set_wait_for_update_needed_for_pipe(struct dc *dc, struct pipe_ctx *p
if (vpos < vupdate_start) {
pipe_ctx->wait_frame_count = cur_frame;
} else {
if (cur_frame + 1 > optc1->max_frame_count)
pipe_ctx->wait_frame_count = cur_frame + 1 - optc1->max_frame_count;
if (cur_frame + 1 > max_frame_count)
pipe_ctx->wait_frame_count = cur_frame + 1 - max_frame_count;
else
pipe_ctx->wait_frame_count = cur_frame + 1;
}

View File

@ -118,6 +118,8 @@ static const struct hwseq_private_funcs dcn10_private_funcs = {
.dsc_pg_control = NULL,
.set_hdr_multiplier = dcn10_set_hdr_multiplier,
.verify_allow_pstate_change_high = dcn10_verify_allow_pstate_change_high,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
void dcn10_hw_sequencer_construct(struct dc *dc)

View File

@ -136,6 +136,8 @@ static const struct hwseq_private_funcs dcn20_private_funcs = {
.dccg_init = dcn20_dccg_init,
.set_blend_lut = dcn20_set_blend_lut,
.set_shaper_3dlut = dcn20_set_shaper_3dlut,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
void dcn20_hw_sequencer_construct(struct dc *dc)

View File

@ -145,6 +145,8 @@ static const struct hwseq_private_funcs dcn30_private_funcs = {
.wait_for_blank_complete = dcn20_wait_for_blank_complete,
.set_blend_lut = dcn30_set_blend_lut,
.set_shaper_3dlut = dcn20_set_shaper_3dlut,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
void dcn30_hw_sequencer_construct(struct dc *dc)

View File

@ -142,6 +142,8 @@ static const struct hwseq_private_funcs dcn301_private_funcs = {
.wait_for_blank_complete = dcn20_wait_for_blank_complete,
.set_blend_lut = dcn30_set_blend_lut,
.set_shaper_3dlut = dcn20_set_shaper_3dlut,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
void dcn301_hw_sequencer_construct(struct dc *dc)

View File

@ -147,6 +147,8 @@ static const struct hwseq_private_funcs dcn31_private_funcs = {
.set_blend_lut = dcn30_set_blend_lut,
.set_shaper_3dlut = dcn20_set_shaper_3dlut,
.setup_hpo_hw_control = dcn31_setup_hpo_hw_control,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
void dcn31_hw_sequencer_construct(struct dc *dc)

View File

@ -154,6 +154,8 @@ static const struct hwseq_private_funcs dcn314_private_funcs = {
.setup_hpo_hw_control = dcn31_setup_hpo_hw_control,
.calculate_dccg_k1_k2_values = dcn314_calculate_dccg_k1_k2_values,
.resync_fifo_dccg_dio = dcn314_resync_fifo_dccg_dio,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
void dcn314_hw_sequencer_construct(struct dc *dc)

View File

@ -163,6 +163,8 @@ static const struct hwseq_private_funcs dcn32_private_funcs = {
.is_dp_dig_pixel_rate_div_policy = dcn32_is_dp_dig_pixel_rate_div_policy,
.apply_single_controller_ctx_to_hw = dce110_apply_single_controller_ctx_to_hw,
.reset_back_end_for_pipe = dcn20_reset_back_end_for_pipe,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
void dcn32_hw_sequencer_init_functions(struct dc *dc)

View File

@ -167,6 +167,8 @@ static const struct hwseq_private_funcs dcn401_private_funcs = {
.perform_3dlut_wa_unlock = dcn401_perform_3dlut_wa_unlock,
.program_pipe_sequence = dcn401_program_pipe_sequence,
.dc_ip_request_cntl = dcn401_dc_ip_request_cntl,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
void dcn401_hw_sequencer_init_functions(struct dc *dc)

View File

@ -470,9 +470,9 @@ static bool is_rmcm_3dlut_fl_supported(struct dc *dc, enum dc_cm2_gpu_mem_size s
if (!dc->caps.color.mpc.rmcm_3d_lut_caps.dma_3d_lut)
return false;
if (size == DC_CM2_GPU_MEM_SIZE_171717)
return (dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_17);
return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_17 != 0u;
else if (size == DC_CM2_GPU_MEM_SIZE_333333)
return (dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33);
return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33 != 0u;
return false;
}
@ -945,6 +945,7 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx,
void dcn42_hardware_release(struct dc *dc)
{
dcn35_hardware_release(dc);
dc_dmub_srv_release_hw(dc);
}
static int count_active_streams(const struct dc *dc)

View File

@ -68,7 +68,6 @@ struct optc {
int pstate_keepout;
struct dc_crtc_timing orginal_patched_timing;
enum signal_type signal;
uint32_t max_frame_count;
};
void optc1_read_otg_state(struct timing_generator *optc, struct dcn_otg_state *s);

View File

@ -2097,10 +2097,10 @@ static enum dc_status enable_link(
return status;
}
static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, int bw)
static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, int stream_bw)
{
struct dc_link *link = stream->sink->link;
int req_bw = bw;
int req_bw = stream_bw;
DC_LOGGER_INIT(link->ctx->logger);
@ -2108,39 +2108,38 @@ static bool allocate_usb4_bandwidth_for_stream(struct dc_stream_state *stream, i
return false;
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
int sink_index = 0;
int sink_index = -1;
unsigned int i = 0;
for (i = 0; i < link->sink_count; i++) {
if (link->remote_sinks[i] == NULL)
continue;
if (stream->sink->sink_id != link->remote_sinks[i]->sink_id)
if (stream->sink->sink_id != link->remote_sinks[i]->sink_id) {
DC_LOG_DEBUG("%s: add remote_sink=%s, request_bw=%d\n", __func__,
(const char *)(&link->remote_sinks[i]->edid_caps.display_name[0]),
link->dpia_bw_alloc_config.remote_sink_req_bw[i]);
req_bw += link->dpia_bw_alloc_config.remote_sink_req_bw[i];
else
} else
sink_index = i;
}
link->dpia_bw_alloc_config.remote_sink_req_bw[sink_index] = bw;
}
if (sink_index >= 0)
link->dpia_bw_alloc_config.remote_sink_req_bw[sink_index] = stream_bw;
else
DC_LOG_WARNING("%s: stream sink_id=%u not found in remote_sinks[]\n",
__func__, stream->sink->sink_id);
link->dpia_bw_alloc_config.dp_overhead = link_dpia_get_dp_overhead(link);
req_bw += link->dpia_bw_alloc_config.dp_overhead;
if (req_bw) {
link->dpia_bw_alloc_config.dp_overhead = link_dpia_get_dp_overhead(link);
req_bw += link->dpia_bw_alloc_config.dp_overhead;
} else
link->dpia_bw_alloc_config.dp_overhead = 0;
}
link_dp_dpia_allocate_usb4_bandwidth_for_stream(link, req_bw);
if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
unsigned int i = 0;
for (i = 0; i < link->sink_count; i++) {
if (link->remote_sinks[i] == NULL)
continue;
DC_LOG_DEBUG("%s, remote_sink=%s, request_bw=%d\n", __func__,
(const char *)(&link->remote_sinks[i]->edid_caps.display_name[0]),
link->dpia_bw_alloc_config.remote_sink_req_bw[i]);
}
}
return true;
}

View File

@ -312,12 +312,14 @@ static void handle_hpd_irq_replay_sink(struct dc_link *link, bool *need_re_enabl
}
}
if (!link->replay_settings.replay_allow_active &&
replay_sink_status.bits.SINK_DEVICE_REPLAY_STATUS == 0x7) {
/* If sink device replay status is 0x7 and replay is disabled,
* it means sink is in a bad state and link retraining is needed to recover
*/
*replay_esd_detection_needed = true;
if (link->ctx->dc->debug.enable_replay_esd_recovery) {
if (!link->replay_settings.replay_allow_active &&
replay_sink_status.bits.SINK_DEVICE_REPLAY_STATUS == 0x7) {
/* If sink device replay status is 0x7 and replay is disabled,
* it means sink is in a bad state and link retraining is needed to recover
*/
*replay_esd_detection_needed = true;
}
}
}

View File

@ -321,6 +321,19 @@ bool dp_pr_copy_settings(struct dc_link *link, struct replay_context *replay_con
cmd.pr_copy_settings.data.flags.bitfields.dsc_enable_status = (pipe_ctx->stream->timing.flags.DSC == 1);
cmd.pr_copy_settings.data.debug.u32All = link->replay_settings.config.debug_flags;
// ALPM settings
cmd.pr_copy_settings.data.flags.bitfields.alpm_mode = (enum dmub_alpm_mode)link->replay_settings.config.alpm_mode;
if (link->replay_settings.config.alpm_mode == DC_ALPM_AUXLESS) {
cmd.pr_copy_settings.data.auxless_alpm_data.lfps_setup_ns = (uint16_t)dc->debug.auxless_alpm_lfps_setup_ns;
cmd.pr_copy_settings.data.auxless_alpm_data.lfps_period_ns = (uint16_t)dc->debug.auxless_alpm_lfps_period_ns;
cmd.pr_copy_settings.data.auxless_alpm_data.lfps_silence_ns = (uint16_t)dc->debug.auxless_alpm_lfps_silence_ns;
cmd.pr_copy_settings.data.auxless_alpm_data.lfps_t1_t2_override_us =
(uint16_t)dc->debug.auxless_alpm_lfps_t1t2_us;
cmd.pr_copy_settings.data.auxless_alpm_data.lfps_t1_t2_offset_us =
(uint16_t)dc->debug.auxless_alpm_lfps_t1t2_offset_us;
cmd.pr_copy_settings.data.auxless_alpm_data.lttpr_count = link->dc->link_srv->dp_get_lttpr_count(link);
}
cmd.pr_copy_settings.data.su_granularity_needed = link->dpcd_caps.vesa_replay_caps.bits.PR_SU_GRANULARITY_NEEDED;
cmd.pr_copy_settings.data.su_x_granularity = link->dpcd_caps.vesa_replay_su_info.pr_su_x_granularity;
cmd.pr_copy_settings.data.su_y_granularity = link->dpcd_caps.vesa_replay_su_info.pr_su_y_granularity;

View File

@ -462,7 +462,7 @@ bool edp_receiver_ready_T9(struct dc_link *link)
do {
sinkstatus = 1;
result = core_link_read_dpcd(link, DP_SINK_STATUS, &sinkstatus, sizeof(sinkstatus));
if (sinkstatus == 0)
if (!(sinkstatus & DP_RECEIVE_PORT_0_STATUS))
break;
if (result != DC_OK)
break;
@ -492,7 +492,7 @@ bool edp_receiver_ready_T7(struct dc_link *link)
do {
sinkstatus = 0;
result = core_link_read_dpcd(link, DP_SINK_STATUS, &sinkstatus, sizeof(sinkstatus));
if (sinkstatus == 1)
if (sinkstatus & DP_RECEIVE_PORT_0_STATUS)
break;
if (result != DC_OK)
break;

View File

@ -621,7 +621,6 @@ void dcn35_timing_generator_init(struct optc *optc1)
optc1->min_v_blank_interlace = 5;
optc1->min_h_sync_width = 4;
optc1->min_v_sync_width = 1;
optc1->max_frame_count = 0xFFFFFF;
dcn35_timing_generator_set_fgcg(
optc1, CTX->dc->debug.enable_fine_grain_clock_gating.bits.optc);

View File

@ -283,9 +283,7 @@ void dcn42_timing_generator_init(struct optc *optc1)
optc1->min_v_blank_interlace = 5;
optc1->min_h_sync_width = 4;
optc1->min_v_sync_width = 1;
optc1->max_frame_count = 0xFFFFFF;
dcn35_timing_generator_set_fgcg(
optc1, CTX->dc->debug.enable_fine_grain_clock_gating.bits.optc);
}

View File

@ -27,9 +27,6 @@ static bool pg_cntl42_dsc_pg_status(struct pg_cntl *pg_cntl, unsigned int dsc_in
struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
uint32_t pwr_status = 0;
if (pg_cntl->ctx->dc->debug.ignore_pg)
return true;
switch (dsc_inst) {
case 0: /* DSC0 */
REG_GET(DOMAIN16_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, &pwr_status);
@ -64,9 +61,11 @@ void pg_cntl42_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo
pg_cntl->ctx->dc->res_pool->dccg->funcs->enable_dsc(
pg_cntl->ctx->dc->res_pool->dccg, dsc_inst);
if (pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->debug.disable_dsc_power_gate ||
pg_cntl->ctx->dc->idle_optimizations_allowed)
bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->debug.disable_dsc_power_gate ||
pg_cntl->ctx->dc->idle_optimizations_allowed;
if (skip_pg && !power_on)
return;
block_enabled = pg_cntl42_dsc_pg_status(pg_cntl, dsc_inst);
@ -261,11 +260,12 @@ void pg_cntl42_hpo_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t power_forceon;
bool block_enabled;
if (pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->debug.disable_hpo_power_gate ||
pg_cntl->ctx->dc->idle_optimizations_allowed)
return;
bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->debug.disable_hpo_power_gate ||
pg_cntl->ctx->dc->idle_optimizations_allowed;
if (skip_pg && !power_on)
return;
block_enabled = pg_cntl42_hpo_pg_status(pg_cntl);
if (power_on) {
if (block_enabled)
@ -316,9 +316,11 @@ void pg_cntl42_io_clk_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t power_forceon;
bool block_enabled;
if (pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->idle_optimizations_allowed ||
pg_cntl->ctx->dc->debug.disable_io_clk_power_gate)
bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->idle_optimizations_allowed ||
pg_cntl->ctx->dc->debug.disable_io_clk_power_gate;
if (skip_pg && !power_on)
return;
block_enabled = pg_cntl42_io_clk_status(pg_cntl);
@ -412,9 +414,11 @@ void pg_cntl42_mem_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t power_forceon;
bool block_enabled;
if (pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->idle_optimizations_allowed ||
pg_cntl->ctx->dc->debug.disable_mem_power_gate)
bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->idle_optimizations_allowed ||
pg_cntl->ctx->dc->debug.disable_mem_power_gate;
if (skip_pg && !power_on)
return;
block_enabled = pg_cntl42_mem_status(pg_cntl);
@ -464,9 +468,10 @@ void pg_cntl42_dio_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t org_ip_request_cntl;
bool block_enabled;
if (pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->idle_optimizations_allowed ||
pg_cntl->ctx->dc->debug.disable_dio_power_gate)
bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->idle_optimizations_allowed ||
pg_cntl->ctx->dc->debug.disable_dio_power_gate;
if (skip_pg && !power_on)
return;
block_enabled = pg_cntl42_dio_pg_status(pg_cntl);

View File

@ -1520,6 +1520,10 @@ static void dcn42_resource_destruct(struct dcn42_resource_pool *pool)
if (pool->base.pg_cntl != NULL)
dcn_pg_cntl_destroy(&pool->base.pg_cntl);
if (pool->base.replay != NULL)
dmub_replay_destroy(&pool->base.replay);
if (pool->base.dccg != NULL)
dcn_dccg_destroy(&pool->base.dccg);
@ -1921,12 +1925,6 @@ static bool dcn42_resource_construct(
dc->caps.max_v_total = (1 << 15) - 1;
dc->caps.vtotal_limited_by_fp2 = true;
dc->caps.seamless_odm = true;
dc->caps.zstate_support = true;
dc->caps.ips_support = true;
dc->caps.max_v_total = (1 << 15) - 1;
dc->caps.vtotal_limited_by_fp2 = true;
/* Color pipeline capabilities */
dc->caps.color.dpp.dcn_arch = 1;
dc->caps.color.dpp.input_lut_shared = 0;

View File

@ -456,6 +456,8 @@
SRI_ARR(OTG_V_SYNC_A, OTG, inst), \
SRI_ARR(OTG_V_SYNC_A_CNTL, OTG, inst), \
SRI_ARR(OTG_CONTROL, OTG, inst), \
SRI_ARR(OTG_MASTER_EN, OTG, inst), \
SRI_ARR(OTG_LONG_VBLANK_STATUS, OTG, inst), \
SRI_ARR(OTG_STEREO_CONTROL, OTG, inst), \
SRI_ARR(OTG_3D_STRUCTURE_CONTROL, OTG, inst), \
SRI_ARR(OTG_STEREO_STATUS, OTG, inst), \
@ -472,6 +474,11 @@
SRI_ARR(OTG_M_CONST_DTO0, OTG, inst), \
SRI_ARR(OTG_M_CONST_DTO1, OTG, inst), \
SRI_ARR(OTG_CLOCK_CONTROL, OTG, inst), \
SRI_ARR(OTG_COUNT_CONTROL, OTG, inst), \
SRI_ARR(OTG_COUNT_RESET, OTG, inst), \
SRI_ARR(OTG_CRC_SIG_BLUE_CONTROL_MASK, OTG, inst), \
SRI_ARR(OTG_CRC_SIG_RED_GREEN_MASK, OTG, inst), \
SRI_ARR(OTG_DRR_TIMING_INT_STATUS, OTG, inst), \
SRI_ARR(OTG_VERTICAL_INTERRUPT0_CONTROL, OTG, inst), \
SRI_ARR(OTG_VERTICAL_INTERRUPT0_POSITION, OTG, inst), \
SRI_ARR(OTG_VERTICAL_INTERRUPT1_CONTROL, OTG, inst), \

View File

@ -1008,30 +1008,15 @@ union dmub_fw_boot_status {
* enum dmub_fw_boot_status_bit - Enum bit definitions for SCRATCH0.
*/
enum dmub_fw_boot_status_bit {
/** @DMUB_FW_BOOT_STATUS_BIT_DAL_FIRMWARE: 1 if DAL FW */
DMUB_FW_BOOT_STATUS_BIT_DAL_FIRMWARE = (1 << 0),
/** @DMUB_FW_BOOT_STATUS_BIT_MAILBOX_READY: 1 if mailbox ready */
DMUB_FW_BOOT_STATUS_BIT_MAILBOX_READY = (1 << 1),
/** @DMUB_FW_BOOT_STATUS_BIT_OPTIMIZED_INIT_DONE: 1 if init done */
DMUB_FW_BOOT_STATUS_BIT_OPTIMIZED_INIT_DONE = (1 << 2),
/** @DMUB_FW_BOOT_STATUS_BIT_RESTORE_REQUIRED: 1 if driver should call restore */
DMUB_FW_BOOT_STATUS_BIT_RESTORE_REQUIRED = (1 << 3),
/**
* @DMUB_FW_BOOT_STATUS_BIT_DEFERRED_LOADED: 1 if VBIOS data is
* deferred programmed
*/
DMUB_FW_BOOT_STATUS_BIT_DEFERRED_LOADED = (1 << 4),
/** @DMUB_FW_BOOT_STATUS_BIT_FAMS_ENABLED: 1 if FAMS is enabled */
DMUB_FW_BOOT_STATUS_BIT_FAMS_ENABLED = (1 << 5),
/**
* @DMUB_FW_BOOT_STATUS_BIT_DETECTION_REQUIRED: 1 if detection needs
* to be triggered by driver
*/
DMUB_FW_BOOT_STATUS_BIT_DETECTION_REQUIRED = (1 << 6),
/** @DMUB_FW_BOOT_STATUS_BIT_HW_POWER_INIT_DONE: 1 if hw power init is completed */
DMUB_FW_BOOT_STATUS_BIT_HW_POWER_INIT_DONE = (1 << 7),
/** @DMUB_FW_BOOT_STATUS_BIT_ONO_REGIONS_ENABLED: if ONO regions are enabled */
DMUB_FW_BOOT_STATUS_BIT_ONO_REGIONS_ENABLED = (1 << 8),
DMUB_FW_BOOT_STATUS_BIT_DAL_FIRMWARE = (1 << 0), /**< 1 if DAL FW */
DMUB_FW_BOOT_STATUS_BIT_MAILBOX_READY = (1 << 1), /**< 1 if mailbox ready */
DMUB_FW_BOOT_STATUS_BIT_OPTIMIZED_INIT_DONE = (1 << 2), /**< 1 if init done */
DMUB_FW_BOOT_STATUS_BIT_RESTORE_REQUIRED = (1 << 3), /**< 1 if driver should call restore */
DMUB_FW_BOOT_STATUS_BIT_DEFERRED_LOADED = (1 << 4), /**< 1 if VBIOS data is deferred programmed */
DMUB_FW_BOOT_STATUS_BIT_FAMS_ENABLED = (1 << 5), /**< 1 if FAMS is enabled*/
DMUB_FW_BOOT_STATUS_BIT_DETECTION_REQUIRED = (1 << 6), /**< 1 if detection need to be triggered by driver*/
DMUB_FW_BOOT_STATUS_BIT_HW_POWER_INIT_DONE = (1 << 7), /**< 1 if hw power init is completed */
DMUB_FW_BOOT_STATUS_BIT_ONO_REGIONS_ENABLED = (1 << 8), /**< 1 if ONO regions are enabled */
};
/* Register bit definition for SCRATCH5 */
@ -1095,22 +1080,18 @@ enum dmub_ips_comand_type {
*/
enum dmub_cursor_offload_comand_type {
/**
* @DMUB_CMD__CURSOR_OFFLOAD_INIT:
* Initializes the cursor offload feature.
*/
DMUB_CMD__CURSOR_OFFLOAD_INIT = 0,
/**
* @DMUB_CMD__CURSOR_OFFLOAD_STREAM_ENABLE: Enables cursor offloading
* for a stream and updates the timing parameters.
* Enables cursor offloading for a stream and updates the timing parameters.
*/
DMUB_CMD__CURSOR_OFFLOAD_STREAM_ENABLE = 1,
/**
* @DMUB_CMD__CURSOR_OFFLOAD_STREAM_DISABLE:
* Disables cursor offloading for a given stream.
*/
DMUB_CMD__CURSOR_OFFLOAD_STREAM_DISABLE = 2,
/**
* @DMUB_CMD__CURSOR_OFFLOAD_STREAM_PROGRAM:
* Programs the latest data for a given stream.
*/
DMUB_CMD__CURSOR_OFFLOAD_STREAM_PROGRAM = 3,
@ -1176,7 +1157,7 @@ enum dmub_fw_boot_options_bit {
* [Header (256 Bytes)][Feature 1 (256 Bytes)][Feature 2 (256 Bytes)]...
*/
/*
/**
* enum dmub_shared_state_feature_id - List of shared state features.
*/
enum dmub_shared_state_feature_id {
@ -1933,36 +1914,30 @@ enum dmub_cmd_type {
*/
enum dmub_out_cmd_type {
/**
* @DMUB_OUT_CMD__NULL: Invalid outbox command, ignored.
* Invalid outbox command, ignored.
*/
DMUB_OUT_CMD__NULL = 0,
/**
* @DMUB_OUT_CMD__DP_AUX_REPLY:
* Command type used for DP AUX Reply data notification
*/
DMUB_OUT_CMD__DP_AUX_REPLY = 1,
/**
* @DMUB_OUT_CMD__DP_HPD_NOTIFY:
* Command type used for DP HPD event notification
*/
DMUB_OUT_CMD__DP_HPD_NOTIFY = 2,
/**
* @DMUB_OUT_CMD__SET_CONFIG_REPLY:
* Command type used for SET_CONFIG Reply notification
*/
DMUB_OUT_CMD__SET_CONFIG_REPLY = 3,
/**
* @DMUB_OUT_CMD__DPIA_NOTIFICATION:
* Command type used for USB4 DPIA notification
*/
DMUB_OUT_CMD__DPIA_NOTIFICATION = 5,
/**
* @DMUB_OUT_CMD__HPD_SENSE_NOTIFY:
* Command type used for HPD redetect notification
*/
DMUB_OUT_CMD__HPD_SENSE_NOTIFY = 6,
/**
* @DMUB_OUT_CMD__FUSED_IO:
* Command type used for Fused IO notification
*/
DMUB_OUT_CMD__FUSED_IO = 7,
@ -2154,19 +2129,19 @@ struct dmub_rb_cmd_PLAT_54186_wa {
*/
enum dmub_cmd_mall_type {
/**
* @DMUB_CMD__MALL_ACTION_ALLOW: Allows display refresh from MALL.
* Allows display refresh from MALL.
*/
DMUB_CMD__MALL_ACTION_ALLOW = 0,
/**
* @DMUB_CMD__MALL_ACTION_DISALLOW: Disallows display refresh from MALL.
* Disallows display refresh from MALL.
*/
DMUB_CMD__MALL_ACTION_DISALLOW = 1,
/**
* @DMUB_CMD__MALL_ACTION_COPY_CURSOR: Cursor copy for MALL.
* Cursor copy for MALL.
*/
DMUB_CMD__MALL_ACTION_COPY_CURSOR = 2,
/**
* @DMUB_CMD__MALL_ACTION_NO_DF_REQ: Controls DF requests.
* Controls DF requests.
*/
DMUB_CMD__MALL_ACTION_NO_DF_REQ = 3,
};
@ -2195,21 +2170,18 @@ struct dmub_rb_cmd_mall {
*/
enum dmub_cmd_cab_type {
/**
* @DMUB_CMD__CAB_NO_IDLE_OPTIMIZATION:
* No idle optimizations (i.e. no CAB)
*/
DMUB_CMD__CAB_NO_IDLE_OPTIMIZATION = 0,
/**
* @DMUB_CMD__CAB_NO_DCN_REQ: No DCN requests for memory
* No DCN requests for memory
*/
DMUB_CMD__CAB_NO_DCN_REQ = 1,
/**
* @DMUB_CMD__CAB_DCN_SS_FIT_IN_CAB:
* Fit surfaces in CAB (i.e. CAB enable)
*/
DMUB_CMD__CAB_DCN_SS_FIT_IN_CAB = 2,
/**
* @DMUB_CMD__CAB_DCN_SS_NOT_FIT_IN_CAB:
* Do not fit surfaces in CAB (i.e. no CAB)
*/
DMUB_CMD__CAB_DCN_SS_NOT_FIT_IN_CAB = 3,
@ -2224,7 +2196,7 @@ struct dmub_rb_cmd_cab_for_ss {
uint8_t debug_bits; /* debug bits */
};
/*
/**
* Enum for indicating which MCLK switch mode per pipe
*/
enum mclk_switch_mode {
@ -2702,18 +2674,11 @@ struct dmub_fams2_stream_static_state_v1 {
* p-state request to allow latency
*/
enum dmub_fams2_allow_delay_check_mode {
/**
* @FAMS2_ALLOW_DELAY_CHECK_NONE: No check for request to allow delay
*/
/* No check for request to allow delay */
FAMS2_ALLOW_DELAY_CHECK_NONE = 0,
/**
* @FAMS2_ALLOW_DELAY_CHECK_FROM_START: Check for request to allow delay
*/
/* Check for request to allow delay */
FAMS2_ALLOW_DELAY_CHECK_FROM_START = 1,
/**
* @FAMS2_ALLOW_DELAY_CHECK_FROM_PREPARE:
* Check for prepare to allow delay
*/
/* Check for prepare to allow delay */
FAMS2_ALLOW_DELAY_CHECK_FROM_PREPARE = 2,
};
@ -2785,28 +2750,27 @@ struct dmub_rb_cmd_ib {
*/
enum dmub_cmd_idle_opt_type {
/**
* @DMUB_CMD__IDLE_OPT_DCN_RESTORE: DCN hardware restore.
* DCN hardware restore.
*/
DMUB_CMD__IDLE_OPT_DCN_RESTORE = 0,
/**
* @DMUB_CMD__IDLE_OPT_DCN_SAVE_INIT: DCN hardware save.
* DCN hardware save.
*/
DMUB_CMD__IDLE_OPT_DCN_SAVE_INIT = 1,
/**
* @DMUB_CMD__IDLE_OPT_DCN_NOTIFY_IDLE: DCN hardware notify idle.
* DCN hardware notify idle.
*/
DMUB_CMD__IDLE_OPT_DCN_NOTIFY_IDLE = 2,
/**
* @DMUB_CMD__IDLE_OPT_SET_DC_POWER_STATE:
* DCN hardware notify power state.
*/
DMUB_CMD__IDLE_OPT_SET_DC_POWER_STATE = 3,
/**
* @DMUB_CMD__IDLE_OPT_RELEASE_HW: DCN notify to release HW.
* DCN notify to release HW.
*/
DMUB_CMD__IDLE_OPT_RELEASE_HW = 4,
};
@ -2835,7 +2799,7 @@ struct dmub_rb_cmd_idle_opt_dcn_notify_idle {
struct dmub_dcn_notify_idle_cntl_data cntl_data;
};
/*
/**
* enum dmub_idle_opt_dc_power_state - DC power states.
*/
enum dmub_idle_opt_dc_power_state {
@ -2877,7 +2841,7 @@ struct dmub_clocks {
*/
enum dmub_cmd_clk_mgr_type {
/**
* @DMUB_CMD__CLK_MGR_NOTIFY_CLOCKS: Notify DMCUB of clock update.
* Notify DMCUB of clock update.
*/
DMUB_CMD__CLK_MGR_NOTIFY_CLOCKS = 0,
};
@ -3122,40 +3086,21 @@ struct dmub_rb_cmd_dpphy_init {
* 4 AUX request command bits are shifted to high nibble.
*/
enum dp_aux_request_action {
/**
* @DP_AUX_REQ_ACTION_I2C_WRITE: I2C-over-AUX write request
*/
/** I2C-over-AUX write request */
DP_AUX_REQ_ACTION_I2C_WRITE = 0x00,
/**
* @DP_AUX_REQ_ACTION_I2C_READ: I2C-over-AUX read request
*/
/** I2C-over-AUX read request */
DP_AUX_REQ_ACTION_I2C_READ = 0x10,
/**
* @DP_AUX_REQ_ACTION_I2C_STATUS_REQ: I2C-over-AUX write status request
*/
/** I2C-over-AUX write status request */
DP_AUX_REQ_ACTION_I2C_STATUS_REQ = 0x20,
/**
* @DP_AUX_REQ_ACTION_I2C_WRITE_MOT:
* I2C-over-AUX write request with MOT=1
*/
/** I2C-over-AUX write request with MOT=1 */
DP_AUX_REQ_ACTION_I2C_WRITE_MOT = 0x40,
/**
* @DP_AUX_REQ_ACTION_I2C_READ_MOT:
* I2C-over-AUX read request with MOT=1
*/
/** I2C-over-AUX read request with MOT=1 */
DP_AUX_REQ_ACTION_I2C_READ_MOT = 0x50,
/**
* @DP_AUX_REQ_ACTION_I2C_STATUS_REQ_MOT:
* I2C-over-AUX write status request with MOT=1
*/
/** I2C-over-AUX write status request with MOT=1 */
DP_AUX_REQ_ACTION_I2C_STATUS_REQ_MOT = 0x60,
/**
* @DP_AUX_REQ_ACTION_DPCD_WRITE: Native AUX write request
*/
/** Native AUX write request */
DP_AUX_REQ_ACTION_DPCD_WRITE = 0x80,
/**
* @DP_AUX_REQ_ACTION_DPCD_READ: Native AUX read request
*/
/** Native AUX read request */
DP_AUX_REQ_ACTION_DPCD_READ = 0x90
};
@ -3163,21 +3108,21 @@ enum dp_aux_request_action {
* enum aux_return_code_type - DP AUX process return code listing.
*/
enum aux_return_code_type {
/** @AUX_RET_SUCCESS: AUX process succeeded */
/** AUX process succeeded */
AUX_RET_SUCCESS = 0,
/** @AUX_RET_ERROR_UNKNOWN: AUX process failed with unknown reason */
/** AUX process failed with unknown reason */
AUX_RET_ERROR_UNKNOWN,
/** @AUX_RET_ERROR_INVALID_REPLY: AUX process completed with invalid reply */
/** AUX process completed with invalid reply */
AUX_RET_ERROR_INVALID_REPLY,
/** @AUX_RET_ERROR_TIMEOUT: AUX process timed out */
/** AUX process timed out */
AUX_RET_ERROR_TIMEOUT,
/** @AUX_RET_ERROR_HPD_DISCON: HPD was low during AUX process */
/** HPD was low during AUX process */
AUX_RET_ERROR_HPD_DISCON,
/** @AUX_RET_ERROR_ENGINE_ACQUIRE: Failed to acquire AUX engine */
/** Failed to acquire AUX engine */
AUX_RET_ERROR_ENGINE_ACQUIRE,
/** @AUX_RET_ERROR_INVALID_OPERATION: AUX request not supported */
/** AUX request not supported */
AUX_RET_ERROR_INVALID_OPERATION,
/** @AUX_RET_ERROR_PROTOCOL_ERROR: AUX process not available */
/** AUX process not available */
AUX_RET_ERROR_PROTOCOL_ERROR,
};
@ -3185,9 +3130,9 @@ enum aux_return_code_type {
* enum aux_channel_type - DP AUX channel type listing.
*/
enum aux_channel_type {
/** @AUX_CHANNEL_LEGACY_DDC: AUX thru Legacy DP AUX */
/** AUX thru Legacy DP AUX */
AUX_CHANNEL_LEGACY_DDC,
/** @AUX_CHANNEL_DPIA: AUX thru DPIA DP tunneling */
/** AUX thru DPIA DP tunneling */
AUX_CHANNEL_DPIA
};
@ -3672,7 +3617,7 @@ enum phy_link_rate {
*/
};
/*
/**
* enum dmub_phy_fsm_state - PHY FSM states.
* PHY FSM state to transit to during PSR enable/disable.
*/
@ -5949,11 +5894,10 @@ struct dmub_rb_cmd_abm_query_caps {
*/
enum dmub_abm_ace_curve_type {
/**
* @ABM_ACE_CURVE_TYPE__SW: ACE curve as defined by the SW layer.
* ACE curve as defined by the SW layer.
*/
ABM_ACE_CURVE_TYPE__SW = 0,
/**
* @ABM_ACE_CURVE_TYPE__SW_IF:
* ACE curve as defined by the SW to HW translation interface layer.
*/
ABM_ACE_CURVE_TYPE__SW_IF = 1,
@ -5964,11 +5908,10 @@ enum dmub_abm_ace_curve_type {
*/
enum dmub_abm_histogram_type {
/**
* @ABM_HISTOGRAM_TYPE__SW: ACE curve as defined by the SW layer.
* ACE curve as defined by the SW layer.
*/
ABM_HISTOGRAM_TYPE__SW = 0,
/**
* @ABM_HISTOGRAM_TYPE__SW_IF:
* ACE curve as defined by the SW to HW translation interface layer.
*/
ABM_HISTOGRAM_TYPE__SW_IF = 1,
@ -6174,17 +6117,14 @@ struct dmub_rb_cmd_get_visual_confirm_color {
*/
enum dmub_cmd_panel_cntl_type {
/**
* @DMUB_CMD__PANEL_CNTL_HW_INIT:
* Initializes embedded panel hardware blocks.
*/
DMUB_CMD__PANEL_CNTL_HW_INIT = 0,
/**
* @DMUB_CMD__PANEL_CNTL_QUERY_BACKLIGHT_INFO:
* Queries backlight info for the embedded panel.
*/
DMUB_CMD__PANEL_CNTL_QUERY_BACKLIGHT_INFO = 1,
/**
* @DMUB_CMD__PANEL_DEBUG_PWM_FREQ:
* Sets the PWM Freq as per user's requirement.
*/
DMUB_CMD__PANEL_DEBUG_PWM_FREQ = 2,

View File

@ -414,27 +414,4 @@ bool mod_power_backlight_nits_to_percent(struct mod_power *mod_power,
unsigned int backlight_millinit,
unsigned int *backlight_millipercent);
void initialize_backlight_caps(struct core_power *core_power, unsigned int inst);
unsigned int backlight_millipercent_to_pwm(
struct core_power *core_power, unsigned int millipercent, unsigned int inst);
unsigned int backlight_millipercent_to_millinit(
struct core_power *core_power, unsigned int millipercent, unsigned int inst);
void fill_backlight_level_params(struct core_power *core_power,
struct set_backlight_level_params *backlight_level_params,
int panel_inst, uint8_t aux_inst, unsigned int backlight_pwm,
enum backlight_control_type backlight_control_type,
unsigned int backlight_millinit, unsigned int transition_time_millisec,
bool is_hdr);
bool mod_power_hw_init_backlight(struct mod_power *mod_power);
void mod_power_update_backlight_on_mode_change(
struct core_power *core_power,
struct dc_link *link,
unsigned int panel_inst,
uint8_t aux_inst,
bool is_hdr);
#endif /* MODULES_INC_MOD_POWER_H_ */

View File

@ -23,9 +23,9 @@
# Makefile for the 'power' sub-module of DAL.
#
MOD_POWER = power_helpers.o power.o power_abm.o
MOD_POWER = power_helpers.o power.o power_abm.o power_psr.o power_replay.o
AMD_DAL_MOD_POWER = $(addprefix $(AMDDALPATH)/modules/power/,$(MOD_POWER))
#$(info ************ DAL POWER MODULE MAKEFILE ************)
AMD_DISPLAY_FILES += $(AMD_DAL_MOD_POWER)
AMD_DISPLAY_FILES += $(AMD_DAL_MOD_POWER)

File diff suppressed because it is too large Load Diff

View File

@ -233,102 +233,6 @@ struct iram_table_v_2_2 {
#define MOD_POWER_MAX_CONCURRENT_STREAMS 32
#define SMOOTH_BRIGHTNESS_ADJUSTMENT_TIME_IN_MS 500
struct backlight_state {
/* HW uses u16.16 format for backlight PWM */
unsigned int backlight_pwm;
/* DM may call power module to set backlight
* targeting percent brightness
*/
unsigned int backlight_millipercent;
/* DM may call power module to set backlight based on an explicit
* nits value.
*/
unsigned int backlight_millinit;
unsigned int frame_ramp;
bool smooth_brightness_enabled;
bool isHDR;
};
struct power_entity {
struct dc_stream_state *stream;
struct psr_caps *caps;
struct mod_power_psr_context *psr_context;
/*PSR cached properties*/
bool psr_enabled;
unsigned int psr_events;
unsigned int psr_power_opt;
unsigned int replay_events;
};
struct pwr_backlight_properties {
bool use_nits_based_brightness;
bool disable_fractional_pwm;
unsigned int min_abm_backlight;
unsigned int num_backlight_levels;
bool backlight_ramping_override;
unsigned int backlight_ramping_reduction;
unsigned int backlight_ramping_start;
/* Backlight cached properties */
unsigned int ac_backlight_percent;
unsigned int dc_backlight_percent;
/* backlight LUT stored in HW u16.16 format*/
unsigned int *backlight_lut;
unsigned int min_backlight_pwm;
unsigned int max_backlight_pwm;
unsigned int backlight_range;
/* Describes the panel's min and max luminance in millinits measured
* on full white screen, in min and max backlight settings.
*/
unsigned int min_brightness_millinits;
unsigned int max_brightness_millinits;
unsigned int nits_range;
bool backlight_caps_valid;
bool use_custom_backlight_caps;
unsigned int custom_backlight_caps_config_no;
bool use_linear_backlight_curve;
};
struct dmcu_varibright_cached_properties {
unsigned int varibright_config_setting;
unsigned int varibright_level;
unsigned int varibright_hw_level;
unsigned int def_varibright_level;
bool varibright_user_enable;
bool varibright_active;
};
struct core_power {
struct mod_power mod_public;
struct dc *dc;
struct power_entity *map;
struct dmcu_varibright_cached_properties varibright_prop;
struct pwr_backlight_properties bl_prop[MAX_NUM_EDP];
struct backlight_state bl_state[MAX_NUM_EDP];
unsigned int edp_num;
bool psr_smu_optimizations_support;
bool multi_disp_optimizations_support;
unsigned int num_entities;
};
union dmcu_abm_set_bl_params {
struct {
unsigned int gradual_change : 1; /* [0:0] */
unsigned int reserved : 15; /* [15:1] */
unsigned int frame_ramp : 16; /* [31:16] */
} bits;
unsigned int u32All;
};
/* If system or panel does not report some sort of brightness percent to nits
* mapping, we will use following default values so backlight control using
* nits based interfaces will still work, but might not describe panel
@ -341,8 +245,6 @@ union dmcu_abm_set_bl_params {
#define MOD_POWER_TO_CORE(mod_power)\
container_of(mod_power, struct core_power, mod_public)
static uint16_t backlight_8_to_16(unsigned int backlight_8bit)
{
return (uint16_t)(backlight_8bit * 0x101);

View File

@ -33,257 +33,7 @@
#define bswap16_based_on_endian(big_endian, value) \
((big_endian) ? cpu_to_be16(value) : cpu_to_le16(value))
/*
* is_psr_su_specific_panel() - check if sink is AMD vendor-specific PSR-SU
* supported eDP device.
*
* @link: dc link pointer
*
* Return: true if AMDGPU vendor specific PSR-SU eDP panel
*/
bool is_psr_su_specific_panel(struct dc_link *link)
{
bool isPSRSUSupported = false;
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
if (dpcd_caps->edp_rev >= DP_EDP_14) {
if (dpcd_caps->psr_info.psr_version >= DP_PSR2_WITH_Y_COORD_ET_SUPPORTED)
isPSRSUSupported = true;
/*
* Some panels will report PSR capabilities over additional DPCD bits.
* Such panels are approved despite reporting only PSR v3, as long as
* the additional bits are reported.
*/
if (dpcd_caps->sink_dev_id == DP_BRANCH_DEVICE_ID_001CF8) {
/*
* This is the temporary workaround to disable PSRSU when system turned on
* DSC function on the sepcific sink.
*/
if (dpcd_caps->psr_info.psr_version < DP_PSR2_WITH_Y_COORD_IS_SUPPORTED)
isPSRSUSupported = false;
else if (dpcd_caps->dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT &&
((dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x08) ||
(dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x07)))
isPSRSUSupported = false;
else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x03)
isPSRSUSupported = false;
else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x01)
isPSRSUSupported = false;
else if (dpcd_caps->psr_info.force_psrsu_cap == 0x1)
isPSRSUSupported = true;
}
}
return isPSRSUSupported;
}
/**
* mod_power_calc_psr_configs() - calculate/update generic psr configuration fields.
* @psr_config: [output], psr configuration structure to be updated
* @link: [input] dc link pointer
* @stream: [input] dc stream state pointer
*
* calculate and update the psr configuration fields that are not DM specific, i.e. such
* fields which are based on DPCD caps or timing information. To setup PSR in DMUB FW,
* this helper is assumed to be called before the call of the DC helper dc_link_setup_psr().
*
* PSR config fields to be updated within the helper:
* - psr_rfb_setup_time
* - psr_sdp_transmit_line_num_deadline
* - line_time_in_us
* - su_y_granularity
* - su_granularity_required
* - psr_frame_capture_indication_req
* - psr_exit_link_training_required
*
* PSR config fields that are DM specific and NOT updated within the helper:
* - allow_smu_optimizations
* - allow_multi_disp_optimizations
*/
void mod_power_calc_psr_configs(struct psr_config *psr_config,
struct dc_link *link,
const struct dc_stream_state *stream)
{
unsigned int num_vblank_lines = 0;
unsigned int vblank_time_in_us = 0;
unsigned int sdp_tx_deadline_in_us = 0;
unsigned int line_time_in_us = 0;
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
const int psr_setup_time_step_in_us = 55; /* refer to eDP spec DPCD 0x071h */
/* timing parameters */
num_vblank_lines = stream->timing.v_total -
stream->timing.v_addressable -
stream->timing.v_border_top -
stream->timing.v_border_bottom;
vblank_time_in_us = (stream->timing.h_total * num_vblank_lines * 1000) / (stream->timing.pix_clk_100hz / 10);
line_time_in_us = ((stream->timing.h_total * 1000) / (stream->timing.pix_clk_100hz / 10)) + 1;
/**
* psr configuration fields
*
* as per eDP 1.5 pg. 377 of 459, DPCD 0x071h bits [3:1], psr setup time bits interpreted as below
* 000b <--> 330 us (default)
* 001b <--> 275 us
* 010b <--> 220 us
* 011b <--> 165 us
* 100b <--> 110 us
* 101b <--> 055 us
* 110b <--> 000 us
*/
psr_config->psr_rfb_setup_time =
(6 - dpcd_caps->psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME) * psr_setup_time_step_in_us;
if (psr_config->psr_rfb_setup_time > vblank_time_in_us) {
link->psr_settings.psr_frame_capture_indication_req = true;
link->psr_settings.psr_sdp_transmit_line_num_deadline = num_vblank_lines;
} else {
sdp_tx_deadline_in_us = vblank_time_in_us - psr_config->psr_rfb_setup_time;
/* Set the last possible line SDP may be transmitted without violating the RFB setup time */
link->psr_settings.psr_frame_capture_indication_req = false;
link->psr_settings.psr_sdp_transmit_line_num_deadline = sdp_tx_deadline_in_us / line_time_in_us;
}
psr_config->psr_sdp_transmit_line_num_deadline = link->psr_settings.psr_sdp_transmit_line_num_deadline;
psr_config->line_time_in_us = line_time_in_us;
psr_config->su_y_granularity = dpcd_caps->psr_info.psr2_su_y_granularity_cap;
psr_config->su_granularity_required = dpcd_caps->psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED;
psr_config->psr_frame_capture_indication_req = link->psr_settings.psr_frame_capture_indication_req;
psr_config->psr_exit_link_training_required =
!link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED;
}
void init_replay_config(struct dc_link *link, struct replay_config *pr_config)
{
link->replay_settings.config = *pr_config;
}
bool mod_power_only_edp(const struct dc_state *context, const struct dc_stream_state *stream)
{
return context && context->stream_count == 1 && dc_is_embedded_signal(stream->signal);
}
bool psr_su_set_dsc_slice_height(struct dc *dc, struct dc_link *link,
struct dc_stream_state *stream,
struct psr_config *config)
{
uint32_t pic_height;
uint32_t slice_height;
config->dsc_slice_height = 0;
if (!(link->connector_signal & SIGNAL_TYPE_EDP) ||
!dc->caps.edp_dsc_support ||
link->panel_config.dsc.disable_dsc_edp ||
!link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT ||
!stream->timing.dsc_cfg.num_slices_v)
return true;
pic_height = stream->timing.v_addressable +
stream->timing.v_border_top + stream->timing.v_border_bottom;
if (stream->timing.dsc_cfg.num_slices_v == 0)
return false;
slice_height = pic_height / stream->timing.dsc_cfg.num_slices_v;
config->dsc_slice_height = (uint16_t)slice_height;
if (slice_height) {
if (config->su_y_granularity &&
(slice_height % config->su_y_granularity)) {
ASSERT(0);
return false;
}
}
return true;
}
void set_replay_frame_skip_number(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t coasting_vtotal_refresh_rate_uhz,
uint32_t flicker_free_refresh_rate_uhz,
bool is_defer)
{
uint32_t *frame_skip_number_array = NULL;
uint32_t frame_skip_number = 0;
if (link == NULL)
return;
if (false == link->replay_settings.config.frame_skip_supported)
return;
if (flicker_free_refresh_rate_uhz == 0 || coasting_vtotal_refresh_rate_uhz == 0)
return;
if (is_defer)
frame_skip_number_array = link->replay_settings.defer_frame_skip_number_table;
else
frame_skip_number_array = link->replay_settings.frame_skip_number_table;
if (frame_skip_number_array == NULL)
return;
frame_skip_number = (coasting_vtotal_refresh_rate_uhz + 500000) / flicker_free_refresh_rate_uhz;
if (frame_skip_number >= 1)
frame_skip_number_array[type] = frame_skip_number - 1;
else
frame_skip_number_array[type] = 0;
}
void set_replay_defer_update_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal)
{
link->replay_settings.defer_update_coasting_vtotal_table[type] = vtotal;
}
void update_replay_coasting_vtotal_from_defer(struct dc_link *link,
enum replay_coasting_vtotal_type type)
{
link->replay_settings.coasting_vtotal_table[type] =
link->replay_settings.defer_update_coasting_vtotal_table[type];
link->replay_settings.frame_skip_number_table[type] =
link->replay_settings.defer_frame_skip_number_table[type];
}
void set_replay_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal)
{
link->replay_settings.coasting_vtotal_table[type] = vtotal;
}
void set_replay_low_rr_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal)
{
link->replay_settings.low_rr_full_screen_video_pseudo_vtotal = vtotal;
}
void calculate_replay_link_off_frame_count(struct dc_link *link,
uint16_t vtotal, uint16_t htotal)
{
uint32_t max_link_off_frame_count = 0;
uint16_t max_deviation_line = 0, pixel_deviation_per_line = 0;
if (!link || link->replay_settings.config.replay_version != DC_FREESYNC_REPLAY)
return;
max_deviation_line = link->dpcd_caps.pr_info.max_deviation_line;
pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line;
if (htotal != 0 && vtotal != 0 && pixel_deviation_per_line != 0)
max_link_off_frame_count = htotal * max_deviation_line / (pixel_deviation_per_line * vtotal);
else
ASSERT(0);
link->replay_settings.link_off_frame_count = max_link_off_frame_count;
}
void reset_replay_dsync_error_count(struct dc_link *link)
{
link->replay_settings.replay_desync_error_fail_count = 0;
}

View File

@ -47,7 +47,99 @@ struct dmcu_iram_parameters {
unsigned int min_abm_backlight;
unsigned int set;
};
struct backlight_state {
/* HW uses u16.16 format for backlight PWM */
unsigned int backlight_pwm;
/* DM may call power module to set backlight
* targeting percent brightness
*/
unsigned int backlight_millipercent;
/* DM may call power module to set backlight based on an explicit
* nits value.
*/
unsigned int backlight_millinit;
unsigned int frame_ramp;
bool smooth_brightness_enabled;
bool isHDR;
};
struct power_entity {
struct dc_stream_state *stream;
struct psr_caps *caps;
struct mod_power_psr_context *psr_context;
/*PSR cached properties*/
bool psr_enabled;
unsigned int psr_events;
unsigned int psr_power_opt;
unsigned int replay_events;
};
struct pwr_backlight_properties {
bool use_nits_based_brightness;
bool disable_fractional_pwm;
unsigned int min_abm_backlight;
unsigned int num_backlight_levels;
bool backlight_ramping_override;
unsigned int backlight_ramping_reduction;
unsigned int backlight_ramping_start;
/* Backlight cached properties */
unsigned int ac_backlight_percent;
unsigned int dc_backlight_percent;
/* backlight LUT stored in HW u16.16 format*/
unsigned int *backlight_lut;
unsigned int min_backlight_pwm;
unsigned int max_backlight_pwm;
unsigned int backlight_range;
/* Describes the panel's min and max luminance in millinits measured
* on full white screen, in min and max backlight settings.
*/
unsigned int min_brightness_millinits;
unsigned int max_brightness_millinits;
unsigned int nits_range;
bool backlight_caps_valid;
bool use_custom_backlight_caps;
unsigned int custom_backlight_caps_config_no;
bool use_linear_backlight_curve;
};
struct dmcu_varibright_cached_properties {
unsigned int varibright_config_setting;
unsigned int varibright_level;
unsigned int varibright_hw_level;
unsigned int def_varibright_level;
bool varibright_user_enable;
bool varibright_active;
};
struct core_power {
struct mod_power mod_public;
struct dc *dc;
struct power_entity *map;
struct dmcu_varibright_cached_properties varibright_prop;
struct pwr_backlight_properties bl_prop[MAX_NUM_EDP];
struct backlight_state bl_state[MAX_NUM_EDP];
unsigned int edp_num;
bool psr_smu_optimizations_support;
bool multi_disp_optimizations_support;
unsigned int num_entities;
};
union dmcu_abm_set_bl_params {
struct {
unsigned int gradual_change : 1; /* [0:0] */
unsigned int reserved : 15; /* [15:1] */
unsigned int frame_ramp : 16; /* [31:16] */
} bits;
unsigned int u32All;
};
bool dmcu_load_iram(struct dmcu *dmcu,
struct dmcu_iram_parameters params);
bool dmub_init_abm_config(struct resource_pool *res_pool,
@ -87,4 +179,33 @@ bool fill_custom_backlight_caps(unsigned int config_no,
void reset_replay_dsync_error_count(struct dc_link *link);
void change_replay_to_psr(struct dc_link *link);
void change_psr_to_replay(struct dc_link *link);
void initialize_backlight_caps(struct core_power *core_power, unsigned int inst);
unsigned int backlight_millipercent_to_pwm(
struct core_power *core_power, unsigned int millipercent, unsigned int inst);
unsigned int backlight_millipercent_to_millinit(
struct core_power *core_power, unsigned int millipercent, unsigned int inst);
void fill_backlight_level_params(struct core_power *core_power,
struct set_backlight_level_params *backlight_level_params,
int panel_inst, uint8_t aux_inst, unsigned int backlight_pwm,
enum backlight_control_type backlight_control_type,
unsigned int backlight_millinit, unsigned int transition_time_millisec,
bool is_hdr);
bool mod_power_hw_init_backlight(struct mod_power *mod_power);
void mod_power_update_backlight_on_mode_change(
struct core_power *core_power,
struct dc_link *link,
unsigned int panel_inst,
uint8_t aux_inst,
bool is_hdr);
unsigned int map_index_from_stream(struct core_power *core_power,
const struct dc_stream_state *stream);
bool mod_power_psr_notify_mode_change(struct mod_power *mod_power,
const struct dc_stream_state *stream,
struct dc_link *link,
unsigned int stream_index);
void mod_power_replay_notify_mode_change(struct mod_power *mod_power,
struct dc *dc,
struct dc_link *link,
const struct dc_stream_state *stream,
unsigned int stream_index);
#endif /* MODULES_POWER_POWER_HELPERS_H_ */

View File

@ -0,0 +1,665 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2026 Advanced Micro Devices, Inc.
#include "dm_services.h"
#include "dc.h"
#include "mod_power.h"
#include "core_types.h"
#include "dmcu.h"
#include "abm.h"
#include "power_helpers.h"
#include "dce/dmub_psr.h"
#include "dal_asic_id.h"
#include "link_service.h"
#include <linux/math.h>
#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dmub_cmd.h"
#define MOD_POWER_TO_CORE(mod_power)\
container_of(mod_power, struct core_power, mod_public)
static unsigned int calc_psr_num_static_frames(unsigned int vsync_rate_hz)
{
/* Initialize fail-safe to 2 static frames. */
unsigned int num_frames_static = 2;
/* Calculate number of frames such that at least 30 ms has passed.
* Round up to ensure the static period is not shorter than 30 ms.
*/
if (vsync_rate_hz != 0)
num_frames_static = DIV_ROUND_UP(30000 * vsync_rate_hz, 1000000);
return num_frames_static;
}
bool mod_power_psr_notify_mode_change(struct mod_power *mod_power,
const struct dc_stream_state *stream,
struct dc_link *link,
unsigned int stream_index)
{
struct core_power *core_power = NULL;
struct dc *dc = NULL;
struct psr_config psr_config = {0};
struct psr_context psr_context = {0};
int active_psr_events = 0;
if ((mod_power == NULL) || (stream == NULL) || (link == NULL))
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
dc = core_power->dc;
// NO num_entities check here - already validated by caller
// stream_index is passed as validated parameter
active_psr_events = core_power->map[stream_index].psr_events;
/* Calculate PSR configurations */
mod_power_calc_psr_configs(&psr_config, link, stream);
psr_config.psr_exit_link_training_required =
core_power->map[stream_index].caps->psr_exit_link_training_required;
if (dc->ctx->asic_id.chip_family >= AMDGPU_FAMILY_GC_11_0_1)
psr_config.allow_smu_optimizations =
core_power->psr_smu_optimizations_support && dc_is_embedded_signal(stream->signal);
else
psr_config.allow_smu_optimizations =
core_power->psr_smu_optimizations_support &&
mod_power_only_edp(dc->current_state, stream);
psr_config.allow_multi_disp_optimizations = core_power->multi_disp_optimizations_support;
psr_config.rate_control_caps = core_power->map[stream_index].caps->rate_control_caps;
if (active_psr_events & psr_event_os_request_force_ffu)
psr_config.os_request_force_ffu = true;
/*
* DSC support:
* DSC slice height value must be 'mod' by su_y_granularity.
* According to Panel Vendor, there might be varied conditions to fulfill.
* Right now, DSC slice height value must be multiple of su_y_granularity.
*
* The value of DSC slice height is determined in DSC Driver but it does not
* propagated out here, so we need to calculate it as below 'slice_height'.
*/
psr_su_set_dsc_slice_height(dc, link,
(struct dc_stream_state *) stream,
&psr_config);
dc_link_setup_psr(link, stream, &psr_config, &psr_context);
return true;
}
static void mod_power_psr_set_power_opt(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int active_psr_events,
bool psr_enable_request)
{
(void)psr_enable_request;
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
unsigned int power_opt = 0;
if (!stream)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
stream_index = map_index_from_stream(core_power, stream);
if (!core_power->map[stream_index].caps->psr_version)
return;
link = dc_stream_get_link(stream);
if (active_psr_events == 0) {
/* Static Screen */
power_opt |= (psr_power_opt_smu_opt_static_screen | psr_power_opt_z10_static_screen |
psr_power_opt_ds_disable_allow);
}
/* psr_power_opt_flag is a configuration parameter into the module that determines
* which optimizations to enable during psr
*/
power_opt &= core_power->map[stream_index].caps->psr_power_opt_flag;
if (core_power->map[stream_index].psr_power_opt != power_opt) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power set_power_opt: psr_power_opt=0x%04x, power_opt=0x%04x active_psr_events=0x%04x, psr_power_opt_flag=0x%04x",
core_power->map[stream_index].psr_power_opt,
power_opt,
active_psr_events,
core_power->map[stream_index].caps->psr_power_opt_flag);
dc_link_set_psr_allow_active(link, NULL, false, false, &power_opt);
core_power->map[stream_index].psr_power_opt = power_opt;
}
}
static bool set_psr_enable(struct mod_power *mod_power,
struct dc_stream_state *stream,
bool psr_enable,
bool wait,
bool force_static)
{
struct core_power *core_power = NULL;
enum dc_psr_state state = PSR_STATE0;
unsigned int retry_count;
const unsigned int max_retry = 1000;
struct dc_link *link = NULL;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: ERROR: stream=%p num_entities=%u",
stream,
core_power->num_entities);
return false;
}
if (psr_enable) {
unsigned int vsync_rate_hz;
struct dc_static_screen_params params = {0};
vsync_rate_hz = (unsigned int)div_u64(div_u64((
stream->timing.pix_clk_100hz * 100),
stream->timing.v_total),
stream->timing.h_total);
params.triggers.cursor_update = true;
params.triggers.overlay_update = true;
params.triggers.surface_update = true;
params.num_frames = calc_psr_num_static_frames(vsync_rate_hz);
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: CALCS: pix_clk_100hz=%u v_total=%u h_total=%u vsync_rate_hz=%u num_frames=%u",
stream->timing.pix_clk_100hz,
stream->timing.v_total,
stream->timing.h_total,
vsync_rate_hz,
params.num_frames);
dc_stream_set_static_screen_params(core_power->dc,
&stream, 1,
&params);
}
link = dc_stream_get_link(stream);
if (!dc_link_set_psr_allow_active(link, &psr_enable, false, force_static, NULL)) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: ERROR: stream=%p link=%p psr_enable=%d",
stream,
link,
psr_enable);
return false;
}
if (wait == true) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: BEGIN WAIT: psr_enable=%d",
(int)psr_enable);
for (retry_count = 0; retry_count <= max_retry; retry_count++) {
dc_link_get_psr_state(link, &state);
if (psr_enable) {
if (state != PSR_STATE0 &&
(!force_static || state == PSR_STATE3))
break;
} else {
if (state == PSR_STATE0)
break;
}
udelay(500);
}
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: END WAIT: psr_enable=%d",
(int)psr_enable);
/* assert if max retry hit */
if (retry_count >= max_retry) {
ASSERT(0);
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: ERROR: retry_count=%u: Unexpectedly long wait for PSR state change.",
retry_count);
}
} else {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_INFORMATION,
WPP_BIT_FLAG_Firmware_PsrState,
"set psr enable: PSR state change initiated (wait=false): psr_enable=%d",
(int)psr_enable);
}
return true;
}
bool mod_power_get_psr_event(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int *active_psr_events)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (!core_power->map[stream_index].caps->psr_version)
return false;
*active_psr_events = core_power->map[stream_index].psr_events;
return true;
}
bool mod_power_set_psr_event(struct mod_power *mod_power,
struct dc_stream_state *stream, bool set_event,
enum psr_event event, bool wait)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
unsigned int active_psr_events = 0;
bool psr_enable_request = false;
bool force_static = false;
if (mod_power == NULL || stream == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
stream_index = map_index_from_stream(core_power, stream);
if (core_power->num_entities == 0) {
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_ERROR,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power set_psr_event: ERROR: stream=%p event=%d num_entities=%u",
stream,
(int)event,
core_power->num_entities);
return false;
}
if (!core_power->map[stream_index].caps->psr_version)
return false;
if (set_event)
core_power->map[stream_index].psr_events |= event;
else
core_power->map[stream_index].psr_events &= ~event;
active_psr_events = core_power->map[stream_index].psr_events;
// ignore other events when we're in forced psr enabled state
if (active_psr_events & psr_event_dynamic_display_switch &&
event != psr_event_dynamic_display_switch)
return false;
// ignore other events when we're in forced psr enabled state
if (active_psr_events & psr_event_os_override_hold &&
event != psr_event_os_override_hold)
return false;
// ignore other events when we're in forced psr enabled state
// dds events need to be processed while in dynamic_link_rate_control
if (active_psr_events & psr_event_dynamic_link_rate_control &&
event != psr_event_dynamic_link_rate_control &&
event != psr_event_dds_defer_stream_enable &&
event != psr_event_dynamic_display_switch)
return false;
if (active_psr_events & (psr_event_test_harness_disable_psr | psr_event_os_request_disable))
psr_enable_request = false;
else if (active_psr_events & psr_event_pause)
psr_enable_request = false;
else if (active_psr_events & psr_event_test_harness_enable_psr)
psr_enable_request = true;
else if (active_psr_events & psr_event_dynamic_display_switch) {
psr_enable_request = true;
force_static = true;
} else if (active_psr_events & psr_event_dynamic_link_rate_control) {
psr_enable_request = true;
force_static = true;
} else if (active_psr_events & psr_event_edp_panel_off_disable_psr)
psr_enable_request = false;
else if (active_psr_events & (psr_event_hw_programming |
psr_event_defer_enable |
psr_event_dds_defer_stream_enable |
psr_event_vrr_transition |
psr_event_immediate_flip))
psr_enable_request = false;
else if (active_psr_events & psr_event_big_screen_video)
psr_enable_request = true;
else if (active_psr_events & psr_event_full_screen)
psr_enable_request = false;
else if (active_psr_events & psr_event_mpo_video_selective_update)
psr_enable_request = true;
else if (active_psr_events & psr_event_vsync)
psr_enable_request = false;
else if (active_psr_events & psr_event_crc_window_active)
psr_enable_request = false;
else
psr_enable_request = true;
DC_TRACE_LEVEL_MESSAGE(DAL_TRACE_LEVEL_VERBOSE,
WPP_BIT_FLAG_Firmware_PsrState,
"mod_power set_psr_event: before: psr_enabled=%d -> request: set_event=%d event=0x%04x -> result: psr_events=0x%04x psr_enable_request=%d",
(int)core_power->map[stream_index].psr_enabled,
(int)set_event,
(unsigned int)event,
(unsigned int)core_power->map[stream_index].psr_events,
(int)psr_enable_request);
mod_power_psr_set_power_opt(mod_power, stream, active_psr_events, psr_enable_request);
if (core_power->map[stream_index].psr_enabled != psr_enable_request || force_static) {
if (set_psr_enable(mod_power, stream, psr_enable_request, wait, force_static))
core_power->map[stream_index].psr_enabled = psr_enable_request;
}
return true;
}
bool mod_power_get_psr_state(struct mod_power *mod_power,
const struct dc_stream_state *stream,
enum dc_psr_state *state)
{
struct core_power *core_power = NULL;
const struct dc_link *link = NULL;
if (!stream)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
link = dc_stream_get_link(stream);
return dc_link_get_psr_state(link, state);
}
bool mod_power_get_psr_enabled_status(struct mod_power *mod_power,
const struct dc_stream_state *stream,
bool *psr_enabled)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (!core_power->map[stream_index].caps->psr_version)
return false;
*psr_enabled = core_power->map[stream_index].psr_enabled;
return true;
}
void mod_power_psr_residency(struct mod_power *mod_power,
const struct dc_stream_state *stream,
unsigned int *residency,
const uint8_t mode)
{
struct core_power *core_power = NULL;
const struct dc_link *link = NULL;
if (!stream)
return;
if (mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
link = dc_stream_get_link(stream);
if (link != NULL)
link->dc->link_srv->edp_get_psr_residency(link, residency, mode);
}
bool mod_power_psr_get_active_psr_events(struct mod_power *mod_power,
const struct dc_stream_state *stream, unsigned int *active_psr_events)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (!stream)
return false;
if (mod_power == NULL)
return false;
if (active_psr_events == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
*active_psr_events = core_power->map[stream_index].psr_events;
return true;
}
bool mod_power_psr_set_sink_vtotal_in_psr_active(struct mod_power *mod_power,
const struct dc_stream_state *stream,
uint16_t psr_vtotal_idle,
uint16_t psr_vtotal_su)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
const struct dc_link *link = NULL;
if (!stream)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (!core_power->map[stream_index].caps->psr_version)
return false;
link = dc_stream_get_link(stream);
return link->dc->link_srv->edp_set_sink_vtotal_in_psr_active(
link, psr_vtotal_idle, psr_vtotal_su);
}
/*
* is_psr_su_specific_panel() - check if sink is AMD vendor-specific PSR-SU
* supported eDP device.
*
* @link: dc link pointer
*
* Return: true if AMDGPU vendor specific PSR-SU eDP panel
*/
bool is_psr_su_specific_panel(struct dc_link *link)
{
bool isPSRSUSupported = false;
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
if (dpcd_caps->edp_rev >= DP_EDP_14) {
if (dpcd_caps->psr_info.psr_version >= DP_PSR2_WITH_Y_COORD_ET_SUPPORTED)
isPSRSUSupported = true;
/*
* Some panels will report PSR capabilities over additional DPCD bits.
* Such panels are approved despite reporting only PSR v3, as long as
* the additional bits are reported.
*/
if (dpcd_caps->sink_dev_id == DP_BRANCH_DEVICE_ID_001CF8) {
/*
* This is the temporary workaround to disable PSRSU when system turned on
* DSC function on the sepcific sink.
*/
if (dpcd_caps->psr_info.psr_version < DP_PSR2_WITH_Y_COORD_IS_SUPPORTED)
isPSRSUSupported = false;
else if (dpcd_caps->dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT &&
((dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x08) ||
(dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x07)))
isPSRSUSupported = false;
else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x03)
isPSRSUSupported = false;
else if (dpcd_caps->sink_dev_id_str[1] == 0x08 && dpcd_caps->sink_dev_id_str[0] == 0x01)
isPSRSUSupported = false;
else if (dpcd_caps->psr_info.force_psrsu_cap == 0x1)
isPSRSUSupported = true;
}
}
return isPSRSUSupported;
}
/**
* mod_power_calc_psr_configs() - calculate/update generic psr configuration fields.
* @psr_config: [output], psr configuration structure to be updated
* @link: [input] dc link pointer
* @stream: [input] dc stream state pointer
*
* calculate and update the psr configuration fields that are not DM specific, i.e. such
* fields which are based on DPCD caps or timing information. To setup PSR in DMUB FW,
* this helper is assumed to be called before the call of the DC helper dc_link_setup_psr().
*
* PSR config fields to be updated within the helper:
* - psr_rfb_setup_time
* - psr_sdp_transmit_line_num_deadline
* - line_time_in_us
* - su_y_granularity
* - su_granularity_required
* - psr_frame_capture_indication_req
* - psr_exit_link_training_required
*
* PSR config fields that are DM specific and NOT updated within the helper:
* - allow_smu_optimizations
* - allow_multi_disp_optimizations
*/
void mod_power_calc_psr_configs(struct psr_config *psr_config,
struct dc_link *link,
const struct dc_stream_state *stream)
{
unsigned int num_vblank_lines = 0;
unsigned int vblank_time_in_us = 0;
unsigned int sdp_tx_deadline_in_us = 0;
unsigned int line_time_in_us = 0;
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
const int psr_setup_time_step_in_us = 55; /* refer to eDP spec DPCD 0x071h */
/* timing parameters */
num_vblank_lines = stream->timing.v_total -
stream->timing.v_addressable -
stream->timing.v_border_top -
stream->timing.v_border_bottom;
vblank_time_in_us = (stream->timing.h_total * num_vblank_lines * 1000) / (stream->timing.pix_clk_100hz / 10);
line_time_in_us = ((stream->timing.h_total * 1000) / (stream->timing.pix_clk_100hz / 10)) + 1;
/**
* psr configuration fields
*
* as per eDP 1.5 pg. 377 of 459, DPCD 0x071h bits [3:1], psr setup time bits interpreted as below
* 000b <--> 330 us (default)
* 001b <--> 275 us
* 010b <--> 220 us
* 011b <--> 165 us
* 100b <--> 110 us
* 101b <--> 055 us
* 110b <--> 000 us
*/
psr_config->psr_rfb_setup_time =
(6 - dpcd_caps->psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME) * psr_setup_time_step_in_us;
if (psr_config->psr_rfb_setup_time > vblank_time_in_us) {
link->psr_settings.psr_frame_capture_indication_req = true;
link->psr_settings.psr_sdp_transmit_line_num_deadline = num_vblank_lines;
} else {
sdp_tx_deadline_in_us = vblank_time_in_us - psr_config->psr_rfb_setup_time;
/* Set the last possible line SDP may be transmitted without violating the RFB setup time */
link->psr_settings.psr_frame_capture_indication_req = false;
link->psr_settings.psr_sdp_transmit_line_num_deadline = sdp_tx_deadline_in_us / line_time_in_us;
}
psr_config->psr_sdp_transmit_line_num_deadline = link->psr_settings.psr_sdp_transmit_line_num_deadline;
psr_config->line_time_in_us = line_time_in_us;
psr_config->su_y_granularity = dpcd_caps->psr_info.psr2_su_y_granularity_cap;
psr_config->su_granularity_required = dpcd_caps->psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED;
psr_config->psr_frame_capture_indication_req = link->psr_settings.psr_frame_capture_indication_req;
psr_config->psr_exit_link_training_required =
!link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED;
}
bool psr_su_set_dsc_slice_height(struct dc *dc, struct dc_link *link,
struct dc_stream_state *stream,
struct psr_config *config)
{
uint32_t pic_height;
uint32_t slice_height;
config->dsc_slice_height = 0;
if (!(link->connector_signal & SIGNAL_TYPE_EDP) ||
!dc->caps.edp_dsc_support ||
link->panel_config.dsc.disable_dsc_edp ||
!link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_SUPPORT ||
!stream->timing.dsc_cfg.num_slices_v)
return true;
pic_height = stream->timing.v_addressable +
stream->timing.v_border_top + stream->timing.v_border_bottom;
if (stream->timing.dsc_cfg.num_slices_v == 0)
return false;
slice_height = pic_height / stream->timing.dsc_cfg.num_slices_v;
config->dsc_slice_height = (uint16_t)slice_height;
if (slice_height) {
if (config->su_y_granularity &&
(slice_height % config->su_y_granularity)) {
ASSERT(0);
return false;
}
}
return true;
}

View File

@ -0,0 +1,911 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2026 Advanced Micro Devices, Inc.
#include "dm_services.h"
#include "dc.h"
#include "mod_power.h"
#include "core_types.h"
#include "dmcu.h"
#include "abm.h"
#include "power_helpers.h"
#include "dce/dmub_psr.h"
#include "dal_asic_id.h"
#include "link_service.h"
#include <linux/math.h>
#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
#define DC_TRACE_LEVEL_MESSAGEP(...) /* do nothing */
#include "power_helpers.h"
#include "dc/inc/hw/dmcu.h"
#include "dc/inc/hw/abm.h"
#include "dc.h"
#include "core_types.h"
#include "dmub_cmd.h"
#define MOD_POWER_TO_CORE(mod_power)\
container_of(mod_power, struct core_power, mod_public)
#define LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND 25000
static bool mod_power_set_replay_active(struct dc_stream_state *stream,
bool replay_active,
bool wait,
bool force_static)
{
uint64_t state;
unsigned int retry_count;
const unsigned int max_retry = 1000;
struct dc_link *link = NULL;
if (!stream)
return false;
link = dc_stream_get_link(stream);
if (!link)
return false;
if (!dc_link_set_replay_allow_active(link, &replay_active, false, force_static, NULL))
return false;
if (wait == true) {
for (retry_count = 0; retry_count <= max_retry; retry_count++) {
dc_link_get_replay_state(link, &state);
if (replay_active) {
if (state != REPLAY_STATE_0 &&
(!force_static || state == REPLAY_STATE_3))
break;
} else {
if (state == REPLAY_STATE_0)
break;
}
udelay(500);
}
/* assert if max retry hit */
if (retry_count >= max_retry)
ASSERT(0);
} else {
/* To-do: Add trace log */
}
return true;
}
static unsigned int mod_power_replay_setup_power_opt(struct dc_link *link,
unsigned int active_replay_events, bool is_ultra_sleep_mode)
{
unsigned int power_opt = 0;
if (is_ultra_sleep_mode) {
/* Static Screen */
power_opt |= (replay_power_opt_smu_opt_static_screen | replay_power_opt_z10_static_screen);
} else if (active_replay_events & replay_event_test_harness_ultra_sleep) {
power_opt |= replay_power_opt_z10_static_screen;
}
/* replay_power_opt_flag is a configuration parameter into the module that determines
* which optimizations to enable during replay
*/
power_opt &= link->replay_settings.config.replay_power_opt_supported;
return power_opt;
}
static bool mod_power_replay_set_power_opt(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int active_replay_events,
bool is_ultra_sleep_mode)
{
(void)mod_power;
struct dc_link *link = NULL;
unsigned int power_opt = 0;
if (!stream)
return false;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode);
if (!dc_link_set_replay_allow_active(link, NULL, false, false, &power_opt))
return false;
return true;
}
bool mod_power_get_replay_event(struct mod_power *mod_power,
struct dc_stream_state *stream,
unsigned int *active_replay_events)
{
struct core_power *core_power = NULL;
unsigned int stream_index = 0;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
*active_replay_events = core_power->map[stream_index].replay_events;
return true;
}
static bool mod_power_update_replay_active_status(unsigned int active_replay_events,
struct dc_link *link, uint32_t *coasting_vtotal, bool *is_full_screen_video,
bool *is_ultra_sleep_mode, uint16_t *frame_skip_number, bool *is_video_playback)
{
if (!link || !coasting_vtotal || !is_full_screen_video || !is_video_playback)
return false;
// Check coasting_vtotal_table has been updated.
if (!link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC]
|| !link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM])
return false;
unsigned int replay_enable_option =
link->replay_settings.config.replay_enable_option;
/* TODO: To support test harness and DDS event */
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM] <= 0xFFFF);
*frame_skip_number = (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];
link->replay_settings.config.replay_timing_sync_supported = false;
*is_full_screen_video = false;
*is_ultra_sleep_mode = false;
*is_video_playback = false;
/* DSAT test scenario */
if (active_replay_events & replay_event_test_harness_mode) {
if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF);
*frame_skip_number =
(uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
}
/* During the ultra sleep mode testing, disable the timing sync in short vblank mode */
if (active_replay_events & (replay_event_test_harness_enable_replay)) {
if ((active_replay_events & replay_event_test_harness_ultra_sleep) &&
!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode)
link->replay_settings.config.replay_timing_sync_supported = false;
return true;
} else
return false;
} else if (active_replay_events & (replay_event_test_harness_enable_replay)) {
if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
/* During the ultra sleep mode testing, disable the timing sync in short vblank mode */
if ((active_replay_events & replay_event_test_harness_ultra_sleep) &&
!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode) {
link->replay_settings.config.replay_timing_sync_supported = false;
}
return true;
} else if (active_replay_events &
(replay_event_test_harness_disable_replay | replay_event_os_request_disable)) {
// set last set coasting vtotal
if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
return false;
}
/* Inactive conditions */
if (active_replay_events & (replay_event_edp_panel_off_disable_psr |
replay_event_hw_programming |
replay_event_vrr |
replay_event_immediate_flip |
replay_event_prepare_vtotal |
replay_event_vrr_transition |
replay_event_pause |
replay_event_disable_replay_while_DPMS |
replay_event_sleep_resume |
replay_event_disable_in_AC |
replay_event_disable_replay_while_detect_display |
replay_event_infopacket |
replay_event_crc_window_active))
return false;
// Full screen scenario
if (active_replay_events & replay_event_full_screen) {
if (!(replay_enable_option & pr_enable_option_full_screen))
return false;
}
/* Full screen video scenario */
if (active_replay_events & replay_event_big_screen_video) {
link->replay_settings.config.replay_timing_sync_supported = false;
if (replay_enable_option & pr_enable_option_full_screen_video_coasting) {
unsigned int fsn_vid =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO];
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_FULL_SCREEN_VIDEO];
ASSERT(fsn_vid <= 0xFFFF);
*frame_skip_number = (uint16_t)fsn_vid;
}
*is_video_playback = true;
if ((replay_enable_option & pr_enable_option_full_screen_video) &&
(replay_enable_option & pr_enable_option_full_screen_video_coasting)) {
*is_full_screen_video = true;
return true;
} else
return false;
}
/* MPO video scenario
* Some of the cases may contain a full screen UI layer in MPO video scenario which is
* not the expected case to enable Replay.
*/
if ((active_replay_events & replay_event_mpo_video_selective_update) &&
!(active_replay_events & replay_event_full_screen)) {
link->replay_settings.config.replay_timing_sync_supported = false;
if (replay_enable_option & pr_enable_option_mpo_video_coasting) {
*coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
{
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
}
*is_video_playback = true;
if (replay_enable_option & pr_enable_option_mpo_video)
return true;
else
return false;
}
/* Static screen scenario */
if (!(active_replay_events & replay_event_vsync)) {
if (replay_enable_option & pr_enable_option_static_screen_coasting) {
// Do not adjust eDP refresh rate if static screen + normal sleep mode
if ((!(link->replay_settings.config.replay_power_opt_supported &
replay_power_opt_z10_static_screen)) ||
(active_replay_events & replay_event_cursor_updating)) {
// normal sleep mode
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM];
{
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
} else {
// ultra sleep mode
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC];
{
uint32_t frame_skip_val =
link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_STATIC];
ASSERT(frame_skip_val <= 0xFFFF);
*frame_skip_number = (uint16_t)frame_skip_val;
}
*is_ultra_sleep_mode = true;
}
}
if (replay_enable_option & pr_enable_option_static_screen) {
if (!link->replay_settings.config.replay_support_fast_resync_in_ultra_sleep_mode)
link->replay_settings.config.replay_timing_sync_supported = false;
return true;
} else
return false;
}
/* General UI scenario */
if (active_replay_events & replay_event_general_ui) {
if (replay_enable_option & pr_enable_option_general_ui)
return true;
else
return false;
}
return false;
}
bool mod_power_replay_set_coasting_vtotal(struct mod_power *mod_power,
const struct dc_stream_state *stream,
uint32_t coasting_vtotal,
uint16_t frame_skip_number)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
if (!stream)
return false;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
return link->dc->link_srv->edp_set_coasting_vtotal(link, coasting_vtotal, frame_skip_number);
}
void mod_power_replay_set_timing_sync_supported(struct mod_power *mod_power,
const struct dc_stream_state *stream)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
union dmub_replay_cmd_set cmd_data = { 0 };
if (!stream || mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index > core_power->num_entities) //invalid index
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
cmd_data.sync_data.timing_sync_supported = link->replay_settings.config.replay_timing_sync_supported;
link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Timing_Sync_Supported,
&cmd_data);
}
void mod_power_replay_disabled_adaptive_sync_sdp(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool force_disabled)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
union dmub_replay_cmd_set cmd_data = { 0 };
if (!stream || mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index > core_power->num_entities) //invalid index
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
cmd_data.disabled_adaptive_sync_sdp_data.force_disabled = force_disabled;
link->dc->link_srv->edp_send_replay_cmd(link, Replay_Disabled_Adaptive_Sync_SDP,
&cmd_data);
}
static void mod_power_replay_set_general_cmd(struct mod_power *mod_power,
const struct dc_stream_state *stream,
const enum dmub_cmd_replay_general_subtype general_cmd_type,
const uint32_t param1, const uint32_t param2)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
union dmub_replay_cmd_set cmd_data = { 0 };
if (!stream || mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index > core_power->num_entities) //invalid index
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
cmd_data.set_general_cmd_data.subtype = general_cmd_type;
cmd_data.set_general_cmd_data.param1 = param1;
cmd_data.set_general_cmd_data.param2 = param2;
link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_General_Cmd,
&cmd_data);
}
void mod_power_replay_disabled_desync_error_detection(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool force_disabled)
{
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_DISABLED_DESYNC_ERROR_DETECTION,
force_disabled, 0);
}
static void mod_power_replay_set_pseudo_vtotal(struct mod_power *mod_power,
const struct dc_stream_state *stream, uint16_t vtotal)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
union dmub_replay_cmd_set cmd_data = { 0 };
if (!stream || mod_power == NULL)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return;
stream_index = map_index_from_stream(core_power, stream);
if (stream_index > core_power->num_entities) //invalid index
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
cmd_data.pseudo_vtotal_data.vtotal = vtotal;
if (link->replay_settings.last_pseudo_vtotal != vtotal) {
link->replay_settings.last_pseudo_vtotal = vtotal;
link->dc->link_srv->edp_send_replay_cmd(link, Replay_Set_Pseudo_VTotal, &cmd_data);
}
}
static void mod_power_update_error_status(struct mod_power *mod_power,
const struct dc_stream_state *stream)
{
struct dc_link *link = NULL;
union replay_debug_flags *pDebug = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link)
return;
pDebug = (union replay_debug_flags *)&link->replay_settings.config.debug_flags;
if (pDebug->bitfields.enable_visual_confirm_debug == 0)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_UPDATE_ERROR_STATUS,
link->replay_settings.config.replay_error_status.raw, 0);
}
void mod_power_set_low_rr_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool low_rr_supported)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_SET_LOW_RR_ACTIVATE,
low_rr_supported, 0);
}
void mod_power_set_video_conferencing_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool video_conferencing_activate)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_VIDEO_CONFERENCING,
video_conferencing_activate, 0);
}
void mod_power_set_coasting_vtotal_without_frame_update(struct mod_power *mod_power,
const struct dc_stream_state *stream, uint32_t coasting_vtotal)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_SET_COASTING_VTOTAL_WITHOUT_FRAME_UPDATE,
coasting_vtotal, 0);
}
void mod_power_set_replay_continuously_resync(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool enable)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_SET_CONTINUOUSLY_RESYNC,
enable, 0);
}
void mod_power_set_live_capture_with_cvt_activate(struct mod_power *mod_power,
const struct dc_stream_state *stream, bool live_capture_with_cvt_activate)
{
struct dc_link *link = NULL;
if (mod_power == NULL || stream == NULL)
return;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return;
// Check if LIVE_CAPTURE_WITH_CVT bit is enabled in replay optimization config
if (!link->replay_settings.config.replay_optimization.bits.LIVE_CAPTURE_WITH_CVT)
return;
if (link->replay_settings.config.live_capture_with_cvt_activated != live_capture_with_cvt_activate) {
link->replay_settings.config.live_capture_with_cvt_activated = live_capture_with_cvt_activate;
mod_power_replay_set_general_cmd(mod_power, stream,
REPLAY_GENERAL_CMD_LIVE_CAPTURE_WITH_CVT,
live_capture_with_cvt_activate, 0);
}
}
bool mod_power_set_replay_event(struct mod_power *mod_power,
struct dc_stream_state *stream, bool set_event,
enum replay_event event, bool wait_for_disable)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int stream_index = 0;
unsigned int active_replay_events = 0;
bool replay_active_request = false;
bool force_static = false;
uint32_t coasting_vtotal = 0;
bool current_timing_sync_status = false;
bool is_full_screen_video = false;
bool is_ultra_sleep_mode = false;
unsigned int sink_duration_us = 0;
bool low_rr_active = false;
uint16_t frame_skip_number = 0;
bool is_video_playback = false;
if (!stream)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
stream_index = map_index_from_stream(core_power, stream);
if (set_event)
core_power->map[stream_index].replay_events |= event;
else
core_power->map[stream_index].replay_events &= ~event;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
if ((core_power->map[stream_index].replay_events & replay_event_disable_replay_while_switching_mux) != 0)
return false;
if ((core_power->map[stream_index].replay_events & replay_event_os_override_hold) != 0)
return false;
active_replay_events = core_power->map[stream_index].replay_events;
current_timing_sync_status =
link->replay_settings.config.replay_timing_sync_supported;
replay_active_request = mod_power_update_replay_active_status(active_replay_events,
link, &coasting_vtotal, &is_full_screen_video, &is_ultra_sleep_mode, &frame_skip_number, &is_video_playback);
if (is_full_screen_video)
mod_power_replay_set_pseudo_vtotal(mod_power, stream,
link->replay_settings.low_rr_full_screen_video_pseudo_vtotal);
else
mod_power_replay_set_pseudo_vtotal(mod_power, stream, 0);
//If timing_sync_status change, then re-enabled set timing_sync_supported value and re-enabled replay
if (current_timing_sync_status != link->replay_settings.config.replay_timing_sync_supported)
mod_power_replay_set_timing_sync_supported(mod_power, stream);
if (link->replay_settings.config.low_rr_supported) {
sink_duration_us =
(unsigned int)(div_u64(((unsigned long long)(coasting_vtotal)
* 10000) * stream->timing.h_total,
stream->timing.pix_clk_100hz));
low_rr_active = sink_duration_us < LOW_REFRESH_RATE_DURATION_US_UPPER_BOUND ? false : true;
if (low_rr_active != link->replay_settings.config.low_rr_activated) {
mod_power_set_low_rr_activate(mod_power, stream, low_rr_active);
link->replay_settings.config.low_rr_activated = low_rr_active;
}
}
// The function return fail when
// 1. DMUB function is not support (for backward compatible).
// 2. active_replay_events or coasting_vtotal is not updated in the same time
if (!mod_power_replay_set_power_opt_and_coasting_vtotal(mod_power,
stream, active_replay_events, coasting_vtotal, is_ultra_sleep_mode, frame_skip_number)) {
if (!mod_power_replay_set_power_opt(mod_power, stream, active_replay_events, is_ultra_sleep_mode))
return false;
if (!mod_power_replay_set_coasting_vtotal(mod_power, stream, coasting_vtotal, frame_skip_number))
return false;
}
mod_power_set_live_capture_with_cvt_activate(mod_power, stream, is_video_playback);
mod_power_update_error_status(mod_power, stream);
// If Replay is going to be enable (No matter is disable -> enable or enable -> enable), we don't need to wait.
// If Replay is going to be disable
// if disable -> disable
// -> Replay DMUB state should be state 0.
// So no matter wait_for_disable is true or not, it should makes no difference.
// if enable -> disable -> We should wait if wait_for_disable is true.
if (replay_active_request)
wait_for_disable = false;
if (!mod_power_set_replay_active(stream, replay_active_request, wait_for_disable, force_static))
return false;
return true;
}
bool mod_power_get_replay_active_status(const struct dc_stream_state *stream,
bool *replay_active)
{
const struct dc_link *link = NULL;
if (!stream)
return false;
link = dc_stream_get_link(stream);
*replay_active = link->replay_settings.replay_allow_active;
return true;
}
void mod_power_replay_residency(const struct dc_stream_state *stream,
unsigned int *residency, const bool is_start, const bool is_alpm)
{
const struct dc_link *link = NULL;
enum pr_residency_mode mode;
if (!stream)
return;
link = dc_stream_get_link(stream);
if (is_alpm)
mode = PR_RESIDENCY_MODE_ALPM;
else
mode = PR_RESIDENCY_MODE_PHY;
if (link && link->dc && link->dc->link_srv)
link->dc->link_srv->edp_replay_residency(link, residency, is_start, mode);
}
bool mod_power_replay_set_power_opt_and_coasting_vtotal(struct mod_power *mod_power,
const struct dc_stream_state *stream, unsigned int active_replay_events, uint32_t coasting_vtotal,
bool is_ultra_sleep_mode, uint16_t frame_skip_number)
{
struct core_power *core_power = NULL;
struct dc_link *link = NULL;
unsigned int power_opt = 0;
if (!stream)
return false;
if (mod_power == NULL)
return false;
core_power = MOD_POWER_TO_CORE(mod_power);
if (core_power->num_entities == 0)
return false;
link = dc_stream_get_link(stream);
if (!link || !link->replay_settings.replay_feature_enabled)
return false;
power_opt = mod_power_replay_setup_power_opt(link, active_replay_events, is_ultra_sleep_mode);
return link->dc->link_srv->edp_set_replay_power_opt_and_coasting_vtotal(link, &power_opt, coasting_vtotal, frame_skip_number);
}
void mod_power_replay_notify_mode_change(struct mod_power *mod_power,
struct dc *dc,
struct dc_link *link,
const struct dc_stream_state *stream,
unsigned int stream_index)
{
struct core_power *core_power = NULL;
int active_replay_events = 0;
if (!mod_power || !dc || !link || !stream)
return;
core_power = MOD_POWER_TO_CORE(mod_power);
active_replay_events = core_power->map[stream_index].replay_events;
link->replay_settings.replay_smu_opt_enable =
(link->replay_settings.config.replay_smu_opt_supported &&
mod_power_only_edp(dc->current_state, stream));
if (active_replay_events & replay_event_os_request_force_ffu) {
link->replay_settings.config.os_request_force_ffu = true;
}
if (dc_is_embedded_signal(stream->signal))
dc->link_srv->dp_setup_replay(link, stream);
}
void init_replay_config(struct dc_link *link, struct replay_config *pr_config)
{
link->replay_settings.config = *pr_config;
}
void set_replay_frame_skip_number(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t coasting_vtotal_refresh_rate_uhz,
uint32_t flicker_free_refresh_rate_uhz,
bool is_defer)
{
uint32_t *frame_skip_number_array = NULL;
uint32_t frame_skip_number = 0;
if (link == NULL)
return;
if (link->replay_settings.config.frame_skip_supported == false)
return;
if (flicker_free_refresh_rate_uhz == 0 || coasting_vtotal_refresh_rate_uhz == 0)
return;
if (is_defer)
frame_skip_number_array = link->replay_settings.defer_frame_skip_number_table;
else
frame_skip_number_array = link->replay_settings.frame_skip_number_table;
if (frame_skip_number_array == NULL)
return;
frame_skip_number = (coasting_vtotal_refresh_rate_uhz + 500000) / flicker_free_refresh_rate_uhz;
if (frame_skip_number >= 1)
frame_skip_number_array[type] = frame_skip_number - 1;
else
frame_skip_number_array[type] = 0;
}
void set_replay_defer_update_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal)
{
link->replay_settings.defer_update_coasting_vtotal_table[type] = vtotal;
}
void update_replay_coasting_vtotal_from_defer(struct dc_link *link,
enum replay_coasting_vtotal_type type)
{
link->replay_settings.coasting_vtotal_table[type] =
link->replay_settings.defer_update_coasting_vtotal_table[type];
link->replay_settings.frame_skip_number_table[type] =
link->replay_settings.defer_frame_skip_number_table[type];
}
void set_replay_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal)
{
link->replay_settings.coasting_vtotal_table[type] = vtotal;
}
void set_replay_low_rr_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal)
{
link->replay_settings.low_rr_full_screen_video_pseudo_vtotal = vtotal;
}
void calculate_replay_link_off_frame_count(struct dc_link *link,
uint16_t vtotal, uint16_t htotal)
{
uint32_t max_link_off_frame_count = 0;
uint16_t max_deviation_line = 0, pixel_deviation_per_line = 0;
if (!link || link->replay_settings.config.replay_version != DC_FREESYNC_REPLAY)
return;
max_deviation_line = link->dpcd_caps.pr_info.max_deviation_line;
pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line;
if (htotal != 0 && vtotal != 0 && pixel_deviation_per_line != 0)
max_link_off_frame_count = htotal * max_deviation_line / (pixel_deviation_per_line * vtotal);
else
ASSERT(0);
link->replay_settings.link_off_frame_count = max_link_off_frame_count;
}
void reset_replay_dsync_error_count(struct dc_link *link)
{
link->replay_settings.replay_desync_error_fail_count = 0;
}

View File

@ -435,10 +435,12 @@ int smu_v15_0_fini_smc_tables(struct smu_context *smu)
smu_table->watermarks_table = NULL;
smu_table->metrics_time = 0;
kfree(smu_dpm->dpm_policies);
kfree(smu_dpm->dpm_context);
kfree(smu_dpm->golden_dpm_context);
kfree(smu_dpm->dpm_current_power_state);
kfree(smu_dpm->dpm_request_power_state);
smu_dpm->dpm_policies = NULL;
smu_dpm->dpm_context = NULL;
smu_dpm->golden_dpm_context = NULL;
smu_dpm->dpm_context_size = 0;

View File

@ -139,7 +139,8 @@ static int amdgpu_ras_get_ras_safe_fb_addr_ranges(struct ras_core_context *ras_c
struct amdgpu_mem_partition_info *mem_ranges;
uint32_t i = 0;
if (cmd->input_size != sizeof(*input_data))
if ((cmd->input_size != sizeof(*input_data)) ||
(cmd->output_buf_size < sizeof(*ranges)))
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
mem_ranges = adev->gmc.mem_partitions;
@ -207,7 +208,8 @@ static int amdgpu_ras_translate_fb_address(struct ras_core_context *ras_core,
(struct ras_cmd_translate_fb_address_rsp *)cmd->output_buff_raw;
int ret = RAS_CMD__ERROR_GENERIC;
if (cmd->input_size != sizeof(struct ras_cmd_translate_fb_address_req))
if ((cmd->input_size != sizeof(struct ras_cmd_translate_fb_address_req)) ||
(cmd->output_buf_size < sizeof(*rsp_buff)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
if ((req_buff->src_addr_type >= RAS_FB_ADDR_UNKNOWN) ||
@ -279,7 +281,7 @@ int amdgpu_ras_submit_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx
cmd->cmd_res = res;
if (cmd->output_size > cmd->output_buf_size) {
if (!res && (cmd->output_size > cmd->output_buf_size)) {
RAS_DEV_ERR(cmd_core->dev,
"Output size 0x%x exceeds output buffer size 0x%x!\n",
cmd->output_size, cmd->output_buf_size);

View File

@ -291,6 +291,8 @@ static int amdgpu_ras_mgr_sw_init(struct amdgpu_ip_block *ip_block)
con->uniras_enabled = false;
if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) ||
adev->debug_enable_ras_aca)
con->uniras_enabled = true;
else
@ -307,13 +309,17 @@ static int amdgpu_ras_mgr_sw_init(struct amdgpu_ip_block *ip_block)
if (!ras_mgr->ras_core) {
RAS_DEV_ERR(adev, "Failed to create ras core!\n");
ret = -EINVAL;
goto err;
goto err1;
}
ras_mgr->ras_core->dev = adev;
amdgpu_ras_process_init(adev);
ras_core_sw_init(ras_mgr->ras_core);
ret = ras_core_sw_init(ras_mgr->ras_core);
if (ret) {
RAS_DEV_ERR(adev, "ras_core_sw_init failed! ret:%d\n", ret);
goto err2;
}
amdgpu_ras_mgr_init_event_mgr(ras_mgr->ras_core);
if (amdgpu_sriov_vf(adev)) {
@ -321,14 +327,22 @@ static int amdgpu_ras_mgr_sw_init(struct amdgpu_ip_block *ip_block)
if (ret) {
RAS_DEV_ERR(adev,
"Virt ras sw_init failed! ret:%d\n", ret);
goto err;
goto err3;
}
}
return 0;
err:
err3:
if (ras_mgr->ras_core)
ras_core_sw_fini(ras_mgr->ras_core);
err2:
amdgpu_ras_process_fini(adev);
if (ras_mgr->ras_core)
ras_core_destroy(ras_mgr->ras_core);
err1:
kfree(ras_mgr);
con->ras_mgr = NULL;
return ret;
}
@ -496,8 +510,10 @@ uint64_t amdgpu_ras_mgr_gen_ras_event_seqno(struct amdgpu_device *adev,
if ((seqno_type == RAS_SEQNO_TYPE_DE) ||
(seqno_type == RAS_SEQNO_TYPE_POISON_CONSUMPTION)) {
ret = ras_core_put_seqno(ras_mgr->ras_core, seqno_type, seq_no);
if (ret)
if (ret) {
RAS_DEV_WARN(adev, "There are too many ras interrupts!");
return 0;
}
}
return seq_no;

View File

@ -192,8 +192,17 @@ static int amdgpu_virt_ras_get_cper_snapshot(struct ras_core_context *ras_core,
return RAS_CMD__SUCCESS;
}
static bool amdgpu_virt_ras_check_batch_cached(struct ras_cmd_batch_trace_record_rsp *rsp,
uint64_t batch_id)
{
return rsp->real_batch_num &&
rsp->real_batch_num <= RAS_CMD_MAX_BATCH_NUM &&
batch_id >= rsp->start_batch_id &&
(batch_id - rsp->start_batch_id) < rsp->real_batch_num;
}
static int amdgpu_virt_ras_get_batch_records(struct ras_core_context *ras_core, uint64_t batch_id,
struct ras_log_info **trace_arr, uint32_t arr_num,
struct ras_log_info *trace_arr, uint32_t arr_num,
struct ras_cmd_batch_trace_record_rsp *rsp_cache)
{
struct ras_cmd_batch_trace_record_req req = {
@ -204,27 +213,34 @@ static int amdgpu_virt_ras_get_batch_records(struct ras_core_context *ras_core,
struct batch_ras_trace_info *batch;
int ret = 0;
uint32_t i;
uint32_t idx;
if (!rsp->real_batch_num || (batch_id < rsp->start_batch_id) ||
(batch_id >= (rsp->start_batch_id + rsp->real_batch_num))) {
if (!amdgpu_virt_ras_check_batch_cached(rsp, batch_id)) {
memset(rsp, 0, sizeof(*rsp));
ret = amdgpu_virt_ras_send_remote_cmd(ras_core, RAS_CMD__GET_BATCH_TRACE_RECORD,
&req, sizeof(req), rsp, sizeof(*rsp));
if (ret)
return -EPIPE;
if (!amdgpu_virt_ras_check_batch_cached(rsp, batch_id)) {
memset(rsp, 0, sizeof(*rsp));
return -EIO;
}
}
batch = &rsp->batchs[batch_id - rsp->start_batch_id];
if (batch_id != batch->batch_id)
return -ENODATA;
for (i = 0; i < batch->trace_num; i++) {
if (i >= arr_num)
break;
trace_arr[i] = &rsp->records[batch->offset + i];
idx = (uint32_t)(batch_id - rsp->start_batch_id);
batch = &rsp->batchs[idx];
if (batch_id != batch->batch_id ||
batch->trace_num > MAX_RECORD_PER_BATCH ||
(uint32_t)batch->offset + batch->trace_num > RAS_CMD_MAX_TRACE_NUM) {
memset(rsp, 0, sizeof(*rsp));
return -EIO;
}
for (i = 0; i < batch->trace_num && i < arr_num; i++)
memcpy(&trace_arr[i],
&rsp->records[batch->offset + i], sizeof(*trace_arr));
return i;
}
@ -240,19 +256,22 @@ static int amdgpu_virt_ras_get_cper_records(struct ras_core_context *ras_core,
(struct ras_cmd_cper_record_rsp *)cmd->output_buff_raw;
struct ras_log_batch_overview *overview = &virt_ras->batch_mgr.batch_overview;
struct ras_cmd_batch_trace_record_rsp *rsp_cache = &virt_ras->batch_mgr.batch_trace;
struct ras_log_info **trace;
struct ras_log_info *trace;
uint32_t trace_count = MAX_RECORD_PER_BATCH;
uint32_t offset = 0, real_data_len = 0;
uint64_t batch_id;
uint8_t *out_buf;
int ret = 0, i, count;
if (cmd->input_size != sizeof(struct ras_cmd_cper_record_req))
if (cmd->input_size != sizeof(struct ras_cmd_cper_record_req) ||
(cmd->output_buf_size < sizeof(*rsp)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
if (!req->buf_size || !req->buf_ptr || !req->cper_num)
if (!req->buf_size || !req->buf_ptr || !req->cper_num ||
req->buf_size > RAS_CMD_MAX_CPER_BUF_SZ)
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
trace = kzalloc_objs(*trace, MAX_RECORD_PER_BATCH);
trace = kzalloc_objs(*trace, trace_count);
if (!trace)
return RAS_CMD__ERROR_GENERIC;
@ -269,7 +288,7 @@ static int amdgpu_virt_ras_get_cper_records(struct ras_core_context *ras_core,
if (batch_id >= overview->last_batch_id)
break;
count = amdgpu_virt_ras_get_batch_records(ras_core, batch_id,
trace, MAX_RECORD_PER_BATCH,
trace, trace_count,
rsp_cache);
if (count > 0) {
ret = ras_cper_generate_cper(ras_core, trace, count,
@ -455,7 +474,7 @@ int amdgpu_virt_ras_handle_cmd(struct ras_core_context *ras_core,
cmd->cmd_res = res;
if (cmd->output_size > cmd->output_buf_size) {
if (!res && (cmd->output_size > cmd->output_buf_size)) {
RAS_DEV_ERR(ras_core->dev,
"Output data size 0x%x exceeds buffer size 0x%x!\n",
cmd->output_size, cmd->output_buf_size);

View File

@ -329,7 +329,6 @@ struct ras_core_context {
struct ras_gfx ras_gfx;
struct ras_mp1 ras_mp1;
struct ras_process ras_proc;
struct ras_cmd_mgr ras_cmd;
struct ras_log_ring ras_log_ring;
const struct ras_sys_func *sys_fn;

View File

@ -28,20 +28,6 @@
#define RAS_CMD_MINOR_VERSION 0
#define RAS_CMD_VERSION (((RAS_CMD_MAJOR_VERSION) << 10) | (RAS_CMD_MINOR_VERSION))
static int ras_cmd_add_device(struct ras_core_context *ras_core)
{
INIT_LIST_HEAD(&ras_core->ras_cmd.head);
ras_core->ras_cmd.ras_core = ras_core;
ras_core->ras_cmd.dev_handle = (uintptr_t)ras_core ^ RAS_CMD_DEV_HANDLE_MAGIC;
return 0;
}
static int ras_cmd_remove_device(struct ras_core_context *ras_core)
{
memset(&ras_core->ras_cmd, 0, sizeof(ras_core->ras_cmd));
return 0;
}
static int ras_get_block_ecc_info(struct ras_core_context *ras_core,
struct ras_cmd_ctx *cmd, void *data)
{
@ -52,7 +38,8 @@ static int ras_get_block_ecc_info(struct ras_core_context *ras_core,
struct ras_ecc_count err_data;
int ret;
if (cmd->input_size != sizeof(struct ras_cmd_block_ecc_info_req))
if ((cmd->input_size != sizeof(struct ras_cmd_block_ecc_info_req)) ||
(cmd->output_buf_size < sizeof(*output_data)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
memset(&err_data, 0, sizeof(err_data));
@ -87,25 +74,31 @@ static int ras_cmd_get_group_bad_pages(struct ras_core_context *ras_core,
struct eeprom_umc_record record;
struct ras_cmd_bad_page_record *ras_cmd_record;
uint32_t i = 0, bp_cnt = 0, group_cnt = 0;
int ret = RAS_CMD__SUCCESS;
output_data->bp_in_group = 0;
output_data->group_index = 0;
mutex_lock(&ras_core->ras_umc.umc_lock);
bp_cnt = ras_umc_get_badpage_count(ras_core);
if (bp_cnt) {
output_data->group_index = group_index;
group_cnt = bp_cnt / RAS_CMD_MAX_BAD_PAGES_PER_GROUP
+ ((bp_cnt % RAS_CMD_MAX_BAD_PAGES_PER_GROUP) ? 1 : 0);
if (group_index >= group_cnt)
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
if (group_index >= group_cnt) {
ret = RAS_CMD__ERROR_INVALID_INPUT_DATA;
goto out;
}
i = group_index * RAS_CMD_MAX_BAD_PAGES_PER_GROUP;
for (;
i < bp_cnt && output_data->bp_in_group < RAS_CMD_MAX_BAD_PAGES_PER_GROUP;
i++) {
if (ras_umc_get_badpage_record(ras_core, i, &record))
return RAS_CMD__ERROR_GENERIC;
if (ras_umc_get_badpage_record(ras_core, i, &record)) {
ret = RAS_CMD__ERROR_GENERIC;
goto out;
}
ras_cmd_record = &output_data->records[i % RAS_CMD_MAX_BAD_PAGES_PER_GROUP];
@ -115,7 +108,10 @@ static int ras_cmd_get_group_bad_pages(struct ras_core_context *ras_core,
}
}
output_data->bp_total_cnt = bp_cnt;
return RAS_CMD__SUCCESS;
out:
mutex_unlock(&ras_core->ras_umc.umc_lock);
return ret;
}
static int ras_cmd_get_bad_pages(struct ras_core_context *ras_core,
@ -127,7 +123,8 @@ static int ras_cmd_get_bad_pages(struct ras_core_context *ras_core,
(struct ras_cmd_bad_pages_info_rsp *)cmd->output_buff_raw;
int ret;
if (cmd->input_size != sizeof(struct ras_cmd_bad_pages_info_req))
if ((cmd->input_size != sizeof(struct ras_cmd_bad_pages_info_req)) ||
(cmd->output_buf_size < sizeof(*output_data)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
ret = ras_cmd_get_group_bad_pages(ras_core, input_data->group_index, output_data);
@ -182,7 +179,8 @@ static int ras_cmd_get_cper_snapshot(struct ras_core_context *ras_core,
(struct ras_cmd_cper_snapshot_rsp *)cmd->output_buff_raw;
struct ras_log_batch_overview overview;
if (cmd->input_size != sizeof(struct ras_cmd_cper_snapshot_req))
if ((cmd->input_size != sizeof(struct ras_cmd_cper_snapshot_req)) ||
(cmd->output_buf_size < sizeof(*output_data)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
ras_log_ring_get_batch_overview(ras_core, &overview);
@ -204,23 +202,32 @@ static int ras_cmd_get_cper_records(struct ras_core_context *ras_core,
(struct ras_cmd_cper_record_req *)cmd->input_buff_raw;
struct ras_cmd_cper_record_rsp *rsp =
(struct ras_cmd_cper_record_rsp *)cmd->output_buff_raw;
struct ras_log_info *trace[MAX_RECORD_PER_BATCH] = {0};
struct ras_log_info *trace = NULL;
uint32_t trace_count = MAX_RECORD_PER_BATCH;
struct ras_log_batch_overview overview;
uint32_t offset = 0, real_data_len = 0;
uint64_t batch_id;
uint8_t *buffer;
uint8_t *buffer = NULL;
int ret = 0, i, count;
if (cmd->input_size != sizeof(struct ras_cmd_cper_record_req))
if ((cmd->input_size != sizeof(struct ras_cmd_cper_record_req)) ||
(cmd->output_buf_size < sizeof(*rsp)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
if (!req->buf_size || !req->buf_ptr || !req->cper_num)
if (!req->buf_size || !req->buf_ptr || !req->cper_num ||
req->buf_size > RAS_CMD_MAX_CPER_BUF_SZ)
return RAS_CMD__ERROR_INVALID_INPUT_DATA;
buffer = kzalloc(req->buf_size, GFP_KERNEL);
if (!buffer)
return RAS_CMD__ERROR_GENERIC;
trace = kcalloc(trace_count, sizeof(*trace), GFP_KERNEL);
if (!trace) {
ret = RAS_CMD__ERROR_GENERIC;
goto out;
}
ras_log_ring_get_batch_overview(ras_core, &overview);
for (i = 0; i < req->cper_num; i++) {
batch_id = req->cper_start_id + i;
@ -228,7 +235,7 @@ static int ras_cmd_get_cper_records(struct ras_core_context *ras_core,
break;
count = ras_log_ring_get_batch_records(ras_core, batch_id, trace,
ARRAY_SIZE(trace));
trace_count);
if (count > 0) {
ret = ras_cper_generate_cper(ras_core, trace, count,
&buffer[offset], req->buf_size - offset, &real_data_len);
@ -241,8 +248,8 @@ static int ras_cmd_get_cper_records(struct ras_core_context *ras_core,
if ((ret && (ret != -ENOMEM)) ||
copy_to_user(u64_to_user_ptr(req->buf_ptr), buffer, offset)) {
kfree(buffer);
return RAS_CMD__ERROR_GENERIC;
ret = RAS_CMD__ERROR_GENERIC;
goto out;
}
rsp->real_data_size = offset;
@ -251,10 +258,12 @@ static int ras_cmd_get_cper_records(struct ras_core_context *ras_core,
rsp->version = 0;
cmd->output_size = sizeof(struct ras_cmd_cper_record_rsp);
ret = RAS_CMD__SUCCESS;
out:
kfree(trace);
kfree(buffer);
return RAS_CMD__SUCCESS;
return ret;
}
static int ras_cmd_get_batch_trace_snapshot(struct ras_core_context *ras_core,
@ -265,7 +274,8 @@ static int ras_cmd_get_batch_trace_snapshot(struct ras_core_context *ras_core,
struct ras_log_batch_overview overview;
if (cmd->input_size != sizeof(struct ras_cmd_batch_trace_snapshot_req))
if ((cmd->input_size != sizeof(struct ras_cmd_batch_trace_snapshot_req)) ||
(cmd->output_buf_size < sizeof(*rsp)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
ras_log_ring_get_batch_overview(ras_core, &overview);
@ -287,13 +297,15 @@ static int ras_cmd_get_batch_trace_records(struct ras_core_context *ras_core,
struct ras_cmd_batch_trace_record_rsp *output_data =
(struct ras_cmd_batch_trace_record_rsp *)cmd->output_buff_raw;
struct ras_log_batch_overview overview;
struct ras_log_info *trace_arry[MAX_RECORD_PER_BATCH] = {0};
struct ras_log_info *trace_arry = NULL;
uint32_t trace_count = MAX_RECORD_PER_BATCH;
struct ras_log_info *record;
int i, j, count = 0, offset = 0;
uint64_t id;
bool completed = false;
if (cmd->input_size != sizeof(struct ras_cmd_batch_trace_record_req))
if ((cmd->input_size != sizeof(struct ras_cmd_batch_trace_record_req)) ||
(cmd->output_buf_size < sizeof(*output_data)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
if ((!input_data->batch_num) || (input_data->batch_num > RAS_CMD_MAX_BATCH_NUM))
@ -304,6 +316,10 @@ static int ras_cmd_get_batch_trace_records(struct ras_core_context *ras_core,
(input_data->start_batch_id >= overview.last_batch_id))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
trace_arry = kcalloc(trace_count, sizeof(*trace_arry), GFP_KERNEL);
if (!trace_arry)
return RAS_CMD__ERROR_GENERIC;
for (i = 0; i < input_data->batch_num; i++) {
id = input_data->start_batch_id + i;
if (id >= overview.last_batch_id) {
@ -312,17 +328,17 @@ static int ras_cmd_get_batch_trace_records(struct ras_core_context *ras_core,
}
count = ras_log_ring_get_batch_records(ras_core,
id, trace_arry, ARRAY_SIZE(trace_arry));
id, trace_arry, trace_count);
if (count > 0) {
if ((offset + count) > RAS_CMD_MAX_TRACE_NUM)
break;
for (j = 0; j < count; j++) {
record = &output_data->records[offset + j];
record->seqno = trace_arry[j]->seqno;
record->timestamp = trace_arry[j]->timestamp;
record->event = trace_arry[j]->event;
record->seqno = trace_arry[j].seqno;
record->timestamp = trace_arry[j].timestamp;
record->event = trace_arry[j].event;
memcpy(&record->aca_reg,
&trace_arry[j]->aca_reg, sizeof(trace_arry[j]->aca_reg));
&trace_arry[j].aca_reg, sizeof(trace_arry[j].aca_reg));
}
} else {
count = 0;
@ -341,6 +357,8 @@ static int ras_cmd_get_batch_trace_records(struct ras_core_context *ras_core,
cmd->output_size = sizeof(struct ras_cmd_batch_trace_record_rsp);
kfree(trace_arry);
return RAS_CMD__SUCCESS;
}
@ -420,6 +438,10 @@ static int ras_cmd_inject_error(struct ras_core_context *ras_core,
.value = req->method,
};
if ((cmd->input_size != sizeof(*req)) ||
(cmd->output_buf_size < sizeof(*output_data)))
return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
ret = ras_psp_trigger_error(ras_core, &block_info, req->instance_mask);
if (!ret) {
output_data->version = 0;
@ -466,12 +488,11 @@ int rascore_handle_cmd(struct ras_core_context *ras_core,
int ras_cmd_init(struct ras_core_context *ras_core)
{
return ras_cmd_add_device(ras_core);
return 0;
}
int ras_cmd_fini(struct ras_core_context *ras_core)
{
ras_cmd_remove_device(ras_core);
return 0;
}
@ -520,8 +541,3 @@ int ras_cmd_translate_bank_to_soc_pa(struct ras_core_context *ras_core,
return ras_umc_translate_soc_pa_and_bank(ras_core, soc_pa, &umc_bank, true);
}
uint64_t ras_cmd_get_dev_handle(struct ras_core_context *ras_core)
{
return ras_core->ras_cmd.dev_handle;
}

View File

@ -29,8 +29,6 @@
#include "ras_log_ring.h"
#include "ras_cper.h"
#define RAS_CMD_DEV_HANDLE_MAGIC 0xFEEDAD00UL
#define RAS_CMD_MAX_IN_SIZE 256
#define RAS_CMD_MAX_GPU_NUM 32
#define RAS_CMD_MAX_BAD_PAGES_PER_GROUP 32
@ -107,12 +105,6 @@ enum ras_error_type {
struct ras_core_context;
struct ras_cmd_ctx;
struct ras_cmd_mgr {
struct list_head head;
struct ras_core_context *ras_core;
uint64_t dev_handle;
};
struct ras_cmd_func_map {
uint32_t cmd_id;
int (*func)(struct ras_core_context *ras_core,
@ -405,6 +397,9 @@ struct batch_ras_trace_info {
#define RAS_CMD_MAX_BATCH_NUM 300
#define RAS_CMD_MAX_TRACE_NUM 300
/* Upper bounds for RAS_CMD__GET_CPER_RECORD to limit kernel allocations and work. */
#define RAS_CMD_MAX_CPER_BUF_SZ (2 * 1024U * 1024U) /* 2 MiB */
struct ras_cmd_batch_trace_record_rsp {
uint32_t version;
uint16_t real_batch_num;
@ -478,7 +473,6 @@ struct ras_cmd_blocks_ecc_rsp {
int ras_cmd_init(struct ras_core_context *ras_core);
int ras_cmd_fini(struct ras_core_context *ras_core);
int rascore_handle_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx *cmd, void *data);
uint64_t ras_cmd_get_dev_handle(struct ras_core_context *ras_core);
int ras_cmd_query_interface_info(struct ras_core_context *ras_core,
struct ras_query_interface_info_rsp *rsp);
int ras_cmd_translate_soc_pa_to_bank(struct ras_core_context *ras_core,

View File

@ -196,9 +196,9 @@ uint64_t ras_core_gen_seqno(struct ras_core_context *ras_core,
{
uint64_t seqno = 0;
if (ras_core->sys_fn &&
ras_core->sys_fn->gen_seqno)
ras_core->sys_fn->gen_seqno(ras_core, type, &seqno);
if (ras_core->sys_fn && ras_core->sys_fn->gen_seqno &&
ras_core->sys_fn->gen_seqno(ras_core, type, &seqno))
return 0;
return seqno;
}

View File

@ -175,14 +175,14 @@ static int fill_section_runtime(struct ras_core_context *ras_core,
}
static int cper_generate_runtime_record(struct ras_core_context *ras_core,
struct cper_section_hdr *hdr, struct ras_log_info **trace_arr, uint32_t arr_num,
struct cper_section_hdr *hdr, struct ras_log_info *trace_arr, uint32_t arr_num,
enum ras_cper_severity sev)
{
struct cper_section_descriptor *descriptor;
struct cper_section_runtime *runtime;
int i;
fill_section_hdr(ras_core, hdr, RAS_CPER_TYPE_RUNTIME, sev, trace_arr[0]);
fill_section_hdr(ras_core, hdr, RAS_CPER_TYPE_RUNTIME, sev, &trace_arr[0]);
hdr->record_length = RAS_HDR_LEN + ((RAS_SEC_DESC_LEN + RAS_NONSTD_SEC_LEN) * arr_num);
hdr->sec_cnt = arr_num;
for (i = 0; i < arr_num; i++) {
@ -194,21 +194,21 @@ static int cper_generate_runtime_record(struct ras_core_context *ras_core,
fill_section_descriptor(ras_core, descriptor, sev, RUNTIME,
RAS_NONSTD_SEC_OFFSET(hdr->sec_cnt, i),
sizeof(struct cper_section_runtime));
fill_section_runtime(ras_core, runtime, trace_arr[i], sev);
fill_section_runtime(ras_core, runtime, &trace_arr[i], sev);
}
return 0;
}
static int cper_generate_fatal_record(struct ras_core_context *ras_core,
uint8_t *buffer, struct ras_log_info **trace_arr, uint32_t arr_num)
uint8_t *buffer, struct ras_log_info *trace_arr, uint32_t arr_num)
{
struct ras_cper_fatal_record record = {0};
int i = 0;
for (i = 0; i < arr_num; i++) {
fill_section_hdr(ras_core, &record.hdr, RAS_CPER_TYPE_FATAL,
RAS_CPER_SEV_FATAL_UE, trace_arr[i]);
RAS_CPER_SEV_FATAL_UE, &trace_arr[i]);
record.hdr.record_length = RAS_HDR_LEN + RAS_SEC_DESC_LEN + RAS_FATAL_SEC_LEN;
record.hdr.sec_cnt = 1;
@ -216,7 +216,7 @@ static int cper_generate_fatal_record(struct ras_core_context *ras_core,
CRASHDUMP, offsetof(struct ras_cper_fatal_record, fatal),
sizeof(struct cper_section_fatal));
fill_section_fatal(ras_core, &record.fatal, trace_arr[i]);
fill_section_fatal(ras_core, &record.fatal, &trace_arr[i]);
memcpy(buffer + (i * record.hdr.record_length),
&record, record.hdr.record_length);
@ -271,7 +271,7 @@ static enum ras_cper_type cper_ras_log_event_to_cper_type(enum ras_log_event eve
}
int ras_cper_generate_cper(struct ras_core_context *ras_core,
struct ras_log_info **trace_list, uint32_t count,
struct ras_log_info *trace_list, uint32_t count,
uint8_t *buf, uint32_t buf_len, uint32_t *real_data_len)
{
uint8_t *buffer = buf;
@ -281,14 +281,14 @@ int ras_cper_generate_cper(struct ras_core_context *ras_core,
/* All the batch traces share the same event */
record_size = cper_get_record_size(
cper_ras_log_event_to_cper_type(trace_list[0]->event), count);
cper_ras_log_event_to_cper_type(trace_list[0].event), count);
if ((record_size + saved_size) > buf_size)
return -ENOMEM;
hdr = (struct cper_section_hdr *)(buffer + saved_size);
switch (trace_list[0]->event) {
switch (trace_list[0].event) {
case RAS_LOG_EVENT_RMA:
cper_generate_runtime_record(ras_core, hdr, trace_list, count, RAS_CPER_SEV_RMA);
break;
@ -304,7 +304,7 @@ int ras_cper_generate_cper(struct ras_core_context *ras_core,
cper_generate_fatal_record(ras_core, buffer + saved_size, trace_list, count);
break;
default:
RAS_DEV_WARN(ras_core->dev, "Unprocessed trace event: %d\n", trace_list[0]->event);
RAS_DEV_WARN(ras_core->dev, "Unprocessed trace event: %d\n", trace_list[0].event);
break;
}

Some files were not shown because too many files have changed in this diff Show More