diff options
| author | Dave Airlie <airlied@redhat.com> | 2026-07-31 15:44:43 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2026-07-31 16:23:53 +1000 |
| commit | 22afd697fbbc000460d09282282b342feb7331c2 (patch) | |
| tree | 3188cdf59cf2eb1afbbd20eb437ef9948690f056 | |
| parent | ea97ab2759506d9a818ffed1009bde01062b4091 (diff) | |
| parent | 6f16fcbb0c46a87e3d9685407e906573d60104b0 (diff) | |
| download | linux-next-22afd697fbbc000460d09282282b342feb7331c2.tar.gz linux-next-22afd697fbbc000460d09282282b342feb7331c2.zip | |
Merge tag 'amd-drm-next-7.3-2026-07-29' of https://gitlab.freedesktop.org/agd5f/linux into drm-next
amd-drm-next-7.3-2026-07-29:
amdgpu:
- VCN 5.3 fix
- UserQ fixes
- GEM close optimization
- HDMI AV mute fix
- UML build fixes
- GFXOFF residency metrics fixes
- SMU 15 fixes
- debug_vm fix
- PSP 15 fixes
- NBIO 7.11.5 fix
- pptable use after free fix
- gpu metrics fetch fix
- DC viewport fix
- DML2.1 fix
- i2c retimer spam fix
- UMD profile pstate fix
- Power metrics format cleanup
- GTT size fix on APUs
- DC context logging fix
- Misc fixes
- IB pool clean up and fixes
- DCN 4.2 updates
- Old BUG() and BUG_ON() removal
- RAS updates
- PASID management updates
- DC MCIF ARB updates
- More DC KUNIT tests
- Refactor dc_validation_set
- Introduce dc_state_get_status unified status accessor
- Apple Studio Display fixes
- DML updates
- DC writeback fixes
- MES updates
- PTL updates
- DC code restructuring
- DC FRL fixes
- Add modifiers for GFX6-8
- Resume fix for MacBookPro15,1
amdkfd:
- Various bounds checking fixes
- Mutex locking fix
- Clean up debug runlist printing
- SR-IOV fixes
- Avoid topology_lock in kfd_mmap
- Fix signal reset event
- SVM eviction fixes
radeon:
- Fix for unset CONFIG_ACPI
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Alex Deucher <alexander.deucher@amd.com>
Link: https://patch.msgid.link/20260729201801.4073097-1-alexander.deucher@amd.com
291 files changed, 16581 insertions, 3538 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index f5d65bd0ac25..7974f9b7944f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -223,6 +223,7 @@ extern int amdgpu_smartshift_bias; extern int amdgpu_use_xgmi_p2p; extern int amdgpu_mtype_local; extern int amdgpu_enforce_isolation; +extern uint amdgpu_debug_mask; #ifdef CONFIG_HSA_AMD extern int sched_policy; extern bool debug_evictions; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index c693c508df1a..be764b6802b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -39,6 +39,7 @@ #if IS_ENABLED(CONFIG_HSA_AMD) #include "kfd_priv.h" #endif +#include "kfd_svm.h" /* Total memory size in system memory and all GPU VRAM. Used to * estimate worst case amount of memory to reserve for page tables @@ -711,7 +712,8 @@ int amdgpu_amdkfd_submit_ib(struct amdgpu_device *adev, goto err; } - ret = amdgpu_job_alloc(adev, NULL, NULL, NULL, 1, &job, 0); + ret = amdgpu_job_alloc(adev, NULL, NULL, NULL, 1, 0, GFP_KERNEL, + &job); if (ret) goto err; @@ -971,3 +973,24 @@ int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev, return kgd2kfd_reset_mes_queue(adev->kfd.dev, node_id, queue_type, pipe, queue, db); } + +int amdgpu_amdkfd_evict_svm_bo(struct amdgpu_bo *bo) +{ + struct dma_resv_iter cursor; + struct dma_fence *fence; + int r = 0; + + dma_resv_iter_begin(&cursor, bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP); + dma_resv_for_each_fence_unlocked(&cursor, fence) { + struct amdgpu_amdkfd_fence *f = to_amdgpu_amdkfd_fence(fence); + + if (f && f->svm_bo) { + r = svm_range_evict_svm_bo(f->svm_bo); + if (r) + break; + } + } + dma_resv_iter_end(&cursor); + + return r; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index 338412a750ed..b4840ee36f2b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -286,6 +286,7 @@ int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev, int queue_type, int pipe, int queue, unsigned int db); +int amdgpu_amdkfd_evict_svm_bo(struct amdgpu_bo *bo); /* Read user wptr from a specified user address space with page fault * disabled. The memory must be pinned and mapped to the hardware when diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c index 6a364357522b..553d26c2744e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c @@ -92,7 +92,7 @@ struct amdgpu_amdkfd_fence *to_amdgpu_amdkfd_fence(struct dma_fence *f) return NULL; fence = container_of(f, struct amdgpu_amdkfd_fence, base); - if (f->ops == &amdkfd_fence_ops) + if (rcu_access_pointer(f->ops) == &amdkfd_fence_ops) return fence; return NULL; @@ -135,9 +135,6 @@ static bool amdkfd_fence_enable_signaling(struct dma_fence *f) if (!fence->svm_bo) { if (!kgd2kfd_schedule_evict_and_restore_process(fence->mm, fence->context_id, f)) return true; - } else { - if (!svm_range_schedule_evict_svm_bo(fence)) - return true; } return false; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 20831dbebc31..1217fb516807 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -3104,7 +3104,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu * process_info->eviction_fence = new_fence; replace_eviction_fence(ef, dma_fence_get(&new_fence->base)); } else { - WARN_ONCE(*ef != &process_info->eviction_fence->base, + WARN_ONCE(rcu_access_pointer(*ef) != &process_info->eviction_fence->base, "KFD eviction fence doesn't match KGD process_info"); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c index 0af8b7be326e..373e47050630 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c @@ -519,7 +519,7 @@ int amdgpu_cper_deferred_init(struct amdgpu_device *adev) int amdgpu_cper_fini(struct amdgpu_device *adev) { - if (amdgpu_sriov_vf(adev)) + if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_cper_en(adev)) return 0; adev->cper.enabled = false; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index d58701908158..6b39c7f0e894 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -267,8 +267,8 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, for (i = 0; i < p->gang_size; ++i) { ret = amdgpu_job_alloc(p->adev, vm, p->entities[i], vm, - num_ibs[i], &p->jobs[i], - p->filp->client_id); + num_ibs[i], p->filp->client_id, + GFP_KERNEL, &p->jobs[i]); if (ret) goto free_all_kdata; switch (p->adev->enforce_isolation[fpriv->xcp_id]) { @@ -1171,19 +1171,6 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) job->vm_pd_addr = amdgpu_gmc_pd_addr(vm->root.bo); } - if (adev->debug_vm) { - /* Invalidate all BOs to test for userspace bugs */ - amdgpu_bo_list_for_each_entry(e, p->bo_list) { - struct amdgpu_bo *bo = e->bo; - - /* ignore duplicates */ - if (!bo) - continue; - - amdgpu_vm_bo_invalidate(bo, false); - } - } - return 0; } @@ -1371,6 +1358,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, /* Cleanup the parser structure */ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) { + struct amdgpu_device *adev = parser->adev; + struct amdgpu_bo_list_entry *e; unsigned int i; amdgpu_sync_free(&parser->sync); @@ -1386,8 +1375,21 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) if (parser->ctx) amdgpu_ctx_put(parser->ctx); - if (parser->bo_list) + if (parser->bo_list) { + if (adev->debug_vm) { + /* Invalidate all BOs to test for userspace bugs */ + amdgpu_bo_list_for_each_entry(e, parser->bo_list) { + struct amdgpu_bo *bo = e->bo; + + /* ignore duplicates */ + if (!bo) + continue; + + amdgpu_vm_bo_invalidate(bo, false); + } + } amdgpu_bo_list_put(parser->bo_list); + } for (i = 0; i < parser->nchunks; i++) kvfree(parser->chunks[i].kdata); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index deab64765388..132d054900b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -44,6 +44,11 @@ #if defined(CONFIG_DEBUG_FS) +/* Encode milliwatts in the raw Q24.8 sensor report format used by UMR. */ +#define AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(power_mw) \ + DIV_ROUND_CLOSEST_ULL((u64)(power_mw) * BIT(8), \ + MILLIWATT_PER_WATT) + /** * amdgpu_debugfs_process_reg_op - Handle MMIO register reads/writes * @@ -1105,6 +1110,10 @@ static ssize_t amdgpu_debugfs_sensor_read(struct file *f, char __user *buf, return r; } + if (idx == AMDGPU_PP_SENSOR_GPU_AVG_POWER || + idx == AMDGPU_PP_SENSOR_GPU_INPUT_POWER) + values[0] = AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(values[0]); + if (size > valuesize) { amdgpu_virt_disable_access_debugfs(adev); return -EINVAL; @@ -1320,8 +1329,8 @@ err: * @size: Number of bytes to read * @pos: Offset to seek to * - * Read the last residency value logged. It doesn't auto update, one needs to - * stop logging before getting the current value. + * Read a live GFXOFF residency sample from firmware. One needs to start logging + * before getting the current value. */ static ssize_t amdgpu_debugfs_gfxoff_residency_read(struct file *f, char __user *buf, size_t size, loff_t *pos) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 472e96ae884e..638dfaf127fd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -745,6 +745,64 @@ void amdgpu_device_mm_access(struct amdgpu_device *adev, loff_t pos, drm_dev_exit(idx); } +#ifdef CONFIG_64BIT +/* + * During early SR-IOV VF init, host-provided init data can live in FB before + * the normal VRAM aperture mapping is ready. Use a temporary BAR0 mapping for + * reads only, and verify it matches the VRAM aperture when aperture information + * is already available. + */ +static int amdgpu_device_read_fb_via_bar0(struct amdgpu_device *adev, + u64 offset, void *buf, size_t size) +{ + resource_size_t aper_base, aper_size, bar_start, bar_size, map_base; + void __iomem *vram; + size_t map_offset, map_size; + unsigned long flags; + u64 end; + + if (!buf || !size) + return -EINVAL; + + flags = pci_resource_flags(adev->pdev, 0); + if ((flags & IORESOURCE_UNSET) || !(flags & IORESOURCE_MEM)) + return -EINVAL; + + bar_start = pci_resource_start(adev->pdev, 0); + bar_size = pci_resource_len(adev->pdev, 0); + if (!bar_size) + return -ENODEV; + + aper_base = adev->gmc.aper_base; + aper_size = adev->gmc.visible_vram_size ? adev->gmc.visible_vram_size : + adev->gmc.aper_size; + + if (aper_base || aper_size) { + if (aper_base != bar_start || aper_size > bar_size) + return -EINVAL; + } else { + aper_base = bar_start; + aper_size = bar_size; + } + + if (check_add_overflow(offset, size, &end) || end > aper_size) + return -EINVAL; + + map_offset = offset_in_page(offset); + map_base = aper_base + (offset & PAGE_MASK); + map_size = PAGE_ALIGN(map_offset + size); + + vram = ioremap_wc(map_base, map_size); + if (!vram) + return -ENOMEM; + + memcpy_fromio(buf, (u8 __iomem *)vram + map_offset, size); + iounmap(vram); + + return 0; +} +#endif + /** * amdgpu_device_aper_access - access vram by vram aperture * @@ -764,8 +822,12 @@ size_t amdgpu_device_aper_access(struct amdgpu_device *adev, loff_t pos, size_t count = 0; uint64_t last; - if (!adev->mman.aper_base_kaddr) + if (!adev->mman.aper_base_kaddr) { + /* Writes still require the regular aperture/MM path. */ + if (!write && !amdgpu_device_read_fb_via_bar0(adev, pos, buf, size)) + return size; return 0; + } last = min(pos + size, adev->gmc.visible_vram_size); if (last > pos) { @@ -1862,16 +1924,25 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev) { struct amdgpu_ip_block *ip_block; struct pci_dev *parent; - bool total, skip_bios; + bool total, skip_bios, early_full_gpu_access = false; uint32_t bios_flags; int i, r; amdgpu_device_enable_virtual_display(adev); if (amdgpu_sriov_vf(adev)) { - r = amdgpu_virt_request_full_gpu(adev, true); - if (r) - return r; + /* + * Legacy hosts do not provide init data before early init, so + * keep the original early full GPU access request for them. Newer + * hosts publish the init data through VF FB, which lets us defer + * full GPU access until after non-GPU early init work is done. + */ + early_full_gpu_access = (adev->virt.req_init_data_ver == 0); + if (early_full_gpu_access) { + r = amdgpu_virt_request_full_gpu(adev, true); + if (r) + return r; + } r = amdgpu_virt_init_critical_region(adev); if (r) @@ -2034,6 +2105,13 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev) if (!total) return -ENODEV; + /* Request full GPU access only for the remaining SR-IOV init work. */ + if (amdgpu_sriov_vf(adev) && !early_full_gpu_access) { + r = amdgpu_virt_request_full_gpu(adev, true); + if (r) + return r; + } + if (adev->gmc.xgmi.supported) amdgpu_xgmi_early_init(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 44e7d2e3e6df..cf9c4b8df28b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -2529,6 +2529,7 @@ static int amdgpu_discovery_set_psp_ip_blocks(struct amdgpu_device *adev) amdgpu_device_ip_block_add(adev, &psp_v14_0_ip_block); break; case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 5): case IP_VERSION(15, 0, 9): amdgpu_device_ip_block_add(adev, &psp_v15_0_ip_block); break; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index f5cd68542442..59ea338b554c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -746,7 +746,7 @@ static int convert_tiling_flags_to_modifier_gfx12(struct amdgpu_framebuffer *afb return 0; } -static int convert_tiling_flags_to_modifier(struct amdgpu_framebuffer *afb) +static int convert_tiling_flags_to_modifier_gfx9(struct amdgpu_framebuffer *afb) { struct amdgpu_device *adev = drm_to_adev(afb->base.dev); uint64_t modifier = 0; @@ -940,25 +940,85 @@ static int convert_tiling_flags_to_modifier(struct amdgpu_framebuffer *afb) return 0; } -/* Mirrors the is_displayable check in radeonsi's gfx6_compute_surface */ +static int convert_tiling_flags_to_modifier_gfx6(struct amdgpu_framebuffer *afb) +{ + const uint32_t array_mode = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE); + const uint32_t pipe_config = AMDGPU_TILING_GET(afb->tiling_flags, PIPE_CONFIG); + const uint32_t tile_split = AMDGPU_TILING_GET(afb->tiling_flags, TILE_SPLIT); + const uint32_t micro_tile_mode = AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE); + const uint32_t bank_width = AMDGPU_TILING_GET(afb->tiling_flags, BANK_WIDTH); + const uint32_t bank_height = AMDGPU_TILING_GET(afb->tiling_flags, BANK_HEIGHT); + const uint32_t macro_tile_aspect = AMDGPU_TILING_GET(afb->tiling_flags, MACRO_TILE_ASPECT); + const uint32_t num_banks = AMDGPU_TILING_GET(afb->tiling_flags, NUM_BANKS); + struct amdgpu_device *adev = drm_to_adev(afb->base.dev); + uint64_t modifier = 0; + + switch (array_mode) { + case DC_ARRAY_LINEAR_GENERAL: + case DC_ARRAY_LINEAR_ALLIGNED: + modifier = DRM_FORMAT_MOD_LINEAR; + break; + + case DC_ARRAY_2D_TILED_THIN1: + /* Macro tiled modes only */ + modifier |= + AMD_FMT_MOD_SET(PIPE_CONFIG, pipe_config) | + AMD_FMT_MOD_SET(TILE_SPLIT, tile_split) | + AMD_FMT_MOD_SET(BANK_WIDTH, bank_width) | + AMD_FMT_MOD_SET(BANK_HEIGHT, bank_height) | + AMD_FMT_MOD_SET(MACRO_TILE_ASPECT, macro_tile_aspect) | + AMD_FMT_MOD_SET(NUM_BANKS, num_banks); + fallthrough; + + case DC_ARRAY_1D_TILED_THIN1: + /* Micro and macro tiled modes */ + modifier |= + AMD_FMT_MOD | + AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX6) | + AMD_FMT_MOD_SET(TILE, array_mode) | + AMD_FMT_MOD_SET(MICROTILE, micro_tile_mode); + break; + + default: + drm_dbg_kms(&adev->ddev, "array mode 0x%x not supported\n", array_mode); + return -EINVAL; + } + + afb->base.modifier = modifier; + afb->base.flags |= DRM_MODE_FB_MODIFIERS; + return 0; +} + static int check_tiling_flags_gfx6(struct amdgpu_framebuffer *afb) { - u64 micro_tile_mode; + const u64 array_mode = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE); + const u64 micro_tile_mode = AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE); - if (AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) == 1) /* LINEAR_ALIGNED */ + /* LINEAR_GENERAL, LINEAR_ALIGNED */ + if (array_mode == 0 || array_mode == 1) return 0; - micro_tile_mode = AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE); - switch (micro_tile_mode) { - case 0: /* DISPLAY */ - case 3: /* RENDER */ - return 0; - default: + /* 1D_TILED_THIN1, 2D_TILED_THIN1 */ + if (array_mode != 2 && array_mode != 4) { + drm_dbg_kms(afb->base.dev, + "Array mode %llu not supported for scanout\n", + array_mode); + return -EINVAL; + } + + /* + * For now, only allow DISPLAY micro tile mode. + * THIN, DEPTH and THICK modes are not displayable. + * ROTATED has never been supported by Linux or Mesa. + */ + if (micro_tile_mode != 0) { drm_dbg_kms(afb->base.dev, "Micro tile mode %llu not supported for scanout\n", micro_tile_mode); return -EINVAL; } + + return 0; } static void get_block_dimensions(unsigned int block_log2, unsigned int cpp, @@ -1053,6 +1113,85 @@ static int amdgpu_display_verify_plane(struct amdgpu_framebuffer *rfb, int plane return 0; } +static int amdgpu_display_verify_sizes_gfx6(struct drm_device *dev, + const u64 modifier, + unsigned int *out_block_width, + unsigned int *out_block_height) +{ + const u32 display_micro_tile_pitch = 32; /* required by DCE */ + const u32 micro_tile_width = 8; + const u32 micro_tile_height = 8; + const u32 micro_tile_mode = AMD_FMT_MOD_GET(MICROTILE, modifier); + const u32 array_mode = AMD_FMT_MOD_GET(TILE, modifier); + u32 num_banks, bank_width, bank_height, pipe_config, macro_tile_aspect; + u32 num_pipes; + + if (AMD_FMT_MOD_GET(DCC, modifier)) { + drm_dbg_kms(dev, "DCC is not displayable on GFX6-8\n"); + return -EINVAL; + } + if (array_mode != AMD_FMT_MOD_TILE_GFX6_1D_TILED_THIN1 && + array_mode != AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1) { + drm_dbg_kms(dev, "Array mode %u not supported for scanout\n", array_mode); + return -EINVAL; + } + if (micro_tile_mode != AMD_FMT_MOD_MICROTILE_DISPLAY) { + drm_dbg_kms(dev, "Microtile mode %u not supported for scanout\n", micro_tile_mode); + return -EINVAL; + } + + num_banks = 2 << AMD_FMT_MOD_GET(NUM_BANKS, modifier); + bank_width = 1 << AMD_FMT_MOD_GET(BANK_WIDTH, modifier); + bank_height = 1 << AMD_FMT_MOD_GET(BANK_HEIGHT, modifier); + pipe_config = AMD_FMT_MOD_GET(PIPE_CONFIG, modifier); + macro_tile_aspect = 1 << AMD_FMT_MOD_GET(MACRO_TILE_ASPECT, modifier); + + switch (pipe_config) { + case AMD_FMT_MOD_PIPE_CONFIG_P16_32x32_8x16: + case AMD_FMT_MOD_PIPE_CONFIG_P16_32x32_16x16: + num_pipes = 16; + break; + + case AMD_FMT_MOD_PIPE_CONFIG_P8_16x16_8x16: + case AMD_FMT_MOD_PIPE_CONFIG_P8_16x32_8x16: + case AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_8x16: + case AMD_FMT_MOD_PIPE_CONFIG_P8_16x32_16x16: + case AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_16x16: + case AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_16x32: + case AMD_FMT_MOD_PIPE_CONFIG_P8_32x64_32x32: + num_pipes = 8; + break; + + case AMD_FMT_MOD_PIPE_CONFIG_P4_8x16: + case AMD_FMT_MOD_PIPE_CONFIG_P4_16x16: + case AMD_FMT_MOD_PIPE_CONFIG_P4_16x32: + case AMD_FMT_MOD_PIPE_CONFIG_P4_32x32: + num_pipes = 4; + break; + + case AMD_FMT_MOD_PIPE_CONFIG_P2: + num_pipes = 2; + break; + + default: + drm_dbg_kms(dev, "Pipe config %u invalid\n", pipe_config); + return -EINVAL; + } + + /* For reference, see SiLib::InitEquationTable() in addrlib */ + if (array_mode < AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1) { + *out_block_width = display_micro_tile_pitch; + *out_block_height = micro_tile_height; + } else { + /* Assume non-PRT macro tiling modes */ + *out_block_width = num_pipes * micro_tile_width * + bank_width * macro_tile_aspect; + *out_block_height = micro_tile_height * bank_height * + num_banks / macro_tile_aspect; + } + + return 0; +} static int amdgpu_display_verify_sizes(struct amdgpu_framebuffer *rfb) { @@ -1093,7 +1232,7 @@ static int amdgpu_display_verify_sizes(struct amdgpu_framebuffer *rfb) get_block_dimensions(block_size_log2, format_info->cpp[i], &block_width, &block_height); - } else { + } else if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) >= AMD_FMT_MOD_TILE_VER_GFX9) { int swizzle = AMD_FMT_MOD_GET(TILE, modifier); switch ((swizzle & ~3) + 1) { @@ -1120,6 +1259,18 @@ static int amdgpu_display_verify_sizes(struct amdgpu_framebuffer *rfb) get_block_dimensions(block_size_log2, format_info->cpp[i], &block_width, &block_height); + } else if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) == AMD_FMT_MOD_TILE_VER_GFX6) { + ret = amdgpu_display_verify_sizes_gfx6(rfb->base.dev, modifier, + &block_width, &block_height); + if (ret) + return ret; + + /* amdgpu_display_verify_plane() assumes the block size is a power of 2 */ + WARN_ON(!is_power_of_2(block_width)); + WARN_ON(!is_power_of_2(block_height)); + WARN_ON(!is_power_of_2(format_info->cpp[i])); + + block_size_log2 = ilog2(block_width * block_height * format_info->cpp[i]); } ret = amdgpu_display_verify_plane(rfb, i, format_info, @@ -1271,8 +1422,10 @@ static int amdgpu_display_framebuffer_init(struct drm_device *dev, !(rfb->base.flags & DRM_MODE_FB_MODIFIERS)) { if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(12, 0, 0)) ret = convert_tiling_flags_to_modifier_gfx12(rfb); + else if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(9, 0, 0)) + ret = convert_tiling_flags_to_modifier_gfx9(rfb); else - ret = convert_tiling_flags_to_modifier(rfb); + ret = convert_tiling_flags_to_modifier_gfx6(rfb); if (ret) { drm_dbg_kms(dev, "Failed to convert tiling flags 0x%llX to a modifier", diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 5362705143bc..0ab380ca7e64 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -1838,357 +1838,357 @@ static const u16 amdgpu_unsupported_pciidlist[] = { }; static const struct pci_device_id pciidlist[] = { - {0x1002, 0x6780, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x6784, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x6788, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x678A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x6790, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x6791, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x6792, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x6798, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x6799, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x679A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x679B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x679E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x679F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI}, - {0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|AMD_IS_MOBILITY}, - {0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|AMD_IS_MOBILITY}, - {0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|AMD_IS_MOBILITY}, - {0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN}, - {0x1002, 0x6600, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6602, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6603, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6604, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6605, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6606, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6607, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6608, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND}, - {0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND}, - {0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND}, - {0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND}, - {0x1002, 0x6617, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6620, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY}, - {0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND}, - {0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6823, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6826, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6827, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x6829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x682A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x682B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x682C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x682D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x682F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY}, - {0x1002, 0x6835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x6837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x6838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x6839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x683B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x683D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x683F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE}, - {0x1002, 0x6660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY}, - {0x1002, 0x6663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY}, - {0x1002, 0x6664, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY}, - {0x1002, 0x6665, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY}, - {0x1002, 0x6667, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY}, - {0x1002, 0x666F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY}, + { PCI_DEVICE(0x1002, 0x6780), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x6784), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x6788), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x678A), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x6790), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x6791), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x6792), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x6798), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x6799), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x679A), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x679B), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x679E), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x679F), .driver_data = CHIP_TAHITI }, + { PCI_DEVICE(0x1002, 0x6800), .driver_data = CHIP_PITCAIRN|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6801), .driver_data = CHIP_PITCAIRN|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6802), .driver_data = CHIP_PITCAIRN|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6806), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6808), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6809), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6810), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6811), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6816), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6817), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6818), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6819), .driver_data = CHIP_PITCAIRN }, + { PCI_DEVICE(0x1002, 0x6600), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6601), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6602), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6603), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6604), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6605), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6606), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6607), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6608), .driver_data = CHIP_OLAND }, + { PCI_DEVICE(0x1002, 0x6610), .driver_data = CHIP_OLAND }, + { PCI_DEVICE(0x1002, 0x6611), .driver_data = CHIP_OLAND }, + { PCI_DEVICE(0x1002, 0x6613), .driver_data = CHIP_OLAND }, + { PCI_DEVICE(0x1002, 0x6617), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6620), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6621), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6623), .driver_data = CHIP_OLAND|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6631), .driver_data = CHIP_OLAND }, + { PCI_DEVICE(0x1002, 0x6820), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6821), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6822), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6823), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6824), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6825), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6826), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6827), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6828), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x6829), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x682A), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x682B), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x682C), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x682D), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x682F), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6830), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6831), .driver_data = CHIP_VERDE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6835), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x6837), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x6838), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x6839), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x683B), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x683D), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x683F), .driver_data = CHIP_VERDE }, + { PCI_DEVICE(0x1002, 0x6660), .driver_data = CHIP_HAINAN|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6663), .driver_data = CHIP_HAINAN|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6664), .driver_data = CHIP_HAINAN|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6665), .driver_data = CHIP_HAINAN|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6667), .driver_data = CHIP_HAINAN|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x666F), .driver_data = CHIP_HAINAN|AMD_IS_MOBILITY }, /* Kaveri */ - {0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x1305, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1306, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x1307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1309, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x130A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x130B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x130C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x130D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x130E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x130F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1313, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, - {0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x1304), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1305), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1306), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1307), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1309), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x130A), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x130B), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x130C), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x130D), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x130E), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x130F), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1310), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1311), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1312), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1313), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1315), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1316), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1317), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1318), .driver_data = CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x131B), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x131C), .driver_data = CHIP_KAVERI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x131D), .driver_data = CHIP_KAVERI|AMD_IS_APU }, /* Bonaire */ - {0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY}, - {0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY}, - {0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY}, - {0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY}, - {0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, - {0x1002, 0x664D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, - {0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, - {0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, - {0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, - {0x1002, 0x665c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, - {0x1002, 0x665d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, - {0x1002, 0x665f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, + { PCI_DEVICE(0x1002, 0x6640), .driver_data = CHIP_BONAIRE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6641), .driver_data = CHIP_BONAIRE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6646), .driver_data = CHIP_BONAIRE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6647), .driver_data = CHIP_BONAIRE|AMD_IS_MOBILITY }, + { PCI_DEVICE(0x1002, 0x6649), .driver_data = CHIP_BONAIRE }, + { PCI_DEVICE(0x1002, 0x664D), .driver_data = CHIP_BONAIRE }, + { PCI_DEVICE(0x1002, 0x6650), .driver_data = CHIP_BONAIRE }, + { PCI_DEVICE(0x1002, 0x6651), .driver_data = CHIP_BONAIRE }, + { PCI_DEVICE(0x1002, 0x6658), .driver_data = CHIP_BONAIRE }, + { PCI_DEVICE(0x1002, 0x665c), .driver_data = CHIP_BONAIRE }, + { PCI_DEVICE(0x1002, 0x665d), .driver_data = CHIP_BONAIRE }, + { PCI_DEVICE(0x1002, 0x665f), .driver_data = CHIP_BONAIRE }, /* Hawaii */ - {0x1002, 0x67A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67A8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67A9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67AA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67B0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67B8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67B9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67BA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, - {0x1002, 0x67BE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII}, + { PCI_DEVICE(0x1002, 0x67A0), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67A1), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67A2), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67A8), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67A9), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67AA), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67B0), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67B1), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67B8), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67B9), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67BA), .driver_data = CHIP_HAWAII }, + { PCI_DEVICE(0x1002, 0x67BE), .driver_data = CHIP_HAWAII }, /* Kabini */ - {0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, - {0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, - {0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, - {0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, - {0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, - {0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, - {0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, - {0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, - {0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x9830), .driver_data = CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9831), .driver_data = CHIP_KABINI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9832), .driver_data = CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9833), .driver_data = CHIP_KABINI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9834), .driver_data = CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9835), .driver_data = CHIP_KABINI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9836), .driver_data = CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9837), .driver_data = CHIP_KABINI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9838), .driver_data = CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9839), .driver_data = CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x983a), .driver_data = CHIP_KABINI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x983b), .driver_data = CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x983c), .driver_data = CHIP_KABINI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x983d), .driver_data = CHIP_KABINI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x983e), .driver_data = CHIP_KABINI|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x983f), .driver_data = CHIP_KABINI|AMD_IS_APU }, /* mullins */ - {0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, - {0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x9850), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9851), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9852), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9853), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9854), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9855), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9856), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9857), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9858), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9859), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x985A), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x985B), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x985C), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x985D), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x985E), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x985F), .driver_data = CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU }, /* topaz */ - {0x1002, 0x6900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, - {0x1002, 0x6901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, - {0x1002, 0x6902, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, - {0x1002, 0x6903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, - {0x1002, 0x6907, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ}, + { PCI_DEVICE(0x1002, 0x6900), .driver_data = CHIP_TOPAZ }, + { PCI_DEVICE(0x1002, 0x6901), .driver_data = CHIP_TOPAZ }, + { PCI_DEVICE(0x1002, 0x6902), .driver_data = CHIP_TOPAZ }, + { PCI_DEVICE(0x1002, 0x6903), .driver_data = CHIP_TOPAZ }, + { PCI_DEVICE(0x1002, 0x6907), .driver_data = CHIP_TOPAZ }, /* tonga */ - {0x1002, 0x6920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x6921, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x6928, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x6929, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x692B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x692F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x6930, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x6938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x6939, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, - {0x1002, 0x693B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, + { PCI_DEVICE(0x1002, 0x6920), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x6921), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x6928), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x6929), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x692B), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x692F), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x6930), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x6938), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x6939), .driver_data = CHIP_TONGA }, + { PCI_DEVICE(0x1002, 0x693B), .driver_data = CHIP_TONGA }, /* fiji */ - {0x1002, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_FIJI}, - {0x1002, 0x730F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_FIJI}, + { PCI_DEVICE(0x1002, 0x7300), .driver_data = CHIP_FIJI }, + { PCI_DEVICE(0x1002, 0x730F), .driver_data = CHIP_FIJI }, /* carrizo */ - {0x1002, 0x9870, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU}, - {0x1002, 0x9874, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU}, - {0x1002, 0x9875, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU}, - {0x1002, 0x9876, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU}, - {0x1002, 0x9877, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x9870), .driver_data = CHIP_CARRIZO|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9874), .driver_data = CHIP_CARRIZO|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9875), .driver_data = CHIP_CARRIZO|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9876), .driver_data = CHIP_CARRIZO|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x9877), .driver_data = CHIP_CARRIZO|AMD_IS_APU }, /* stoney */ - {0x1002, 0x98E4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_STONEY|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x98E4), .driver_data = CHIP_STONEY|AMD_IS_APU }, /* Polaris11 */ - {0x1002, 0x67E0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, - {0x1002, 0x67E3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, - {0x1002, 0x67E8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, - {0x1002, 0x67EB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, - {0x1002, 0x67EF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, - {0x1002, 0x67FF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, - {0x1002, 0x67E1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, - {0x1002, 0x67E7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, - {0x1002, 0x67E9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS11}, + { PCI_DEVICE(0x1002, 0x67E0), .driver_data = CHIP_POLARIS11 }, + { PCI_DEVICE(0x1002, 0x67E3), .driver_data = CHIP_POLARIS11 }, + { PCI_DEVICE(0x1002, 0x67E8), .driver_data = CHIP_POLARIS11 }, + { PCI_DEVICE(0x1002, 0x67EB), .driver_data = CHIP_POLARIS11 }, + { PCI_DEVICE(0x1002, 0x67EF), .driver_data = CHIP_POLARIS11 }, + { PCI_DEVICE(0x1002, 0x67FF), .driver_data = CHIP_POLARIS11 }, + { PCI_DEVICE(0x1002, 0x67E1), .driver_data = CHIP_POLARIS11 }, + { PCI_DEVICE(0x1002, 0x67E7), .driver_data = CHIP_POLARIS11 }, + { PCI_DEVICE(0x1002, 0x67E9), .driver_data = CHIP_POLARIS11 }, /* Polaris10 */ - {0x1002, 0x67C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67C2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67D0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67D4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67C8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67CA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67CC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x67CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, - {0x1002, 0x6FDF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, + { PCI_DEVICE(0x1002, 0x67C0), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67C1), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67C2), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67C4), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67C7), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67D0), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67D4), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67DF), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67C8), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67C9), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67CA), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67CC), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x67CF), .driver_data = CHIP_POLARIS10 }, + { PCI_DEVICE(0x1002, 0x6FDF), .driver_data = CHIP_POLARIS10 }, /* Polaris12 */ - {0x1002, 0x6980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, - {0x1002, 0x6981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, - {0x1002, 0x6985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, - {0x1002, 0x6986, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, - {0x1002, 0x6987, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, - {0x1002, 0x698F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, - {0x1002, 0x6995, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, - {0x1002, 0x6997, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, - {0x1002, 0x699F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, + { PCI_DEVICE(0x1002, 0x6980), .driver_data = CHIP_POLARIS12 }, + { PCI_DEVICE(0x1002, 0x6981), .driver_data = CHIP_POLARIS12 }, + { PCI_DEVICE(0x1002, 0x6985), .driver_data = CHIP_POLARIS12 }, + { PCI_DEVICE(0x1002, 0x6986), .driver_data = CHIP_POLARIS12 }, + { PCI_DEVICE(0x1002, 0x6987), .driver_data = CHIP_POLARIS12 }, + { PCI_DEVICE(0x1002, 0x698F), .driver_data = CHIP_POLARIS12 }, + { PCI_DEVICE(0x1002, 0x6995), .driver_data = CHIP_POLARIS12 }, + { PCI_DEVICE(0x1002, 0x6997), .driver_data = CHIP_POLARIS12 }, + { PCI_DEVICE(0x1002, 0x699F), .driver_data = CHIP_POLARIS12 }, /* VEGAM */ - {0x1002, 0x694C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGAM}, - {0x1002, 0x694E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGAM}, - {0x1002, 0x694F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGAM}, + { PCI_DEVICE(0x1002, 0x694C), .driver_data = CHIP_VEGAM }, + { PCI_DEVICE(0x1002, 0x694E), .driver_data = CHIP_VEGAM }, + { PCI_DEVICE(0x1002, 0x694F), .driver_data = CHIP_VEGAM }, /* Vega 10 */ - {0x1002, 0x6860, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x6861, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x6862, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x6863, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x6864, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x6867, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x6868, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x6869, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x686a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x686b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x686c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x686d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x686e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x686f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, - {0x1002, 0x687f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA10}, + { PCI_DEVICE(0x1002, 0x6860), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x6861), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x6862), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x6863), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x6864), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x6867), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x6868), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x6869), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x686a), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x686b), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x686c), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x686d), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x686e), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x686f), .driver_data = CHIP_VEGA10 }, + { PCI_DEVICE(0x1002, 0x687f), .driver_data = CHIP_VEGA10 }, /* Vega 12 */ - {0x1002, 0x69A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA12}, - {0x1002, 0x69A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA12}, - {0x1002, 0x69A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA12}, - {0x1002, 0x69A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA12}, - {0x1002, 0x69AF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA12}, + { PCI_DEVICE(0x1002, 0x69A0), .driver_data = CHIP_VEGA12 }, + { PCI_DEVICE(0x1002, 0x69A1), .driver_data = CHIP_VEGA12 }, + { PCI_DEVICE(0x1002, 0x69A2), .driver_data = CHIP_VEGA12 }, + { PCI_DEVICE(0x1002, 0x69A3), .driver_data = CHIP_VEGA12 }, + { PCI_DEVICE(0x1002, 0x69AF), .driver_data = CHIP_VEGA12 }, /* Vega 20 */ - {0x1002, 0x66A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20}, - {0x1002, 0x66A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20}, - {0x1002, 0x66A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20}, - {0x1002, 0x66A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20}, - {0x1002, 0x66A4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20}, - {0x1002, 0x66A7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20}, - {0x1002, 0x66AF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VEGA20}, + { PCI_DEVICE(0x1002, 0x66A0), .driver_data = CHIP_VEGA20 }, + { PCI_DEVICE(0x1002, 0x66A1), .driver_data = CHIP_VEGA20 }, + { PCI_DEVICE(0x1002, 0x66A2), .driver_data = CHIP_VEGA20 }, + { PCI_DEVICE(0x1002, 0x66A3), .driver_data = CHIP_VEGA20 }, + { PCI_DEVICE(0x1002, 0x66A4), .driver_data = CHIP_VEGA20 }, + { PCI_DEVICE(0x1002, 0x66A7), .driver_data = CHIP_VEGA20 }, + { PCI_DEVICE(0x1002, 0x66AF), .driver_data = CHIP_VEGA20 }, /* Raven */ - {0x1002, 0x15dd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RAVEN|AMD_IS_APU}, - {0x1002, 0x15d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RAVEN|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x15dd), .driver_data = CHIP_RAVEN|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x15d8), .driver_data = CHIP_RAVEN|AMD_IS_APU }, /* Arcturus */ - {0x1002, 0x738C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARCTURUS}, - {0x1002, 0x7388, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARCTURUS}, - {0x1002, 0x738E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARCTURUS}, - {0x1002, 0x7390, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARCTURUS}, + { PCI_DEVICE(0x1002, 0x738C), .driver_data = CHIP_ARCTURUS }, + { PCI_DEVICE(0x1002, 0x7388), .driver_data = CHIP_ARCTURUS }, + { PCI_DEVICE(0x1002, 0x738E), .driver_data = CHIP_ARCTURUS }, + { PCI_DEVICE(0x1002, 0x7390), .driver_data = CHIP_ARCTURUS }, /* Navi10 */ - {0x1002, 0x7310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, - {0x1002, 0x7312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, - {0x1002, 0x7318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, - {0x1002, 0x7319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, - {0x1002, 0x731A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, - {0x1002, 0x731B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, - {0x1002, 0x731E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, - {0x1002, 0x731F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI10}, + { PCI_DEVICE(0x1002, 0x7310), .driver_data = CHIP_NAVI10 }, + { PCI_DEVICE(0x1002, 0x7312), .driver_data = CHIP_NAVI10 }, + { PCI_DEVICE(0x1002, 0x7318), .driver_data = CHIP_NAVI10 }, + { PCI_DEVICE(0x1002, 0x7319), .driver_data = CHIP_NAVI10 }, + { PCI_DEVICE(0x1002, 0x731A), .driver_data = CHIP_NAVI10 }, + { PCI_DEVICE(0x1002, 0x731B), .driver_data = CHIP_NAVI10 }, + { PCI_DEVICE(0x1002, 0x731E), .driver_data = CHIP_NAVI10 }, + { PCI_DEVICE(0x1002, 0x731F), .driver_data = CHIP_NAVI10 }, /* Navi14 */ - {0x1002, 0x7340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI14}, - {0x1002, 0x7341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI14}, - {0x1002, 0x7347, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI14}, - {0x1002, 0x734F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI14}, + { PCI_DEVICE(0x1002, 0x7340), .driver_data = CHIP_NAVI14 }, + { PCI_DEVICE(0x1002, 0x7341), .driver_data = CHIP_NAVI14 }, + { PCI_DEVICE(0x1002, 0x7347), .driver_data = CHIP_NAVI14 }, + { PCI_DEVICE(0x1002, 0x734F), .driver_data = CHIP_NAVI14 }, /* Renoir */ - {0x1002, 0x15E7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RENOIR|AMD_IS_APU}, - {0x1002, 0x1636, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RENOIR|AMD_IS_APU}, - {0x1002, 0x1638, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RENOIR|AMD_IS_APU}, - {0x1002, 0x164C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RENOIR|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x15E7), .driver_data = CHIP_RENOIR|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1636), .driver_data = CHIP_RENOIR|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1638), .driver_data = CHIP_RENOIR|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x164C), .driver_data = CHIP_RENOIR|AMD_IS_APU }, /* Navi12 */ - {0x1002, 0x7360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI12}, - {0x1002, 0x7362, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI12}, + { PCI_DEVICE(0x1002, 0x7360), .driver_data = CHIP_NAVI12 }, + { PCI_DEVICE(0x1002, 0x7362), .driver_data = CHIP_NAVI12 }, /* Sienna_Cichlid */ - {0x1002, 0x73A0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73A2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73A3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73A5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73A8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73A9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73AB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73AC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73AD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73AE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73AF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, - {0x1002, 0x73BF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_SIENNA_CICHLID}, + { PCI_DEVICE(0x1002, 0x73A0), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73A1), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73A2), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73A3), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73A5), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73A8), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73A9), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73AB), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73AC), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73AD), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73AE), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73AF), .driver_data = CHIP_SIENNA_CICHLID }, + { PCI_DEVICE(0x1002, 0x73BF), .driver_data = CHIP_SIENNA_CICHLID }, /* Yellow Carp */ - {0x1002, 0x164D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_YELLOW_CARP|AMD_IS_APU}, - {0x1002, 0x1681, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_YELLOW_CARP|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x164D), .driver_data = CHIP_YELLOW_CARP|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x1681), .driver_data = CHIP_YELLOW_CARP|AMD_IS_APU }, /* Navy_Flounder */ - {0x1002, 0x73C0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, - {0x1002, 0x73C1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, - {0x1002, 0x73C3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, - {0x1002, 0x73DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, - {0x1002, 0x73DB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, - {0x1002, 0x73DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, - {0x1002, 0x73DD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, - {0x1002, 0x73DE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, - {0x1002, 0x73DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVY_FLOUNDER}, + { PCI_DEVICE(0x1002, 0x73C0), .driver_data = CHIP_NAVY_FLOUNDER }, + { PCI_DEVICE(0x1002, 0x73C1), .driver_data = CHIP_NAVY_FLOUNDER }, + { PCI_DEVICE(0x1002, 0x73C3), .driver_data = CHIP_NAVY_FLOUNDER }, + { PCI_DEVICE(0x1002, 0x73DA), .driver_data = CHIP_NAVY_FLOUNDER }, + { PCI_DEVICE(0x1002, 0x73DB), .driver_data = CHIP_NAVY_FLOUNDER }, + { PCI_DEVICE(0x1002, 0x73DC), .driver_data = CHIP_NAVY_FLOUNDER }, + { PCI_DEVICE(0x1002, 0x73DD), .driver_data = CHIP_NAVY_FLOUNDER }, + { PCI_DEVICE(0x1002, 0x73DE), .driver_data = CHIP_NAVY_FLOUNDER }, + { PCI_DEVICE(0x1002, 0x73DF), .driver_data = CHIP_NAVY_FLOUNDER }, /* DIMGREY_CAVEFISH */ - {0x1002, 0x73E0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73E1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73E2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73E3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73E8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73E9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73EA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73EB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73EC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73ED, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73EF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, - {0x1002, 0x73FF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_DIMGREY_CAVEFISH}, + { PCI_DEVICE(0x1002, 0x73E0), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73E1), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73E2), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73E3), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73E8), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73E9), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73EA), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73EB), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73EC), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73ED), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73EF), .driver_data = CHIP_DIMGREY_CAVEFISH }, + { PCI_DEVICE(0x1002, 0x73FF), .driver_data = CHIP_DIMGREY_CAVEFISH }, /* Aldebaran */ - {0x1002, 0x7408, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN}, - {0x1002, 0x740C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN}, - {0x1002, 0x740F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN}, - {0x1002, 0x7410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN}, + { PCI_DEVICE(0x1002, 0x7408), .driver_data = CHIP_ALDEBARAN }, + { PCI_DEVICE(0x1002, 0x740C), .driver_data = CHIP_ALDEBARAN }, + { PCI_DEVICE(0x1002, 0x740F), .driver_data = CHIP_ALDEBARAN }, + { PCI_DEVICE(0x1002, 0x7410), .driver_data = CHIP_ALDEBARAN }, /* CYAN_SKILLFISH */ - {0x1002, 0x13DB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, - {0x1002, 0x13F9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, - {0x1002, 0x13FA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, - {0x1002, 0x13FB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, - {0x1002, 0x13FC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, - {0x1002, 0x13FE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, - {0x1002, 0x143F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU}, + { PCI_DEVICE(0x1002, 0x13DB), .driver_data = CHIP_CYAN_SKILLFISH|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x13F9), .driver_data = CHIP_CYAN_SKILLFISH|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x13FA), .driver_data = CHIP_CYAN_SKILLFISH|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x13FB), .driver_data = CHIP_CYAN_SKILLFISH|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x13FC), .driver_data = CHIP_CYAN_SKILLFISH|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x13FE), .driver_data = CHIP_CYAN_SKILLFISH|AMD_IS_APU }, + { PCI_DEVICE(0x1002, 0x143F), .driver_data = CHIP_CYAN_SKILLFISH|AMD_IS_APU }, /* BEIGE_GOBY */ - {0x1002, 0x7420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY}, - {0x1002, 0x7421, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY}, - {0x1002, 0x7422, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY}, - {0x1002, 0x7423, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY}, - {0x1002, 0x7424, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY}, - {0x1002, 0x743F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY}, + { PCI_DEVICE(0x1002, 0x7420), .driver_data = CHIP_BEIGE_GOBY }, + { PCI_DEVICE(0x1002, 0x7421), .driver_data = CHIP_BEIGE_GOBY }, + { PCI_DEVICE(0x1002, 0x7422), .driver_data = CHIP_BEIGE_GOBY }, + { PCI_DEVICE(0x1002, 0x7423), .driver_data = CHIP_BEIGE_GOBY }, + { PCI_DEVICE(0x1002, 0x7424), .driver_data = CHIP_BEIGE_GOBY }, + { PCI_DEVICE(0x1002, 0x743F), .driver_data = CHIP_BEIGE_GOBY }, { PCI_DEVICE(0x1002, PCI_ANY_ID), .class = PCI_CLASS_DISPLAY_VGA << 8, @@ -2205,7 +2205,7 @@ static const struct pci_device_id pciidlist[] = { .class_mask = 0xffffff, .driver_data = CHIP_IP_DISCOVERY }, - {0, 0, 0} + { } }; MODULE_DEVICE_TABLE(pci, pciidlist); @@ -2920,6 +2920,19 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev) return 0; } +static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device *adev) +{ + enum amd_dpm_forced_level level; + uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; + + level = amdgpu_dpm_get_performance_level(adev); + if (level & profile_mode_mask) + amdgpu_asic_update_umd_stable_pstate(adev, true); +} + static int amdgpu_pmops_runtime_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -2964,6 +2977,8 @@ static int amdgpu_pmops_runtime_resume(struct device *dev) if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX) drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; + + amdgpu_restore_umd_profile_pstate_after_runpm(adev); adev->in_runpm = false; return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index 1e275c2e7dd3..9d3b40c385c9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -1713,8 +1713,9 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) owner = (void *)(unsigned long)atomic_inc_return(&counter); r = amdgpu_job_alloc_with_ib(ring->adev, &entity, owner, - ib_size_dw * sizeof(uint32_t), 0, &job, - AMDGPU_KERNEL_JOB_ID_CLEANER_SHADER); + ib_size_dw * sizeof(uint32_t), 0, + AMDGPU_KERNEL_JOB_ID_CLEANER_SHADER, + &job); if (r) goto err; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 4000b2c6fc98..3da50132c5d0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -761,7 +761,8 @@ void amdgpu_gmc_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid, r = amdgpu_job_alloc_with_ib(ring->adev, &adev->mman.default_entity.base, AMDGPU_FENCE_OWNER_UNDEFINED, 16 * 4, AMDGPU_IB_POOL_IMMEDIATE, - &job, AMDGPU_KERNEL_JOB_ID_FLUSH_GPU_TLB); + AMDGPU_KERNEL_JOB_ID_FLUSH_GPU_TLB, + &job); if (r) goto error_alloc; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c index 63f62c670df5..634b3f7a5fff 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c @@ -351,6 +351,30 @@ free_fence: */ int amdgpu_ib_pool_init(struct amdgpu_device *adev) { + const int sizes[AMDGPU_IB_POOL_MAX] = { + [AMDGPU_IB_POOL_DELAYED] = SZ_1M, + [AMDGPU_IB_POOL_IMMEDIATE] = SZ_128K, + [AMDGPU_IB_POOL_DIRECT] = SZ_512K + }; + const gfp_t gfp_flags[AMDGPU_IB_POOL_MAX] = { + /* + * For normal page table updates and recoverable retry faults + * (for SVM), further restricted by the VM eviction lock to not + * wait for memory reclaim. + */ + [AMDGPU_IB_POOL_DELAYED] = GFP_KERNEL, + /* + * For redirecting unrecoverable retry faults to the dummy page + * or set the PRT bits. dma_fence submissions might depend on + * that so we need the emmergency reserves. + */ + [AMDGPU_IB_POOL_IMMEDIATE] = GFP_ATOMIC, + /* + * For IB tests during GPU resets. Only very small and temporary + * allocation to allow dma_fences to signal. + */ + [AMDGPU_IB_POOL_DIRECT] = GFP_ATOMIC + }; int r, i; if (adev->ib_pool_ready) @@ -358,8 +382,7 @@ int amdgpu_ib_pool_init(struct amdgpu_device *adev) for (i = 0; i < AMDGPU_IB_POOL_MAX; i++) { r = amdgpu_sa_bo_manager_init(adev, &adev->ib_pools[i], - AMDGPU_IB_POOL_SIZE, 256, - AMDGPU_GEM_DOMAIN_GTT); + sizes[i], gfp_flags[i]); if (r) goto error; } @@ -394,6 +417,17 @@ void amdgpu_ib_pool_fini(struct amdgpu_device *adev) } /** + * amdgpu_ib_pool_gfp_flags - Returns the gfp flags to use for each pool + * @adev: amdgpu device pointer + * @type: the IB pool type + */ +gfp_t amdgpu_ib_pool_gfp_flags(struct amdgpu_device *adev, + enum amdgpu_ib_pool_type type) +{ + return adev->ib_pools[type].gfp_flags; +} + +/** * amdgpu_ib_ring_tests - test IBs on the rings * * @adev: amdgpu_device pointer diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index 124fb38eb465..8a2d64f0ebc3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -51,16 +51,16 @@ struct amdgpu_pasid_cb { /** * amdgpu_pasid_alloc - Allocate a PASID - * @bits: Maximum width of the PASID in bits, must be at least 1 + * @bits: Number of PASID bits supported by the hardware + * @fpriv: DRM file private to associate with the PASID * - * Uses kernel's IDR cyclic allocator (same as PID allocation). + * Uses the kernel's XArray cyclic allocator. * Allocates sequentially with automatic wrap-around. * - * Returns a positive integer on success. Returns %-EINVAL if bits==0. - * Returns %-ENOSPC if no PASID was available. Returns %-ENOMEM on - * memory allocation failure. + * Returns: + * Allocated PASID on success or a negative error code on failure. */ -int amdgpu_pasid_alloc(unsigned int bits) +int amdgpu_pasid_alloc(unsigned int bits, struct amdgpu_fpriv *fpriv) { u32 pasid; int r; @@ -68,9 +68,9 @@ int amdgpu_pasid_alloc(unsigned int bits) if (bits == 0) return -EINVAL; - r = xa_alloc_cyclic_irq(&amdgpu_pasid_xa, &pasid, xa_mk_value(0), - XA_LIMIT(1, (1U << bits) - 1), - &amdgpu_pasid_xa_next, GFP_KERNEL); + r = xa_alloc_cyclic_irq(&amdgpu_pasid_xa, &pasid, fpriv, + XA_LIMIT(1, (1U << bits) - 1), + &amdgpu_pasid_xa_next, GFP_KERNEL); if (r < 0) return r; @@ -107,6 +107,71 @@ static void amdgpu_pasid_free_cb(struct dma_fence *fence, } /** + * amdgpu_pasid_clear_owner - Clear the owner associated with a PASID + * @pasid: PASID whose owner should be cleared + * + * Replace the stored owner with NULL while keeping the PASID allocated. + * + * This is used by the delayed PASID free path so that future PASID + * lookups cannot resolve a stale DRM file-private object while the PASID + * is still waiting for outstanding fences before being released. + */ +static void amdgpu_pasid_clear_owner(u32 pasid) +{ + unsigned long flags; + + if (!pasid) + return; + + xa_lock_irqsave(&amdgpu_pasid_xa, flags); + __xa_store(&amdgpu_pasid_xa, pasid, NULL, GFP_ATOMIC); + xa_unlock_irqrestore(&amdgpu_pasid_xa, flags); +} + +/** + * amdgpu_pasid_lock - acquire the global PASID xarray lock + * @flags: storage for interrupt state + * + * Acquire the global PASID xarray lock with interrupts disabled. + * The saved interrupt state must be passed to + * amdgpu_pasid_unlock(). + */ +void amdgpu_pasid_lock(unsigned long *flags) +{ + xa_lock_irqsave(&amdgpu_pasid_xa, *flags); +} + +/** + * amdgpu_pasid_unlock - release the global PASID xarray lock + * @flags: interrupt state returned by amdgpu_pasid_lock() + * + * Release the global PASID xarray lock and restore the previous + * interrupt state. + */ +void amdgpu_pasid_unlock(unsigned long flags) +{ + xa_unlock_irqrestore(&amdgpu_pasid_xa, flags); +} + +/** + * amdgpu_pasid_get_fpriv_locked - get the DRM owner of a PASID + * @pasid: PASID to resolve + * + * The caller must hold the PASID XArray lock. + * + * Returns: + * Pointer to the owning DRM file private, or %NULL if the PASID has no + * current owner. + * + * The returned pointer is only valid while the PASID lock remains held + * and must not be retained after calling amdgpu_pasid_unlock(). + */ +struct amdgpu_fpriv *amdgpu_pasid_get_fpriv_locked(u32 pasid) +{ + return xa_load(&amdgpu_pasid_xa, pasid); +} + +/** * amdgpu_pasid_free_delayed - free pasid when fences signal * * @resv: reservation object with the fences to wait for @@ -121,6 +186,8 @@ void amdgpu_pasid_free_delayed(struct dma_resv *resv, struct dma_fence *fence; int r; + amdgpu_pasid_clear_owner(pasid); + r = dma_resv_get_singleton(resv, DMA_RESV_USAGE_BOOKKEEP, &fence); if (r) goto fallback; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h index a57919478d3b..4b55d0d9703a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h @@ -34,6 +34,7 @@ #define AMDGPU_NUM_VMID 16 struct amdgpu_device; +struct amdgpu_fpriv; struct amdgpu_vm; struct amdgpu_ring; struct amdgpu_sync; @@ -70,7 +71,10 @@ struct amdgpu_vmid_mgr { bool reserved_vmid; }; -int amdgpu_pasid_alloc(unsigned int bits); +int amdgpu_pasid_alloc(unsigned int bits, struct amdgpu_fpriv *fpriv); +void amdgpu_pasid_lock(unsigned long *flags); +void amdgpu_pasid_unlock(unsigned long flags); +struct amdgpu_fpriv *amdgpu_pasid_get_fpriv_locked(u32 pasid); void amdgpu_pasid_free(u32 pasid); void amdgpu_pasid_free_delayed(struct dma_resv *resv, u32 pasid); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index cff73f1b5a72..d97cf1212e0f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -198,8 +198,8 @@ exit: int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct drm_sched_entity *entity, void *owner, - unsigned int num_ibs, struct amdgpu_job **job, - u64 drm_client_id) + unsigned int num_ibs, u64 drm_client_id, + gfp_t gfp_flags, struct amdgpu_job **job) { struct amdgpu_fence *af; int r; @@ -207,18 +207,18 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (num_ibs == 0) return -EINVAL; - *job = kzalloc_flex(**job, ibs, num_ibs); + *job = kzalloc_flex(**job, ibs, num_ibs, gfp_flags); if (!*job) return -ENOMEM; - af = kzalloc_obj(struct amdgpu_fence); + af = kzalloc_obj(struct amdgpu_fence, gfp_flags); if (!af) { r = -ENOMEM; goto err_job; } (*job)->hw_fence = af; - af = kzalloc_obj(struct amdgpu_fence); + af = kzalloc_obj(struct amdgpu_fence, gfp_flags); if (!af) { r = -ENOMEM; goto err_fence; @@ -252,12 +252,13 @@ err_job: int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, struct drm_sched_entity *entity, void *owner, size_t size, enum amdgpu_ib_pool_type pool_type, - struct amdgpu_job **job, u64 k_job_id) + u64 k_job_id, struct amdgpu_job **job) { int r; - r = amdgpu_job_alloc(adev, NULL, entity, owner, 1, job, - k_job_id); + r = amdgpu_job_alloc(adev, NULL, entity, owner, 1, k_job_id, + amdgpu_ib_pool_gfp_flags(adev, pool_type), + job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h index e70a1117b812..7e03bd2ff639 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h @@ -115,13 +115,13 @@ static inline struct amdgpu_ring *amdgpu_job_ring(struct amdgpu_job *job) int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct drm_sched_entity *entity, void *owner, - unsigned int num_ibs, struct amdgpu_job **job, - u64 drm_client_id); + unsigned int num_ibs, u64 drm_client_id, + gfp_t gfp_flags, struct amdgpu_job **job); int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev, struct drm_sched_entity *entity, void *owner, size_t size, enum amdgpu_ib_pool_type pool_type, - struct amdgpu_job **job, - u64 k_job_id); + u64 k_job_id, + struct amdgpu_job **job); void amdgpu_job_set_resources(struct amdgpu_job *job, struct amdgpu_bo *gds, struct amdgpu_bo *gws, struct amdgpu_bo *oa); void amdgpu_job_free_resources(struct amdgpu_job *job); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c index 57935c321515..363b99168478 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c @@ -197,8 +197,9 @@ static int amdgpu_jpeg_dec_set_reg(struct amdgpu_ring *ring, uint32_t handle, int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job, - AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_IB_POOL_DIRECT, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 287ede40cd91..242c48e85912 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1211,7 +1211,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return -EINVAL; } } - ui32 >>= 8; + ui32 /= MILLIWATT_PER_WATT; break; case AMDGPU_INFO_SENSOR_GPU_INPUT_POWER: /* get input GPU power */ @@ -1220,7 +1220,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) (void *)&ui32, &ui32_size)) { return -EINVAL; } - ui32 >>= 8; + ui32 /= MILLIWATT_PER_WATT; break; case AMDGPU_INFO_SENSOR_VDDNB: /* get VDDNB in millivolts */ @@ -1376,11 +1376,11 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) memset(&gpuvm_fault, 0, sizeof(gpuvm_fault)); - xa_lock_irqsave(&adev->vm_manager.pasids, flags); + amdgpu_pasid_lock(&flags); gpuvm_fault.addr = vm->fault_info.addr; gpuvm_fault.status = vm->fault_info.status; gpuvm_fault.vmhub = vm->fault_info.vmhub; - xa_unlock_irqrestore(&adev->vm_manager.pasids, flags); + amdgpu_pasid_unlock(flags); return copy_to_user(out, &gpuvm_fault, min((size_t)size, sizeof(gpuvm_fault))) ? -EFAULT : 0; @@ -1465,7 +1465,7 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) struct amdgpu_device *adev = drm_to_adev(dev); struct amdgpu_fpriv *fpriv; struct drm_exec exec; - int r, pasid; + int r, pasid = 0; /* Ensure IB tests are run on ring */ flush_delayed_work(&adev->delayed_init_work); @@ -1488,22 +1488,23 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) goto out_suspend; } - pasid = amdgpu_pasid_alloc(16); - if (pasid < 0) { - dev_warn(adev->dev, "No more PASIDs available!"); - pasid = 0; - } - r = amdgpu_xcp_open_device(adev, fpriv, file_priv); if (r) goto error_pasid; amdgpu_debugfs_vm_init(file_priv); - r = amdgpu_vm_init(adev, &fpriv->vm, fpriv->xcp_id, pasid); + r = amdgpu_vm_init(adev, &fpriv->vm, fpriv->xcp_id); if (r) goto error_pasid; + pasid = amdgpu_pasid_alloc(16, fpriv); + if (pasid < 0) { + dev_warn(adev->dev, "No more PASIDs available!"); + pasid = 0; + } + fpriv->vm.pasid = pasid; + drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 0); drm_exec_until_all_locked(&exec) { r = amdgpu_vm_lock_pd(&fpriv->vm, &exec, 0); @@ -1547,12 +1548,14 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) goto out_suspend; error_vm: + if (pasid) { + amdgpu_pasid_free(pasid); + pasid = 0; + } + amdgpu_vm_fini(adev, &fpriv->vm); error_pasid: - if (pasid) - amdgpu_pasid_free(pasid); - kfree(fpriv); out_suspend: @@ -1608,10 +1611,11 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev, } amdgpu_ctx_mgr_fini(&fpriv->ctx_mgr); - amdgpu_vm_fini(adev, &fpriv->vm); if (pasid) amdgpu_pasid_free_delayed(pd->tbo.base.resv, pasid); + + amdgpu_vm_fini(adev, &fpriv->vm); amdgpu_bo_unref(&pd); xa_for_each(&fpriv->bo_list_handles, handle, list) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c index 1e3a23c81552..9af7c97af5be 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c @@ -952,9 +952,11 @@ int amdgpu_mes_rs64mem_init(struct amdgpu_mes *mes) return 0; } -/** - * amdgpu_mes_rs64mem_fini - tear down RS64 local memory management - */ + /** + * amdgpu_mes_rs64mem_fini - tear down RS64 local memory management + * + * @mes: MES instance + */ void amdgpu_mes_rs64mem_fini(struct amdgpu_mes *mes) { if (mes->ctx_array_size_bo) { @@ -1038,9 +1040,10 @@ int amdgpu_mes_rs64mem_setup_bitmaps(struct amdgpu_mes *mes) * amdgpu_mes_alloc_proc_ctx_index - allocate a process context slot * * @mes: MES instance + * @queue: Usermode queue receiving the allocated process context index * - * Returns 0 on success, -ENOSPC if all slots are used (caller should - * fall back to system memory path). + * Returns 0 on success, -ENOSPC if all slots are used, or + * -EOPNOTSUPP if RS64 local memory is unavailable. */ int amdgpu_mes_alloc_proc_ctx_index(struct amdgpu_mes *mes, struct amdgpu_usermode_queue *queue) @@ -1064,9 +1067,12 @@ int amdgpu_mes_alloc_proc_ctx_index(struct amdgpu_mes *mes, return 0; } -/** - * amdgpu_mes_free_proc_ctx_index - free a process context slot - */ + /** + * amdgpu_mes_free_proc_ctx_index - free a process context slot + * + * @mes: MES instance + * @queue: Usermode queue whose process context index is released + */ void amdgpu_mes_free_proc_ctx_index(struct amdgpu_mes *mes, struct amdgpu_usermode_queue *queue) { @@ -1080,9 +1086,15 @@ void amdgpu_mes_free_proc_ctx_index(struct amdgpu_mes *mes, amdgpu_mes_unlock(mes); } -/** - * amdgpu_mes_alloc_gang_ctx_index - allocate a gang context slot - */ + /** + * amdgpu_mes_alloc_gang_ctx_index - allocate a gang context slot + * + * @mes: MES instance + * @queue: Usermode queue receiving the allocated gang context index + * + * Returns 0 on success, -ENOSPC if all slots are used, or + * -EOPNOTSUPP if RS64 local memory is unavailable. + */ int amdgpu_mes_alloc_gang_ctx_index(struct amdgpu_mes *mes, struct amdgpu_usermode_queue *queue) { @@ -1105,9 +1117,12 @@ int amdgpu_mes_alloc_gang_ctx_index(struct amdgpu_mes *mes, return 0; } -/** - * amdgpu_mes_free_gang_ctx_index - free a gang context slot - */ + /** + * amdgpu_mes_free_gang_ctx_index - free a gang context slot + * + * @mes: MES instance + * @queue: Usermode queue whose gang context index is released + */ void amdgpu_mes_free_gang_ctx_index(struct amdgpu_mes *mes, struct amdgpu_usermode_queue *queue) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 8069fc41cc7f..7c784277396a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -509,6 +509,7 @@ struct amdgpu_crtc { struct drm_pending_vblank_event *event; bool wb_pending; + bool wb_frame_done; bool wb_enabled; struct drm_writeback_connector *wb_conn; }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index e0c0d7872e45..04f6ebf31cca 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -275,6 +275,7 @@ static int psp_early_init(struct amdgpu_ip_block *ip_block) psp->boot_time_tmr = false; break; case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 5): case IP_VERSION(15, 0, 9): psp_v15_0_0_set_psp_funcs(psp); psp->boot_time_tmr = false; @@ -1222,6 +1223,33 @@ int psp_memory_partition(struct psp_context *psp, int mode) return ret; } +int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int ret; + struct psp_context *psp = &adev->psp; + struct psp_gfx_cmd_resp *cmd = acquire_psp_cmd_buf(psp); + + cmd->cmd_id = GFX_CMD_ID_SET_MMHUB_ECO_SEC_LEVEL; + + ret = psp_cmd_submit_buf(psp, NULL, cmd, psp->fence_buf_mc_addr); + if (ret) { + dev_err(psp->adev->dev, + "PSP request failed to set mmuhub eco sec level with ret=%d\n", ret); + release_psp_cmd_buf(psp); + return ret; + } + + if (cmd->resp.status) { + dev_err(psp->adev->dev, + "MMHUB ECO SEC LEVEL command 0x%x failed, PSP response status: 0x%X\n", + cmd->cmd_id, cmd->resp.status); + ret = -EIO; + } + release_psp_cmd_buf(psp); + + return ret; +} + static int psp_ptl_fmt_verify(struct psp_context *psp, enum amdgpu_ptl_fmt fmt, uint32_t *ptl_fmt) { @@ -1264,8 +1292,19 @@ static int psp_ptl_invoke(struct psp_context *psp, u32 req_code, { struct psp_gfx_cmd_resp *cmd; struct amdgpu_ptl *ptl = &psp->ptl; + struct amdgpu_device *adev = psp->adev; int ret; + if (amdgpu_sriov_vf(adev)) { + ret = amdgpu_virt_ptl_request(adev, req_code, ptl_state, fmt1, fmt2); + if (!ret) { + ptl->enabled = *ptl_state; + ptl->fmt1 = *fmt1; + ptl->fmt2 = *fmt2; + } + return ret; + } + cmd = acquire_psp_cmd_buf(psp); cmd->cmd_id = GFX_CMD_ID_PERF_HW; @@ -1328,15 +1367,18 @@ int amdgpu_ptl_perf_monitor_ctrl(struct amdgpu_device *adev, u32 req_code, if (!adev || !ptl_state || !fmt1 || !fmt2) return -EINVAL; - if (amdgpu_sriov_vf(adev)) - return 0; - psp = &adev->psp; ptl = &psp->ptl; if (ptl->permanently_disabled && *ptl_state == 1) return 0; + if (amdgpu_sriov_vf(adev)) { + ptl_fmt1 = *fmt1; + ptl_fmt2 = *fmt2; + return psp_ptl_invoke(psp, req_code, ptl_state, &ptl_fmt1, &ptl_fmt2); + } + if (amdgpu_ip_version(adev, GC_HWIP, 0) != IP_VERSION(9, 4, 4) || psp->sos.fw_version < 0x0036081a) return -EOPNOTSUPP; @@ -1475,6 +1517,18 @@ static ssize_t ptl_enable_show(struct device *dev, struct device_attribute *attr struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); struct amdgpu_ptl *ptl = &adev->psp.ptl; + uint32_t ptl_state, fmt1, fmt2; + int ret; + + if (amdgpu_sriov_vf(adev)) { + ptl_state = ptl->enabled; + fmt1 = ptl->fmt1; + fmt2 = ptl->fmt2; + ret = amdgpu_ptl_perf_monitor_ctrl(adev, PSP_PTL_PERF_MON_QUERY, + &ptl_state, &fmt1, &fmt2); + if (ret) + return ret; + } if (ptl->permanently_disabled) return sysfs_emit(buf, "permanently disabled\n"); @@ -1533,11 +1587,23 @@ static ssize_t ptl_format_show(struct device *dev, struct device_attribute *attr { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - struct psp_context *psp = &adev->psp; + struct amdgpu_ptl *ptl = &adev->psp.ptl; + uint32_t ptl_state, fmt1, fmt2; + int ret; + + if (amdgpu_sriov_vf(adev)) { + ptl_state = ptl->enabled; + fmt1 = ptl->fmt1; + fmt2 = ptl->fmt2; + ret = amdgpu_ptl_perf_monitor_ctrl(adev, PSP_PTL_PERF_MON_QUERY, + &ptl_state, &fmt1, &fmt2); + if (ret) + return ret; + } return sysfs_emit(buf, "%s,%s\n", - amdgpu_ptl_fmt_str[psp->ptl.fmt1], - amdgpu_ptl_fmt_str[psp->ptl.fmt2]); + amdgpu_ptl_fmt_str[ptl->fmt1], + amdgpu_ptl_fmt_str[ptl->fmt2]); } static umode_t amdgpu_ptl_is_visible(struct kobject *kobj, struct attribute *attr, int idx) @@ -1547,7 +1613,7 @@ static umode_t amdgpu_ptl_is_visible(struct kobject *kobj, struct attribute *att struct amdgpu_device *adev = drm_to_adev(ddev); /* Only show PTL sysfs files if PTL hardware is supported */ - if (!adev->psp.ptl.hw_supported) + if (adev->psp.ptl.hw_supported_state != AMDGPU_PTL_HW_SUPPORTED) return 0; return attr->mode; @@ -1558,7 +1624,7 @@ int amdgpu_ptl_sysfs_init(struct amdgpu_device *adev) struct amdgpu_ptl *ptl = &adev->psp.ptl; int ret; - if (!ptl->hw_supported) + if (ptl->hw_supported_state != AMDGPU_PTL_HW_SUPPORTED) return 0; if (ptl->ptl_sysfs_created) @@ -1575,7 +1641,7 @@ void amdgpu_ptl_sysfs_fini(struct amdgpu_device *adev) { struct amdgpu_ptl *ptl = &adev->psp.ptl; - if (!ptl->hw_supported) + if (ptl->hw_supported_state != AMDGPU_PTL_HW_SUPPORTED) return; if (!ptl->ptl_sysfs_created) @@ -1861,6 +1927,12 @@ invoke: /* note down the capbility flag for XGMI TA */ psp->xgmi_context.xgmi_ta_caps = xgmi_cmd->caps_flag; + if (!amdgpu_sriov_vf(psp->adev)) + psp->xgmi_context.supports_ext_link_info = psp->xgmi_context.xgmi_ta_caps & + EXTEND_PEER_LINK_INFO_CMD_FLAG; + else + psp->xgmi_context.supports_ext_link_info = amdgpu_sriov_xgmi_ta_ext_peer_link_en(psp->adev); + return ret; } @@ -2038,15 +2110,13 @@ int psp_xgmi_get_topology_info(struct psp_context *psp, amdgpu_ip_version(psp->adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) || amdgpu_sriov_vf(psp->adev); - bool ta_port_num_support = psp->xgmi_context.xgmi_ta_caps & EXTEND_PEER_LINK_INFO_CMD_FLAG || - amdgpu_sriov_xgmi_ta_ext_peer_link_en(psp->adev); /* popluate the shared output buffer rather than the cmd input buffer * with node_ids as the input for GET_PEER_LINKS command execution. * This is required for GET_PEER_LINKS per xgmi ta implementation. * The same requirement for GET_EXTEND_PEER_LINKS command. */ - if (ta_port_num_support) { + if (psp->xgmi_context.supports_ext_link_info) { link_extend_info_output = &xgmi_cmd->xgmi_out_message.get_extend_link_info; for (i = 0; i < topology->num_nodes; i++) @@ -2069,7 +2139,7 @@ int psp_xgmi_get_topology_info(struct psp_context *psp, return ret; for (i = 0; i < topology->num_nodes; i++) { - uint8_t node_num_links = ta_port_num_support ? + uint8_t node_num_links = psp->xgmi_context.supports_ext_link_info ? link_extend_info_output->nodes[i].num_links : link_info_output->nodes[i].num_links; /* accumulate num_links on extended data */ if (get_extended_data) { @@ -2079,7 +2149,7 @@ int psp_xgmi_get_topology_info(struct psp_context *psp, topology->nodes[i].num_links : node_num_links; } /* popluate the connected port num info if supported and available */ - if (ta_port_num_support && topology->nodes[i].num_links) { + if (psp->xgmi_context.supports_ext_link_info && topology->nodes[i].num_links) { memcpy(topology->nodes[i].port_num, link_extend_info_output->nodes[i].port_num, sizeof(struct xgmi_connected_port_num) * TA_XGMI__MAX_PORT_NUM); } @@ -3476,8 +3546,10 @@ static int psp_load_non_psp_fw(struct psp_context *psp) amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 0) || amdgpu_ip_version(adev, MP0_HWIP, 0) == - IP_VERSION(15, 0, 8) || + IP_VERSION(15, 0, 5) || amdgpu_ip_version(adev, MP0_HWIP, 0) == + IP_VERSION(15, 0, 8) || + amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 9)) && (ucode->ucode_id == AMDGPU_UCODE_ID_SDMA1 || ucode->ucode_id == AMDGPU_UCODE_ID_SDMA2 || diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h index 2d838b1b2b11..d80c85793e3b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h @@ -232,6 +232,7 @@ struct psp_xgmi_context { struct ta_context context; struct psp_xgmi_topology_info top_info; bool supports_extended_data; + bool supports_ext_link_info; uint8_t xgmi_ta_caps; }; @@ -656,5 +657,6 @@ int amdgpu_psp_reg_program_no_ring(struct psp_context *psp, uint32_t val, void amdgpu_psp_debugfs_init(struct amdgpu_device *adev); int amdgpu_psp_get_fw_type(struct amdgpu_firmware_info *ucode, enum psp_gfx_fw_type *type); +int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 24fd24e8d874..297f8b04c4eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -1436,7 +1436,7 @@ static int amdgpu_uniras_clear_badpages_info(struct amdgpu_device *adev) &req, sizeof(req), NULL, 0); if (ret) { dev_err(adev->dev, "Failed to clear bad pages info, ret: %d\n", ret); - return ret; + return -EINVAL; } return 0; @@ -1536,7 +1536,7 @@ static int amdgpu_uniras_error_inject(struct amdgpu_device *adev, inject_req.method = info->value; return amdgpu_ras_mgr_handle_ras_cmd(adev, RAS_CMD__INJECT_ERROR, - &inject_req, sizeof(inject_req), &rsp, sizeof(rsp)); + &inject_req, sizeof(inject_req), &rsp, sizeof(rsp)) ? -EINVAL : 0; } /* wrapper of psp_ras_trigger_error */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 0d34f0eca991..686c92e96025 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -526,7 +526,7 @@ static ssize_t amdgpu_ras_cper_debugfs_read(struct file *f, char __user *buf, snapshot_req, sizeof(struct ras_cmd_cper_snapshot_req), snapshot_rsp, sizeof(struct ras_cmd_cper_snapshot_rsp)); if (r) - return r; + return -EINVAL; if (!snapshot_rsp->total_cper_num) { if (!read_header) @@ -568,7 +568,7 @@ static ssize_t amdgpu_ras_cper_debugfs_read(struct file *f, char __user *buf, record_rsp, sizeof(struct ras_cmd_cper_record_rsp)); if (r) - return r; + return -EINVAL; if (!record_rsp->real_data_size || !record_rsp->real_cper_num) break; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index 9d3934b4f106..015623b8fd05 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -68,8 +68,6 @@ enum amdgpu_ring_priority_level { #define to_amdgpu_ring(s) container_of((s), struct amdgpu_ring, sched) -#define AMDGPU_IB_POOL_SIZE (1024 * 1024) - enum amdgpu_ring_type { AMDGPU_RING_TYPE_GFX = AMDGPU_HW_IP_GFX, AMDGPU_RING_TYPE_COMPUTE = AMDGPU_HW_IP_COMPUTE, @@ -585,6 +583,8 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs, struct dma_fence **f); int amdgpu_ib_pool_init(struct amdgpu_device *adev); void amdgpu_ib_pool_fini(struct amdgpu_device *adev); +gfp_t amdgpu_ib_pool_gfp_flags(struct amdgpu_device *adev, + enum amdgpu_ib_pool_type type); int amdgpu_ib_ring_tests(struct amdgpu_device *adev); bool amdgpu_ring_sched_ready(struct amdgpu_ring *ring); void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c index 39070b2a4c04..74124f80601e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c @@ -46,11 +46,13 @@ int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, struct amdgpu_sa_manager *sa_manager, - unsigned int size, u32 suballoc_align, u32 domain) + unsigned int size, gfp_t gfp_flags) { int r; - r = amdgpu_bo_create_kernel(adev, size, AMDGPU_GPU_PAGE_SIZE, domain, + sa_manager->gfp_flags = gfp_flags; + r = amdgpu_bo_create_kernel(adev, size, AMDGPU_GPU_PAGE_SIZE, + AMDGPU_GEM_DOMAIN_GTT, &sa_manager->bo, &sa_manager->gpu_addr, &sa_manager->cpu_ptr); if (r) { @@ -59,7 +61,8 @@ int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, } memset(sa_manager->cpu_ptr, 0, size); - drm_suballoc_manager_init(&sa_manager->base, size, suballoc_align); + drm_suballoc_manager_init(&sa_manager->base, size, 256); + return r; } @@ -73,7 +76,8 @@ void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev, drm_suballoc_manager_fini(&sa_manager->base); - amdgpu_bo_free_kernel(&sa_manager->bo, &sa_manager->gpu_addr, &sa_manager->cpu_ptr); + amdgpu_bo_free_kernel(&sa_manager->bo, &sa_manager->gpu_addr, + &sa_manager->cpu_ptr); } int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, @@ -81,7 +85,8 @@ int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, unsigned int size) { struct drm_suballoc *sa = drm_suballoc_new(&sa_manager->base, size, - GFP_KERNEL, false, 0); + sa_manager->gfp_flags, + false, 0); if (IS_ERR(sa)) { *sa_bo = NULL; @@ -110,6 +115,7 @@ void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, { struct drm_printer p = drm_seq_file_printer(m); - drm_suballoc_dump_debug_info(&sa_manager->base, &p, sa_manager->gpu_addr); + drm_suballoc_dump_debug_info(&sa_manager->base, &p, + sa_manager->gpu_addr); } #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h index 8c85c80fc119..1d1c89348709 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h @@ -35,6 +35,7 @@ struct amdgpu_sa_manager { struct amdgpu_bo *bo; uint64_t gpu_addr; void *cpu_ptr; + gfp_t gfp_flags; }; static inline struct amdgpu_sa_manager * @@ -57,7 +58,7 @@ static inline void *amdgpu_sa_bo_cpu_addr(struct drm_suballoc *sa_bo) int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, struct amdgpu_sa_manager *sa_manager, - unsigned size, u32 align, u32 domain); + unsigned size, gfp_t gfp_flags); void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev, struct amdgpu_sa_manager *sa_manager); int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 7920675af1d1..91b7bf6e65f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -244,8 +244,9 @@ static int amdgpu_ttm_map_buffer(struct amdgpu_ttm_buffer_entity *entity, r = amdgpu_job_alloc_with_ib(adev, &entity->base, AMDGPU_FENCE_OWNER_UNDEFINED, num_dw * 4 + num_bytes, - AMDGPU_IB_POOL_DELAYED, &job, - AMDGPU_KERNEL_JOB_ID_TTM_MAP_BUFFER); + AMDGPU_IB_POOL_DELAYED, + AMDGPU_KERNEL_JOB_ID_TTM_MAP_BUFFER, + &job); if (r) return r; @@ -1487,6 +1488,7 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, const struct ttm_place *place) { struct dma_resv_iter resv_cursor; + struct amdgpu_bo *abo; struct dma_fence *f; if (!amdgpu_bo_is_amdgpu_bo(bo)) @@ -1496,6 +1498,21 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, if (bo->resource->mem_type == TTM_PL_SYSTEM) return true; + abo = ttm_to_amdgpu_bo(bo); + if (abo->flags & AMDGPU_GEM_CREATE_DISCARDABLE) { + /* + * SVM BOs are migrated to system memory synchronously in this + * TTM eviction context. The migration needs the owning + * process's mmap lock, but the normal lock order is + * mmap_lock -> BO reservation and the BO is already reserved + * here. svm_range_evict_svm_bo() only trylocks the mmap lock; + * if the eviction fails for any reason, we return false so TTM + * skips this BO instead of risking a deadlock. + */ + if (amdgpu_amdkfd_evict_svm_bo(abo) < 0) + return false; + } + if (bo->type == ttm_bo_type_kernel && !amdgpu_vm_evictable(ttm_to_amdgpu_bo(bo))) return false; @@ -1592,8 +1609,8 @@ static int amdgpu_ttm_access_memory_sdma(struct ttm_buffer_object *bo, r = amdgpu_job_alloc_with_ib(adev, &adev->mman.default_entity.base, AMDGPU_FENCE_OWNER_UNDEFINED, num_dw * 4, AMDGPU_IB_POOL_DELAYED, - &job, - AMDGPU_KERNEL_JOB_ID_TTM_ACCESS_MEMORY_SDMA); + AMDGPU_KERNEL_JOB_ID_TTM_ACCESS_MEMORY_SDMA, + &job); if (r) goto out; @@ -2173,6 +2190,18 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) gtt_size = configured_size; } + /* Cap GTT so that it does not exceed total physical RAM. */ + if (adev->flags & AMD_IS_APU) { + u64 phys_ram = (u64)totalram_pages() << PAGE_SHIFT; + + if (gtt_size > phys_ram) { + gtt_size = phys_ram; + dev_info(adev->dev, + "Capping GTT to %uM to not exceed available system memory\n", + (unsigned int)(gtt_size / (1024 * 1024))); + } + } + /* Initialize GTT memory pool */ r = amdgpu_gtt_mgr_init(adev, gtt_size); if (r) { @@ -2443,7 +2472,7 @@ static int amdgpu_ttm_prepare_job(struct amdgpu_device *adev, int r; r = amdgpu_job_alloc_with_ib(adev, &entity->base, AMDGPU_FENCE_OWNER_UNDEFINED, - num_dw * 4, pool, job, k_job_id); + num_dw * 4, pool, k_job_id, job); if (r) return r; @@ -2460,6 +2489,27 @@ static int amdgpu_ttm_prepare_job(struct amdgpu_device *adev, DMA_RESV_USAGE_BOOKKEEP); } +static int amdgpu_calc_bytes_per_packet(u32 max_bytes_per_packet, + u32 byte_count) +{ + /* Byte count is dword-aligned and fits a single packet */ + if (!(byte_count & 0x3) && byte_count <= max_bytes_per_packet) + return max_bytes_per_packet; + + /* + * Align down maximum byte count to 256 bytes so that + * the copy optimally uses all memory channels and + * also to ensure that SDMA can use its dword mode, which + * is faster. + * + * This assumes that the starting addresses of BOs are always + * dword aligned, which should be the case for every copy + * operation in the kernel, because the kernel always copies + * pages. + */ + return ALIGN_DOWN(max_bytes_per_packet, SZ_256); +} + int amdgpu_copy_buffer(struct amdgpu_device *adev, struct amdgpu_ttm_buffer_entity *entity, uint64_t src_offset, @@ -2483,7 +2533,8 @@ int amdgpu_copy_buffer(struct amdgpu_device *adev, return -EINVAL; } - max_bytes = adev->mman.buffer_funcs->copy_max_bytes; + max_bytes = amdgpu_calc_bytes_per_packet(adev->mman.buffer_funcs->copy_max_bytes, + byte_count); num_loops = DIV_ROUND_UP(byte_count, max_bytes); num_dw = ALIGN(num_loops * adev->mman.buffer_funcs->copy_num_dw, 8); r = amdgpu_ttm_prepare_job(adev, entity, num_dw, @@ -2527,7 +2578,8 @@ static int amdgpu_ttm_fill_mem(struct amdgpu_device *adev, unsigned int i; int r; - max_bytes = adev->mman.buffer_funcs->fill_max_bytes; + max_bytes = amdgpu_calc_bytes_per_packet(adev->mman.buffer_funcs->fill_max_bytes, + byte_count); num_loops = DIV_ROUND_UP_ULL(byte_count, max_bytes); num_dw = ALIGN(num_loops * adev->mman.buffer_funcs->fill_num_dw, 8); r = amdgpu_ttm_prepare_job(adev, entity, num_dw, resv, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index babaabe2d891..3fe10d6af757 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -123,6 +123,8 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work) struct amdgpu_device *adev = uq_mgr->adev; const struct amdgpu_userq_funcs *userq_funcs = adev->userq_funcs[queue->queue_type]; + struct drm_wedge_task_info *info = NULL; + struct amdgpu_task_info *ti = NULL; bool gpu_reset = false; if (unlikely(adev->debug_disable_gpu_ring_reset)) { @@ -137,6 +139,14 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work) if (!amdgpu_gpu_recovery) return; + if (queue->vm && queue->vm->pasid) { + ti = amdgpu_vm_get_task_info_pasid(adev, queue->vm->pasid); + if (ti) { + amdgpu_vm_print_task_info(adev, ti); + info = &ti->task; + } + } + if (amdgpu_userq_is_reset_type_supported(adev, queue->queue_type, AMDGPU_RESET_TYPE_PER_QUEUE)) { int r; @@ -146,11 +156,17 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work) queue, NULL, NULL); else r = userq_funcs->reset(queue); - if (r) + if (r) { gpu_reset = true; + } else { + atomic_inc(&adev->gpu_reset_counter); + amdgpu_userq_fence_driver_force_completion(queue); + drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, info); + } } else { gpu_reset = true; } + amdgpu_vm_put_task_info(ti); /* * Don't schedule the work here! Scheduling or queue work from one reset @@ -985,6 +1001,7 @@ amdgpu_userq_vm_validate_and_restore_queue(struct amdgpu_userq_mgr *uq_mgr) struct amdgpu_vm *vm = &fpriv->vm; unsigned long key, tmp_key; struct amdgpu_bo_va *bo_va; + struct amdgpu_usermode_queue *queue; struct amdgpu_bo *bo; struct drm_exec exec; struct xarray xa; @@ -1100,6 +1117,24 @@ retry_lock: dma_fence_wait(bo_va->last_pt_update, false); dma_fence_wait(vm->last_update, false); + xa_for_each(&uq_mgr->userq_xa, tmp_key, queue) { + bo = queue->wptr_obj.obj; + if (!bo) { + ret = -EINVAL; + goto unlock_all; + } + + ret = amdgpu_ttm_alloc_gart(&bo->tbo); + if (unlikely(ret)) { + drm_file_err(uq_mgr->file, + "failed to bind wptr bo to gart on resume, qid=%lu ret=%d\n", + tmp_key, ret); + goto unlock_all; + } + + queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(bo); + } + ret = amdgpu_evf_mgr_rearm(&fpriv->evf_mgr, &exec); if (ret) { drm_file_err(uq_mgr->file, "Failed to replace eviction fence\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 23383ac5323f..e8b0c62f72be 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -1165,8 +1165,9 @@ static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring, struct amdgpu_bo *bo, r = amdgpu_job_alloc_with_ib(ring->adev, &adev->uvd.entity, AMDGPU_FENCE_OWNER_UNDEFINED, 64, direct ? AMDGPU_IB_POOL_DIRECT : - AMDGPU_IB_POOL_DELAYED, &job, - AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_IB_POOL_DELAYED, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index eef3c9853a5c..1f7a98cdce5a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -473,7 +473,8 @@ static int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle, r = amdgpu_job_alloc_with_ib(ring->adev, &ring->adev->vce.entity, AMDGPU_FENCE_OWNER_UNDEFINED, ib_size_dw * 4, AMDGPU_IB_POOL_DIRECT, - &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; @@ -564,8 +565,9 @@ static int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle, AMDGPU_FENCE_OWNER_UNDEFINED, ib_size_dw * 4, direct ? AMDGPU_IB_POOL_DIRECT : - AMDGPU_IB_POOL_DELAYED, &job, - AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_IB_POOL_DELAYED, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index fe504f1a3fc8..17db7264269e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -629,7 +629,8 @@ static int amdgpu_vcn_dec_send_msg(struct amdgpu_ring *ring, r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, 64, AMDGPU_IB_POOL_DIRECT, - &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) goto err; @@ -809,7 +810,8 @@ static int amdgpu_vcn_dec_sw_send_msg(struct amdgpu_ring *ring, r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, AMDGPU_IB_POOL_DIRECT, - &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) goto err; @@ -939,7 +941,8 @@ static int amdgpu_vcn_enc_get_create_msg(struct amdgpu_ring *ring, uint32_t hand r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, AMDGPU_IB_POOL_DIRECT, - &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; @@ -1006,7 +1009,8 @@ static int amdgpu_vcn_enc_get_destroy_msg(struct amdgpu_ring *ring, uint32_t han r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, AMDGPU_IB_POOL_DIRECT, - &job, AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c index 8f4b86063507..b43fc643668d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c @@ -2090,3 +2090,39 @@ int amdgpu_virt_send_remote_ras_cmd(struct amdgpu_device *adev, return ret; } + +int amdgpu_virt_ptl_request(struct amdgpu_device *adev, u32 req_code, + uint32_t *ptl_state, uint32_t *fmt1, uint32_t *fmt2) +{ + int ret; + + if (!ptl_state || !fmt1 || !fmt2) + return -EINVAL; + + if (!amdgpu_sriov_ptl_support(adev) || + !adev->virt.ops || !adev->virt.ops->req_ptl_update) + return -EOPNOTSUPP; + + if (req_code == PSP_PTL_PERF_MON_SET && *ptl_state) { + if (*fmt1 == *fmt2) { + dev_warn(adev->dev, + "PTL formats must be different (fmt1=%u, fmt2=%u)\n", + *fmt1, *fmt2); + return -EINVAL; + } + } + + ret = adev->virt.ops->req_ptl_update(adev, req_code, *ptl_state, *fmt1, *fmt2); + if (ret) { + dev_warn(adev->dev, "VF PTL update request failed: %d\n", ret); + return ret; + } + + if (req_code == PSP_PTL_PERF_MON_QUERY) { + *ptl_state = adev->virt.ptl_state; + *fmt1 = adev->virt.ptl_pref_format1; + *fmt2 = adev->virt.ptl_pref_format2; + } + + return 0; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h index d8500c3e48a1..bcf7156a4a9e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h @@ -116,6 +116,8 @@ struct amdgpu_virt_ops { int (*req_ras_chk_criti)(struct amdgpu_device *adev, u64 addr); int (*req_remote_ras_cmd)(struct amdgpu_device *adev, u32 param1, u32 param2, u32 param3); + int (*req_ptl_update)(struct amdgpu_device *adev, + u32 req_code, u32 ptl_state, u32 fmt1, u32 fmt2); }; /* @@ -341,6 +343,11 @@ struct amdgpu_virt { /* Spinlock to protect access to the RLCG register interface */ spinlock_t rlcg_reg_lock; + /* PTL (Performance Throttle Limiter) response from host */ + uint32_t ptl_state; + uint32_t ptl_pref_format1; + uint32_t ptl_pref_format2; + struct mutex access_req_mutex; union amd_sriov_ras_caps ras_en_caps; @@ -447,6 +454,8 @@ static inline bool is_virtual_machine(void) ((adev)->virt.gim_feature & AMDGIM_FEATURE_MES_INFO_ENABLE) #define amdgpu_sriov_is_unitid_support(adev) \ ((adev)->virt.gim_feature & AMDGIM_FEATURE_UNITID_SUPPORT) +#define amdgpu_sriov_ptl_support(adev) \ + ((adev)->virt.gim_feature & AMDGIM_FEATURE_PTL_SUPPORT) #define amdgpu_virt_xgmi_migrate_enabled(adev) \ ((adev)->virt.is_xgmi_node_migrate_enabled && (adev)->gmc.xgmi.node_segment_size != 0) @@ -457,6 +466,8 @@ int amdgpu_virt_request_full_gpu(struct amdgpu_device *adev, bool init); int amdgpu_virt_release_full_gpu(struct amdgpu_device *adev, bool init); int amdgpu_virt_reset_gpu(struct amdgpu_device *adev); void amdgpu_virt_request_init_data(struct amdgpu_device *adev); +int amdgpu_virt_ptl_request(struct amdgpu_device *adev, u32 req_code, + uint32_t *ptl_state, uint32_t *fmt1, uint32_t *fmt2); void amdgpu_virt_ready_to_reset(struct amdgpu_device *adev); int amdgpu_virt_wait_reset(struct amdgpu_device *adev); int amdgpu_virt_alloc_mm_table(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 157d4168907c..aac8ace9d7a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1553,6 +1553,8 @@ int amdgpu_vm_clear_freed(struct amdgpu_device *adev, struct amdgpu_sync sync; int r; + if (list_empty(&vm->freed)) + return 0; /* * Implicitly sync to command submissions in the same VM before @@ -2504,14 +2506,16 @@ amdgpu_vm_get_task_info_vm(struct amdgpu_vm *vm) struct amdgpu_task_info * amdgpu_vm_get_task_info_pasid(struct amdgpu_device *adev, u32 pasid) { + struct amdgpu_fpriv *fpriv; struct amdgpu_task_info *ti; struct amdgpu_vm *vm; unsigned long flags; - xa_lock_irqsave(&adev->vm_manager.pasids, flags); - vm = xa_load(&adev->vm_manager.pasids, pasid); + amdgpu_pasid_lock(&flags); + fpriv = amdgpu_pasid_get_fpriv_locked(pasid); + vm = fpriv ? &fpriv->vm : NULL; ti = amdgpu_vm_get_task_info_vm(vm); - xa_unlock_irqrestore(&adev->vm_manager.pasids, flags); + amdgpu_pasid_unlock(flags); return ti; } @@ -2552,7 +2556,6 @@ void amdgpu_vm_set_task_info(struct amdgpu_vm *vm) * @adev: amdgpu_device pointer * @vm: requested vm * @xcp_id: GPU partition selection id - * @pasid: the pasid the VM is using on this GPU * * Init @vm fields. * @@ -2560,7 +2563,7 @@ void amdgpu_vm_set_task_info(struct amdgpu_vm *vm) * 0 for success, error for failure. */ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, - int32_t xcp_id, uint32_t pasid) + int32_t xcp_id) { struct amdgpu_bo *root_bo; struct amdgpu_bo_vm *root; @@ -2635,26 +2638,12 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (r) dev_dbg(adev->dev, "Failed to create task info for VM\n"); - /* Store new PASID in XArray (if non-zero) */ - if (pasid != 0) { - r = xa_err(xa_store_irq(&adev->vm_manager.pasids, pasid, vm, GFP_KERNEL)); - if (r < 0) - goto error_free_root; - - vm->pasid = pasid; - } - amdgpu_bo_unreserve(vm->root.bo); amdgpu_bo_unref(&root_bo); return 0; error_free_root: - /* If PASID was partially set, erase it from XArray before failing */ - if (vm->pasid != 0) { - xa_erase_irq(&adev->vm_manager.pasids, vm->pasid); - vm->pasid = 0; - } amdgpu_vm_pt_free_root(adev, vm); amdgpu_bo_unreserve(vm->root.bo); amdgpu_bo_unref(&root_bo); @@ -2761,11 +2750,6 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm) root = amdgpu_bo_ref(vm->root.bo); amdgpu_bo_reserve(root, true); - /* Remove PASID mapping before destroying VM */ - if (vm->pasid != 0) { - xa_erase_irq(&adev->vm_manager.pasids, vm->pasid); - vm->pasid = 0; - } dma_fence_wait(vm->last_unlocked, false); dma_fence_put(vm->last_unlocked); dma_fence_wait(vm->last_tlb_flush, false); @@ -2861,8 +2845,6 @@ void amdgpu_vm_manager_init(struct amdgpu_device *adev) #else adev->vm_manager.vm_update_mode = 0; #endif - - xa_init_flags(&adev->vm_manager.pasids, XA_FLAGS_LOCK_IRQ); } /** @@ -2874,9 +2856,6 @@ void amdgpu_vm_manager_init(struct amdgpu_device *adev) */ void amdgpu_vm_manager_fini(struct amdgpu_device *adev) { - WARN_ON(!xa_empty(&adev->vm_manager.pasids)); - xa_destroy(&adev->vm_manager.pasids); - amdgpu_vmid_mgr_fini(adev); amdgpu_pasid_mgr_cleanup(); } @@ -2933,14 +2912,16 @@ struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev, u32 pasid, struct drm_exec *exec) { unsigned long irqflags; + struct amdgpu_fpriv *fpriv; struct amdgpu_bo *root; struct amdgpu_vm *vm; int r; - xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); - vm = xa_load(&adev->vm_manager.pasids, pasid); - root = vm ? amdgpu_bo_ref(vm->root.bo) : NULL; - xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); + amdgpu_pasid_lock(&irqflags); + fpriv = amdgpu_pasid_get_fpriv_locked(pasid); + vm = fpriv ? &fpriv->vm : NULL; + root = vm && vm->root.bo ? amdgpu_bo_ref(vm->root.bo) : NULL; + amdgpu_pasid_unlock(irqflags); if (!root) return NULL; @@ -2952,11 +2933,17 @@ struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev, } /* Double check that the VM still exists */ - xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); - vm = xa_load(&adev->vm_manager.pasids, pasid); - if (vm && vm->root.bo != root) + amdgpu_pasid_lock(&irqflags); + fpriv = amdgpu_pasid_get_fpriv_locked(pasid); + if (!fpriv) { vm = NULL; - xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); + } else { + vm = &fpriv->vm; + if (vm->root.bo != root) + vm = NULL; + } + amdgpu_pasid_unlock(irqflags); + if (!vm) { drm_exec_unlock_obj(exec, &root->tbo.base); amdgpu_bo_unref(&root); @@ -3153,12 +3140,14 @@ void amdgpu_vm_update_fault_cache(struct amdgpu_device *adev, uint32_t status, unsigned int vmhub) { + struct amdgpu_fpriv *fpriv; struct amdgpu_vm *vm; unsigned long flags; - xa_lock_irqsave(&adev->vm_manager.pasids, flags); + amdgpu_pasid_lock(&flags); - vm = xa_load(&adev->vm_manager.pasids, pasid); + fpriv = amdgpu_pasid_get_fpriv_locked(pasid); + vm = fpriv ? &fpriv->vm : NULL; /* Don't update the fault cache if status is 0. In the multiple * fault case, subsequent faults will return a 0 status which is * useless for userspace and replaces the useful fault status, so @@ -3191,7 +3180,7 @@ void amdgpu_vm_update_fault_cache(struct amdgpu_device *adev, WARN_ONCE(1, "Invalid vmhub %u\n", vmhub); } } - xa_unlock_irqrestore(&adev->vm_manager.pasids, flags); + amdgpu_pasid_unlock(flags); } void amdgpu_vm_print_task_info(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 5822836fa4a3..2f8234560764 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -481,10 +481,6 @@ struct amdgpu_vm_manager { */ int vm_update_mode; - /* PASID to VM mapping, will be used in interrupt context to - * look up VM of a page fault - */ - struct xarray pasids; /* Global registration of recent page fault information */ struct amdgpu_vm_fault_info fault_info; }; @@ -502,7 +498,7 @@ void amdgpu_vm_manager_init(struct amdgpu_device *adev); void amdgpu_vm_manager_fini(struct amdgpu_device *adev); long amdgpu_vm_wait_idle(struct amdgpu_vm *vm, long timeout); -int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, int32_t xcp_id, uint32_t pasid); +int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, int32_t xcp_id); 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, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c index fd09a2b5a147..430274237689 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c @@ -56,7 +56,7 @@ static int amdgpu_vm_sdma_alloc_job(struct amdgpu_vm_update_params *p, ndw = min(ndw, AMDGPU_VM_SDMA_MAX_NUM_DW); r = amdgpu_job_alloc_with_ib(p->adev, entity, AMDGPU_FENCE_OWNER_VM, - ndw * 4, pool, &p->job, k_job_id); + ndw * 4, pool, k_job_id, &p->job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c index c0f581e416f9..2725230aa5e3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c @@ -559,7 +559,7 @@ static int amdgpu_xgmi_sysfs_add_dev_info(struct amdgpu_device *adev, pr_err("failed to create xgmi_num_links\n"); /* Create xgmi port num file if supported */ - if (adev->psp.xgmi_context.xgmi_ta_caps & EXTEND_PEER_LINK_INFO_CMD_FLAG) { + if (adev->psp.xgmi_context.supports_ext_link_info) { ret = device_create_file(adev->dev, &dev_attr_xgmi_port_num); if (ret) dev_err(adev->dev, "failed to create xgmi_port_num\n"); @@ -595,7 +595,7 @@ remove_file: device_remove_file(adev->dev, &dev_attr_xgmi_error); device_remove_file(adev->dev, &dev_attr_xgmi_num_hops); device_remove_file(adev->dev, &dev_attr_xgmi_num_links); - if (adev->psp.xgmi_context.xgmi_ta_caps & EXTEND_PEER_LINK_INFO_CMD_FLAG) + if (adev->psp.xgmi_context.supports_ext_link_info) device_remove_file(adev->dev, &dev_attr_xgmi_port_num); success: @@ -613,7 +613,7 @@ static void amdgpu_xgmi_sysfs_rem_dev_info(struct amdgpu_device *adev, device_remove_file(adev->dev, &dev_attr_xgmi_error); device_remove_file(adev->dev, &dev_attr_xgmi_num_hops); device_remove_file(adev->dev, &dev_attr_xgmi_num_links); - if (adev->psp.xgmi_context.xgmi_ta_caps & EXTEND_PEER_LINK_INFO_CMD_FLAG) + if (adev->psp.xgmi_context.supports_ext_link_info) device_remove_file(adev->dev, &dev_attr_xgmi_port_num); if (hive->kobj.parent != (&adev->dev->kobj)) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h index d80f01c0e754..33421ccdff04 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h @@ -379,7 +379,7 @@ struct amd_sriov_msg_vf2pf_info { struct { uint8_t id; uint32_t version; - } ucode_info[AMD_SRIOV_MSG_RESERVE_UCODE]; + } __packed ucode_info[AMD_SRIOV_MSG_RESERVE_UCODE]; uint64_t dummy_page_addr; /* FB allocated for guest MES to record UQ info */ uint64_t mes_info_addr; @@ -404,6 +404,9 @@ enum amd_sriov_mailbox_request_message { MB_REQ_RAS_ERROR_COUNT = 203, MB_REQ_RAS_CPER_DUMP = 204, MB_REQ_RAS_BAD_PAGES = 205, + MB_REQ_RAS_CHK_CRITI = 206, + MB_REQ_RAS_REMOTE_CMD = 207, + MB_REQ_MSG_PTL_UPDATE = 208, }; /* mailbox message send from host to guest */ @@ -424,9 +427,33 @@ enum amd_sriov_mailbox_response_message { MB_RES_MSG_RAS_BAD_PAGES_READY = 15, MB_RES_MSG_RAS_BAD_PAGES_NOTIFICATION = 16, MB_RES_MSG_UNRECOV_ERR_NOTIFICATION = 17, + MB_RES_RAS_CHK_CRITI_READY = 18, + MB_RES_RAS_REMOTE_CMD_READY = 19, + MB_RES_MSG_PTL_UPDATE_READY = 20, MB_RES_MSG_TEXT_MESSAGE = 255 }; +/* + * Generic response status codes for mailbox data fields. + * Used in msg_data[1..N] to indicate operation result. + */ +enum amd_sriov_response_status { + AMD_SRIOV_RESP_SUCCESS = 0, + AMD_SRIOV_RESP_FAIL = 1, + AMD_SRIOV_RESP_UNSUPPORTED = 2, +}; + +/* + * PTL mailbox data format: + * Request: msg_data[1]=req_code, msg_data[2]=ptl_state, msg_data[3]=(fmt1<<16)|fmt2 + * Response: msg_data[1]=(status<<16)|ptl_state, msg_data[2]=(fmt1<<16)|fmt2 + */ +#define AMD_SRIOV_PTL_PACK_FORMATS(fmt1, fmt2) ((((fmt1) & 0xFFFF) << 16) | ((fmt2) & 0xFFFF)) +#define AMD_SRIOV_PTL_UNPACK_STATUS(dw) (((dw) >> 16) & 0xFFFF) +#define AMD_SRIOV_PTL_UNPACK_STATE(dw) ((dw) & 0xFFFF) +#define AMD_SRIOV_PTL_UNPACK_FMT1(dw) (((dw) >> 16) & 0xFFFF) +#define AMD_SRIOV_PTL_UNPACK_FMT2(dw) ((dw) & 0xFFFF) + enum amd_sriov_ras_telemetry_gpu_block { RAS_TELEMETRY_GPU_BLOCK_UMC = 0, RAS_TELEMETRY_GPU_BLOCK_SDMA = 1, diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index a998ce77da40..00798d80479f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -4580,7 +4580,7 @@ static const struct amdgpu_gfx_funcs gfx_v10_0_gfx_funcs = { static void gfx_v10_0_gpu_early_init(struct amdgpu_device *adev) { - u32 gb_addr_config; + u32 gb_addr_config = 0; switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { case IP_VERSION(10, 1, 10): @@ -4620,8 +4620,9 @@ static void gfx_v10_0_gpu_early_init(struct amdgpu_device *adev) gb_addr_config = CYAN_SKILLFISH_GB_ADDR_CONFIG_GOLDEN; break; default: - BUG(); - break; + dev_warn(adev->dev, "Unsupported GC version 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return; } adev->gfx.config.gb_addr_config = gb_addr_config; @@ -8602,13 +8603,16 @@ static u64 gfx_v10_0_ring_get_rptr_compute(struct amdgpu_ring *ring) static u64 gfx_v10_0_ring_get_wptr_compute(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; /* XXX check if swapping is necessary on BE */ - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)ring->wptr_cpu_addr); - else - BUG(); + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); + wptr = 0; + } return wptr; } @@ -8621,7 +8625,7 @@ static void gfx_v10_0_ring_set_wptr_compute(struct amdgpu_ring *ring) ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { - BUG(); /* only DOORBELL method supported on gfx10 now */ + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); } } @@ -9379,7 +9383,7 @@ static void gfx_v10_0_handle_priv_fault(struct amdgpu_device *adev, } break; default: - BUG(); + break; } } @@ -9449,7 +9453,7 @@ static int gfx_v10_0_kiq_set_interrupt_state(struct amdgpu_device *adev, } break; default: - BUG(); /* kiq only support GENERIC2_INT now */ + /* kiq only support GENERIC2_INT now */ break; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 0cbbdc3694f4..3b203961abb9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -1145,8 +1145,9 @@ static int gfx_v11_0_gpu_early_init(struct amdgpu_device *adev) adev->gfx.config.sc_earlyz_tile_fifo_size = 0x300; break; default: - BUG(); - break; + dev_warn(adev->dev, "Unsupported GC version 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return -EINVAL; } return 0; @@ -5954,13 +5955,16 @@ static u64 gfx_v11_0_ring_get_rptr_compute(struct amdgpu_ring *ring) static u64 gfx_v11_0_ring_get_wptr_compute(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; /* XXX check if swapping is necessary on BE */ - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)ring->wptr_cpu_addr); - else - BUG(); + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); + wptr = 0; + } return wptr; } @@ -5974,7 +5978,7 @@ static void gfx_v11_0_ring_set_wptr_compute(struct amdgpu_ring *ring) ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { - BUG(); /* only DOORBELL method supported on gfx11 now */ + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); } } @@ -6747,7 +6751,6 @@ static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev, } break; default: - BUG(); break; } } @@ -6832,7 +6835,7 @@ static int gfx_v11_0_kiq_set_interrupt_state(struct amdgpu_device *adev, } break; default: - BUG(); /* kiq only support GENERIC2_INT now */ + /* kiq only support GENERIC2_INT now */ break; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index b3887c5262a0..5f0a24951f4a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -964,8 +964,9 @@ static int gfx_v12_0_gpu_early_init(struct amdgpu_device *adev) adev->gfx.config.sc_earlyz_tile_fifo_size = 0x4C0; break; default: - BUG(); - break; + dev_warn(adev->dev, "Unsupported GC version 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return -EINVAL; } return 0; @@ -4464,13 +4465,16 @@ static u64 gfx_v12_0_ring_get_rptr_compute(struct amdgpu_ring *ring) static u64 gfx_v12_0_ring_get_wptr_compute(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; /* XXX check if swapping is necessary on BE */ - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)ring->wptr_cpu_addr); - else - BUG(); + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); + wptr = 0; + } return wptr; } @@ -4484,7 +4488,7 @@ static void gfx_v12_0_ring_set_wptr_compute(struct amdgpu_ring *ring) ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { - BUG(); /* only DOORBELL method supported on gfx12 now */ + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); } } @@ -5079,7 +5083,6 @@ static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev, } break; default: - BUG(); break; } } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c index 1f7e243d2ad9..e49d8a79045c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c @@ -891,8 +891,9 @@ static int gfx_v12_1_gpu_early_init(struct amdgpu_device *adev) adev->gfx.config.sc_earlyz_tile_fifo_size = 0x4C0; break; default: - BUG(); - break; + dev_warn(adev->dev, "Unsupported GC version 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return -EINVAL; } return 0; @@ -3673,13 +3674,16 @@ static u64 gfx_v12_1_ring_get_rptr_compute(struct amdgpu_ring *ring) static u64 gfx_v12_1_ring_get_wptr_compute(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; /* XXX check if swapping is necessary on BE */ - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)ring->wptr_cpu_addr); - else - BUG(); + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); + wptr = 0; + } return wptr; } @@ -3693,7 +3697,7 @@ static void gfx_v12_1_ring_set_wptr_compute(struct amdgpu_ring *ring) ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { - BUG(); /* only DOORBELL method supported on gfx12 now */ + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); } } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c index ac90d8e9d86a..cd2a14ecdba8 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c @@ -347,7 +347,8 @@ static int gfx_v6_0_init_microcode(struct amdgpu_device *adev) case CHIP_HAINAN: chip_name = "hainan"; break; - default: BUG(); + default: + return -EINVAL; } err = amdgpu_ucode_request(adev, &adev->gfx.pfp_fw, @@ -1736,8 +1737,8 @@ static void gfx_v6_0_constants_init(struct amdgpu_device *adev) gb_addr_config = HAINAN_GB_ADDR_CONFIG_GOLDEN; break; default: - BUG(); - break; + dev_warn(adev->dev, "Unsupported asic_type 0x%08x\n", adev->asic_type); + return; } WREG32(mmGRBM_CNTL, (0xff << GRBM_CNTL__READ_TIMEOUT__SHIFT)); @@ -2202,7 +2203,7 @@ static u64 gfx_v6_0_ring_get_wptr(struct amdgpu_ring *ring) else if (ring == &adev->gfx.compute_ring[1]) return RREG32(mmCP_RB2_WPTR); else - BUG(); + return 0; } static void gfx_v6_0_ring_set_wptr_gfx(struct amdgpu_ring *ring) @@ -2223,8 +2224,6 @@ static void gfx_v6_0_ring_set_wptr_compute(struct amdgpu_ring *ring) } else if (ring == &adev->gfx.compute_ring[1]) { WREG32(mmCP_RB2_WPTR, lower_32_bits(ring->wptr)); (void)RREG32(mmCP_RB2_WPTR); - } else { - BUG(); } } @@ -3310,7 +3309,6 @@ static void gfx_v6_0_set_compute_eop_interrupt_state(struct amdgpu_device *adev, } default: - BUG(); break; } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 65b8497ad5f0..b93bad1d2a6f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -933,7 +933,8 @@ static int gfx_v7_0_init_microcode(struct amdgpu_device *adev) chip_name = "mullins"; break; default: - BUG(); + dev_warn(adev->dev, "Unsupported asic_type 0x%08x\n", adev->asic_type); + return -EINVAL; } err = amdgpu_ucode_request(adev, &adev->gfx.pfp_fw, diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 9e0840df8849..6cf427995078 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -977,7 +977,8 @@ static int gfx_v8_0_init_microcode(struct amdgpu_device *adev) chip_name = "vegam"; break; default: - BUG(); + dev_warn(adev->dev, "Unsupported asic_type 0x%08x\n", adev->asic_type); + return -EINVAL; } if (adev->asic_type >= CHIP_POLARIS10 && adev->asic_type <= CHIP_POLARIS12) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 1cad6dc9476e..c3322058720f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -2107,8 +2107,9 @@ static int gfx_v9_0_gpu_early_init(struct amdgpu_device *adev) return err; break; default: - BUG(); - break; + dev_warn(adev->dev, "Unsupported GC version 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return -EINVAL; } adev->gfx.config.gb_addr_config = gb_addr_config; @@ -5681,13 +5682,16 @@ static u64 gfx_v9_0_ring_get_rptr_compute(struct amdgpu_ring *ring) static u64 gfx_v9_0_ring_get_wptr_compute(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; /* XXX check if swapping is necessary on BE */ - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)ring->wptr_cpu_addr); - else - BUG(); + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); + wptr = 0; + } return wptr; } @@ -5699,8 +5703,8 @@ static void gfx_v9_0_ring_set_wptr_compute(struct amdgpu_ring *ring) if (ring->use_doorbell) { atomic64_set((atomic64_t *)ring->wptr_cpu_addr, ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); - } else{ - BUG(); /* only DOORBELL method supported on gfx9 now */ + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); } } diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index 44d38b76cb4f..522981fa182d 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -2331,7 +2331,7 @@ err_compute: } } for (xcc_id--; xcc_id >= 0; xcc_id--) { - for (m = adev->gfx.mec.num_mec - 1; m <= 0; m--) { + for (m = adev->gfx.mec.num_mec - 1; m >= 0; m--) { for (p = adev->gfx.mec.num_pipe_per_mec - 1; p >= 0; p--) { irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP + (m * adev->gfx.mec.num_pipe_per_mec) + p; @@ -2400,7 +2400,10 @@ static int gfx_v9_4_3_perf_monitor_ptl_init(struct amdgpu_device *adev, bool ena if (!adev->psp.funcs) return -EOPNOTSUPP; - if (!ptl->hw_supported) { + if (ptl->hw_supported_state == AMDGPU_PTL_HW_NOT_SUPPORTED) + return -EOPNOTSUPP; + + if (ptl->hw_supported_state == AMDGPU_PTL_HW_UNINIT) { fmt1 = GFX_FTYPE_VECTOR; fmt2 = GFX_FTYPE_F8; } else { @@ -2411,10 +2414,17 @@ static int gfx_v9_4_3_perf_monitor_ptl_init(struct amdgpu_device *adev, bool ena /* initialize PTL with default formats: GFX_FTYPE_VECTOR & GFX_FTYPE_F8 */ r = amdgpu_ptl_perf_monitor_ctrl(adev, PSP_PTL_PERF_MON_SET, &ptl_state, &fmt1, &fmt2); - if (r) + if (r) { + if (ptl->hw_supported_state == AMDGPU_PTL_HW_UNINIT) + ptl->hw_supported_state = AMDGPU_PTL_HW_NOT_SUPPORTED; + + if (r != -EOPNOTSUPP) + dev_err(adev->dev, "PTL initialization failed (%d)\n", r); + return r; + } - ptl->hw_supported = true; + ptl->hw_supported_state = AMDGPU_PTL_HW_SUPPORTED; atomic_set(&ptl->disable_ref, 0); if (!enable && !amdgpu_in_reset(adev) && !adev->in_suspend) { @@ -2459,7 +2469,8 @@ static int gfx_v9_4_3_hw_fini(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int i, num_xcc; - if (adev->psp.ptl.hw_supported && !amdgpu_in_reset(adev)) + if (adev->psp.ptl.hw_supported_state == AMDGPU_PTL_HW_SUPPORTED && + !amdgpu_in_reset(adev)) gfx_v9_4_3_perf_monitor_ptl_init(adev, false); amdgpu_irq_put(adev, &adev->gfx.bad_op_irq, 0); @@ -3029,13 +3040,17 @@ static u64 gfx_v9_4_3_ring_get_rptr_compute(struct amdgpu_ring *ring) static u64 gfx_v9_4_3_ring_get_wptr_compute(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; /* XXX check if swapping is necessary on BE */ - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)&ring->adev->wb.wb[ring->wptr_offs]); - else - BUG(); + } else { + dev_warn_once(adev->dev, + "%s requires doorbell!\n", __func__); + wptr = 0; + } return wptr; } @@ -3048,7 +3063,8 @@ static void gfx_v9_4_3_ring_set_wptr_compute(struct amdgpu_ring *ring) atomic64_set((atomic64_t *)&adev->wb.wb[ring->wptr_offs], ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { - BUG(); /* only DOORBELL method supported on gfx9 now */ + dev_warn_once(adev->dev, + "%s requires doorbell!\n", __func__); } } diff --git a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c index 05b164f38c97..09dc01a425d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c @@ -377,8 +377,9 @@ static void imu_v11_0_program_rlc_ram(struct amdgpu_device *adev) imu_v11_0_3_program_rlc_ram(adev); break; default: - BUG(); - break; + WARN(1, "Invalid GFX/IMU IP version 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return; } //Indicate the contents of the RAM are valid diff --git a/drivers/gpu/drm/amd/amdgpu/imu_v12_0.c b/drivers/gpu/drm/amd/amdgpu/imu_v12_0.c index 58cd87db8061..4256225f2495 100644 --- a/drivers/gpu/drm/amd/amdgpu/imu_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/imu_v12_0.c @@ -382,8 +382,9 @@ static void imu_v12_0_program_rlc_ram(struct amdgpu_device *adev) (const u32)ARRAY_SIZE(imu_rlc_ram_golden_12_0_1)); break; default: - BUG(); - break; + WARN(1, "Invalid GFX/IMU IP version 0x%08x\n", + amdgpu_ip_version(adev, GC_HWIP, 0)); + return; } //Indicate the latest entry diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c index 66d4c487eee5..68152b2ce015 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c @@ -400,6 +400,25 @@ static void jpeg_v5_3_0_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx) } /** + * jpeg_v5_3_0_set_mmhub_eco_sec_level - set jpeg sec lvl reg + * + * @adev: amdgpu_device pointer + * + * request psp to set secure lvl + */ +static int jpeg_v5_3_0_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int r = 0; + + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { + /* Request to PSP to program JPEG secure lvl */ + r = psp_set_mmhub_eco_sec_level(adev); + } + + return r; +} + +/** * jpeg_v5_3_0_start - start JPEG block * * @adev: amdgpu_device pointer @@ -424,6 +443,11 @@ static int jpeg_v5_3_0_start(struct amdgpu_device *adev) if (r) return r; + /* program JPEG secure lvl register */ + r = jpeg_v5_3_0_set_mmhub_eco_sec_level(adev); + if (r) + return r; + /* JPEG disable CGC */ jpeg_v5_3_0_disable_clock_gating(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index e099304b4ac4..639e4a37f872 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -70,27 +70,23 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, ret = -EINVAL; goto fail_map; } - - /* TODO use eviction fence instead of pinning. */ - ret = amdgpu_bo_pin(wptr_obj->obj, AMDGPU_GEM_DOMAIN_GTT); + /* Keep WPTR BO under eviction-fence control instead of pinning. */ + ret = amdgpu_evf_mgr_attach_fence(&uq_mgr_to_fpriv(uq_mgr)->evf_mgr, wptr_obj->obj); if (ret) { - DRM_ERROR("Failed to pin wptr bo. ret %d\n", ret); + DRM_ERROR("Failed to attach eviction fence to wptr bo. ret %d\n", ret); goto fail_map; } ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo); if (ret) { - DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret); - goto fail_alloc_gart; + DRM_ERROR("Failed to bind wptr bo to GART. ret %d\n", ret); + goto fail_map; } queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj); drm_exec_fini(&exec); return 0; - -fail_alloc_gart: - amdgpu_bo_unpin(wptr_obj->obj); fail_map: amdgpu_bo_unref(&wptr_obj->obj); fail_lock: @@ -239,9 +235,7 @@ int mes_userq_reset_queue(struct amdgpu_device *adev, r = mes_userq_unmap(uq); if (r) return r; - atomic_inc(&adev->gpu_reset_counter); amdgpu_userq_fence_driver_force_completion(uq); - drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL); break; } } @@ -513,9 +507,6 @@ static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue) amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, &queue->mqd.cpu_ptr); - amdgpu_bo_reserve(queue->wptr_obj.obj, true); - amdgpu_bo_unpin(queue->wptr_obj.obj); - amdgpu_bo_unreserve(queue->wptr_obj.obj); amdgpu_bo_unref(&queue->wptr_obj.obj); } diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index b72da75dd851..553f9d39add8 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -86,7 +86,7 @@ static void mes_v11_0_ring_set_wptr(struct amdgpu_ring *ring) ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { - BUG(); + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); } } @@ -97,12 +97,15 @@ static u64 mes_v11_0_ring_get_rptr(struct amdgpu_ring *ring) static u64 mes_v11_0_ring_get_wptr(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)ring->wptr_cpu_addr); - else - BUG(); + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); + wptr = 0; + } return wptr; } @@ -294,8 +297,8 @@ static int convert_to_mes_queue_type(int queue_type) else if (queue_type == AMDGPU_RING_TYPE_SDMA) return MES_QUEUE_TYPE_SDMA; else - BUG(); - return -1; + WARN(1, "Invalid queue type %d\n", queue_type); + return MES_QUEUE_TYPE_GFX; } static int convert_to_mes_priority_level(int priority_level) @@ -1599,12 +1602,14 @@ static int mes_v11_0_queue_init(struct amdgpu_device *adev, struct amdgpu_ring *ring; int r; - if (pipe == AMDGPU_MES_KIQ_PIPE) + if (pipe == AMDGPU_MES_KIQ_PIPE) { ring = &adev->gfx.kiq[0].ring; - else if (pipe == AMDGPU_MES_SCHED_PIPE) + } else if (pipe == AMDGPU_MES_SCHED_PIPE) { ring = &adev->mes.ring[0]; - else - BUG(); + } else { + WARN(1, "Invalid MES pipe %d\n", pipe); + return -EINVAL; + } if ((pipe == AMDGPU_MES_SCHED_PIPE) && (amdgpu_in_reset(adev) || adev->in_suspend)) { @@ -1687,7 +1692,7 @@ static int mes_v11_0_mqd_sw_init(struct amdgpu_device *adev, else if (pipe == AMDGPU_MES_SCHED_PIPE) ring = &adev->mes.ring[0]; else - BUG(); + return -EINVAL; if (ring->mqd_obj) return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c index 04465804c254..3b2b955cf82e 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c @@ -59,7 +59,7 @@ static void mes_v12_0_ring_set_wptr(struct amdgpu_ring *ring) ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { - BUG(); + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); } } @@ -70,12 +70,15 @@ static u64 mes_v12_0_ring_get_rptr(struct amdgpu_ring *ring) static u64 mes_v12_0_ring_get_wptr(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)ring->wptr_cpu_addr); - else - BUG(); + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); + wptr = 0; + } return wptr; } @@ -279,8 +282,8 @@ static int convert_to_mes_queue_type(int queue_type) else if (queue_type == AMDGPU_RING_TYPE_MES) return MES_QUEUE_TYPE_SCHQ; else - BUG(); - return -1; + WARN(1, "Invalid queue type %d\n", queue_type); + return MES_QUEUE_TYPE_GFX; } static int convert_to_mes_priority_level(int priority_level) diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c index dbdb9ba02a5f..f1098118d5c0 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c @@ -61,7 +61,7 @@ static void mes_v12_1_ring_set_wptr(struct amdgpu_ring *ring) ring->wptr); WDOORBELL64(ring->doorbell_index, ring->wptr); } else { - BUG(); + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); } } @@ -72,12 +72,16 @@ static u64 mes_v12_1_ring_get_rptr(struct amdgpu_ring *ring) static u64 mes_v12_1_ring_get_wptr(struct amdgpu_ring *ring) { + struct amdgpu_device *adev = ring->adev; u64 wptr; - if (ring->use_doorbell) + if (ring->use_doorbell) { wptr = atomic64_read((atomic64_t *)ring->wptr_cpu_addr); - else - BUG(); + } else { + dev_warn_once(adev->dev, "%s requires doorbell!\n", __func__); + wptr = 0; + + } return wptr; } @@ -278,8 +282,8 @@ static int convert_to_mes_queue_type(int queue_type) else if (queue_type == AMDGPU_RING_TYPE_MES) return MES_QUEUE_TYPE_SCHQ; else - BUG(); - return -1; + WARN(1, "Invalid queue type %d\n", queue_type); + return MES_QUEUE_TYPE_GFX; } static int mes_v12_1_add_hw_queue(struct amdgpu_mes *mes, diff --git a/drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c b/drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c index 9a40107a0869..fec08f235b92 100644 --- a/drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c +++ b/drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c @@ -185,8 +185,11 @@ static int xgpu_ai_send_access_requests(struct amdgpu_device *adev, } else if (req == IDH_REQ_GPU_INIT_DATA){ /* Dummy REQ_GPU_INIT_DATA handling */ r = xgpu_ai_poll_msg(adev, IDH_REQ_GPU_INIT_DATA_READY); - /* version set to 0 since dummy */ - adev->virt.req_init_data_ver = 0; + /* + * AI uses the GPU_CRIT_REGION_V1 layout in practice, so fix the + * dummy version value to match the actual init-data format. + */ + adev->virt.req_init_data_ver = GPU_CRIT_REGION_V1; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c b/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c index f2e456390b27..eb537b2ae685 100644 --- a/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c +++ b/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c @@ -214,6 +214,9 @@ send_request: case IDH_REQ_RAS_REMOTE_CMD: event = IDH_REQ_RAS_REMOTE_CMD_READY; break; + case IDH_REQ_PTL_UPDATE: + event = IDH_PTL_UPDATE_READY; + break; default: break; } @@ -253,6 +256,23 @@ send_request: adev->virt.fw_reserve.checksum_key = RREG32_NO_KIQ(mmMAILBOX_MSGBUF_RCV_DW2); } + + /* Retrieve PTL response from mailbox */ + if (req == IDH_REQ_PTL_UPDATE) { + u32 dw1 = RREG32_NO_KIQ(mmMAILBOX_MSGBUF_RCV_DW1); + u32 dw2 = RREG32_NO_KIQ(mmMAILBOX_MSGBUF_RCV_DW2); + u32 status = AMD_SRIOV_PTL_UNPACK_STATUS(dw1); + + if (status == AMD_SRIOV_RESP_UNSUPPORTED) { + r = -EOPNOTSUPP; + } else if (status == AMD_SRIOV_RESP_FAIL) { + r = -EIO; + } else { + adev->virt.ptl_state = AMD_SRIOV_PTL_UNPACK_STATE(dw1); + adev->virt.ptl_pref_format1 = AMD_SRIOV_PTL_UNPACK_FMT1(dw2); + adev->virt.ptl_pref_format2 = AMD_SRIOV_PTL_UNPACK_FMT2(dw2); + } + } } out: @@ -597,6 +617,13 @@ static int xgpu_nv_req_remote_ras_cmd(struct amdgpu_device *adev, adev, IDH_REQ_RAS_REMOTE_CMD, param1, param2, param3); } +static int xgpu_nv_req_ptl_update(struct amdgpu_device *adev, + u32 req_code, u32 ptl_state, u32 fmt1, u32 fmt2) +{ + return xgpu_nv_send_access_requests_with_param(adev, IDH_REQ_PTL_UPDATE, + req_code, ptl_state, AMD_SRIOV_PTL_PACK_FORMATS(fmt1, fmt2)); +} + const struct amdgpu_virt_ops xgpu_nv_virt_ops = { .req_full_gpu = xgpu_nv_request_full_gpu_access, .rel_full_gpu = xgpu_nv_release_full_gpu_access, @@ -612,4 +639,5 @@ const struct amdgpu_virt_ops xgpu_nv_virt_ops = { .req_bad_pages = xgpu_nv_req_ras_bad_pages, .req_ras_chk_criti = xgpu_nv_check_vf_critical_region, .req_remote_ras_cmd = xgpu_nv_req_remote_ras_cmd, + .req_ptl_update = xgpu_nv_req_ptl_update, }; diff --git a/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.h b/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.h index dc57a4f697ee..5023113e7600 100644 --- a/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.h +++ b/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.h @@ -45,6 +45,7 @@ enum idh_request { IDH_REQ_RAS_BAD_PAGES = 205, IDH_REQ_RAS_CHK_CRITI = 206, IDH_REQ_RAS_REMOTE_CMD = 207, + IDH_REQ_PTL_UPDATE = 208 }; enum idh_event { @@ -66,6 +67,7 @@ enum idh_event { IDH_UNRECOV_ERR_NOTIFICATION = 17, IDH_REQ_RAS_CHK_CRITI_READY = 18, IDH_REQ_RAS_REMOTE_CMD_READY = 19, + IDH_PTL_UPDATE_READY = 20, IDH_TEXT_MESSAGE = 255, }; diff --git a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c index c78f0598637f..000516b5845a 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c @@ -30,70 +30,76 @@ #include "ivsrcid/nbio/irqsrcs_nbif_7_4.h" #include <uapi/linux/kfd_ioctl.h> -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10 0x4f0aeb -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10 0x4f0aec -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10 0x4f0aed -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10 0x4f0aee -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10 0x4f0aef -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10 0x4f0af0 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10 0x4f0af1 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10 0x4f0af2 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10 0x4f0af3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10 0x4f0af4 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10 0x4f0af5 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2 - -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5 0x8e13 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5 0x8e14 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5 0x8e15 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5 0x8e4d -#define regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5 0x8e4e -#define regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 - -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5 0xd000 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX0_BIF_FB_EN_nbio_7_11_5 0x8e20 -#define regBIF_BX0_BIF_FB_EN_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5 0x8e11 -#define regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5 0x8e12 -#define regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5 0x8e26 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5 0x8e27 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5_BASE_IDX 5 - -#define regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5 0x8e17 -#define regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10 0x4f0aeb +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10 0x4f0aec +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10 0x4f0aed +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10 0x4f0aee +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10 0x4f0aef +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10 0x4f0af0 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10 0x4f0af1 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10 0x4f0af2 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10 0x4f0af3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10 0x4f0af4 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10 0x4f0af5 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3 +#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021 +#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2 + +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5 0x8e13 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5 0x8e14 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5 0x8e15 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5_BASE_IDX 5 + +#define regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5 0x012d +#define regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5 0x012e +#define regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 + +#define regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5 0x0021 +#define regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5_BASE_IDX 2 + +#define regBIF_BX1_BIF_FB_EN_nbio_7_11_5 0x0100 +#define regBIF_BX1_BIF_FB_EN_nbio_7_11_5_BASE_IDX 2 + +#define regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5 0x00f1 +#define regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5 0x00f2 +#define regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5_BASE_IDX 2 + +#define regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5 0x0106 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5 0x0107 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5_BASE_IDX 2 + +#define regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5 0x00f7 +#define regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 + +//BIF_BX1_BIF_FB_EN +#define BIF_BX1_BIF_FB_EN__FB_READ_EN__SHIFT_nbio_7_11_5 0x0 +#define BIF_BX1_BIF_FB_EN__FB_WRITE_EN__SHIFT_nbio_7_11_5 0x1 +#define BIF_BX1_BIF_FB_EN__FB_READ_EN_MASK_nbio_7_11_5 0x00000001L +#define BIF_BX1_BIF_FB_EN__FB_WRITE_EN_MASK_nbio_7_11_5 0x00000002L static void nbif_v6_3_1_remap_hdp_registers(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) { - WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5, adev->rmmio_remap.reg_offset + KFD_MMIO_REMAP_HDP_MEM_FLUSH_CNTL); - WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5, adev->rmmio_remap.reg_offset + KFD_MMIO_REMAP_HDP_REG_FLUSH_CNTL); } else { WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL, @@ -110,7 +116,7 @@ static u32 nbif_v6_3_1_get_rev_id(struct amdgpu_device *adev) if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 4)) tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10); else if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5); + tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5); else tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0); @@ -124,11 +130,11 @@ static void nbif_v6_3_1_mc_access_enable(struct amdgpu_device *adev, bool enable { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) { if (enable) - WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN_nbio_7_11_5, - BIF_BX0_BIF_FB_EN__FB_READ_EN_MASK | - BIF_BX0_BIF_FB_EN__FB_WRITE_EN_MASK); + WREG32_SOC15(NBIO, 0, regBIF_BX1_BIF_FB_EN_nbio_7_11_5, + BIF_BX1_BIF_FB_EN__FB_READ_EN_MASK_nbio_7_11_5 | + BIF_BX1_BIF_FB_EN__FB_WRITE_EN_MASK_nbio_7_11_5); else - WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN_nbio_7_11_5, 0); + WREG32_SOC15(NBIO, 0, regBIF_BX1_BIF_FB_EN_nbio_7_11_5, 0); } else { if (enable) WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN, @@ -383,12 +389,12 @@ static void nbif_v6_3_1_ih_control(struct amdgpu_device *adev) /* setup interrupt control */ if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5, adev->dummy_page_addr >> 8); else WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL2, adev->dummy_page_addr >> 8); - interrupt_cntl = RREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5); + interrupt_cntl = RREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5); /* * BIF_BX0_INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi * BIF_BX0_INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN @@ -401,7 +407,7 @@ static void nbif_v6_3_1_ih_control(struct amdgpu_device *adev) IH_REQ_NONSNOOP_EN, 0); if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5, interrupt_cntl); + WREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5, interrupt_cntl); else WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL, interrupt_cntl); } @@ -427,7 +433,7 @@ nbif_v6_3_1_get_clockgating_state(struct amdgpu_device *adev, static u32 nbif_v6_3_1_get_hdp_flush_req_offset(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5); + return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5); else return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_REQ); } @@ -435,7 +441,7 @@ static u32 nbif_v6_3_1_get_hdp_flush_req_offset(struct amdgpu_device *adev) static u32 nbif_v6_3_1_get_hdp_flush_done_offset(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5); + return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5); else return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_DONE); } @@ -622,7 +628,7 @@ static void nbif_v6_3_1_set_reg_remap(struct amdgpu_device *adev) } else { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, - regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5) << 2; + regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5) << 2; else adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL) << 2; diff --git a/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h b/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h index ac34bac3c839..ad618b78685f 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h +++ b/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h @@ -110,6 +110,7 @@ enum psp_gfx_cmd_id GFX_CMD_ID_PERF_HW = 0x0000004C, /* performance monitor */ GFX_CMD_ID_FB_FW_RESERV_ADDR = 0x00000050, /* Query FW reservation addr */ GFX_CMD_ID_FB_FW_RESERV_EXT_ADDR = 0x00000051, /* Query FW reservation extended addr */ + GFX_CMD_ID_SET_MMHUB_ECO_SEC_LEVEL = 0x0000005D, /* Set MMHUB ECO sec lvls on VCN block */ }; /* PSP boot config sub-commands */ diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c index 479690c44f0d..bd3bb3bfb668 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c @@ -136,7 +136,9 @@ static int psp_v11_0_init_microcode(struct psp_context *psp) err = psp_init_toc_microcode(psp, ucode_prefix); break; default: - BUG(); + dev_warn(adev->dev, "Unsupported MP0 version 0x%08x\n", + amdgpu_ip_version(adev, MP0_HWIP, 0)); + return -EINVAL; } return err; diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c index 00b4a34e6601..f9a3fbfc0634 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c @@ -133,7 +133,9 @@ static int psp_v13_0_init_microcode(struct psp_context *psp) return err; break; default: - BUG(); + dev_warn(adev->dev, "Unsupported MP0 version 0x%08x\n", + amdgpu_ip_version(adev, MP0_HWIP, 0)); + return -EINVAL; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v13_0_4.c b/drivers/gpu/drm/amd/amdgpu/psp_v13_0_4.c index 3d5e26b3fa00..e1acdbe01833 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v13_0_4.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v13_0_4.c @@ -50,7 +50,9 @@ static int psp_v13_0_4_init_microcode(struct psp_context *psp) return err; break; default: - BUG(); + dev_warn(adev->dev, "Unsupported MP0 version 0x%08x\n", + amdgpu_ip_version(adev, MP0_HWIP, 0)); + return -EINVAL; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v14_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v14_0.c index 040a61aefa86..888c747d6c5c 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v14_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v14_0.c @@ -85,7 +85,9 @@ static int psp_v14_0_init_microcode(struct psp_context *psp) return err; break; default: - BUG(); + dev_warn(adev->dev, "Unsupported MP0 version 0x%08x\n", + amdgpu_ip_version(adev, MP0_HWIP, 0)); + return -EINVAL; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c index 2a4d91368ac6..abaa40b6def9 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c @@ -33,9 +33,22 @@ MODULE_FIRMWARE("amdgpu/psp_15_0_0_toc.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_0_ta.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_5_toc.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_5_ta.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_9_toc.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_9_ta.bin"); +#define regMPASP_PCRU0_MPASP_C2PMSG_64 0x4280 +#define regMPASP_PCRU0_MPASP_C2PMSG_64_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_67 0x4283 +#define regMPASP_PCRU0_MPASP_C2PMSG_67_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_69 0x4285 +#define regMPASP_PCRU0_MPASP_C2PMSG_69_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_70 0x4286 +#define regMPASP_PCRU0_MPASP_C2PMSG_70_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_71 0x4287 +#define regMPASP_PCRU0_MPASP_C2PMSG_71_BASE_IDX 2 + static int psp_v15_0_0_init_microcode(struct psp_context *psp) { struct amdgpu_device *adev = psp->adev; @@ -71,14 +84,25 @@ static int psp_v15_0_0_ring_stop(struct psp_context *psp, ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_SMN_C2PMSG_101), 0x80000000, 0x80000000, false); } else { - /* Write the ring destroy command*/ - WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64, - GFX_CTRL_CMD_ID_DESTROY_RINGS); - /* there might be handshake issue with hardware which needs delay */ - mdelay(20); - /* Wait for response flag (bit 31) */ - ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), - 0x80000000, 0x80000000, false); + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) { + /* Write the ring destroy command*/ + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64, + GFX_CTRL_CMD_ID_DESTROY_RINGS); + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + /* Wait for response flag (bit 31) */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + } else { + /* Write the ring destroy command*/ + WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64, + GFX_CTRL_CMD_ID_DESTROY_RINGS); + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + /* Wait for response flag (bit 31) */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + } } return ret; @@ -118,9 +142,10 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp, 0x80000000, 0x8000FFFF, false); } else { - /* Wait for sOS ready for ring creation */ - ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), - 0x80000000, 0x80000000, false); + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) { + /* Wait for sOS ready for ring creation */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); if (ret) { DRM_ERROR("Failed to wait for trust OS ready for ring creation\n"); return ret; @@ -128,6 +153,35 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp, /* Write low address of the ring to C2PMSG_69 */ psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr); + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_69, psp_ring_reg); + /* Write high address of the ring to C2PMSG_70 */ + psp_ring_reg = upper_32_bits(ring->ring_mem_mc_addr); + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_70, psp_ring_reg); + /* Write size of ring to C2PMSG_71 */ + psp_ring_reg = ring->ring_size; + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_71, psp_ring_reg); + /* Write the ring initialization command to C2PMSG_64 */ + psp_ring_reg = ring_type; + psp_ring_reg = psp_ring_reg << 16; + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64, psp_ring_reg); + + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + + /* Wait for response flag (bit 31) in C2PMSG_64 */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x8000FFFF, false); + } else { + /* Wait for sOS ready for ring creation */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + if (ret) { + DRM_ERROR("Failed to wait for trust OS ready for ring creation\n"); + return ret; + } + + /* Write low address of the ring to C2PMSG_69 */ + psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr); WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_69, psp_ring_reg); /* Write high address of the ring to C2PMSG_70 */ psp_ring_reg = upper_32_bits(ring->ring_mem_mc_addr); @@ -146,6 +200,7 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp, /* Wait for response flag (bit 31) in C2PMSG_64 */ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), 0x80000000, 0x8000FFFF, false); + } } return ret; @@ -176,8 +231,12 @@ static uint32_t psp_v15_0_0_ring_get_wptr(struct psp_context *psp) if (amdgpu_sriov_vf(adev)) data = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_102); - else - data = RREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67); + else { + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) + data = RREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_67); + else + data = RREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67); + } return data; } @@ -190,8 +249,12 @@ static void psp_v15_0_0_ring_set_wptr(struct psp_context *psp, uint32_t value) WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_102, value); WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_101, GFX_CTRL_CMD_ID_CONSUME_CMD); - } else - WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67, value); + } else { + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_67, value); + else + WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67, value); + } } static const struct psp_funcs psp_v15_0_0_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 713746a75d1f..1f9a9c46377a 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -407,6 +407,7 @@ soc21_asic_reset_method(struct amdgpu_device *adev) case IP_VERSION(14, 0, 4): case IP_VERSION(14, 0, 5): case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 5): case IP_VERSION(15, 0, 9): return AMD_RESET_METHOD_MODE2; default: diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index 8bb9592b0981..bbd0e05d39e5 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -217,8 +217,9 @@ static int uvd_v6_0_enc_get_create_msg(struct amdgpu_ring *ring, uint32_t handle int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job, - AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_IB_POOL_DIRECT, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; @@ -282,8 +283,9 @@ static int uvd_v6_0_enc_get_destroy_msg(struct amdgpu_ring *ring, int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job, - AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_IB_POOL_DIRECT, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c index df2c83348315..46222fc30be6 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c @@ -225,8 +225,9 @@ static int uvd_v7_0_enc_get_create_msg(struct amdgpu_ring *ring, u32 handle, int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job, - AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_IB_POOL_DIRECT, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; @@ -289,8 +290,9 @@ static int uvd_v7_0_enc_get_destroy_msg(struct amdgpu_ring *ring, u32 handle, int i, r; r = amdgpu_job_alloc_with_ib(ring->adev, NULL, NULL, ib_size_dw * 4, - AMDGPU_IB_POOL_DIRECT, &job, - AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST); + AMDGPU_IB_POOL_DIRECT, + AMDGPU_KERNEL_JOB_ID_VCN_RING_TEST, + &job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c index 45580e9c4e0c..886e2c2074a9 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c @@ -796,6 +796,27 @@ static int vcn_v5_0_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, } /** + * vcn_v5_0_0_set_mmhub_eco_sec_level - set vcn sec lvl reg + * + * @adev: amdgpu_device pointer + * + * request psp to set sec lvl + */ +static int vcn_v5_0_0_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int r = 0; + + if (amdgpu_ip_version(adev, VCN_HWIP, 0) == IP_VERSION(5, 3, 0)) { + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { + /* Request to PSP to program VCN secure lvl */ + r = psp_set_mmhub_eco_sec_level(adev); + } + } + + return r; +} + +/** * vcn_v5_0_0_start - VCN start * * @vinst: VCN instance @@ -819,6 +840,11 @@ static int vcn_v5_0_0_start(struct amdgpu_vcn_inst *vinst) fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr; + /* program VCN secure lvl register */ + r = vcn_v5_0_0_set_mmhub_eco_sec_level(adev); + if (r) + return r; + if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) return vcn_v5_0_0_start_dpg_mode(vinst, adev->vcn.inst[i].indirect_sram); diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index 514715793f78..352c27103305 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -1387,10 +1387,30 @@ static uint64_t vi_get_pcie_replay_count(struct amdgpu_device *adev) return (nak_r + nak_g); } +struct vi_reset_quirk { + u16 device; + u16 subsystem_vendor; + u16 subsystem_device; + u8 revision; +}; + +static const struct vi_reset_quirk vi_reset_quirks[] = { + { 0x67ef, PCI_VENDOR_ID_APPLE, 0x0190, 0xe3 }, /* Radeon Pro 555X */ + { 0x67ef, PCI_VENDOR_ID_APPLE, 0x018f, 0xc2 }, /* Radeon Pro 560X */ +}; + static bool vi_need_reset_on_init(struct amdgpu_device *adev) { + unsigned int i; u32 clock_cntl, pc; + for (i = 0; i < ARRAY_SIZE(vi_reset_quirks); i++) + if (adev->pdev->device == vi_reset_quirks[i].device && + adev->pdev->subsystem_vendor == vi_reset_quirks[i].subsystem_vendor && + adev->pdev->subsystem_device == vi_reset_quirks[i].subsystem_device && + adev->pdev->revision == vi_reset_quirks[i].revision) + return true; + if (adev->flags & AMD_IS_APU) return false; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 2c1a936459ac..309510e23315 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1783,6 +1783,9 @@ static int kfd_ptl_control(struct kfd_process_device *pdd, bool enable) uint32_t ptl_state = enable ? 1 : 0; int ret; + if (ptl->hw_supported_state != AMDGPU_PTL_HW_SUPPORTED) + return -EOPNOTSUPP; + if (!pdd->dev->kfd2kgd || !pdd->dev->kfd2kgd->ptl_ctrl) return -EOPNOTSUPP; @@ -1801,7 +1804,7 @@ int kfd_ptl_disable_request(struct kfd_process_device *pdd, struct amdgpu_ptl *ptl = &adev->psp.ptl; int ret = 0; - if (!ptl->hw_supported) + if (ptl->hw_supported_state != AMDGPU_PTL_HW_SUPPORTED) return -EOPNOTSUPP; mutex_lock(&ptl->mutex); @@ -1833,7 +1836,7 @@ int kfd_ptl_disable_release(struct kfd_process_device *pdd, struct amdgpu_ptl *ptl = &adev->psp.ptl; int ret = 0; - if (!ptl->hw_supported) + if (ptl->hw_supported_state != AMDGPU_PTL_HW_SUPPORTED) return -EOPNOTSUPP; mutex_lock(&ptl->mutex); @@ -3112,10 +3115,14 @@ static int kfd_ioctl_set_debug_trap(struct file *filep, struct kfd_process *p, v goto out; } - /* Check if target is still PTRACED. */ + /* + * Verify debugger has permission to debug target process. + * For cross-process debugging, require active ptrace relationship. + * This applies to ALL operations to prevent unauthorized interference. + */ rcu_read_lock(); - if (target != p && args->op != KFD_IOC_DBG_TRAP_DISABLE - && ptrace_parent(target->lead_thread) != current) { + if (target != p && ptrace_parent(target->lead_thread) != current + && target->debugger_process != p) { pr_err("PID %i is not PTRACED and cannot be debugged\n", args->pid); r = -EPERM; } @@ -3349,7 +3356,7 @@ static inline uint32_t profile_lock_device(struct kfd_process *p, kfd->profiler_process = p; status = 0; mutex_unlock(&kfd->profiler_lock); - if (ptl->hw_supported) { + if (ptl->hw_supported_state == AMDGPU_PTL_HW_SUPPORTED) { status = kfd_ptl_disable_request(pdd, p); if (status != 0) dev_err(kfd_device, @@ -3367,7 +3374,7 @@ static inline uint32_t profile_lock_device(struct kfd_process *p, status = 0; mutex_unlock(&kfd->profiler_lock); - if (ptl->hw_supported) { + if (ptl->hw_supported_state == AMDGPU_PTL_HW_SUPPORTED) { status = kfd_ptl_disable_release(pdd, p); if (status) dev_err(kfd_device, @@ -3715,10 +3722,10 @@ static int kfd_mmio_mmap(struct kfd_node *dev, struct kfd_process *process, vma->vm_page_prot); } - static int kfd_mmap(struct file *filep, struct vm_area_struct *vma) { struct kfd_process *process; + struct kfd_process_device *pdd; struct kfd_node *dev = NULL; unsigned long mmap_offset; unsigned int gpu_id; @@ -3732,8 +3739,10 @@ static int kfd_mmap(struct file *filep, struct vm_area_struct *vma) mmap_offset = vma->vm_pgoff << PAGE_SHIFT; gpu_id = KFD_MMAP_GET_GPU_ID(mmap_offset); - if (gpu_id) - dev = kfd_device_by_id(gpu_id); + + pdd = kfd_process_device_data_by_id(process, gpu_id); + if (pdd) + dev = pdd->dev; switch (mmap_offset & KFD_MMAP_TYPE_MASK) { case KFD_MMAP_TYPE_DOORBELL: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index 2a239f45fc24..6e0df685503d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -1412,6 +1412,15 @@ int kfd_parse_crat_table(void *crat_image, struct list_head *device_list, break; } + /* Validate subtype fits within remaining image */ + if ((char *)sub_type_hdr + sub_type_hdr->length > + (char *)crat_image + image_len) { + pr_warn("CRAT subtype length %u exceeds image bounds\n", + sub_type_hdr->length); + ret = -EINVAL; + break; + } + if (sub_type_hdr->flags & CRAT_SUBTYPE_FLAGS_ENABLED) { ret = kfd_parse_subtype(sub_type_hdr, device_list); if (ret) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 5446d89a84b3..51ee9c39104b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -448,6 +448,9 @@ int kfd_reset_queue_mes(struct device_queue_manager *dqm, int queue_type, static int reset_queues_mes(struct device_queue_manager *dqm, struct queue *q) { struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev; + struct drm_wedge_task_info *info = NULL; + struct amdgpu_task_info *ti = NULL; + struct kfd_process_device *pdd; unsigned int num_hung = 0; int r = 0; struct mes_remove_queue_input queue_input; @@ -476,13 +479,27 @@ static int reset_queues_mes(struct device_queue_manager *dqm, struct queue *q) r = amdgpu_gfx_reset_mes_compute(adev, NULL, NULL, NULL, &num_hung, &queue_input); if (r) goto fail; + pdd = kfd_get_process_device_data(q->device, q->process); + if (pdd) { + ti = amdgpu_vm_get_task_info_pasid(adev, pdd->pasid); + if (ti) { + amdgpu_vm_print_task_info(adev, ti); + info = &ti->task; + } + } dqm->detect_hang_count = num_hung; /* When MES doesn't detect any queue hang, no reset happens. Don't signal reset * event. */ - if (dqm->detect_hang_count) + if (dqm->detect_hang_count) { kfd_signal_reset_event(dqm->dev); + if (pdd && pdd->has_reset_queue) { + atomic_inc(&adev->gpu_reset_counter); + drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, info); + } + } + amdgpu_vm_put_task_info(ti); fail: dqm->detect_hang_count = 0; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index f5fd78f0df7d..6e32a892d56f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -452,6 +452,9 @@ int kfd_criu_restore_event(struct file *devkfd, ret = create_other_event(p, ev, &ev_priv->event_id); break; + default: + ret = -EINVAL; + break; } mutex_unlock(&p->event_mutex); @@ -473,15 +476,27 @@ int kfd_criu_checkpoint_events(struct kfd_process *p, int ret = 0; struct kfd_event *ev; uint32_t ev_id; + uint32_t num_events; - uint32_t num_events = kfd_get_num_events(p); + /* Serialize the count and the walk below against concurrent event + * create/destroy. Those paths take only p->event_mutex, not the + * p->mutex held by the CRIU checkpoint caller, so without this the + * event_idr can grow between kfd_get_num_events() and the loop and the + * walk writes past the ev_privs allocation. + */ + mutex_lock(&p->event_mutex); - if (!num_events) + num_events = kfd_get_num_events(p); + if (!num_events) { + mutex_unlock(&p->event_mutex); return 0; + } ev_privs = kvzalloc(num_events * sizeof(*ev_privs), GFP_KERNEL); - if (!ev_privs) + if (!ev_privs) { + mutex_unlock(&p->event_mutex); return -ENOMEM; + } idr_for_each_entry(&p->event_idr, ev, ev_id) { @@ -522,6 +537,8 @@ int kfd_criu_checkpoint_events(struct kfd_process *p, i++; } + mutex_unlock(&p->event_mutex); + ret = copy_to_user(user_priv_data + *priv_data_offset, ev_privs, num_events * sizeof(*ev_privs)); if (ret) { @@ -1147,6 +1164,8 @@ void kfd_signal_reset_event(struct kfd_node *dev) struct kfd_event *ev; unsigned int temp; uint32_t id, idx; + int user_gpu_id; + struct kfd_process_device *pdd; int reset_cause = atomic_read(&dev->sram_ecc_flag) ? KFD_HW_EXCEPTION_ECC : KFD_HW_EXCEPTION_GPU_HANG; @@ -1162,17 +1181,14 @@ void kfd_signal_reset_event(struct kfd_node *dev) idx = srcu_read_lock(&kfd_processes_srcu); hash_for_each_rcu(kfd_processes_table, temp, p, kfd_processes) { - int user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); - struct kfd_process_device *pdd = kfd_get_process_device_data(dev, p); - - if (unlikely(user_gpu_id == -EINVAL)) { - WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%x\n", dev->id); + pdd = kfd_get_process_device_data(dev, p); + if (!pdd) + /* no process is using this device */ continue; - } + user_gpu_id = kfd_process_get_user_gpu_id(p, dev->id); - if (unlikely(!pdd)) { - WARN_ONCE(1, "Could not get device data from process pid:%d\n", - p->lead_thread->pid); + if (unlikely(user_gpu_id == -EINVAL)) { + WARN_ONCE(1, "Could not get user_gpu_id from dev->id:%d\n", dev->id); continue; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 7cd236c1ff75..ed3649a81332 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -68,8 +68,8 @@ svm_migrate_gart_map(struct amdgpu_ring *ring, AMDGPU_FENCE_OWNER_UNDEFINED, num_dw * 4 + num_bytes, AMDGPU_IB_POOL_DELAYED, - &job, - AMDGPU_KERNEL_JOB_ID_KFD_GART_MAP); + AMDGPU_KERNEL_JOB_ID_KFD_GART_MAP, + &job); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c index b1a6eb349bb3..85bf1790a6c8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c @@ -269,9 +269,19 @@ build_runlist_ib: } pm->is_over_subscription = !!is_over_subscription; - for (i = 0; i < alloc_size_bytes / sizeof(uint32_t); i++) - pr_debug("0x%2X ", rl_buffer[i]); - pr_debug("\n"); + pr_debug("Runlist dump:"); + for (i = 0; i < alloc_size_bytes / sizeof(uint32_t); i += 8) { + char buf[128]; + int j, len = 0; + + /* Dump 8 entries per line with an index for each line */ + len += scnprintf(buf + len, sizeof(buf) - len, "%4u:", i); + + for (j = 0; j < 8 && (i + j) < alloc_size_bytes / sizeof(uint32_t); j++) + len += scnprintf(buf + len, sizeof(buf) - len, " 0x%08x", rl_buffer[i + j]); + + pr_debug("%s\n", buf); + } return retval; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c index 3d2375817c3e..54868b81086e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c @@ -309,6 +309,67 @@ static inline void pm_build_dequeue_wait_counts_packet_info(struct packet_manage reg_data); } +/* pm_grace_period_0_supported - whether firmware tolerates a CWSR grace + * period of 0 on this ASIC. + * + * The debugger may request a grace period of 0 via AMDKFD_IOC_DBG_TRAP. + * Most firmware revisions locked up on an infinite (0) grace period, so + * an earlier change clamped 0 to 1 for all ASICs. Firmware has since been + * fixed on most ASICs; return true only where the running MEC firmware is + * known to handle 0. The +32768 offset on IP_VERSION(9, 0, 1) accounts for + * the SR-IOV firmware version encoding. + * + * Navi3x (gfx11) and MI350 (IP_VERSION(9, 5, 0)) support a grace period of + * 0 in every firmware revision and need no version check. MI100 + * (IP_VERSION(9, 4, 1)) never received the fix and is intentionally kept + * clamped. Any unlisted or future ASIC defaults to the safe behaviour + * (clamp). + */ +static bool pm_grace_period_0_supported(struct packet_manager *pm) +{ + struct kfd_node *dev = pm->dqm->dev; + uint32_t mec_fw_version = dev->kfd->mec_fw_version; + + /* Navi3x (gfx11) always supports a grace period of 0. */ + if (KFD_GC_VERSION(dev) >= IP_VERSION(11, 0, 0) && + KFD_GC_VERSION(dev) < IP_VERSION(12, 0, 0)) + return true; + + switch (KFD_GC_VERSION(dev)) { + case IP_VERSION(9, 0, 1): + return mec_fw_version >= 461 + 32768; + case IP_VERSION(9, 1, 0): + case IP_VERSION(9, 2, 1): + case IP_VERSION(9, 2, 2): + case IP_VERSION(9, 3, 0): + case IP_VERSION(9, 4, 0): + return mec_fw_version >= 461; + /* MI100/Arcturus never received the firmware fix; keep clamped. */ + case IP_VERSION(9, 4, 1): + return false; + case IP_VERSION(9, 4, 2): + return mec_fw_version >= 63; + case IP_VERSION(9, 4, 3): + case IP_VERSION(9, 4, 4): + return mec_fw_version >= 96; + /* MI350 supports a grace period of 0 in every firmware revision. */ + case IP_VERSION(9, 5, 0): + return true; + case IP_VERSION(10, 1, 10): + case IP_VERSION(10, 1, 2): + case IP_VERSION(10, 1, 1): + return mec_fw_version >= 146; + case IP_VERSION(10, 3, 0): + case IP_VERSION(10, 3, 2): + case IP_VERSION(10, 3, 1): + case IP_VERSION(10, 3, 4): + case IP_VERSION(10, 3, 5): + return mec_fw_version >= 93; + default: + return false; + } +} + /* pm_config_dequeue_wait_counts_v9: Builds WRITE_DATA packet with * register/value for configuring dequeue wait counts * @@ -357,11 +418,12 @@ static int pm_config_dequeue_wait_counts_v9(struct packet_manager *pm, break; case KFD_DEQUEUE_WAIT_SET_SCH_WAVE: - /* The CP cannot handle value 0 and it will result in - * an infinite grace period being set so set to 1 to prevent this. Also - * avoid debugger API breakage as it sets 0 and expects a low value. + /* A grace period of 0 requests an infinite CWSR grace period. + * Older firmware locks up on this, so clamp to 1 unless the ASIC + * firmware is known to handle 0. Also avoid debugger API breakage + * as it sets 0 and expects a low value. */ - if (!value) + if (!value && !pm_grace_period_0_supported(pm)) value = 1; pm_build_dequeue_wait_counts_packet_info(pm, value, 0, ®_offset, ®_data); break; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 97e77a5a35e5..8054e4fe0381 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -383,7 +383,7 @@ int pqm_create_queue(struct process_queue_manager *pqm, false); if (retval) { dev_err(dev->adev->dev, "failed to allocate process context bo\n"); - return retval; + goto err_allocate_pqn; } memset(pdd->proc_ctx_cpu_ptr, 0, AMDGPU_MES_PROC_CTX_SIZE); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index 98a5512b701b..b249e7d1af48 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -288,7 +288,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope } err = kfd_queue_buffer_get(vm, (void *)properties->eop_ring_buffer_address, &properties->eop_buf_bo, - ALIGN(properties->eop_ring_buffer_size, PAGE_SIZE)); + ALIGN((u64)properties->eop_ring_buffer_size, PAGE_SIZE)); if (err) goto out_err_unreserve; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 30ad10bbd47e..4c6700c6e88d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -69,7 +69,6 @@ struct criu_svm_metadata { struct kfd_criu_svm_range_priv_data data; }; -static void svm_range_evict_svm_bo_worker(struct work_struct *work); static bool svm_range_cpu_invalidate_pagetables(struct mmu_interval_notifier *mni, const struct mmu_notifier_range *range, @@ -97,7 +96,7 @@ static void svm_range_unlink(struct svm_range *prange) if (prange->svm_bo) { spin_lock(&prange->svm_bo->list_lock); - list_del(&prange->svm_bo_list); + list_del_init(&prange->svm_bo_list); spin_unlock(&prange->svm_bo->list_lock); } @@ -286,6 +285,17 @@ static void svm_range_free(struct svm_range *prange, bool do_unmap) pr_debug("svms 0x%p prange 0x%p [0x%lx 0x%lx]\n", prange->svms, prange, prange->start, prange->last); + /* Unlink from range_list; no-op if already unlinked. */ + if (prange->svm_bo) { + spin_lock(&prange->svm_bo->list_lock); + list_del_init(&prange->svm_bo_list); + spin_unlock(&prange->svm_bo->list_lock); + } + + /* Wait for any in-flight eviction of this range to finish. */ + mutex_lock(&prange->migrate_mutex); + mutex_unlock(&prange->migrate_mutex); + svm_range_vram_node_free(prange); if (do_unmap) svm_range_dma_unmap(prange); @@ -588,7 +598,6 @@ svm_range_vram_node_new(struct kfd_node *node, struct svm_range *prange, mm, svm_bo, p->context_id); mmput(mm); - INIT_WORK(&svm_bo->eviction_work, svm_range_evict_svm_bo_worker); svm_bo->evicting = 0; memset(&bp, 0, sizeof(bp)); bp.size = prange->npages * PAGE_SIZE; @@ -3631,39 +3640,36 @@ svm_range_trigger_migration(struct mm_struct *mm, struct svm_range *prange, return 0; } -int svm_range_schedule_evict_svm_bo(struct amdgpu_amdkfd_fence *fence) -{ - /* Dereferencing fence->svm_bo is safe here because the fence hasn't - * signaled yet and we're under the protection of the fence->lock. - * After the fence is signaled in svm_range_bo_release, we cannot get - * here any more. - * - * Reference is dropped in svm_range_evict_svm_bo_worker. - */ - if (svm_bo_ref_unless_zero(fence->svm_bo)) { - WRITE_ONCE(fence->svm_bo->evicting, 1); - schedule_work(&fence->svm_bo->eviction_work); - } - - return 0; -} - -static void svm_range_evict_svm_bo_worker(struct work_struct *work) +int svm_range_evict_svm_bo(struct svm_range_bo *svm_bo) { - struct svm_range_bo *svm_bo; struct mm_struct *mm; int r = 0; - svm_bo = container_of(work, struct svm_range_bo, eviction_work); + if (!svm_bo_ref_unless_zero(svm_bo)) + return 0; - if (mmget_not_zero(svm_bo->eviction_fence->mm)) { - mm = svm_bo->eviction_fence->mm; - } else { + if (!mmget_not_zero(svm_bo->eviction_fence->mm)) { svm_range_bo_unref(svm_bo); - return; + return 0; } + mm = svm_bo->eviction_fence->mm; + + /* + * Called with the BO reserved; lock order is mmap_lock -> BO + * reservation. Only trylock mmap to invert that order safely: a + * trylock never blocks, so it cannot deadlock against the reservation + * and lockdep records no reverse dependency. On contention return + * -EBUSY so TTM skips this BO. + */ + if (!mmap_read_trylock(mm)) { + pr_debug("skip eviction, contended to take mmap_read lock\n"); + mmput_async(mm); + svm_range_bo_unref(svm_bo); + return -EBUSY; + } + + WRITE_ONCE(svm_bo->evicting, 1); - mmap_read_lock(mm); spin_lock(&svm_bo->list_lock); while (!list_empty(&svm_bo->range_list) && !r) { struct svm_range *prange = @@ -3671,13 +3677,24 @@ static void svm_range_evict_svm_bo_worker(struct work_struct *work) struct svm_range, svm_bo_list); int retries = 3; + /* + * Trylock migrate_mutex under list_lock, before unlinking the + * range, so svm_range_free() cannot free it under us. On + * contention the owner is migrating this range; skip the BO. + */ + if (!mutex_trylock(&prange->migrate_mutex)) { + pr_debug("skip eviction, contended migrate_mutex\n"); + /* Clear evicting so the BO keeps being reused. */ + WRITE_ONCE(svm_bo->evicting, 0); + r = -EBUSY; + break; + } list_del_init(&prange->svm_bo_list); spin_unlock(&svm_bo->list_lock); pr_debug("svms 0x%p [0x%lx 0x%lx]\n", prange->svms, prange->start, prange->last); - mutex_lock(&prange->migrate_mutex); do { /* migrate all vram pages in this prange to sys ram * after that prange->actual_loc should be zero @@ -3701,15 +3718,24 @@ static void svm_range_evict_svm_bo_worker(struct work_struct *work) } spin_unlock(&svm_bo->list_lock); mmap_read_unlock(mm); - mmput(mm); + /* Defer mmput: exit_mmap() must not run under the BO reservation. */ + mmput_async(mm); - dma_fence_signal(&svm_bo->eviction_fence->base); + /* + * Only signal the eviction fence once the ranges have been processed. + * On -EBUSY we bailed out without migrating; leave the BO in VRAM and + * let TTM retry later. + */ + if (r != -EBUSY) + dma_fence_signal(&svm_bo->eviction_fence->base); /* This is the last reference to svm_bo, after svm_range_vram_node_free * has been called in svm_migrate_vram_to_ram */ WARN_ONCE(!r && kref_read(&svm_bo->kref) != 1, "This was not the last reference\n"); svm_range_bo_unref(svm_bo); + + return r; } static int diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h index a63dfc95b602..4232c47422e6 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h @@ -44,7 +44,6 @@ struct svm_range_bo { struct list_head range_list; /* all svm ranges shared this bo */ spinlock_t list_lock; struct amdgpu_amdkfd_fence *eviction_fence; - struct work_struct eviction_work; uint32_t evicting; struct work_struct release_work; struct kfd_node *node; @@ -175,7 +174,8 @@ void svm_range_vram_node_free(struct svm_range *prange); int svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid, uint32_t vmid, uint32_t node_id, uint64_t addr, uint64_t ts, bool write_fault); -int svm_range_schedule_evict_svm_bo(struct amdgpu_amdkfd_fence *fence); +int svm_range_evict_svm_bo(struct svm_range_bo *svm_bo); + void svm_range_add_list_work(struct svm_range_list *svms, struct svm_range *prange, struct mm_struct *mm, enum svm_work_list_ops op); @@ -229,11 +229,9 @@ static inline int svm_range_restore_pages(struct amdgpu_device *adev, return -EFAULT; } -static inline int svm_range_schedule_evict_svm_bo( - struct amdgpu_amdkfd_fence *fence) +static inline int svm_range_evict_svm_bo(struct svm_range_bo *svm_bo) { - WARN_ONCE(1, "SVM eviction fence triggered, but SVM is disabled"); - return -EINVAL; + return 0; } static inline void svm_range_get_info(struct kfd_process *p, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile index d83878e35b61..d1faf2d8370f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile @@ -45,7 +45,9 @@ AMDGPUDM = \ amdgpu_dm_backlight.o \ amdgpu_dm_audio.o \ amdgpu_dm_dmub.o \ - amdgpu_dm_connector.o + amdgpu_dm_connector.o \ + amdgpu_dm_freesync.o \ + amdgpu_dm_cursor.o ifdef CONFIG_DRM_AMD_DC_FP AMDGPUDM += dc_fpu.o diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index e7080880b221..f22388e57e98 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -70,7 +70,9 @@ #include "amdgpu_dm_audio.h" #include "amdgpu_dm_dmub.h" #include "amdgpu_dm_connector.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "amdgpu_dm_pp_smu.h" +#include "amdgpu_dm_freesync.h" +#include "amdgpu_dm_cursor.h" #include "ivsrcid/ivsrcid_vislands30.h" @@ -127,7 +129,6 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU); /* basic init/fini API */ static int amdgpu_dm_init(struct amdgpu_device *adev); static void amdgpu_dm_fini(struct amdgpu_device *adev); -static void reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state); /* * initializes drm_device display related structures, based on the information @@ -142,15 +143,11 @@ static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm); static int amdgpu_dm_atomic_setup_commit(struct drm_atomic_commit *state); static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state); -static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context); +STATIC_IFN_KUNIT void dm_enable_per_frame_crtc_master_sync(struct dc_state *context); static int amdgpu_dm_atomic_check(struct drm_device *dev, struct drm_atomic_commit *state); -STATIC_IFN_KUNIT bool -is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, - struct drm_crtc_state *new_crtc_state); - static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc) { if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter) @@ -170,7 +167,7 @@ static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc) * @return * Counter for vertical blanks */ -static u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc) +STATIC_IFN_KUNIT u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc) { struct amdgpu_crtc *acrtc = NULL; @@ -187,9 +184,10 @@ static u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc) return dc_stream_get_vblank_counter(acrtc->dm_irq_params.stream); } +EXPORT_IF_KUNIT(dm_vblank_get_counter); -static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc, - u32 *vbl, u32 *position) +STATIC_IFN_KUNIT int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc, + u32 *vbl, u32 *position) { u32 v_blank_start = 0, v_blank_end = 0, h_position = 0, v_position = 0; struct amdgpu_crtc *acrtc = NULL; @@ -224,38 +222,28 @@ static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc, return 0; } +EXPORT_IF_KUNIT(dm_crtc_get_scanoutpos); -static bool dm_is_idle(struct amdgpu_ip_block *ip_block) +STATIC_IFN_KUNIT bool dm_is_idle(struct amdgpu_ip_block *ip_block) { /* XXX todo */ return true; } +EXPORT_IF_KUNIT(dm_is_idle); -static int dm_wait_for_idle(struct amdgpu_ip_block *ip_block) +STATIC_IFN_KUNIT int dm_wait_for_idle(struct amdgpu_ip_block *ip_block) { /* XXX todo */ return 0; } +EXPORT_IF_KUNIT(dm_wait_for_idle); -static int dm_soft_reset(struct amdgpu_ip_block *ip_block) +STATIC_IFN_KUNIT int dm_soft_reset(struct amdgpu_ip_block *ip_block) { /* XXX todo */ return 0; } - -STATIC_IFN_KUNIT bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, - struct dm_crtc_state *new_state) -{ - if (new_state->stream->adjust.timing_adjust_pending) - return true; - if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED) - return true; - else if (amdgpu_dm_crtc_vrr_active(old_state) != amdgpu_dm_crtc_vrr_active(new_state)) - return true; - else - return false; -} -EXPORT_IF_KUNIT(is_dc_timing_adjust_needed); +EXPORT_IF_KUNIT(dm_soft_reset); /* * DC will program planes with their z-order determined by their ordering @@ -311,17 +299,19 @@ static inline bool update_planes_and_stream_adapter(struct dc *dc, stream_update); } -static int dm_set_clockgating_state(struct amdgpu_ip_block *ip_block, - enum amd_clockgating_state state) +STATIC_IFN_KUNIT int dm_set_clockgating_state(struct amdgpu_ip_block *ip_block, + enum amd_clockgating_state state) { return 0; } +EXPORT_IF_KUNIT(dm_set_clockgating_state); -static int dm_set_powergating_state(struct amdgpu_ip_block *ip_block, - enum amd_powergating_state state) +STATIC_IFN_KUNIT int dm_set_powergating_state(struct amdgpu_ip_block *ip_block, + enum amd_powergating_state state) { return 0; } +EXPORT_IF_KUNIT(dm_set_powergating_state); /* Prototypes of private functions */ static int dm_early_init(struct amdgpu_ip_block *ip_block); @@ -404,91 +394,6 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_ } -struct amdgpu_stutter_quirk { - u16 chip_vendor; - u16 chip_device; - u16 subsys_vendor; - u16 subsys_device; - u8 revision; -}; - -static const struct amdgpu_stutter_quirk amdgpu_stutter_quirk_list[] = { - /* https://bugzilla.kernel.org/show_bug.cgi?id=214417 */ - { 0x1002, 0x15dd, 0x1002, 0x15dd, 0xc8 }, - { 0, 0, 0, 0, 0 }, -}; - -static bool dm_should_disable_stutter(struct pci_dev *pdev) -{ - const struct amdgpu_stutter_quirk *p = amdgpu_stutter_quirk_list; - - while (p && p->chip_device != 0) { - if (pdev->vendor == p->chip_vendor && - pdev->device == p->chip_device && - pdev->subsystem_vendor == p->subsys_vendor && - pdev->subsystem_device == p->subsys_device && - pdev->revision == p->revision) { - return true; - } - ++p; - } - return false; -} - - -void* -dm_allocate_gpu_mem( - struct amdgpu_device *adev, - enum dc_gpu_mem_alloc_type type, - size_t size, - long long *addr) -{ - struct dal_allocation *da; - u32 domain = (type == DC_MEM_ALLOC_TYPE_GART) ? - AMDGPU_GEM_DOMAIN_GTT : AMDGPU_GEM_DOMAIN_VRAM; - int ret; - - da = kzalloc_obj(struct dal_allocation); - if (!da) - return NULL; - - ret = amdgpu_bo_create_kernel(adev, size, PAGE_SIZE, - domain, &da->bo, - &da->gpu_addr, &da->cpu_ptr); - - *addr = da->gpu_addr; - - if (ret) { - kfree(da); - return NULL; - } - - /* add da to list in dm */ - list_add(&da->list, &adev->dm.da_list); - - return da->cpu_ptr; -} - -void -dm_free_gpu_mem( - struct amdgpu_device *adev, - enum dc_gpu_mem_alloc_type type, - void *pvMem) -{ - struct dal_allocation *da; - - /* walk the da list in DM */ - list_for_each_entry(da, &adev->dm.da_list, list) { - if (pvMem == da->cpu_ptr) { - amdgpu_bo_free_kernel(&da->bo, &da->gpu_addr, &da->cpu_ptr); - list_del(&da->list); - kfree(da); - break; - } - } - -} - static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm) { struct mod_power_init_params init_data[MAX_NUM_EDP]; @@ -1398,57 +1303,6 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend) drm_connector_list_iter_end(&iter); } -static int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev) -{ - int ret = 0; - - /* This interface is for dGPU Navi1x.Linux dc-pplib interface depends - * on window driver dc implementation. - * For Navi1x, clock settings of dcn watermarks are fixed. the settings - * should be passed to smu during boot up and resume from s3. - * boot up: dc calculate dcn watermark clock settings within dc_create, - * dcn20_resource_construct - * then call pplib functions below to pass the settings to smu: - * smu_set_watermarks_for_clock_ranges - * smu_set_watermarks_table - * navi10_set_watermarks_table - * smu_write_watermarks_table - * - * For Renoir, clock settings of dcn watermark are also fixed values. - * dc has implemented different flow for window driver: - * dc_hardware_init / dc_set_power_state - * dcn10_init_hw - * notify_wm_ranges - * set_wm_ranges - * -- Linux - * smu_set_watermarks_for_clock_ranges - * renoir_set_watermarks_table - * smu_write_watermarks_table - * - * For Linux, - * dc_hardware_init -> amdgpu_dm_init - * dc_set_power_state --> dm_resume - * - * therefore, this function apply to navi10/12/14 but not Renoir - * * - */ - switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { - case IP_VERSION(2, 0, 2): - case IP_VERSION(2, 0, 0): - break; - default: - return 0; - } - - ret = amdgpu_dpm_write_watermarks_table(adev); - if (ret) { - drm_err(adev_to_drm(adev), "Failed to update WMTABLE!\n"); - return ret; - } - - return 0; -} - static int dm_oem_i2c_hw_init(struct amdgpu_device *adev) { struct amdgpu_display_manager *dm = &adev->dm; @@ -1671,7 +1525,7 @@ static void dm_destroy_cached_state(struct amdgpu_device *adev) for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) { new_crtc_state->active_changed = true; dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); - reset_freesync_config_for_crtc(dm_new_crtc_state); + amdgpu_dm_reset_freesync_config_for_crtc(dm_new_crtc_state); } /* @@ -1910,6 +1764,7 @@ void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev, ppatch->wait_after_dpcd_poweroff_ms / 1000); } } +EXPORT_IF_KUNIT(amdgpu_dm_apply_delay_after_dpcd_poweroff); /** * amdgpu_dm_dump_links_and_sinks - Debug dump of all DC links and their sinks @@ -2278,7 +2133,7 @@ int dm_atomic_get_state(struct drm_atomic_commit *state, return 0; } -static struct dm_atomic_state * +STATIC_IFN_KUNIT struct dm_atomic_state * dm_atomic_get_new_state(struct drm_atomic_commit *state) { struct drm_device *dev = state->dev; @@ -2295,6 +2150,7 @@ dm_atomic_get_new_state(struct drm_atomic_commit *state) return NULL; } +EXPORT_IF_KUNIT(dm_atomic_get_new_state); static struct drm_private_state * dm_atomic_duplicate_state(struct drm_private_obj *obj) @@ -2320,8 +2176,8 @@ dm_atomic_duplicate_state(struct drm_private_obj *obj) return &new_state->base; } -static void dm_atomic_destroy_state(struct drm_private_obj *obj, - struct drm_private_state *state) +STATIC_IFN_KUNIT void dm_atomic_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state) { struct dm_atomic_state *dm_state = to_dm_atomic_state(state); @@ -2330,6 +2186,7 @@ static void dm_atomic_destroy_state(struct drm_private_obj *obj, kfree(dm_state); } +EXPORT_IF_KUNIT(dm_atomic_destroy_state); static struct drm_private_state * dm_atomic_create_state(struct drm_private_obj *obj) @@ -2798,10 +2655,11 @@ static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm) * * Calculate and program the display watermarks and line buffer allocation. */ -static void dm_bandwidth_update(struct amdgpu_device *adev) +STATIC_IFN_KUNIT void dm_bandwidth_update(struct amdgpu_device *adev) { /* TODO: implement later */ } +EXPORT_IF_KUNIT(dm_bandwidth_update); static const struct amdgpu_display_funcs dm_display_funcs = { .bandwidth_update = dm_bandwidth_update, /* called unconditionally */ @@ -3059,7 +2917,6 @@ EXPORT_IF_KUNIT(fill_plane_color_attributes); static int fill_dc_plane_info_and_addr(struct amdgpu_device *adev, const struct drm_plane_state *plane_state, - const u64 tiling_flags, struct dc_plane_info *plane_info, struct dc_plane_address *address, bool tmz_surface) @@ -3157,7 +3014,7 @@ fill_dc_plane_info_and_addr(struct amdgpu_device *adev, return ret; ret = amdgpu_dm_plane_fill_plane_buffer_attributes(adev, afb, plane_info->format, - plane_info->rotation, tiling_flags, + plane_info->rotation, &plane_info->tiling_info, &plane_info->plane_size, &plane_info->dcc, address, @@ -3193,7 +3050,6 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev, dc_plane_state->scaling_quality = scaling_info.scaling_quality; ret = fill_dc_plane_info_and_addr(adev, plane_state, - afb->tiling_flags, &plane_info, &dc_plane_state->address, afb->tmz_surface); @@ -3393,62 +3249,6 @@ ffu: &flip_addrs->dirty_rect_count, true); } -void amdgpu_dm_update_stream_scaling_settings(struct drm_device *dev, - const struct drm_display_mode *mode, - const struct dm_connector_state *dm_state, - struct dc_stream_state *stream) -{ - enum amdgpu_rmx_type rmx_type; - - struct rect src = { 0 }; /* viewport in composition space*/ - struct rect dst = { 0 }; /* stream addressable area */ - - /* no mode. nothing to be done */ - if (!mode) - return; - - /* Full screen scaling by default */ - src.width = mode->hdisplay; - src.height = mode->vdisplay; - dst.width = stream->timing.h_addressable; - dst.height = stream->timing.v_addressable; - - if (dm_state) { - rmx_type = dm_state->scaling; - if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) { - if (src.width * dst.height < - src.height * dst.width) { - /* height needs less upscaling/more downscaling */ - dst.width = src.width * - dst.height / src.height; - } else { - /* width needs less upscaling/more downscaling */ - dst.height = src.height * - dst.width / src.width; - } - } else if (rmx_type == RMX_CENTER) { - dst = src; - } - - dst.x = (stream->timing.h_addressable - dst.width) / 2; - dst.y = (stream->timing.v_addressable - dst.height) / 2; - - if (dm_state->underscan_enable) { - dst.x += dm_state->underscan_hborder / 2; - dst.y += dm_state->underscan_vborder / 2; - dst.width -= dm_state->underscan_hborder; - dst.height -= dm_state->underscan_vborder; - } - } - - stream->src = src; - stream->dst = dst; - - drm_dbg_kms(dev, "Destination Rectangle x:%d y:%d width:%d height:%d\n", - dst.x, dst.y, dst.width, dst.height); - -} - static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_commit *state, struct dc_state *dc_state, struct dsc_mst_fairness_vars *vars) @@ -3644,12 +3444,13 @@ is_scaling_state_different(const struct dm_connector_state *dm_state, } EXPORT_IF_KUNIT(is_scaling_state_different); -static bool is_content_protection_different(struct drm_crtc_state *new_crtc_state, - struct drm_crtc_state *old_crtc_state, - struct drm_connector_state *new_conn_state, - struct drm_connector_state *old_conn_state, - const struct drm_connector *connector, - struct hdcp_workqueue *hdcp_w) +STATIC_IFN_KUNIT bool +is_content_protection_different(struct drm_crtc_state *new_crtc_state, + struct drm_crtc_state *old_crtc_state, + struct drm_connector_state *new_conn_state, + struct drm_connector_state *old_conn_state, + const struct drm_connector *connector, + struct hdcp_workqueue *hdcp_w) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state); @@ -3763,6 +3564,7 @@ static bool is_content_protection_different(struct drm_crtc_state *new_crtc_stat pr_debug("[HDCP_DM] DESIRED->ENABLED %s :false\n", __func__); return false; } +EXPORT_IF_KUNIT(is_content_protection_different); static void remove_stream(struct amdgpu_device *adev, struct amdgpu_crtc *acrtc, @@ -3793,213 +3595,6 @@ static void prepare_flip_isr(struct amdgpu_crtc *acrtc) acrtc->crtc_id); } -static void update_freesync_state_on_stream( - struct amdgpu_display_manager *dm, - struct dm_crtc_state *new_crtc_state, - struct dc_stream_state *new_stream, - struct dc_plane_state *surface, - u32 flip_timestamp_in_us) -{ - struct mod_vrr_params vrr_params; - struct dc_info_packet vrr_infopacket = {0}; - struct amdgpu_device *adev = dm->adev; - struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc); - unsigned long flags; - bool pack_sdp_v1_3 = false; - struct amdgpu_dm_connector *aconn; - enum vrr_packet_type packet_type = PACKET_TYPE_VRR; - - if (!new_stream) - return; - - /* - * TODO: Determine why min/max totals and vrefresh can be 0 here. - * For now it's sufficient to just guard against these conditions. - */ - - if (!new_stream->timing.h_total || !new_stream->timing.v_total) - return; - - spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); - vrr_params = acrtc->dm_irq_params.vrr_params; - - if (surface) { - mod_freesync_handle_preflip( - dm->freesync_module, - surface, - new_stream, - flip_timestamp_in_us, - &vrr_params); - - if (adev->family < AMDGPU_FAMILY_AI && - amdgpu_dm_crtc_vrr_active(new_crtc_state)) { - mod_freesync_handle_v_update(dm->freesync_module, - new_stream, &vrr_params); - - /* Need to call this before the frame ends. */ - dc_stream_adjust_vmin_vmax(dm->dc, - new_crtc_state->stream, - &vrr_params.adjust); - } - } - - aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context; - - if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) { - pack_sdp_v1_3 = aconn->pack_sdp_v1_3; - - if (aconn->vsdb_info.amd_vsdb_version == 1) - packet_type = PACKET_TYPE_FS_V1; - else if (aconn->vsdb_info.amd_vsdb_version == 2) - packet_type = PACKET_TYPE_FS_V2; - else if (aconn->vsdb_info.amd_vsdb_version == 3) - packet_type = PACKET_TYPE_FS_V3; - - mod_build_adaptive_sync_infopacket(new_stream, aconn->as_type, NULL, - &new_stream->adaptive_sync_infopacket); - } - - mod_freesync_build_vrr_infopacket( - dm->freesync_module, - new_stream, - &vrr_params, - packet_type, - TRANSFER_FUNC_UNKNOWN, - &vrr_infopacket, - pack_sdp_v1_3); - - new_crtc_state->freesync_vrr_info_changed |= - (memcmp(&new_crtc_state->vrr_infopacket, - &vrr_infopacket, - sizeof(vrr_infopacket)) != 0); - - acrtc->dm_irq_params.vrr_params = vrr_params; - new_crtc_state->vrr_infopacket = vrr_infopacket; - - new_stream->vrr_infopacket = vrr_infopacket; - new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params); - - if (new_crtc_state->freesync_vrr_info_changed) - drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d", - new_crtc_state->base.crtc->base.id, - (int)new_crtc_state->base.vrr_enabled, - (int)vrr_params.state); - - spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); -} - -static void update_stream_irq_parameters( - struct amdgpu_display_manager *dm, - struct dm_crtc_state *new_crtc_state) -{ - struct dc_stream_state *new_stream = new_crtc_state->stream; - struct mod_vrr_params vrr_params; - struct mod_freesync_config config = new_crtc_state->freesync_config; - struct amdgpu_device *adev = dm->adev; - struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc); - unsigned long flags; - - if (!new_stream) - return; - - /* - * TODO: Determine why min/max totals and vrefresh can be 0 here. - * For now it's sufficient to just guard against these conditions. - */ - if (!new_stream->timing.h_total || !new_stream->timing.v_total) - return; - - spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); - vrr_params = acrtc->dm_irq_params.vrr_params; - - if (new_crtc_state->vrr_supported && - config.min_refresh_in_uhz && - config.max_refresh_in_uhz) { - /* - * if freesync compatible mode was set, config.state will be set - * in atomic check - */ - if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz && - (!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) || - new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) { - vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz; - vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz; - vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz; - vrr_params.state = VRR_STATE_ACTIVE_FIXED; - } else { - config.state = new_crtc_state->base.vrr_enabled ? - VRR_STATE_ACTIVE_VARIABLE : - VRR_STATE_INACTIVE; - } - } else { - config.state = VRR_STATE_UNSUPPORTED; - } - - mod_freesync_build_vrr_params(dm->freesync_module, - new_stream, - &config, &vrr_params); - - new_crtc_state->freesync_config = config; - /* Copy state for access from DM IRQ handler */ - acrtc->dm_irq_params.freesync_config = config; - acrtc->dm_irq_params.active_planes = new_crtc_state->active_planes; - acrtc->dm_irq_params.vrr_params = vrr_params; - spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); -} - -static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm, - struct dm_crtc_state *old_state, - struct dm_crtc_state *new_state) -{ - struct amdgpu_device *adev = dm->adev; - bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state); - bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state); - - /* Only DCE gates vupdate on VRR, keep it enabled for DCN */ - bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0; - - if (!old_vrr_active && new_vrr_active) { - /* Transition VRR inactive -> active: - * While VRR is active, we must not disable vblank irq, as a - * reenable after disable would compute bogus vblank/pflip - * timestamps if it likely happened inside display front-porch. - * - * We also need vupdate irq for the actual core vblank handling - * at end of vblank. - */ - if (vrr_gates_vupdate) - WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0); - WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0); - drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n", - __func__, new_state->base.crtc->base.id); - - scoped_guard(mutex, &dm->dc_lock) { - dc_exit_ips_for_hw_access(dm->dc); - amdgpu_dm_psr_set_event(dm, new_state->stream, true, - psr_event_vrr_transition, true); - amdgpu_dm_replay_set_event(dm, new_state->stream, true, - replay_event_vrr, true); - } - } else if (old_vrr_active && !new_vrr_active) { - /* Transition VRR active -> inactive: - * Allow vblank irq disable again for fixed refresh rate. - */ - if (vrr_gates_vupdate) - WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0); - drm_crtc_vblank_put(new_state->base.crtc); - drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n", - __func__, new_state->base.crtc->base.id); - - scoped_guard(mutex, &dm->dc_lock) { - dc_exit_ips_for_hw_access(dm->dc); - amdgpu_dm_psr_set_event(dm, new_state->stream, false, - psr_event_vrr_transition, false); - amdgpu_dm_replay_set_event(dm, new_state->stream, false, - replay_event_vrr, false); - } - } -} - static void amdgpu_dm_commit_cursors(struct drm_atomic_commit *state) { struct drm_plane *plane; @@ -4281,7 +3876,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, fill_dc_plane_info_and_addr( dm->adev, new_plane_state, - afb->tiling_flags, &bundle->plane_infos[planes_count], &bundle->flip_addrs[planes_count].address, afb->tmz_surface); @@ -4356,7 +3950,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, } if (plane == pcrtc->primary) - update_freesync_state_on_stream( + amdgpu_dm_update_freesync_state_on_stream( dm, acrtc_state, acrtc_state->stream, @@ -4474,7 +4068,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, * re-adjust the min/max bounds now that DC doesn't handle this * as part of commit. */ - if (is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) { + if (amdgpu_dm_is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) { spin_lock_irqsave(&pcrtc->dev->event_lock, flags); dc_stream_adjust_vmin_vmax( dm->dc, acrtc_state->stream, @@ -4673,6 +4267,7 @@ bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc) spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags); pending = acrtc->wb_pending; acrtc->wb_pending = false; + acrtc->wb_frame_done = false; spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags); if (!pending) @@ -4683,6 +4278,7 @@ bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc) return true; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_complete_writeback); static void dm_clear_writeback(struct amdgpu_display_manager *dm, struct amdgpu_crtc *acrtc, @@ -5140,6 +4736,7 @@ static void dm_set_writeback(struct amdgpu_display_manager *dm, * cannot run its matching vblank_put before this get. */ WARN_ON(drm_crtc_vblank_get(&acrtc->base)); + acrtc->wb_frame_done = false; acrtc->wb_pending = true; } @@ -5330,7 +4927,7 @@ STATIC_IFN_KUNIT void set_master_stream(struct dc_stream_state *stream_set[], } EXPORT_IF_KUNIT(set_master_stream); -static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context) +STATIC_IFN_KUNIT void dm_enable_per_frame_crtc_master_sync(struct dc_state *context) { int i = 0; struct dc_stream_state *stream; @@ -5358,6 +4955,7 @@ static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context) set_multisync_trigger_params(stream); } } +EXPORT_IF_KUNIT(dm_enable_per_frame_crtc_master_sync); /** * amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation. @@ -5521,7 +5119,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state) dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); /* For freesync config update on crtc state and params for irq */ - update_stream_irq_parameters(dm, dm_new_crtc_state); + amdgpu_dm_update_stream_irq_parameters(dm, dm_new_crtc_state); #ifdef CONFIG_DEBUG_FS spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); @@ -5721,106 +5319,6 @@ static int do_aquire_global_lock(struct drm_device *dev, return ret < 0 ? ret : 0; } -static void get_freesync_config_for_crtc( - struct dm_crtc_state *new_crtc_state, - struct dm_connector_state *new_con_state) -{ - struct mod_freesync_config config = {0}; - struct amdgpu_dm_connector *aconnector; - struct drm_display_mode *mode = &new_crtc_state->base.mode; - int vrefresh = drm_mode_vrefresh(mode); - bool fs_vid_mode = false; - - if (new_con_state->base.connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - return; - - aconnector = to_amdgpu_dm_connector(new_con_state->base.connector); - - new_crtc_state->vrr_supported = new_con_state->freesync_capable && - vrefresh >= aconnector->min_vfreq && - vrefresh <= aconnector->max_vfreq; - - if (new_crtc_state->vrr_supported) { - new_crtc_state->stream->ignore_msa_timing_param = true; - fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED; - - config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000; - config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000; - config.vsif_supported = true; - config.btr = true; - - if (fs_vid_mode) { - config.state = VRR_STATE_ACTIVE_FIXED; - config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz; - goto out; - } else if (new_crtc_state->base.vrr_enabled) { - config.state = VRR_STATE_ACTIVE_VARIABLE; - } else { - config.state = VRR_STATE_INACTIVE; - } - } else { - config.state = VRR_STATE_UNSUPPORTED; - } -out: - new_crtc_state->freesync_config = config; -} - -static void reset_freesync_config_for_crtc( - struct dm_crtc_state *new_crtc_state) -{ - new_crtc_state->vrr_supported = false; - - memset(&new_crtc_state->vrr_infopacket, 0, - sizeof(new_crtc_state->vrr_infopacket)); -} - -STATIC_IFN_KUNIT bool -is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, - struct drm_crtc_state *new_crtc_state) -{ - const struct drm_display_mode *old_mode, *new_mode; - - if (!old_crtc_state || !new_crtc_state) - return false; - - old_mode = &old_crtc_state->mode; - new_mode = &new_crtc_state->mode; - - if (old_mode->clock == new_mode->clock && - old_mode->hdisplay == new_mode->hdisplay && - old_mode->vdisplay == new_mode->vdisplay && - old_mode->htotal == new_mode->htotal && - old_mode->vtotal != new_mode->vtotal && - old_mode->hsync_start == new_mode->hsync_start && - old_mode->vsync_start != new_mode->vsync_start && - old_mode->hsync_end == new_mode->hsync_end && - old_mode->vsync_end != new_mode->vsync_end && - old_mode->hskew == new_mode->hskew && - old_mode->vscan == new_mode->vscan && - (old_mode->vsync_end - old_mode->vsync_start) == - (new_mode->vsync_end - new_mode->vsync_start)) - return true; - - return false; -} -EXPORT_IF_KUNIT(is_timing_unchanged_for_freesync); - -STATIC_IFN_KUNIT void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state) -{ - u64 num, den, res; - struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base; - - dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; - - num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000; - den = (unsigned long long)new_crtc_state->mode.htotal * - (unsigned long long)new_crtc_state->mode.vtotal; - - res = div_u64(num, den); - dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res; -} -EXPORT_IF_KUNIT(set_freesync_fixed_config); - static int dm_update_crtc_state(struct amdgpu_display_manager *dm, struct drm_atomic_commit *state, struct drm_crtc *crtc, @@ -5917,7 +5415,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, */ if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream && - is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state)) + amdgpu_dm_is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state)) goto skip_modeset; if (dm_new_crtc_state->stream && @@ -5959,14 +5457,14 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream && dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) && dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream) && - is_timing_unchanged_for_freesync(new_crtc_state, + amdgpu_dm_is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state)) { new_crtc_state->mode_changed = false; drm_dbg_driver(adev_to_drm(adev), "Mode change not required for front porch change, setting mode_changed to %d", new_crtc_state->mode_changed); - set_freesync_fixed_config(dm_new_crtc_state); + amdgpu_dm_set_freesync_fixed_config(dm_new_crtc_state); goto skip_modeset; } else if (amdgpu_freesync_vid_mode && aconnector && @@ -5976,7 +5474,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, high_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false); if (!drm_mode_equal(&new_crtc_state->mode, high_mode)) - set_freesync_fixed_config(dm_new_crtc_state); + amdgpu_dm_set_freesync_fixed_config(dm_new_crtc_state); } ret = dm_atomic_get_state(state, &dm_state); @@ -5998,7 +5496,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, dc_stream_release(dm_old_crtc_state->stream); dm_new_crtc_state->stream = NULL; - reset_freesync_config_for_crtc(dm_new_crtc_state); + amdgpu_dm_reset_freesync_config_for_crtc(dm_new_crtc_state); *lock_and_validation_needed = true; @@ -6086,7 +5584,7 @@ skip_modeset: } /* Update Freesync settings. */ - get_freesync_config_for_crtc(dm_new_crtc_state, + amdgpu_dm_get_freesync_config_for_crtc(dm_new_crtc_state, dm_new_conn_state); return ret; @@ -6250,140 +5748,13 @@ static bool should_reset_plane(struct drm_atomic_commit *state, new_afb = (struct amdgpu_framebuffer *)new_other_state->fb; /* Tiling and DCC changes also require bandwidth updates. */ - if (old_afb->tiling_flags != new_afb->tiling_flags || - old_afb->base.modifier != new_afb->base.modifier) + if (old_afb->base.modifier != new_afb->base.modifier) return true; } return false; } -static int dm_check_cursor_fb(struct amdgpu_crtc *new_acrtc, - struct drm_plane_state *new_plane_state, - struct drm_framebuffer *fb) -{ - struct amdgpu_device *adev = drm_to_adev(new_acrtc->base.dev); - struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(fb); - unsigned int pitch; - bool linear; - - if (fb->width > new_acrtc->max_cursor_width || - fb->height > new_acrtc->max_cursor_height) { - drm_dbg_atomic(adev_to_drm(adev), "Bad cursor FB size %dx%d\n", - new_plane_state->fb->width, - new_plane_state->fb->height); - return -EINVAL; - } - if (new_plane_state->src_w != fb->width << 16 || - new_plane_state->src_h != fb->height << 16) { - drm_dbg_atomic(adev_to_drm(adev), "Cropping not supported for cursor plane\n"); - return -EINVAL; - } - - /* Pitch in pixels */ - pitch = fb->pitches[0] / fb->format->cpp[0]; - - if (fb->width != pitch) { - drm_dbg_atomic(adev_to_drm(adev), "Cursor FB width %d doesn't match pitch %d", - fb->width, pitch); - return -EINVAL; - } - - switch (pitch) { - case 64: - case 128: - case 256: - /* FB pitch is supported by cursor plane */ - break; - default: - drm_dbg_atomic(adev_to_drm(adev), "Bad cursor FB pitch %d px\n", pitch); - return -EINVAL; - } - - /* Core DRM takes care of checking FB modifiers, so we only need to - * check tiling flags when the FB doesn't have a modifier. - */ - if (!(fb->flags & DRM_MODE_FB_MODIFIERS)) { - if (adev->family == AMDGPU_FAMILY_GC_12_0_0) { - linear = AMDGPU_TILING_GET(afb->tiling_flags, GFX12_SWIZZLE_MODE) == 0; - } else if (adev->family >= AMDGPU_FAMILY_AI) { - linear = AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE) == 0; - } else { - linear = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_2D_TILED_THIN1 && - AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_1D_TILED_THIN1 && - AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE) == 0; - } - if (!linear) { - drm_dbg_atomic(adev_to_drm(adev), "Cursor FB not linear"); - return -EINVAL; - } - } - - return 0; -} - -/* - * Helper function for checking the cursor in native mode - */ -static int dm_check_native_cursor_state(struct drm_crtc *new_plane_crtc, - struct drm_plane *plane, - struct drm_plane_state *new_plane_state, - bool enable) -{ - - struct amdgpu_crtc *new_acrtc; - int ret; - - if (!enable || !new_plane_crtc || - drm_atomic_plane_disabling(plane->state, new_plane_state)) - return 0; - - new_acrtc = to_amdgpu_crtc(new_plane_crtc); - - if (new_plane_state->src_x != 0 || new_plane_state->src_y != 0) { - drm_dbg_atomic(new_plane_crtc->dev, "Cropping not supported for cursor plane\n"); - return -EINVAL; - } - - if (new_plane_state->fb) { - ret = dm_check_cursor_fb(new_acrtc, new_plane_state, - new_plane_state->fb); - if (ret) - return ret; - } - - return 0; -} - -static bool dm_should_update_native_cursor(struct drm_atomic_commit *state, - struct drm_crtc *old_plane_crtc, - struct drm_crtc *new_plane_crtc, - bool enable) -{ - struct drm_crtc_state *old_crtc_state, *new_crtc_state; - struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; - - if (!enable) { - if (old_plane_crtc == NULL) - return true; - - old_crtc_state = drm_atomic_get_old_crtc_state( - state, old_plane_crtc); - dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); - - return dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE; - } else { - if (new_plane_crtc == NULL) - return true; - - new_crtc_state = drm_atomic_get_new_crtc_state( - state, new_plane_crtc); - dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); - - return dm_new_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE; - } -} - static int dm_update_plane_state(struct dc *dc, struct drm_atomic_commit *state, struct drm_plane *plane, @@ -6408,14 +5779,14 @@ static int dm_update_plane_state(struct dc *dc, dm_new_plane_state = to_dm_plane_state(new_plane_state); dm_old_plane_state = to_dm_plane_state(old_plane_state); - update_native_cursor = dm_should_update_native_cursor(state, - old_plane_crtc, - new_plane_crtc, - enable); + update_native_cursor = amdgpu_dm_should_update_native_cursor(state, + old_plane_crtc, + new_plane_crtc, + enable); if (plane->type == DRM_PLANE_TYPE_CURSOR && update_native_cursor) { - ret = dm_check_native_cursor_state(new_plane_crtc, plane, - new_plane_state, enable); + ret = amdgpu_dm_check_native_cursor_state(new_plane_crtc, plane, + new_plane_state, enable); if (ret) return ret; @@ -6545,8 +5916,8 @@ static int dm_update_plane_state(struct dc *dc, out: /* If enabling cursor overlay failed, attempt fallback to native mode */ if (enable && ret == -EINVAL && plane->type == DRM_PLANE_TYPE_CURSOR) { - ret = dm_check_native_cursor_state(new_plane_crtc, plane, - new_plane_state, enable); + ret = amdgpu_dm_check_native_cursor_state(new_plane_crtc, plane, + new_plane_state, enable); if (ret) return ret; @@ -6556,37 +5927,6 @@ out: return ret; } -STATIC_IFN_KUNIT void dm_get_oriented_plane_size(struct drm_plane_state *plane_state, - int *src_w, int *src_h) -{ - switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) { - case DRM_MODE_ROTATE_90: - case DRM_MODE_ROTATE_270: - *src_w = plane_state->src_h >> 16; - *src_h = plane_state->src_w >> 16; - break; - case DRM_MODE_ROTATE_0: - case DRM_MODE_ROTATE_180: - default: - *src_w = plane_state->src_w >> 16; - *src_h = plane_state->src_h >> 16; - break; - } -} -EXPORT_IF_KUNIT(dm_get_oriented_plane_size); - -STATIC_IFN_KUNIT void -dm_get_plane_scale(struct drm_plane_state *plane_state, - int *out_plane_scale_w, int *out_plane_scale_h) -{ - int plane_src_w, plane_src_h; - - dm_get_oriented_plane_size(plane_state, &plane_src_w, &plane_src_h); - *out_plane_scale_w = plane_src_w ? plane_state->crtc_w * 1000 / plane_src_w : 0; - *out_plane_scale_h = plane_src_h ? plane_state->crtc_h * 1000 / plane_src_h : 0; -} -EXPORT_IF_KUNIT(dm_get_plane_scale); - /* * The normalized_zpos value cannot be used by this iterator directly. It's only * calculated for enabled planes, potentially causing normalized_zpos collisions @@ -6594,7 +5934,7 @@ EXPORT_IF_KUNIT(dm_get_plane_scale); * so that the iterator will not generate the same object twice, or loop * indefinitely. */ -static inline struct __drm_planes_state *__get_next_zpos( +struct __drm_planes_state *amdgpu_dm_get_next_zpos( struct drm_atomic_commit *state, struct __drm_planes_state *prev) { @@ -6632,19 +5972,6 @@ static inline struct __drm_planes_state *__get_next_zpos( return &state->planes[highest_i]; } -/* - * Use the uniqueness of the plane's (zpos, drm obj ID) combination to iterate - * by descending zpos, as read from the new plane state. This is the same - * ordering as defined by drm_atomic_normalize_zpos(). - */ -#define for_each_oldnew_plane_in_descending_zpos(__state, plane, old_plane_state, new_plane_state) \ - for (struct __drm_planes_state *__i = __get_next_zpos((__state), NULL); \ - __i != NULL; __i = __get_next_zpos((__state), __i)) \ - for_each_if(((plane) = __i->ptr, \ - (void)(plane) /* Only to avoid unused-but-set-variable warning */, \ - (old_plane_state) = __i->old_state, \ - (new_plane_state) = __i->new_state, 1)) - static int add_affected_mst_dsc_crtcs(struct drm_atomic_commit *state, struct drm_crtc *crtc) { struct drm_connector *connector; @@ -6675,244 +6002,6 @@ static int add_affected_mst_dsc_crtcs(struct drm_atomic_commit *state, struct dr return drm_dp_mst_add_affected_dsc_crtcs(state, &aconnector->mst_root->mst_mgr); } -/** - * DOC: Cursor Modes - Native vs Overlay - * - * In native mode, the cursor uses a integrated cursor pipe within each DCN hw - * plane. It does not require a dedicated hw plane to enable, but it is - * subjected to the same z-order and scaling as the hw plane. It also has format - * restrictions, a RGB cursor in native mode cannot be enabled within a non-RGB - * hw plane. - * - * In overlay mode, the cursor uses a separate DCN hw plane, and thus has its - * own scaling and z-pos. It also has no blending restrictions. It lends to a - * cursor behavior more akin to a DRM client's expectations. However, it does - * occupy an extra DCN plane, and therefore will only be used if a DCN plane is - * available. - */ - -/** - * dm_plane_color_pipeline_active() - Check if a plane's color pipeline active. - * @state: DRM atomic state - * @plane: DRM plane to check - * @use_old: if true, inspect the old colorop states; otherwise the new ones - * - * A color pipeline may be selected (color_pipeline != NULL) but still is - * inactive if every colorop in the chain is bypassed. Only return - * true when at least one colorop has bypass == false, meaning the cursor - * would be subjected to the transformation in native mode. - * - * Return: true if the pipeline modifies pixels, false otherwise. - */ -static bool dm_plane_color_pipeline_active(struct drm_atomic_commit *state, - struct drm_plane *plane, - bool use_old) -{ - struct drm_colorop *colorop; - struct drm_colorop_state *old_colorop_state, *new_colorop_state; - int i; - - for_each_oldnew_colorop_in_state(state, colorop, old_colorop_state, new_colorop_state, i) { - struct drm_colorop_state *cstate = use_old ? old_colorop_state : new_colorop_state; - - if (cstate->colorop->plane != plane) - continue; - if (!cstate->bypass) - return true; - } - return false; -} - -/** - * dm_crtc_get_cursor_mode() - Determine the required cursor mode on crtc - * @adev: amdgpu device - * @state: DRM atomic state - * @dm_crtc_state: amdgpu state for the CRTC containing the cursor - * @cursor_mode: Returns the required cursor mode on dm_crtc_state - * - * Get whether the cursor should be enabled in native mode, or overlay mode, on - * the dm_crtc_state. - * - * The cursor should be enabled in overlay mode if there exists an underlying - * plane - on which the cursor may be blended - that is either YUV formatted, - * scaled differently from the cursor, or has a color pipeline active. - * - * Since zpos info is required, drm_atomic_normalize_zpos must be called before - * calling this function. - * - * Return: 0 on success, or an error code if getting the cursor plane state - * failed. - */ -static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev, - struct drm_atomic_commit *state, - struct dm_crtc_state *dm_crtc_state, - enum amdgpu_dm_cursor_mode *cursor_mode) -{ - struct drm_plane_state *old_plane_state, *plane_state, *cursor_state; - struct drm_crtc_state *crtc_state = &dm_crtc_state->base; - struct drm_plane *plane; - bool consider_mode_change = false; - bool entire_crtc_covered = false; - bool cursor_changed = false; - int underlying_scale_w, underlying_scale_h; - int cursor_scale_w, cursor_scale_h; - int i; - - /* Overlay cursor not supported on HW before DCN - * DCN401/420 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions - * as previous DCN generations, so enable native mode on DCN401/420 - * - * Always set native cursor mode when the CRTC is disabled, - * to make sure it doesn't cause atomic commits to fail when - * they are trying to disable the CRTC. - */ - if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1) || - amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) || - amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) || - !dm_crtc_state->base.enable) { - *cursor_mode = DM_CURSOR_NATIVE_MODE; - return 0; - } - - /* Init cursor_mode to be the same as current */ - *cursor_mode = dm_crtc_state->cursor_mode; - - /* - * Cursor mode can change if a plane's format changes, scale changes, is - * enabled/disabled, z-order changes, or color management properties change. - */ - for_each_oldnew_plane_in_state(state, plane, old_plane_state, plane_state, i) { - int new_scale_w, new_scale_h, old_scale_w, old_scale_h; - - /* Only care about planes on this CRTC */ - if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0) - continue; - - if (plane->type == DRM_PLANE_TYPE_CURSOR) - cursor_changed = true; - - if (drm_atomic_plane_enabling(old_plane_state, plane_state) || - drm_atomic_plane_disabling(old_plane_state, plane_state) || - old_plane_state->fb->format != plane_state->fb->format) { - consider_mode_change = true; - break; - } - - dm_get_plane_scale(plane_state, &new_scale_w, &new_scale_h); - dm_get_plane_scale(old_plane_state, &old_scale_w, &old_scale_h); - if (new_scale_w != old_scale_w || new_scale_h != old_scale_h) { - consider_mode_change = true; - break; - } - - /* - * A non-cursor plane moving or resizing (without a scale change) - * changes how much of the CRTC it covers. This can create or - * remove a hole under the cursor and thus flip the required - * cursor mode (native vs overlay), so its destination rect must - * be re-evaluated too. - * - * The cursor plane itself is deliberately excluded: the cursor - * mode depends on the underlying planes' coverage, not on the - * cursor's position (see the entire_crtc_covered logic below). - * Triggering on cursor movement would force every legacy cursor - * update off its fast path, and in a cursor-only commit - where - * the underlying planes are not part of the state - the coverage - * loop would see no covering plane and misevaluate the mode as - * overlay, regressing flip-vs-cursor-legacy. - */ - if (plane->type != DRM_PLANE_TYPE_CURSOR && - (old_plane_state->crtc_x != plane_state->crtc_x || - old_plane_state->crtc_y != plane_state->crtc_y || - old_plane_state->crtc_w != plane_state->crtc_w || - old_plane_state->crtc_h != plane_state->crtc_h)) { - consider_mode_change = true; - break; - } - - if (dm_plane_color_pipeline_active(state, plane, true) != - dm_plane_color_pipeline_active(state, plane, false)) { - consider_mode_change = true; - break; - } - } - - if (!consider_mode_change && !crtc_state->zpos_changed) - return 0; - - /* - * If no cursor change on this CRTC, and not enabled on this CRTC, then - * no need to set cursor mode. This avoids needlessly locking the cursor - * state. - */ - if (!cursor_changed && - !(drm_plane_mask(crtc_state->crtc->cursor) & crtc_state->plane_mask)) { - return 0; - } - - cursor_state = drm_atomic_get_plane_state(state, - crtc_state->crtc->cursor); - if (IS_ERR(cursor_state)) - return PTR_ERR(cursor_state); - - /* Cursor is disabled */ - if (!cursor_state->fb) - return 0; - - /* For all planes in descending z-order (all of which are below cursor - * as per zpos definitions), check their scaling and format - */ - for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, plane_state) { - - /* Only care about non-cursor planes on this CRTC */ - if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0 || - plane->type == DRM_PLANE_TYPE_CURSOR) - continue; - - /* Underlying plane is YUV format - use overlay cursor */ - if (amdgpu_dm_plane_is_video_format(plane_state->fb->format->format)) { - *cursor_mode = DM_CURSOR_OVERLAY_MODE; - return 0; - } - - /* Underlying plane has an active color pipeline - cursor would be transformed */ - if (dm_plane_color_pipeline_active(state, plane, false)) { - *cursor_mode = DM_CURSOR_OVERLAY_MODE; - return 0; - } - - dm_get_plane_scale(plane_state, - &underlying_scale_w, &underlying_scale_h); - dm_get_plane_scale(cursor_state, - &cursor_scale_w, &cursor_scale_h); - - /* Underlying plane has different scale - use overlay cursor */ - if (cursor_scale_w != underlying_scale_w && - cursor_scale_h != underlying_scale_h) { - *cursor_mode = DM_CURSOR_OVERLAY_MODE; - return 0; - } - - /* If this plane covers the whole CRTC, no need to check planes underneath */ - if (plane_state->crtc_x <= 0 && plane_state->crtc_y <= 0 && - plane_state->crtc_x + plane_state->crtc_w >= crtc_state->mode.hdisplay && - plane_state->crtc_y + plane_state->crtc_h >= crtc_state->mode.vdisplay) { - entire_crtc_covered = true; - break; - } - } - - /* If planes do not cover the entire CRTC, use overlay mode to enable - * cursor over holes - */ - if (entire_crtc_covered) - *cursor_mode = DM_CURSOR_NATIVE_MODE; - else - *cursor_mode = DM_CURSOR_OVERLAY_MODE; - - return 0; -} - static bool amdgpu_dm_crtc_mem_type_changed(struct drm_device *dev, struct drm_atomic_commit *state, struct drm_crtc_state *crtc_state) @@ -7116,8 +6205,8 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); - ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state, - &dm_new_crtc_state->cursor_mode); + ret = amdgpu_dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state, + &dm_new_crtc_state->cursor_mode); if (ret) { drm_dbg(dev, "Failed to determine cursor mode: %pe\n", ERR_PTR(ret)); goto fail; @@ -7251,8 +6340,8 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, } /* If HW can only do native cursor, check restrictions again */ - ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state, - &required_cursor_mode); + ret = amdgpu_dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state, + &required_cursor_mode); if (ret) { drm_dbg_driver(crtc->dev, "[CRTC:%d:%s] Checking cursor mode failed\n", diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index f396803d1485..1ddf5bc88a03 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -1134,30 +1134,62 @@ bool amdgpu_dm_is_headless(struct amdgpu_device *adev); bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc); void retrieve_dmi_info(struct amdgpu_display_manager *dm); +struct pci_dev; +bool dm_should_disable_stutter(struct pci_dev *pdev); void amdgpu_dm_emulated_link_detect(struct dc_link *link); void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev, struct dc_sink *sink); +struct __drm_planes_state *amdgpu_dm_get_next_zpos(struct drm_atomic_commit *state, + struct __drm_planes_state *prev); + +/* + * Use the uniqueness of the plane's (zpos, drm obj ID) combination to iterate + * by descending zpos, as read from the new plane state. This is the same + * ordering as defined by drm_atomic_normalize_zpos(). + */ +#define for_each_oldnew_plane_in_descending_zpos(__state, plane, old_plane_state, new_plane_state) \ + for (struct __drm_planes_state *__i = amdgpu_dm_get_next_zpos((__state), NULL); \ + __i != NULL; __i = amdgpu_dm_get_next_zpos((__state), __i)) \ + for_each_if(((plane) = __i->ptr, \ + (void)(plane) /* Only to avoid unused-but-set-variable warning */, \ + (old_plane_state) = __i->old_state, \ + (new_plane_state) = __i->new_state, 1)) + #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +struct amdgpu_ip_block; +bool dm_is_idle(struct amdgpu_ip_block *ip_block); +int dm_wait_for_idle(struct amdgpu_ip_block *ip_block); +int dm_soft_reset(struct amdgpu_ip_block *ip_block); +int dm_set_clockgating_state(struct amdgpu_ip_block *ip_block, + enum amd_clockgating_state state); +int dm_set_powergating_state(struct amdgpu_ip_block *ip_block, + enum amd_powergating_state state); +void dm_bandwidth_update(struct amdgpu_device *adev); +u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc); +int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc, + u32 *vbl, u32 *position); +struct dm_atomic_state *dm_atomic_get_new_state(struct drm_atomic_commit *state); +void dm_atomic_destroy_state(struct drm_private_obj *obj, + struct drm_private_state *state); int dm_plane_layer_index_cmp(const void *a, const void *b); int fill_plane_color_attributes(const struct drm_plane_state *plane_state, const enum surface_pixel_format format, enum dc_color_space *color_space); bool modereset_required(struct drm_crtc_state *crtc_state); -void dm_get_oriented_plane_size(struct drm_plane_state *plane_state, - int *src_w, int *src_h); -void dm_get_plane_scale(struct drm_plane_state *plane_state, - int *out_plane_scale_w, int *out_plane_scale_h); bool is_scaling_state_different(const struct dm_connector_state *dm_state, const struct dm_connector_state *old_dm_state); -bool is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, - struct drm_crtc_state *new_crtc_state); -void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state); -bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, - struct dm_crtc_state *new_state); void set_multisync_trigger_params(struct dc_stream_state *stream); void set_master_stream(struct dc_stream_state *stream_set[], int stream_count); +void dm_enable_per_frame_crtc_master_sync(struct dc_state *context); +struct hdcp_workqueue; +bool is_content_protection_different(struct drm_crtc_state *new_crtc_state, + struct drm_crtc_state *old_crtc_state, + struct drm_connector_state *new_conn_state, + struct drm_connector_state *old_conn_state, + const struct drm_connector *connector, + struct hdcp_workqueue *hdcp_w); #endif #endif /* __AMDGPU_DM_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c index e97ce5c2c0e0..199f339839b2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c @@ -26,7 +26,7 @@ #include "amdgpu.h" #include "amdgpu_dm.h" #include "amdgpu_dm_audio.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" #include "dc.h" #include <linux/component.h> @@ -321,6 +321,7 @@ notify: amdgpu_dm_audio_eld_notify(adev, inst); } } +EXPORT_IF_KUNIT(amdgpu_dm_commit_audio); #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) int amdgpu_dm_audio_get_param(void) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c index 4eaf5e303850..b66ca60e697d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c @@ -47,7 +47,7 @@ #include "amdgpu_dm_trace.h" #include "amd_shared.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm, int bl_idx) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index 357c7c5c85cf..7b68c6846039 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -33,7 +33,7 @@ #include "amdgpu_dm_colorop.h" #include "dc.h" #include "modules/color/color_gamma.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" /** @@ -172,6 +172,7 @@ void amdgpu_dm_init_color_mod(void) { setup_x_points_distribution(); } +EXPORT_IF_KUNIT(amdgpu_dm_init_color_mod); STATIC_IFN_KUNIT INLINE_IFN_KUNIT struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x) @@ -617,9 +618,10 @@ EXPORT_IF_KUNIT(__drm_ctm_3x4_to_dc_matrix); * Returns: * 0 in case of success, -ENOMEM if fails */ -static int __set_legacy_tf(struct dc_transfer_func *func, - const struct drm_color_lut *lut, uint32_t lut_size, - bool has_rom) +STATIC_IFN_KUNIT int +__set_legacy_tf(struct dc_transfer_func *func, + const struct drm_color_lut *lut, uint32_t lut_size, + bool has_rom) { struct dc_gamma *gamma = NULL; struct calculate_buffer cal_buffer = {0}; @@ -644,6 +646,7 @@ static int __set_legacy_tf(struct dc_transfer_func *func, return res ? 0 : -ENOMEM; } +EXPORT_IF_KUNIT(__set_legacy_tf); /** * __set_output_tf - calculates the output transfer function based on expected input space. @@ -655,9 +658,10 @@ static int __set_legacy_tf(struct dc_transfer_func *func, * Returns: * 0 in case of success. -ENOMEM if fails. */ -static int __set_output_tf(struct dc_transfer_func *func, - const struct drm_color_lut *lut, uint32_t lut_size, - bool has_rom) +STATIC_IFN_KUNIT int +__set_output_tf(struct dc_transfer_func *func, + const struct drm_color_lut *lut, uint32_t lut_size, + bool has_rom) { struct dc_gamma *gamma = NULL; struct calculate_buffer cal_buffer = {0}; @@ -702,6 +706,7 @@ static int __set_output_tf(struct dc_transfer_func *func, return res ? 0 : -ENOMEM; } +EXPORT_IF_KUNIT(__set_output_tf); /** * __set_output_tf_32 - calculates the output transfer function based on expected input space. @@ -713,9 +718,10 @@ static int __set_output_tf(struct dc_transfer_func *func, * Returns: * 0 in case of success. -ENOMEM if fails. */ -static int __set_output_tf_32(struct dc_transfer_func *func, - const struct drm_color_lut32 *lut, uint32_t lut_size, - bool has_rom) +STATIC_IFN_KUNIT int +__set_output_tf_32(struct dc_transfer_func *func, + const struct drm_color_lut32 *lut, uint32_t lut_size, + bool has_rom) { struct dc_gamma *gamma = NULL; struct calculate_buffer cal_buffer = {0}; @@ -758,6 +764,7 @@ static int __set_output_tf_32(struct dc_transfer_func *func, return res ? 0 : -ENOMEM; } +EXPORT_IF_KUNIT(__set_output_tf_32); STATIC_IFN_KUNIT void __set_tf_bypass(struct dc_transfer_func *tf) { @@ -819,8 +826,9 @@ EXPORT_IF_KUNIT(amdgpu_dm_set_atomic_regamma); * Returns: * 0 in case of success. -ENOMEM if fails. */ -static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *func, - const struct drm_color_lut *lut, uint32_t lut_size) +STATIC_IFN_KUNIT int __set_input_tf(struct dc_color_caps *caps, + struct dc_transfer_func *func, + const struct drm_color_lut *lut, uint32_t lut_size) { struct dc_gamma *gamma = NULL; bool res; @@ -843,6 +851,7 @@ static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *f return res ? 0 : -ENOMEM; } +EXPORT_IF_KUNIT(__set_input_tf); /** * __set_input_tf_32 - calculates the input transfer function based on expected @@ -855,8 +864,9 @@ static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *f * Returns: * 0 in case of success. -ENOMEM if fails. */ -static int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func *func, - const struct drm_color_lut32 *lut, uint32_t lut_size) +STATIC_IFN_KUNIT int __set_input_tf_32(struct dc_color_caps *caps, + struct dc_transfer_func *func, + const struct drm_color_lut32 *lut, uint32_t lut_size) { struct dc_gamma *gamma = NULL; bool res; @@ -879,6 +889,7 @@ static int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func return res ? 0 : -ENOMEM; } +EXPORT_IF_KUNIT(__set_input_tf_32); STATIC_IFN_KUNIT enum dc_transfer_func_predefined @@ -1177,6 +1188,7 @@ int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev, return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_verify_lut3d_size); /** * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes @@ -1321,6 +1333,7 @@ int amdgpu_dm_check_crtc_color_mgmt(struct dm_crtc_state *crtc, return r; } +EXPORT_IF_KUNIT(amdgpu_dm_check_crtc_color_mgmt); /** * amdgpu_dm_update_crtc_color_mgmt: Maps DRM color management to DC stream. @@ -1379,6 +1392,7 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc) return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_update_crtc_color_mgmt); static int map_crtc_degamma_to_dc_plane(struct dm_crtc_state *crtc, @@ -1545,7 +1559,7 @@ __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, } EXPORT_IF_KUNIT(__set_colorop_in_tf_1d_curve); -static int +STATIC_IFN_KUNIT int __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, struct drm_colorop *colorop) @@ -1571,8 +1585,9 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state); } +EXPORT_IF_KUNIT(__set_dm_plane_colorop_degamma); -static int +STATIC_IFN_KUNIT int __set_dm_plane_colorop_3x4_matrix(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, struct drm_colorop *colorop) @@ -1612,8 +1627,9 @@ __set_dm_plane_colorop_3x4_matrix(struct drm_plane_state *plane_state, return 0; } +EXPORT_IF_KUNIT(__set_dm_plane_colorop_3x4_matrix); -static int +STATIC_IFN_KUNIT int __set_dm_plane_colorop_multiplier(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, struct drm_colorop *colorop) @@ -1641,6 +1657,7 @@ __set_dm_plane_colorop_multiplier(struct drm_plane_state *plane_state, return 0; } +EXPORT_IF_KUNIT(__set_dm_plane_colorop_multiplier); static int __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, @@ -2125,3 +2142,4 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, return amdgpu_dm_plane_set_color_properties(plane_state, dc_plane_state); } +EXPORT_IF_KUNIT(amdgpu_dm_update_plane_color_mgmt); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h index 8dbbcb3ab156..3e8a3d3fc855 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h @@ -44,6 +44,7 @@ struct dc_plane_state; struct fixed31_32; struct tetrahedral_params; struct dc_transfer_func; +struct dc_3dlut; #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) /* @@ -63,6 +64,20 @@ void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, struct fixed31_32 *matrix); void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm, struct fixed31_32 *matrix); +int __set_legacy_tf(struct dc_transfer_func *func, + const struct drm_color_lut *lut, uint32_t lut_size, + bool has_rom); +int __set_output_tf(struct dc_transfer_func *func, + const struct drm_color_lut *lut, uint32_t lut_size, + bool has_rom); +int __set_output_tf_32(struct dc_transfer_func *func, + const struct drm_color_lut32 *lut, uint32_t lut_size, + bool has_rom); +struct dc_color_caps; +int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *func, + const struct drm_color_lut *lut, uint32_t lut_size); +int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func *func, + const struct drm_color_lut32 *lut, uint32_t lut_size); enum dc_transfer_func_predefined amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf); enum dc_transfer_func_predefined @@ -113,6 +128,17 @@ int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blend_lut, struct dc_plane_cm *cm); int __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, struct drm_colorop_state *colorop_state); +struct drm_plane_state; +struct drm_colorop; +int __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, + struct dc_plane_state *dc_plane_state, + struct drm_colorop *colorop); +int __set_dm_plane_colorop_3x4_matrix(struct drm_plane_state *plane_state, + struct dc_plane_state *dc_plane_state, + struct drm_colorop *colorop); +int __set_dm_plane_colorop_multiplier(struct drm_plane_state *plane_state, + struct dc_plane_state *dc_plane_state, + struct drm_colorop *colorop); #endif #endif /* __AMDGPU_DM_COLOR_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c index 056a76b88f43..fae9007b9998 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c @@ -31,7 +31,7 @@ #include "amdgpu.h" #include "amdgpu_dm_colorop.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" #include "dc.h" const u64 amdgpu_dm_supported_degam_tfs = diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index 5c5f9c3e6d77..db211884616c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -42,7 +42,6 @@ #include "amdgpu_display.h" #include "amdgpu_dm.h" #include "amdgpu_dm_connector.h" -#include "amdgpu_dm_kunit_helpers.h" #include "amdgpu_dm_plane.h" #include "amdgpu_dm_crtc.h" #include "amdgpu_dm_wb.h" @@ -91,11 +90,12 @@ static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = { .destroy = amdgpu_dm_encoder_destroy, }; -static void dm_encoder_helper_disable(struct drm_encoder *encoder) +STATIC_IFN_KUNIT void dm_encoder_helper_disable(struct drm_encoder *encoder) { } +EXPORT_IF_KUNIT(dm_encoder_helper_disable); -static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, +STATIC_IFN_KUNIT int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -165,6 +165,7 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, } return 0; } +EXPORT_IF_KUNIT(dm_encoder_helper_atomic_check); const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = { .disable = dm_encoder_helper_disable, @@ -333,7 +334,7 @@ int amdgpu_dm_detect_mst_link_for_all_connectors(struct drm_device *dev) } EXPORT_IF_KUNIT(amdgpu_dm_detect_mst_link_for_all_connectors); -static void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector) +STATIC_IFN_KUNIT void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector) { struct cec_notifier *n = aconnector->notifier; @@ -342,6 +343,7 @@ static void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector) cec_notifier_phys_addr_invalidate(n); } +EXPORT_IF_KUNIT(hdmi_cec_unset_edid); void amdgpu_dm_hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector) { @@ -354,6 +356,7 @@ void amdgpu_dm_hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector) cec_notifier_set_phys_addr(n, connector->display_info.source_physical_address); } +EXPORT_IF_KUNIT(amdgpu_dm_hdmi_cec_set_edid); void amdgpu_dm_s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend) { @@ -374,6 +377,7 @@ void amdgpu_dm_s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend) } drm_connector_list_iter_end(&conn_iter); } +EXPORT_IF_KUNIT(amdgpu_dm_s3_handle_hdmi_cec); struct drm_connector * @@ -655,6 +659,7 @@ void amdgpu_dm_update_connector_after_detect( if (!drm_kms_helper_is_poll_worker()) mutex_unlock(&dev->mode_config.mutex); } +EXPORT_IF_KUNIT(amdgpu_dm_update_connector_after_detect); enum dc_color_depth amdgpu_dm_convert_color_depth_from_display_info(const struct drm_connector *connector, @@ -1384,7 +1389,64 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector, } #endif -static struct dc_stream_state * +void amdgpu_dm_update_stream_scaling_settings(struct drm_device *dev, + const struct drm_display_mode *mode, + const struct dm_connector_state *dm_state, + struct dc_stream_state *stream) +{ + enum amdgpu_rmx_type rmx_type; + + struct rect src = { 0 }; /* viewport in composition space*/ + struct rect dst = { 0 }; /* stream addressable area */ + + /* no mode. nothing to be done */ + if (!mode) + return; + + /* Full screen scaling by default */ + src.width = mode->hdisplay; + src.height = mode->vdisplay; + dst.width = stream->timing.h_addressable; + dst.height = stream->timing.v_addressable; + + if (dm_state) { + rmx_type = dm_state->scaling; + if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) { + if (src.width * dst.height < + src.height * dst.width) { + /* height needs less upscaling/more downscaling */ + dst.width = src.width * + dst.height / src.height; + } else { + /* width needs less upscaling/more downscaling */ + dst.height = src.height * + dst.width / src.width; + } + } else if (rmx_type == RMX_CENTER) { + dst = src; + } + + dst.x = (stream->timing.h_addressable - dst.width) / 2; + dst.y = (stream->timing.v_addressable - dst.height) / 2; + + if (dm_state->underscan_enable) { + dst.x += dm_state->underscan_hborder / 2; + dst.y += dm_state->underscan_vborder / 2; + dst.width -= dm_state->underscan_hborder; + dst.height -= dm_state->underscan_vborder; + } + } + + stream->src = src; + stream->dst = dst; + + drm_dbg_kms(dev, "Destination Rectangle x:%d y:%d width:%d height:%d\n", + dst.x, dst.y, dst.width, dst.height); + +} +EXPORT_IF_KUNIT(amdgpu_dm_update_stream_scaling_settings); + +STATIC_IFN_KUNIT struct dc_stream_state * create_stream_for_sink(struct drm_connector *connector, const struct drm_display_mode *drm_mode, const struct dm_connector_state *dm_state, @@ -1568,6 +1630,7 @@ finish: return stream; } +EXPORT_IF_KUNIT(create_stream_for_sink); /** * amdgpu_dm_connector_poll - Poll a connector to see if it's connected to a display @@ -1583,7 +1646,7 @@ finish: * * Return: The probed connector status (connected/disconnected/unknown). */ -static enum drm_connector_status +STATIC_IFN_KUNIT enum drm_connector_status amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force) { struct drm_connector *connector = &aconnector->base; @@ -1635,6 +1698,40 @@ amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force) mutex_unlock(&aconnector->hpd_lock); return status; } +EXPORT_IF_KUNIT(amdgpu_dm_connector_poll); + +/* + * Apple Studio Display exposes two SST DP links for a 2x1 tiled panel. + * The primary tile advertises the full 5120x2880 mode (with DSC on the + * bandwidth-sufficient link) while the secondary carries a per-tile + * 2560x2880 timing on a insufficient bandwidth link. Hide the secondary + * connector from userspace so compositors configure a single 5K stream + * on the primary link only. + */ +static bool amdgpu_dm_hide_secondary_tile_from_userspace(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + + if (!aconnector->dc_sink) + return false; + + if (!aconnector->dc_sink->edid_caps.panel_patch.disable_second_tile) + return false; + + drm_edid_connector_update(connector, aconnector->drm_edid); + + if (!connector->has_tile) + return false; + + if (!connector->tile_h_loc && !connector->tile_v_loc) + return false; + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] hiding secondary Apple Studio Display tile from userspace\n", + connector->base.id, connector->name); + + return true; +} /** * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display @@ -1658,7 +1755,7 @@ amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force) * Return: The connector status (connected, disconnected, or unknown). * */ -static enum drm_connector_status +STATIC_IFN_KUNIT enum drm_connector_status amdgpu_dm_connector_detect(struct drm_connector *connector, bool force) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); @@ -1679,9 +1776,13 @@ amdgpu_dm_connector_detect(struct drm_connector *connector, bool force) (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog)) return amdgpu_dm_connector_poll(aconnector, force); + if (amdgpu_dm_hide_secondary_tile_from_userspace(connector)) + return connector_status_disconnected; + return (aconnector->dc_sink ? connector_status_connected : connector_status_disconnected); } +EXPORT_IF_KUNIT(amdgpu_dm_connector_detect); int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector, struct drm_connector_state *connector_state, @@ -1800,7 +1901,7 @@ int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector, } EXPORT_IF_KUNIT(amdgpu_dm_connector_atomic_get_property); -static void amdgpu_dm_connector_unregister(struct drm_connector *connector) +STATIC_IFN_KUNIT void amdgpu_dm_connector_unregister(struct drm_connector *connector) { struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); @@ -1810,8 +1911,9 @@ static void amdgpu_dm_connector_unregister(struct drm_connector *connector) cec_notifier_conn_unregister(amdgpu_dm_connector->notifier); drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux); } +EXPORT_IF_KUNIT(amdgpu_dm_connector_unregister); -static void amdgpu_dm_connector_destroy(struct drm_connector *connector) +STATIC_IFN_KUNIT void amdgpu_dm_connector_destroy(struct drm_connector *connector) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); struct amdgpu_device *adev = drm_to_adev(connector->dev); @@ -1852,6 +1954,7 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector) kfree(connector); } +EXPORT_IF_KUNIT(amdgpu_dm_connector_destroy); void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector) { @@ -1913,7 +2016,7 @@ amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector) } EXPORT_IF_KUNIT(amdgpu_dm_connector_atomic_duplicate_state); -static int +STATIC_IFN_KUNIT int amdgpu_dm_connector_late_register(struct drm_connector *connector) { struct amdgpu_dm_connector *amdgpu_dm_connector = @@ -1943,8 +2046,9 @@ amdgpu_dm_connector_late_register(struct drm_connector *connector) return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_connector_late_register); -static void amdgpu_dm_connector_funcs_force(struct drm_connector *connector) +STATIC_IFN_KUNIT void amdgpu_dm_connector_funcs_force(struct drm_connector *connector) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); struct dc_link *dc_link = aconnector->dc_link; @@ -1980,6 +2084,7 @@ static void amdgpu_dm_connector_funcs_force(struct drm_connector *connector) &dc_em_sink->edid_caps); } } +EXPORT_IF_KUNIT(amdgpu_dm_connector_funcs_force); static const struct drm_connector_funcs amdgpu_dm_connector_funcs = { .reset = amdgpu_dm_connector_funcs_reset, @@ -2000,7 +2105,7 @@ static int get_modes(struct drm_connector *connector) return amdgpu_dm_connector_get_modes(connector); } -static void create_eml_sink(struct amdgpu_dm_connector *aconnector) +STATIC_IFN_KUNIT void create_eml_sink(struct amdgpu_dm_connector *aconnector) { struct drm_connector *connector = &aconnector->base; struct dc_link *dc_link = aconnector->dc_link; @@ -2045,8 +2150,9 @@ static void create_eml_sink(struct amdgpu_dm_connector *aconnector) dc_sink_retain(aconnector->dc_sink); } } +EXPORT_IF_KUNIT(create_eml_sink); -static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector) +STATIC_IFN_KUNIT void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector) { struct dc_link *link = (struct dc_link *)aconnector->dc_link; @@ -2061,8 +2167,9 @@ static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector) create_eml_sink(aconnector); } +EXPORT_IF_KUNIT(handle_edid_mgmt); -static enum dc_status dm_validate_stream_and_context(struct dc *dc, +STATIC_IFN_KUNIT enum dc_status dm_validate_stream_and_context(struct dc *dc, struct dc_stream_state *stream) { enum dc_status dc_result = DC_ERROR_UNEXPECTED; @@ -2124,6 +2231,7 @@ cleanup: return dc_result; } +EXPORT_IF_KUNIT(dm_validate_stream_and_context); static enum dc_status dm_validate_stream_color_format(const struct drm_connector_state *drm_state, @@ -2262,6 +2370,7 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector, return stream; } +EXPORT_IF_KUNIT(amdgpu_dm_create_validate_stream_for_sink); enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector, const struct drm_display_mode *mode) @@ -2315,6 +2424,7 @@ fail: /* TODO: error handling*/ return result; } +EXPORT_IF_KUNIT(amdgpu_dm_connector_mode_valid); int amdgpu_dm_fill_hdr_info_packet(const struct drm_connector_state *state, struct dc_info_packet *out) @@ -2529,7 +2639,7 @@ STATIC_IFN_KUNIT int to_drm_connector_type(enum signal_type st, uint32_t connect } EXPORT_IF_KUNIT(to_drm_connector_type); -static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector) +STATIC_IFN_KUNIT struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector) { struct drm_encoder *encoder; @@ -2539,8 +2649,9 @@ static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector * return NULL; } +EXPORT_IF_KUNIT(amdgpu_dm_connector_to_encoder); -static void amdgpu_dm_get_native_mode(struct drm_connector *connector) +STATIC_IFN_KUNIT void amdgpu_dm_get_native_mode(struct drm_connector *connector) { struct drm_encoder *encoder; struct amdgpu_encoder *amdgpu_encoder; @@ -2568,8 +2679,9 @@ static void amdgpu_dm_get_native_mode(struct drm_connector *connector) } } +EXPORT_IF_KUNIT(amdgpu_dm_get_native_mode); -static struct drm_display_mode * +STATIC_IFN_KUNIT struct drm_display_mode * amdgpu_dm_create_common_mode(struct drm_encoder *encoder, const char *name, int hdisplay, int vdisplay) @@ -2592,6 +2704,7 @@ amdgpu_dm_create_common_mode(struct drm_encoder *encoder, return mode; } +EXPORT_IF_KUNIT(amdgpu_dm_create_common_mode); static const struct amdgpu_dm_mode_size { char name[DRM_DISPLAY_MODE_LEN]; @@ -2611,7 +2724,7 @@ static const struct amdgpu_dm_mode_size { {"1920x1200", 1920, 1200} }; -static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder, +STATIC_IFN_KUNIT void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder, struct drm_connector *connector) { struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); @@ -2659,6 +2772,7 @@ static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder, amdgpu_dm_connector->num_modes++; } } +EXPORT_IF_KUNIT(amdgpu_dm_connector_add_common_modes); void amdgpu_set_panel_orientation(struct drm_connector *connector) { @@ -2690,7 +2804,48 @@ void amdgpu_set_panel_orientation(struct drm_connector *connector) native_mode->vdisplay); } -static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, +/* + * The Apple Studio Display primary tile advertises both the full 5120x2880 + * mode and the per-tile 2560x2880 timing. As the secondary tile is hidden from + * userspace (see amdgpu_dm_hide_secondary_tile_from_userspace()), drop the + * per-tile timing from the primary connector so compositors only pick the full + * 5K mode. + */ +static void amdgpu_dm_prune_primary_tile_modes(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + struct drm_display_mode *mode, *t; + + if (!aconnector->dc_sink) + return; + + if (!aconnector->dc_sink->edid_caps.panel_patch.disable_second_tile) + return; + + if (!connector->has_tile) + return; + + /* Only prune the per-tile timing from the primary tile. */ + if (connector->tile_h_loc || connector->tile_v_loc) + return; + + list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { + if (mode->hdisplay != connector->tile_h_size || + mode->vdisplay != connector->tile_v_size) + continue; + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] pruning per-tile %dx%d timing from primary Apple Studio Display tile\n", + connector->base.id, connector->name, + mode->hdisplay, mode->vdisplay); + + list_del(&mode->head); + drm_mode_destroy(connector->dev, mode); + aconnector->num_modes--; + } +} + +STATIC_IFN_KUNIT void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, const struct drm_edid *drm_edid) { struct amdgpu_dm_connector *amdgpu_dm_connector = @@ -2702,6 +2857,8 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, amdgpu_dm_connector->num_modes = drm_edid_connector_add_modes(connector); + amdgpu_dm_prune_primary_tile_modes(connector); + /* sorting the probed modes before calling function * amdgpu_dm_get_native_mode() since EDID can have * more than one preferred mode. The modes that are @@ -2722,6 +2879,7 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, amdgpu_dm_connector->num_modes = 0; } } +EXPORT_IF_KUNIT(amdgpu_dm_connector_ddc_get_modes); STATIC_IFN_KUNIT bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector, struct drm_display_mode *mode) @@ -2737,7 +2895,7 @@ STATIC_IFN_KUNIT bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector, } EXPORT_IF_KUNIT(is_duplicate_mode); -static uint add_fs_modes(struct amdgpu_dm_connector *aconnector) +STATIC_IFN_KUNIT uint add_fs_modes(struct amdgpu_dm_connector *aconnector) { const struct drm_display_mode *m; struct drm_display_mode *new_mode; @@ -2812,8 +2970,9 @@ static uint add_fs_modes(struct amdgpu_dm_connector *aconnector) out: return new_modes_count; } +EXPORT_IF_KUNIT(add_fs_modes); -static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector, +STATIC_IFN_KUNIT void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector, const struct drm_edid *drm_edid) { struct amdgpu_dm_connector *amdgpu_dm_connector = @@ -2836,6 +2995,7 @@ static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connect amdgpu_dm_connector->num_modes += add_fs_modes(amdgpu_dm_connector); } +EXPORT_IF_KUNIT(amdgpu_dm_connector_add_freesync_modes); static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) { @@ -3068,7 +3228,7 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, } } -static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, +STATIC_IFN_KUNIT int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); @@ -3112,11 +3272,13 @@ static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, kfree(cmd.payloads); return result; } +EXPORT_IF_KUNIT(amdgpu_dm_i2c_xfer); -static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap) +STATIC_IFN_KUNIT u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } +EXPORT_IF_KUNIT(amdgpu_dm_i2c_func); static const struct i2c_algorithm amdgpu_dm_i2c_algo = { .master_xfer = amdgpu_dm_i2c_xfer, @@ -3477,7 +3639,7 @@ static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector, return ret; } -static void parse_edid_displayid_vrr(struct drm_connector *connector, +STATIC_IFN_KUNIT void parse_edid_displayid_vrr(struct drm_connector *connector, const struct edid *edid) { u8 *edid_ext = NULL; @@ -3519,8 +3681,9 @@ static void parse_edid_displayid_vrr(struct drm_connector *connector, j++; } } +EXPORT_IF_KUNIT(parse_edid_displayid_vrr); -static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector, +STATIC_IFN_KUNIT int get_amd_vsdb(struct amdgpu_dm_connector *aconnector, struct amdgpu_hdmi_vsdb_info *vsdb_info) { struct drm_connector *connector = &aconnector->base; @@ -3530,8 +3693,9 @@ static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector, return connector->display_info.amd_vsdb.version != 0; } +EXPORT_IF_KUNIT(get_amd_vsdb); -static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, +STATIC_IFN_KUNIT int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, const struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info) { @@ -3562,6 +3726,7 @@ static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, return valid_vsdb_found ? i : -ENODEV; } +EXPORT_IF_KUNIT(parse_hdmi_amd_vsdb); /** * amdgpu_dm_update_freesync_caps - Update Freesync capabilities diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h index 60f7ab63b782..13c54229d72c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h @@ -146,6 +146,38 @@ int amdgpu_dm_encoder_init(struct drm_device *dev, uint32_t link_index); #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num); +u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap); +void parse_edid_displayid_vrr(struct drm_connector *connector, + const struct edid *edid); +int get_amd_vsdb(struct amdgpu_dm_connector *aconnector, + struct amdgpu_hdmi_vsdb_info *vsdb_info); +int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, + const struct edid *edid, + struct amdgpu_hdmi_vsdb_info *vsdb_info); +void amdgpu_dm_connector_funcs_force(struct drm_connector *connector); +enum dc_status dm_validate_stream_and_context(struct dc *dc, + struct dc_stream_state *stream); +struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector); +void amdgpu_dm_get_native_mode(struct drm_connector *connector); +struct drm_display_mode *amdgpu_dm_create_common_mode(struct drm_encoder *encoder, + const char *name, + int hdisplay, int vdisplay); +void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder, + struct drm_connector *connector); +void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid); +uint add_fs_modes(struct amdgpu_dm_connector *aconnector); +void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid); +void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector); +void create_eml_sink(struct amdgpu_dm_connector *aconnector); +void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector); +void dm_encoder_helper_disable(struct drm_encoder *encoder); +int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); enum drm_mode_subconnector get_subconnector_type(struct dc_link *link); void update_subconnector_property(struct amdgpu_dm_connector *aconnector); void amdgpu_dm_fbc_init(struct drm_connector *connector); @@ -158,6 +190,19 @@ void fill_stream_properties_from_drm_display_mode( const struct drm_connector_state *connector_state, const struct dc_stream_state *old_stream, int requested_bpc); +struct dc_stream_state * +create_stream_for_sink(struct drm_connector *connector, + const struct drm_display_mode *drm_mode, + const struct dm_connector_state *dm_state, + const struct dc_stream_state *old_stream, + int requested_bpc); +enum drm_connector_status +amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force); +enum drm_connector_status +amdgpu_dm_connector_detect(struct drm_connector *connector, bool force); +void amdgpu_dm_connector_unregister(struct drm_connector *connector); +int amdgpu_dm_connector_late_register(struct drm_connector *connector); +void amdgpu_dm_connector_destroy(struct drm_connector *connector); enum display_content_type get_output_content_type(const struct drm_connector_state *connector_state); bool adjust_colour_depth_from_display_info(struct dc_crtc_timing *timing_out, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c index 970490c401e9..05387bedf24a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c @@ -33,7 +33,7 @@ #include "amdgpu_securedisplay.h" #include "amdgpu_dm_psr.h" #include "amdgpu_dm_replay.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" static const char *const pipe_crc_sources[] = { "none", @@ -152,6 +152,7 @@ const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc, *count = ARRAY_SIZE(pipe_crc_sources); return pipe_crc_sources; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_get_crc_sources); #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY static void update_phy_id_mapping(struct amdgpu_device *adev) @@ -564,6 +565,7 @@ amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name, *values_cnt = 3; return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_verify_crc_source); int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc, struct dm_crtc_state *dm_crtc_state, @@ -636,6 +638,7 @@ unlock: return ret; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_configure_crc_source); int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) { @@ -813,6 +816,7 @@ cleanup: return ret; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_set_crc_source); /** * amdgpu_dm_crtc_handle_crc_irq: Report to DRM the CRC on given CRTC. @@ -870,6 +874,7 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc) drm_crtc_accurate_vblank_count(crtc), crcs); } } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_handle_crc_irq); #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) void amdgpu_dm_crtc_handle_crc_window_irq(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index d63688b9d93d..079d4ccc88da 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -34,7 +34,7 @@ #include "amdgpu_dm_plane.h" #include "amdgpu_dm_trace.h" #include "amdgpu_dm_debugfs.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" #include "modules/inc/mod_power.h" #define HPD_DETECTION_PERIOD_uS 2000000 @@ -287,10 +287,19 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable) * is enabled. On DCE, vupdate is only needed in VRR mode. */ if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) { - rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable); + if (enable) { + rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc); + } else { + rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type); + drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc); + } } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) { if (enable) { - /* vblank irq on -> Only need vupdate irq in vrr mode */ + /* vblank irq on -> Only need vupdate irq in vrr mode + * Not ref-counted since we need explicit enable/disable + * for DCE VRR handling + */ if (amdgpu_dm_crtc_vrr_active(acrtc_state)) rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true); } else { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_cursor.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_cursor.c new file mode 100644 index 000000000000..ab50d58896ab --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_cursor.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_blend.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_plane.h> +#include <drm/drm_colorop.h> + +#include "dc.h" +#include "dal_asic_id.h" +#include "amdgpu.h" +#include "amdgpu_display.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_plane.h" +#include "amdgpu_dm_cursor.h" +#include "dm_helpers.h" + +static int dm_check_cursor_fb(struct amdgpu_crtc *new_acrtc, + struct drm_plane_state *new_plane_state, + struct drm_framebuffer *fb) +{ + struct amdgpu_device *adev = drm_to_adev(new_acrtc->base.dev); + struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(fb); + unsigned int pitch; + bool linear; + + if (fb->width > new_acrtc->max_cursor_width || + fb->height > new_acrtc->max_cursor_height) { + drm_dbg_atomic(adev_to_drm(adev), "Bad cursor FB size %dx%d\n", + new_plane_state->fb->width, + new_plane_state->fb->height); + return -EINVAL; + } + if (new_plane_state->src_w != fb->width << 16 || + new_plane_state->src_h != fb->height << 16) { + drm_dbg_atomic(adev_to_drm(adev), "Cropping not supported for cursor plane\n"); + return -EINVAL; + } + + /* Pitch in pixels */ + pitch = fb->pitches[0] / fb->format->cpp[0]; + + if (fb->width != pitch) { + drm_dbg_atomic(adev_to_drm(adev), "Cursor FB width %d doesn't match pitch %d", + fb->width, pitch); + return -EINVAL; + } + + switch (pitch) { + case 64: + case 128: + case 256: + /* FB pitch is supported by cursor plane */ + break; + default: + drm_dbg_atomic(adev_to_drm(adev), "Bad cursor FB pitch %d px\n", pitch); + return -EINVAL; + } + + /* Core DRM takes care of checking FB modifiers, so we only need to + * check tiling flags when the FB doesn't have a modifier. + */ + if (!(fb->flags & DRM_MODE_FB_MODIFIERS)) { +#if defined(CONFIG_DRM_AMD_DC_DCN6_0) || defined(CONFIG_DRM_AMD_DC_DCN5_0) + if (adev->family == AMDGPU_FAMILY_GC_12_0_0 + || adev->family == AMDGPU_FAMILY_GC_13_0_1) { +#else + if (adev->family == AMDGPU_FAMILY_GC_12_0_0) { +#endif + linear = AMDGPU_TILING_GET(afb->tiling_flags, GFX12_SWIZZLE_MODE) == 0; + } else if (adev->family >= AMDGPU_FAMILY_AI) { + linear = AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE) == 0; + } else { + linear = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_2D_TILED_THIN1 && + AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_1D_TILED_THIN1 && + AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE) == 0; + } + if (!linear) { + drm_dbg_atomic(adev_to_drm(adev), "Cursor FB not linear"); + return -EINVAL; + } + } + + return 0; +} + +/* + * Helper function for checking the cursor in native mode + */ +int amdgpu_dm_check_native_cursor_state(struct drm_crtc *new_plane_crtc, + struct drm_plane *plane, + struct drm_plane_state *new_plane_state, + bool enable) +{ + + struct amdgpu_crtc *new_acrtc; + int ret; + + if (!enable || !new_plane_crtc || + drm_atomic_plane_disabling(plane->state, new_plane_state)) + return 0; + + new_acrtc = to_amdgpu_crtc(new_plane_crtc); + + if (new_plane_state->src_x != 0 || new_plane_state->src_y != 0) { + drm_dbg_atomic(new_plane_crtc->dev, "Cropping not supported for cursor plane\n"); + return -EINVAL; + } + + if (new_plane_state->fb) { + ret = dm_check_cursor_fb(new_acrtc, new_plane_state, + new_plane_state->fb); + if (ret) + return ret; + } + + return 0; +} + +bool amdgpu_dm_should_update_native_cursor(struct drm_atomic_commit *state, + struct drm_crtc *old_plane_crtc, + struct drm_crtc *new_plane_crtc, + bool enable) +{ + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; + + if (!enable) { + if (old_plane_crtc == NULL) + return true; + + old_crtc_state = drm_atomic_get_old_crtc_state( + state, old_plane_crtc); + dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); + + return dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE; + } + + if (new_plane_crtc == NULL) + return true; + + new_crtc_state = drm_atomic_get_new_crtc_state( + state, new_plane_crtc); + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + + return dm_new_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE; +} +EXPORT_IF_KUNIT(amdgpu_dm_should_update_native_cursor); + +STATIC_IFN_KUNIT void dm_get_oriented_plane_size(struct drm_plane_state *plane_state, + int *src_w, int *src_h) +{ + switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_90: + case DRM_MODE_ROTATE_270: + *src_w = plane_state->src_h >> 16; + *src_h = plane_state->src_w >> 16; + break; + case DRM_MODE_ROTATE_0: + case DRM_MODE_ROTATE_180: + default: + *src_w = plane_state->src_w >> 16; + *src_h = plane_state->src_h >> 16; + break; + } +} +EXPORT_IF_KUNIT(dm_get_oriented_plane_size); + +STATIC_IFN_KUNIT void +dm_get_plane_scale(struct drm_plane_state *plane_state, + int *out_plane_scale_w, int *out_plane_scale_h) +{ + int plane_src_w, plane_src_h; + + dm_get_oriented_plane_size(plane_state, &plane_src_w, &plane_src_h); + *out_plane_scale_w = plane_src_w ? plane_state->crtc_w * 1000 / plane_src_w : 0; + *out_plane_scale_h = plane_src_h ? plane_state->crtc_h * 1000 / plane_src_h : 0; +} +EXPORT_IF_KUNIT(dm_get_plane_scale); + +/** + * DOC: Cursor Modes - Native vs Overlay + * + * In native mode, the cursor uses a integrated cursor pipe within each DCN hw + * plane. It does not require a dedicated hw plane to enable, but it is + * subjected to the same z-order and scaling as the hw plane. It also has format + * restrictions, a RGB cursor in native mode cannot be enabled within a non-RGB + * hw plane. + * + * In overlay mode, the cursor uses a separate DCN hw plane, and thus has its + * own scaling and z-pos. It also has no blending restrictions. It lends to a + * cursor behavior more akin to a DRM client's expectations. However, it does + * occupy an extra DCN plane, and therefore will only be used if a DCN plane is + * available. + */ + +/** + * dm_plane_color_pipeline_active() - Check if a plane's color pipeline active. + * @state: DRM atomic state + * @plane: DRM plane to check + * @use_old: if true, inspect the old colorop states; otherwise the new ones + * + * A color pipeline may be selected (color_pipeline != NULL) but still is + * inactive if every colorop in the chain is bypassed. Only return + * true when at least one colorop has bypass == false, meaning the cursor + * would be subjected to the transformation in native mode. + * + * Return: true if the pipeline modifies pixels, false otherwise. + */ +static bool dm_plane_color_pipeline_active(struct drm_atomic_commit *state, + struct drm_plane *plane, + bool use_old) +{ + struct drm_colorop *colorop; + struct drm_colorop_state *old_colorop_state, *new_colorop_state; + int i; + + for_each_oldnew_colorop_in_state(state, colorop, old_colorop_state, new_colorop_state, i) { + struct drm_colorop_state *cstate = use_old ? old_colorop_state : new_colorop_state; + + if (cstate->colorop->plane != plane) + continue; + if (!cstate->bypass) + return true; + } + return false; +} + +/** + * amdgpu_dm_crtc_get_cursor_mode() - Determine the required cursor mode on crtc + * @adev: amdgpu device + * @state: DRM atomic state + * @dm_crtc_state: amdgpu state for the CRTC containing the cursor + * @cursor_mode: Returns the required cursor mode on dm_crtc_state + * + * Get whether the cursor should be enabled in native mode, or overlay mode, on + * the dm_crtc_state. + * + * The cursor should be enabled in overlay mode if there exists an underlying + * plane - on which the cursor may be blended - that is either YUV formatted, + * scaled differently from the cursor, or has a color pipeline active. + * + * Since zpos info is required, drm_atomic_normalize_zpos must be called before + * calling this function. + * + * Return: 0 on success, or an error code if getting the cursor plane state + * failed. + */ +int amdgpu_dm_crtc_get_cursor_mode(struct amdgpu_device *adev, + struct drm_atomic_commit *state, + struct dm_crtc_state *dm_crtc_state, + enum amdgpu_dm_cursor_mode *cursor_mode) +{ + struct drm_plane_state *old_plane_state, *plane_state, *cursor_state; + struct drm_crtc_state *crtc_state = &dm_crtc_state->base; + struct drm_plane *plane; + bool consider_mode_change = false; + bool entire_crtc_covered = false; + bool cursor_changed = false; + int underlying_scale_w, underlying_scale_h; + int cursor_scale_w, cursor_scale_h; + int i; + + /* Overlay cursor not supported on HW before DCN + * DCN401/420 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions + * as previous DCN generations, so enable native mode on DCN401/420 + */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1) || + amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) || +#if defined(CONFIG_DRM_AMD_DC_DCN6_0) + amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) || + amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(6, 0, 0)) { +#else + amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1)) { +#endif + *cursor_mode = DM_CURSOR_NATIVE_MODE; + return 0; + } + + /* Init cursor_mode to be the same as current */ + *cursor_mode = dm_crtc_state->cursor_mode; + + /* + * Cursor mode can change if a plane's format changes, scale changes, is + * enabled/disabled, z-order changes, or color management properties change. + */ + for_each_oldnew_plane_in_state(state, plane, old_plane_state, plane_state, i) { + int new_scale_w, new_scale_h, old_scale_w, old_scale_h; + + /* Only care about planes on this CRTC */ + if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0) + continue; + + if (plane->type == DRM_PLANE_TYPE_CURSOR) + cursor_changed = true; + + if (drm_atomic_plane_enabling(old_plane_state, plane_state) || + drm_atomic_plane_disabling(old_plane_state, plane_state) || + old_plane_state->fb->format != plane_state->fb->format) { + consider_mode_change = true; + break; + } + + dm_get_plane_scale(plane_state, &new_scale_w, &new_scale_h); + dm_get_plane_scale(old_plane_state, &old_scale_w, &old_scale_h); + if (new_scale_w != old_scale_w || new_scale_h != old_scale_h) { + consider_mode_change = true; + break; + } + + /* + * A non-cursor plane moving or resizing (without a scale change) + * changes how much of the CRTC it covers. This can create or + * remove a hole under the cursor and thus flip the required + * cursor mode (native vs overlay), so its destination rect must + * be re-evaluated too. + * + * The cursor plane itself is deliberately excluded: the cursor + * mode depends on the underlying planes' coverage, not on the + * cursor's position (see the entire_crtc_covered logic below). + * Triggering on cursor movement would force every legacy cursor + * update off its fast path, and in a cursor-only commit - where + * the underlying planes are not part of the state - the coverage + * loop would see no covering plane and misevaluate the mode as + * overlay, regressing flip-vs-cursor-legacy. + */ + if (plane->type != DRM_PLANE_TYPE_CURSOR && + (old_plane_state->crtc_x != plane_state->crtc_x || + old_plane_state->crtc_y != plane_state->crtc_y || + old_plane_state->crtc_w != plane_state->crtc_w || + old_plane_state->crtc_h != plane_state->crtc_h)) { + consider_mode_change = true; + break; + } + + if (dm_plane_color_pipeline_active(state, plane, true) != + dm_plane_color_pipeline_active(state, plane, false)) { + consider_mode_change = true; + break; + } + } + + if (!consider_mode_change && !crtc_state->zpos_changed) + return 0; + + /* + * If no cursor change on this CRTC, and not enabled on this CRTC, then + * no need to set cursor mode. This avoids needlessly locking the cursor + * state. + */ + if (!cursor_changed && + !(drm_plane_mask(crtc_state->crtc->cursor) & crtc_state->plane_mask)) { + return 0; + } + + cursor_state = drm_atomic_get_plane_state(state, + crtc_state->crtc->cursor); + if (IS_ERR(cursor_state)) + return PTR_ERR(cursor_state); + + /* Cursor is disabled */ + if (!cursor_state->fb) + return 0; + + /* For all planes in descending z-order (all of which are below cursor + * as per zpos definitions), check their scaling and format + */ + for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, plane_state) { + + /* Only care about non-cursor planes on this CRTC */ + if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0 || + plane->type == DRM_PLANE_TYPE_CURSOR) + continue; + + /* Underlying plane is YUV format - use overlay cursor */ + if (amdgpu_dm_plane_is_video_format(plane_state->fb->format->format)) { + *cursor_mode = DM_CURSOR_OVERLAY_MODE; + return 0; + } + + /* Underlying plane has an active color pipeline - cursor would be transformed */ + if (dm_plane_color_pipeline_active(state, plane, false)) { + *cursor_mode = DM_CURSOR_OVERLAY_MODE; + return 0; + } + + dm_get_plane_scale(plane_state, + &underlying_scale_w, &underlying_scale_h); + dm_get_plane_scale(cursor_state, + &cursor_scale_w, &cursor_scale_h); + + /* Underlying plane has different scale - use overlay cursor */ + if (cursor_scale_w != underlying_scale_w && + cursor_scale_h != underlying_scale_h) { + *cursor_mode = DM_CURSOR_OVERLAY_MODE; + return 0; + } + + /* If this plane covers the whole CRTC, no need to check planes underneath */ + if (plane_state->crtc_x <= 0 && plane_state->crtc_y <= 0 && + plane_state->crtc_x + plane_state->crtc_w >= crtc_state->mode.hdisplay && + plane_state->crtc_y + plane_state->crtc_h >= crtc_state->mode.vdisplay) { + entire_crtc_covered = true; + break; + } + } + + /* If planes do not cover the entire CRTC, use overlay mode to enable + * cursor over holes + */ + if (entire_crtc_covered) + *cursor_mode = DM_CURSOR_NATIVE_MODE; + else + *cursor_mode = DM_CURSOR_OVERLAY_MODE; + + return 0; +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_cursor.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_cursor.h new file mode 100644 index 000000000000..5f619e4474aa --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_cursor.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __AMDGPU_DM_CURSOR_H__ +#define __AMDGPU_DM_CURSOR_H__ + +int amdgpu_dm_check_native_cursor_state(struct drm_crtc *new_plane_crtc, + struct drm_plane *plane, + struct drm_plane_state *new_plane_state, + bool enable); + +bool amdgpu_dm_should_update_native_cursor(struct drm_atomic_commit *state, + struct drm_crtc *old_plane_crtc, + struct drm_crtc *new_plane_crtc, + bool enable); + +int amdgpu_dm_crtc_get_cursor_mode(struct amdgpu_device *adev, + struct drm_atomic_commit *state, + struct dm_crtc_state *dm_crtc_state, + enum amdgpu_dm_cursor_mode *cursor_mode); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +void dm_get_oriented_plane_size(struct drm_plane_state *plane_state, + int *src_w, int *src_h); +void dm_get_plane_scale(struct drm_plane_state *plane_state, + int *out_plane_scale_w, int *out_plane_scale_h); +#endif /* CONFIG_DRM_AMD_DC_KUNIT_TEST */ + +#endif /* __AMDGPU_DM_CURSOR_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c index c17488e57eae..8e35d9ec22d6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c @@ -38,7 +38,7 @@ #include "amdgpu_ucode.h" #include "amdgpu_dm.h" #include "amdgpu_dm_dmub.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" #include <linux/component.h> #include <linux/firmware.h> @@ -385,7 +385,7 @@ dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev, return DMUB_STATUS_TIMEOUT; } -static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev) +STATIC_IFN_KUNIT void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev) { void *bb; long long addr; @@ -440,6 +440,7 @@ free_bb: return NULL; } +EXPORT_IF_KUNIT(dm_dmub_get_vbios_bounding_box); enum dmub_ips_disable_type dm_get_default_ips_mode( struct amdgpu_device *adev) @@ -829,8 +830,9 @@ out: mutex_unlock(&adev->dm.dpia_aux_lock); return ret; } +EXPORT_IF_KUNIT(amdgpu_dm_process_dmub_aux_transfer_sync); -static void abort_fused_io( +STATIC_IFN_KUNIT void abort_fused_io( struct dc_context *ctx, const struct dmub_cmd_fused_request *request ) @@ -844,6 +846,7 @@ static void abort_fused_io( io->request = *request; dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT); } +EXPORT_IF_KUNIT(abort_fused_io); static bool execute_fused_io( struct amdgpu_device *dev, @@ -931,6 +934,7 @@ int amdgpu_dm_process_dmub_set_config_sync( mutex_unlock(&adev->dm.dpia_aux_lock); return ret; } +EXPORT_IF_KUNIT(amdgpu_dm_process_dmub_set_config_sync); bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) { @@ -939,6 +943,7 @@ bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, e guard(spinlock_irqsave)(&adev->dm.dmub_lock); return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type); } +EXPORT_IF_KUNIT(dm_execute_dmub_cmd); bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h index a4a03e40ec37..c53728d1c131 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h @@ -65,4 +65,13 @@ int dm_init_microcode(struct amdgpu_device *adev); #define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin" #define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin" +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +struct dc_context; +struct dmub_cmd_fused_request; + +void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev); +void abort_fused_io(struct dc_context *ctx, + const struct dmub_cmd_fused_request *request); +#endif + #endif /* AMDGPU_DM_AMDGPU_DM_DMUB_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c new file mode 100644 index 000000000000..7e484adb4120 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#include <drm/drm_atomic.h> +#include <drm/drm_modes.h> +#include <drm/drm_vblank.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_crtc.h" +#include "amdgpu_dm_psr.h" +#include "amdgpu_dm_replay.h" +#include "amdgpu_dm_freesync.h" +#include "dm_helpers.h" +#include "modules/inc/mod_freesync.h" + +bool amdgpu_dm_is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, + struct dm_crtc_state *new_state) +{ + if (new_state->stream->adjust.timing_adjust_pending) + return true; + if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED) + return true; + else if (amdgpu_dm_crtc_vrr_active(old_state) != amdgpu_dm_crtc_vrr_active(new_state)) + return true; + else + return false; +} +EXPORT_IF_KUNIT(amdgpu_dm_is_dc_timing_adjust_needed); + +bool +amdgpu_dm_is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state) +{ + const struct drm_display_mode *old_mode, *new_mode; + + if (!old_crtc_state || !new_crtc_state) + return false; + + old_mode = &old_crtc_state->mode; + new_mode = &new_crtc_state->mode; + + if (old_mode->clock == new_mode->clock && + old_mode->hdisplay == new_mode->hdisplay && + old_mode->vdisplay == new_mode->vdisplay && + old_mode->htotal == new_mode->htotal && + old_mode->vtotal != new_mode->vtotal && + old_mode->hsync_start == new_mode->hsync_start && + old_mode->vsync_start != new_mode->vsync_start && + old_mode->hsync_end == new_mode->hsync_end && + old_mode->vsync_end != new_mode->vsync_end && + old_mode->hskew == new_mode->hskew && + old_mode->vscan == new_mode->vscan && + (old_mode->vsync_end - old_mode->vsync_start) == + (new_mode->vsync_end - new_mode->vsync_start)) + return true; + + return false; +} +EXPORT_IF_KUNIT(amdgpu_dm_is_timing_unchanged_for_freesync); + +void amdgpu_dm_set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state) +{ + u64 num, den, res; + struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base; + + dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; + + num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000; + den = (unsigned long long)new_crtc_state->mode.htotal * + (unsigned long long)new_crtc_state->mode.vtotal; + + res = div_u64(num, den); + dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res; +} +EXPORT_IF_KUNIT(amdgpu_dm_set_freesync_fixed_config); + +void amdgpu_dm_reset_freesync_config_for_crtc( + struct dm_crtc_state *new_crtc_state) +{ + new_crtc_state->vrr_supported = false; + + memset(&new_crtc_state->vrr_infopacket, 0, + sizeof(new_crtc_state->vrr_infopacket)); +} +EXPORT_IF_KUNIT(amdgpu_dm_reset_freesync_config_for_crtc); + +void amdgpu_dm_get_freesync_config_for_crtc( + struct dm_crtc_state *new_crtc_state, + struct dm_connector_state *new_con_state) +{ + struct mod_freesync_config config = {0}; + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode *mode = &new_crtc_state->base.mode; + int vrefresh = drm_mode_vrefresh(mode); + bool fs_vid_mode = false; + + if (new_con_state->base.connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + return; + + aconnector = to_amdgpu_dm_connector(new_con_state->base.connector); + + new_crtc_state->vrr_supported = new_con_state->freesync_capable && + vrefresh >= aconnector->min_vfreq && + vrefresh <= aconnector->max_vfreq; + + if (new_crtc_state->vrr_supported) { + new_crtc_state->stream->ignore_msa_timing_param = true; + fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED; + + config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000; + config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000; + config.vsif_supported = true; + config.btr = true; + + if (fs_vid_mode) { + config.state = VRR_STATE_ACTIVE_FIXED; + config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz; + goto out; + } else if (new_crtc_state->base.vrr_enabled) { + config.state = VRR_STATE_ACTIVE_VARIABLE; + } else { + config.state = VRR_STATE_INACTIVE; + } + } else { + config.state = VRR_STATE_UNSUPPORTED; + } +out: + new_crtc_state->freesync_config = config; +} +EXPORT_IF_KUNIT(amdgpu_dm_get_freesync_config_for_crtc); + +void amdgpu_dm_update_freesync_state_on_stream( + struct amdgpu_display_manager *dm, + struct dm_crtc_state *new_crtc_state, + struct dc_stream_state *new_stream, + struct dc_plane_state *surface, + u32 flip_timestamp_in_us) +{ + struct mod_vrr_params vrr_params; + struct dc_info_packet vrr_infopacket = {0}; + struct amdgpu_device *adev = dm->adev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc); + unsigned long flags; + bool pack_sdp_v1_3 = false; + struct amdgpu_dm_connector *aconn; + enum vrr_packet_type packet_type = PACKET_TYPE_VRR; + + if (!new_stream) + return; + + /* + * TODO: Determine why min/max totals and vrefresh can be 0 here. + * For now it's sufficient to just guard against these conditions. + */ + + if (!new_stream->timing.h_total || !new_stream->timing.v_total) + return; + + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); + vrr_params = acrtc->dm_irq_params.vrr_params; + + if (surface) { + mod_freesync_handle_preflip( + dm->freesync_module, + surface, + new_stream, + flip_timestamp_in_us, + &vrr_params); + + if (adev->family < AMDGPU_FAMILY_AI && + amdgpu_dm_crtc_vrr_active(new_crtc_state)) { + mod_freesync_handle_v_update(dm->freesync_module, + new_stream, &vrr_params); + + /* Need to call this before the frame ends. */ + dc_stream_adjust_vmin_vmax(dm->dc, + new_crtc_state->stream, + &vrr_params.adjust); + } + } + + aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context; + + if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) { + pack_sdp_v1_3 = aconn->pack_sdp_v1_3; + + if (aconn->vsdb_info.amd_vsdb_version == 1) + packet_type = PACKET_TYPE_FS_V1; + else if (aconn->vsdb_info.amd_vsdb_version == 2) + packet_type = PACKET_TYPE_FS_V2; + else if (aconn->vsdb_info.amd_vsdb_version == 3) + packet_type = PACKET_TYPE_FS_V3; + + mod_build_adaptive_sync_infopacket(new_stream, aconn->as_type, NULL, + &new_stream->adaptive_sync_infopacket); + } + + mod_freesync_build_vrr_infopacket( + dm->freesync_module, + new_stream, + &vrr_params, + packet_type, + TRANSFER_FUNC_UNKNOWN, + &vrr_infopacket, + pack_sdp_v1_3); + + new_crtc_state->freesync_vrr_info_changed |= + (memcmp(&new_crtc_state->vrr_infopacket, + &vrr_infopacket, + sizeof(vrr_infopacket)) != 0); + + acrtc->dm_irq_params.vrr_params = vrr_params; + new_crtc_state->vrr_infopacket = vrr_infopacket; + + new_stream->vrr_infopacket = vrr_infopacket; + new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params); + + if (new_crtc_state->freesync_vrr_info_changed) + drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d", + new_crtc_state->base.crtc->base.id, + (int)new_crtc_state->base.vrr_enabled, + (int)vrr_params.state); + + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); +} + +void amdgpu_dm_update_stream_irq_parameters( + struct amdgpu_display_manager *dm, + struct dm_crtc_state *new_crtc_state) +{ + struct dc_stream_state *new_stream = new_crtc_state->stream; + struct mod_vrr_params vrr_params; + struct mod_freesync_config config = new_crtc_state->freesync_config; + struct amdgpu_device *adev = dm->adev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc); + unsigned long flags; + + if (!new_stream) + return; + + /* + * TODO: Determine why min/max totals and vrefresh can be 0 here. + * For now it's sufficient to just guard against these conditions. + */ + if (!new_stream->timing.h_total || !new_stream->timing.v_total) + return; + + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); + vrr_params = acrtc->dm_irq_params.vrr_params; + + if (new_crtc_state->vrr_supported && + config.min_refresh_in_uhz && + config.max_refresh_in_uhz) { + /* + * if freesync compatible mode was set, config.state will be set + * in atomic check + */ + if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz && + (!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) || + new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) { + vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz; + vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz; + vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz; + vrr_params.state = VRR_STATE_ACTIVE_FIXED; + } else { + config.state = new_crtc_state->base.vrr_enabled ? + VRR_STATE_ACTIVE_VARIABLE : + VRR_STATE_INACTIVE; + } + } else { + config.state = VRR_STATE_UNSUPPORTED; + } + + mod_freesync_build_vrr_params(dm->freesync_module, + new_stream, + &config, &vrr_params); + + new_crtc_state->freesync_config = config; + /* Copy state for access from DM IRQ handler */ + acrtc->dm_irq_params.freesync_config = config; + acrtc->dm_irq_params.active_planes = new_crtc_state->active_planes; + acrtc->dm_irq_params.vrr_params = vrr_params; + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); +} + +void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm, + struct dm_crtc_state *old_state, + struct dm_crtc_state *new_state) +{ + struct amdgpu_device *adev = dm->adev; + bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state); + bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state); + + /* Only DCE gates vupdate on VRR, keep it enabled for DCN */ + bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0; + + if (!old_vrr_active && new_vrr_active) { + /* Transition VRR inactive -> active: + * While VRR is active, we must not disable vblank irq, as a + * reenable after disable would compute bogus vblank/pflip + * timestamps if it likely happened inside display front-porch. + * + * We also need vupdate irq for the actual core vblank handling + * at end of vblank. + */ + if (vrr_gates_vupdate) + WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0); + WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0); + drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n", + __func__, new_state->base.crtc->base.id); + + scoped_guard(mutex, &dm->dc_lock) { + dc_exit_ips_for_hw_access(dm->dc); + amdgpu_dm_psr_set_event(dm, new_state->stream, true, + psr_event_vrr_transition, true); + amdgpu_dm_replay_set_event(dm, new_state->stream, true, + replay_event_vrr, true); + } + } else if (old_vrr_active && !new_vrr_active) { + /* Transition VRR active -> inactive: + * Allow vblank irq disable again for fixed refresh rate. + */ + if (vrr_gates_vupdate) + WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0); + drm_crtc_vblank_put(new_state->base.crtc); + drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n", + __func__, new_state->base.crtc->base.id); + + scoped_guard(mutex, &dm->dc_lock) { + dc_exit_ips_for_hw_access(dm->dc); + amdgpu_dm_psr_set_event(dm, new_state->stream, false, + psr_event_vrr_transition, false); + amdgpu_dm_replay_set_event(dm, new_state->stream, false, + replay_event_vrr, false); + } + } +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.h new file mode 100644 index 000000000000..24c410306284 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __AMDGPU_DM_FREESYNC_H__ +#define __AMDGPU_DM_FREESYNC_H__ + +#include <linux/types.h> + +struct amdgpu_display_manager; +struct dm_crtc_state; +struct dm_connector_state; +struct dc_stream_state; +struct dc_plane_state; +struct drm_crtc_state; + +bool amdgpu_dm_is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, + struct dm_crtc_state *new_state); + +bool amdgpu_dm_is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state); + +void amdgpu_dm_set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state); + +void amdgpu_dm_reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state); + +void amdgpu_dm_get_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state, + struct dm_connector_state *new_con_state); + +void amdgpu_dm_update_freesync_state_on_stream(struct amdgpu_display_manager *dm, + struct dm_crtc_state *new_crtc_state, + struct dc_stream_state *new_stream, + struct dc_plane_state *surface, + u32 flip_timestamp_in_us); + +void amdgpu_dm_update_stream_irq_parameters(struct amdgpu_display_manager *dm, + struct dm_crtc_state *new_crtc_state); + +void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm, + struct dm_crtc_state *old_state, + struct dm_crtc_state *new_state); + +#endif /* __AMDGPU_DM_FREESYNC_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c index 5f43f6a74ffd..c887a70ae74d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c @@ -31,7 +31,6 @@ #include "dm_helpers.h" #include <drm/display/drm_hdcp_helper.h> #include "hdcp_psp.h" -#include "amdgpu_dm_kunit_helpers.h" /* * If the SRM version being loaded is less than or equal to the @@ -39,8 +38,7 @@ */ #define PSP_SRM_VERSION_MAX 0xFFFF -static bool -lp_write_i2c(void *handle, uint32_t address, const uint8_t *data, uint32_t size) +STATIC_IFN_KUNIT bool lp_write_i2c(void *handle, uint32_t address, const uint8_t *data, uint32_t size) { struct dc_link *link = handle; struct i2c_payload i2c_payloads[] = {{true, address, size, (void *)data} }; @@ -49,9 +47,9 @@ lp_write_i2c(void *handle, uint32_t address, const uint8_t *data, uint32_t size) return dm_helpers_submit_i2c(link->ctx, link, &cmd); } +EXPORT_IF_KUNIT(lp_write_i2c); -static bool -lp_read_i2c(void *handle, uint32_t address, uint8_t offset, uint8_t *data, uint32_t size) +STATIC_IFN_KUNIT bool lp_read_i2c(void *handle, uint32_t address, uint8_t offset, uint8_t *data, uint32_t size) { struct dc_link *link = handle; @@ -62,52 +60,56 @@ lp_read_i2c(void *handle, uint32_t address, uint8_t offset, uint8_t *data, uint3 return dm_helpers_submit_i2c(link->ctx, link, &cmd); } +EXPORT_IF_KUNIT(lp_read_i2c); -static bool -lp_write_dpcd(void *handle, uint32_t address, const uint8_t *data, uint32_t size) +STATIC_IFN_KUNIT bool lp_write_dpcd(void *handle, uint32_t address, const uint8_t *data, uint32_t size) { struct dc_link *link = handle; return dm_helpers_dp_write_dpcd(link->ctx, link, address, data, size); } +EXPORT_IF_KUNIT(lp_write_dpcd); -static bool -lp_read_dpcd(void *handle, uint32_t address, uint8_t *data, uint32_t size) +STATIC_IFN_KUNIT bool lp_read_dpcd(void *handle, uint32_t address, uint8_t *data, uint32_t size) { struct dc_link *link = handle; return dm_helpers_dp_read_dpcd(link->ctx, link, address, data, size); } - -static bool lp_atomic_write_poll_read_i2c( - void *handle, - const struct mod_hdcp_atomic_op_i2c *write, - const struct mod_hdcp_atomic_op_i2c *poll, - struct mod_hdcp_atomic_op_i2c *read, - uint32_t poll_timeout_us, - uint8_t poll_mask_msb +EXPORT_IF_KUNIT(lp_read_dpcd); + +STATIC_IFN_KUNIT bool lp_atomic_write_poll_read_i2c( + void *handle, + const struct mod_hdcp_atomic_op_i2c *write, + const struct mod_hdcp_atomic_op_i2c *poll, + struct mod_hdcp_atomic_op_i2c *read, + uint32_t poll_timeout_us, + uint8_t poll_mask_msb ) { struct dc_link *link = handle; return dm_atomic_write_poll_read_i2c(link, write, poll, read, poll_timeout_us, poll_mask_msb); } - -static bool lp_atomic_write_poll_read_aux( - void *handle, - const struct mod_hdcp_atomic_op_aux *write, - const struct mod_hdcp_atomic_op_aux *poll, - struct mod_hdcp_atomic_op_aux *read, - uint32_t poll_timeout_us, - uint8_t poll_mask_msb +EXPORT_IF_KUNIT(lp_atomic_write_poll_read_i2c); + +STATIC_IFN_KUNIT bool lp_atomic_write_poll_read_aux( + void *handle, + const struct mod_hdcp_atomic_op_aux *write, + const struct mod_hdcp_atomic_op_aux *poll, + struct mod_hdcp_atomic_op_aux *read, + uint32_t poll_timeout_us, + uint8_t poll_mask_msb ) { struct dc_link *link = handle; return dm_atomic_write_poll_read_aux(link, write, poll, read, poll_timeout_us, poll_mask_msb); } +EXPORT_IF_KUNIT(lp_atomic_write_poll_read_aux); -static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size) +STATIC_IFN_KUNIT +uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size) { struct ta_hdcp_shared_memory *hdcp_cmd; @@ -130,8 +132,10 @@ static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint return hdcp_cmd->out_msg.hdcp_get_srm.srm_buf; } +EXPORT_IF_KUNIT(psp_get_srm); -static int psp_set_srm(struct psp_context *psp, +STATIC_IFN_KUNIT +int psp_set_srm(struct psp_context *psp, u8 *srm, uint32_t srm_size, uint32_t *srm_version) { struct ta_hdcp_shared_memory *hdcp_cmd; @@ -158,6 +162,7 @@ static int psp_set_srm(struct psp_context *psp, *srm_version = hdcp_cmd->out_msg.hdcp_set_srm.srm_version; return 0; } +EXPORT_IF_KUNIT(psp_set_srm); STATIC_IFN_KUNIT void process_output(struct hdcp_workqueue *hdcp_work) @@ -246,7 +251,8 @@ void hdcp_get_link_display_adjustments( } EXPORT_IF_KUNIT(hdcp_get_link_display_adjustments); -static void link_lock(struct hdcp_workqueue *work, bool lock) +STATIC_IFN_KUNIT +void link_lock(struct hdcp_workqueue *work, bool lock) { int i = 0; @@ -257,6 +263,7 @@ static void link_lock(struct hdcp_workqueue *work, bool lock) mutex_unlock(&work[i].mutex); } } +EXPORT_IF_KUNIT(link_lock); STATIC_IFN_KUNIT void hdcp_update_display_encryption_control(struct hdcp_workqueue *hdcp_work, @@ -312,8 +319,10 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, process_output(hdcp_w); } +EXPORT_IF_KUNIT(hdcp_update_display); -static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, +STATIC_IFN_KUNIT +void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index, struct amdgpu_dm_connector *aconnector) { @@ -343,6 +352,7 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, } process_output(hdcp_w); } +EXPORT_IF_KUNIT(hdcp_remove_display); void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index) { @@ -366,6 +376,7 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_inde process_output(hdcp_w); } +EXPORT_IF_KUNIT(hdcp_reset_display); void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, unsigned int link_index) { @@ -375,7 +386,8 @@ void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, unsigned int link_index } EXPORT_IF_KUNIT(hdcp_handle_cpirq); -static void event_callback(struct work_struct *work) +STATIC_IFN_KUNIT +void event_callback(struct work_struct *work) { struct hdcp_workqueue *hdcp_work; @@ -391,6 +403,7 @@ static void event_callback(struct work_struct *work) process_output(hdcp_work); } +EXPORT_IF_KUNIT(event_callback); STATIC_IFN_KUNIT void event_property_update(struct work_struct *work) @@ -454,7 +467,8 @@ void event_property_update(struct work_struct *work) } EXPORT_IF_KUNIT(event_property_update); -static void event_property_validate(struct work_struct *work) +STATIC_IFN_KUNIT +void event_property_validate(struct work_struct *work) { struct hdcp_workqueue *hdcp_work = container_of(to_delayed_work(work), struct hdcp_workqueue, property_validate_dwork); @@ -503,8 +517,10 @@ static void event_property_validate(struct work_struct *work) } } } +EXPORT_IF_KUNIT(event_property_validate); -static void event_watchdog_timer(struct work_struct *work) +STATIC_IFN_KUNIT +void event_watchdog_timer(struct work_struct *work) { struct hdcp_workqueue *hdcp_work; @@ -522,8 +538,10 @@ static void event_watchdog_timer(struct work_struct *work) process_output(hdcp_work); } +EXPORT_IF_KUNIT(event_watchdog_timer); -static void event_cpirq(struct work_struct *work) +STATIC_IFN_KUNIT +void event_cpirq(struct work_struct *work) { struct hdcp_workqueue *hdcp_work; @@ -535,6 +553,7 @@ static void event_cpirq(struct work_struct *work) process_output(hdcp_work); } +EXPORT_IF_KUNIT(event_cpirq); void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work) { @@ -551,8 +570,10 @@ void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work) kfree(hdcp_work->srm_temp); kfree(hdcp_work); } +EXPORT_IF_KUNIT(hdcp_destroy); -static bool enable_assr(void *handle, struct dc_link *link) +STATIC_IFN_KUNIT +bool enable_assr(void *handle, struct dc_link *link) { struct hdcp_workqueue *hdcp_work = handle; struct mod_hdcp hdcp = hdcp_work->hdcp; @@ -585,8 +606,10 @@ static bool enable_assr(void *handle, struct dc_link *link) return true; } +EXPORT_IF_KUNIT(enable_assr); -static void update_config(void *handle, struct cp_psp_stream_config *config) +STATIC_IFN_KUNIT +void update_config(void *handle, struct cp_psp_stream_config *config) { struct hdcp_workqueue *hdcp_work = handle; struct amdgpu_dm_connector *aconnector; @@ -672,6 +695,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) hdcp_w->aconnector[conn_index] = aconnector; process_output(hdcp_w); } +EXPORT_IF_KUNIT(update_config); /** * DOC: Add sysfs interface for set/get srm @@ -721,7 +745,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) * -if we try to "1. SET" a newer version and PSP rejects it. That means the format is * incorrect/corrupted and we should correct our SRM by getting it from PSP */ -static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, +STATIC_IFN_KUNIT ssize_t srm_data_write(struct file *filp, struct kobject *kobj, const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { @@ -744,8 +768,9 @@ static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, return count; } +EXPORT_IF_KUNIT(srm_data_write); -static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, +STATIC_IFN_KUNIT ssize_t srm_data_read(struct file *filp, struct kobject *kobj, const struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { @@ -781,6 +806,7 @@ ret: link_lock(work, false); return ret; } +EXPORT_IF_KUNIT(srm_data_read); /* From the hdcp spec (5.Renewability) SRM needs to be stored in a non-volatile memory. * diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h index a82a20b80518..665fa4839a25 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h @@ -44,6 +44,12 @@ struct mod_hdcp_link; struct mod_hdcp_display; struct cp_psp; struct amdgpu_device; +struct psp_context; +struct file; +struct kobject; +struct bin_attribute; +struct mod_hdcp_atomic_op_i2c; +struct mod_hdcp_atomic_op_aux; struct hdcp_workqueue { struct work_struct cpirq_work; @@ -113,6 +119,37 @@ void hdcp_update_display_encryption_control(struct hdcp_workqueue *hdcp_work, unsigned int conn_index, bool enable_encryption); void event_property_update(struct work_struct *work); +void event_property_validate(struct work_struct *work); +void event_callback(struct work_struct *work); +void event_watchdog_timer(struct work_struct *work); +void event_cpirq(struct work_struct *work); +void link_lock(struct hdcp_workqueue *work, bool lock); +void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index, + struct amdgpu_dm_connector *aconnector); +uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size); +int psp_set_srm(struct psp_context *psp, u8 *srm, uint32_t srm_size, uint32_t *srm_version); +bool enable_assr(void *handle, struct dc_link *link); +void update_config(void *handle, struct cp_psp_stream_config *config); +ssize_t srm_data_write(struct file *filp, struct kobject *kobj, + const struct bin_attribute *bin_attr, char *buffer, + loff_t pos, size_t count); +ssize_t srm_data_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *bin_attr, char *buffer, + loff_t pos, size_t count); +bool lp_write_i2c(void *handle, uint32_t address, const uint8_t *data, uint32_t size); +bool lp_read_i2c(void *handle, uint32_t address, uint8_t offset, uint8_t *data, uint32_t size); +bool lp_write_dpcd(void *handle, uint32_t address, const uint8_t *data, uint32_t size); +bool lp_read_dpcd(void *handle, uint32_t address, uint8_t *data, uint32_t size); +bool lp_atomic_write_poll_read_i2c(void *handle, + const struct mod_hdcp_atomic_op_i2c *write, + const struct mod_hdcp_atomic_op_i2c *poll, + struct mod_hdcp_atomic_op_i2c *read, + uint32_t poll_timeout_us, uint8_t poll_mask_msb); +bool lp_atomic_write_poll_read_aux(void *handle, + const struct mod_hdcp_atomic_op_aux *write, + const struct mod_hdcp_atomic_op_aux *poll, + struct mod_hdcp_atomic_op_aux *read, + uint32_t poll_timeout_us, uint8_t poll_mask_msb); #endif #endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 30ac1f32fad5..c720c319e795 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -48,7 +48,6 @@ #include "dm_helpers.h" #include "ddc_service_types.h" #include "clk_mgr.h" -#include "amdgpu_dm_kunit_helpers.h" #include "amdgpu_dm_helpers.h" #define MCCS_DEST_ADDR (0x6E >> 1) @@ -137,6 +136,17 @@ STATIC_IFN_KUNIT void apply_edid_quirks(struct dc_link *link, struct edid *edid, drm_dbg_driver(dev, "Skip PHY SSC reduction on panel id %X\n", panel_id); link->wa_flags.skip_phy_ssc_reduction = true; break; + /* + * Workaround for Apple Studio Display which exposes a 2x1 tiled panel + * over two SST DP links. Hide the secondary tile from userspace so + * compositors drive a single 5K stream on the primary link only. + */ + case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE3A): + case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE42): + case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE46): + drm_dbg_driver(dev, "Hiding secondary tile on panel id %X\n", panel_id); + edid_caps->panel_patch.disable_second_tile = true; + break; default: return; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c index ae3f81f53074..9be63996b062 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -33,7 +33,7 @@ #include "amdgpu_display.h" #include "amdgpu_dm.h" #include "amdgpu_dm_irq.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" #include "amdgpu_dm_crtc.h" #include "amdgpu_dm_hdcp.h" #include "amdgpu_dm_mst_types.h" @@ -1960,23 +1960,26 @@ static void dm_crtc_high_irq_handler(struct amdgpu_device *adev, bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0; if (acrtc->wb_conn && acrtc->wb_pending) { - struct dc_stream_state *stream = acrtc->dm_irq_params.stream; - unsigned int v_total, refresh_hz; - - v_total = stream->adjust.v_total_max ? - stream->adjust.v_total_max : stream->timing.v_total; - refresh_hz = div_u64((uint64_t) stream->timing.pix_clk_100hz * - 100LL, (v_total * stream->timing.h_total)); - mdelay(1000 / refresh_hz); - - /* - * Completion (signalling the out fence and releasing the vblank - * reference taken in dm_set_writeback()) is handled by the shared - * helper, which is also used by the teardown path. - */ - if (amdgpu_dm_crtc_complete_writeback(acrtc)) + if (acrtc->wb_frame_done) { + /* + * Second vblank: the DMA for the captured frame has + * had a full frame period to flush to memory. Signal + * the out fence now. + */ + amdgpu_dm_crtc_complete_writeback(acrtc); + } else { + /* + * First vblank after arming: the frame has been + * scanned out and the DMA is finishing. Disable + * writeback immediately to prevent the hardware from + * starting a new capture that would overwrite the + * buffer. Signal completion on the next vblank to + * ensure the DMA is fully flushed to memory. + */ dc_stream_fc_disable_writeback(adev->dm.dc, acrtc->dm_irq_params.stream, 0); + acrtc->wb_frame_done = true; + } } vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c index a9575bf25fc2..4e57572e12b6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c @@ -32,7 +32,7 @@ #include "amdgpu_dm_ism.h" #include "amdgpu_dm_crtc.h" #include "amdgpu_dm_trace.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" /** diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h deleted file mode 100644 index 1f910a6a00c0..000000000000 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright 2026 Advanced Micro Devices, Inc. - */ - -#ifndef AMDGPU_DM_KUNIT_HELPERS_H -#define AMDGPU_DM_KUNIT_HELPERS_H - -#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) -#define STATIC_IFN_KUNIT -#define INLINE_IFN_KUNIT inline -#define EXPORT_IF_KUNIT(symbol) EXPORT_SYMBOL(symbol) - -#else -#define STATIC_IFN_KUNIT static -#define INLINE_IFN_KUNIT -#define EXPORT_IF_KUNIT(symbol) -#endif - -#endif /* AMDGPU_DM_KUNIT_HELPERS_H */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index a45d8d79f3b2..e001d868db80 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -40,7 +40,6 @@ #include "dc.h" #include "dm_helpers.h" -#include "amdgpu_dm_kunit_helpers.h" #include "ddc_service_types.h" #include "dpcd_defs.h" diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 26f35434a92f..0c6f0cac666e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -37,7 +37,7 @@ #include "amdgpu_display.h" #include "amdgpu_dm_trace.h" #include "amdgpu_dm_plane.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" #include "amdgpu_dm_colorop.h" #include "gc/gc_11_0_0_offset.h" #include "gc/gc_11_0_0_sh_mask.h" @@ -180,6 +180,22 @@ STATIC_IFN_KUNIT void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *si } EXPORT_IF_KUNIT(amdgpu_dm_plane_add_modifier); +STATIC_IFN_KUNIT void amdgpu_dm_plane_add_modifier_dedup(uint64_t **mods, uint64_t *size, + uint64_t *cap, uint64_t mod) +{ + uint64_t i; + + if (!*mods) + return; + + for (i = 0; i < *size; ++i) + if ((*mods)[i] == mod) + return; + + amdgpu_dm_plane_add_modifier(mods, size, cap, mod); +} +EXPORT_IF_KUNIT(amdgpu_dm_plane_add_modifier_dedup); + STATIC_IFN_KUNIT bool amdgpu_dm_plane_modifier_has_dcc(uint64_t modifier) { return IS_AMD_FMT_MOD(modifier) && AMD_FMT_MOD_GET(DCC, modifier); @@ -195,38 +211,40 @@ STATIC_IFN_KUNIT unsigned int amdgpu_dm_plane_modifier_gfx9_swizzle_mode(uint64_ } EXPORT_IF_KUNIT(amdgpu_dm_plane_modifier_gfx9_swizzle_mode); -STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_info *tiling_info, - uint64_t tiling_flags) +STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx6_tiling_info_from_modifier( + struct dc_tiling_info *tiling_info, + uint64_t modifier) { - /* Fill GFX8 params */ - if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == DC_ARRAY_2D_TILED_THIN1) { - unsigned int bankw, bankh, mtaspect, tile_split, num_banks; - bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH); - bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT); - mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT); - tile_split = AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT); - num_banks = AMDGPU_TILING_GET(tiling_flags, NUM_BANKS); - + if (modifier == DRM_FORMAT_MOD_LINEAR) { + tiling_info->gfx8.array_mode = DC_ARRAY_LINEAR_GENERAL; tiling_info->gfxversion = DcGfxVersion8; - /* XXX fix me for VI */ - tiling_info->gfx8.num_banks = num_banks; - tiling_info->gfx8.array_mode = - DC_ARRAY_2D_TILED_THIN1; - tiling_info->gfx8.tile_split = tile_split; - tiling_info->gfx8.bank_width = bankw; - tiling_info->gfx8.bank_height = bankh; - tiling_info->gfx8.tile_aspect = mtaspect; - tiling_info->gfx8.tile_mode = - DC_ADDR_SURF_MICRO_TILING_DISPLAY; - } else if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) - == DC_ARRAY_1D_TILED_THIN1) { - tiling_info->gfx8.array_mode = DC_ARRAY_1D_TILED_THIN1; + + return 0; } - tiling_info->gfx8.pipe_config = - AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); + if (!IS_AMD_FMT_MOD(modifier)) + return -EINVAL; + + if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) != AMD_FMT_MOD_TILE_VER_GFX6) + return -EINVAL; + + tiling_info->gfx8.array_mode = AMD_FMT_MOD_GET(TILE, modifier); + tiling_info->gfxversion = DcGfxVersion8; + tiling_info->gfx8.tile_mode = AMD_FMT_MOD_GET(MICROTILE, modifier); + + if (AMD_FMT_MOD_GET(TILE, modifier) < AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1) + return 0; + + tiling_info->gfx8.pipe_config = AMD_FMT_MOD_GET(PIPE_CONFIG, modifier); + tiling_info->gfx8.tile_split = AMD_FMT_MOD_GET(TILE_SPLIT, modifier); + tiling_info->gfx8.bank_width = AMD_FMT_MOD_GET(BANK_WIDTH, modifier); + tiling_info->gfx8.bank_height = AMD_FMT_MOD_GET(BANK_HEIGHT, modifier); + tiling_info->gfx8.tile_aspect = AMD_FMT_MOD_GET(MACRO_TILE_ASPECT, modifier); + tiling_info->gfx8.num_banks = AMD_FMT_MOD_GET(NUM_BANKS, modifier); + + return 0; } -EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags); +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx6_tiling_info_from_modifier); STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev, struct dc_tiling_info *tiling_info) @@ -468,6 +486,249 @@ static void amdgpu_dm_plane_add_gfx10_1_modifiers(const struct amdgpu_device *ad AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9)); } +/** + * amdgpu_dm_plane_get_gfx6_tile_idx() - Get tile mode index on GFX6-8 + * + * @adev: amdgpu_device pointer + * @bpp: bits per pixel in the image + * @arr: array mode (aka. tile mode) of the image layout + * + * Select which tiling mode from the table is suitable for the given bits per pixel + * using the given array mode, assuming the displayable micro tile mode is used. + * + * On GFX6-8, the GFX block can only use pre-programmed tiling modes from GB_TILE_MODEn + * registers which are programmed by the kernel according to the tiling mode table. + * Note that the tiling table is uAPI and userspace relies on specific modes being + * present at specific indices. + * See SiLib::HwlSetupTileInfo() and CiLib::HwlSetupTileInfo() in addrlib. + */ +static u32 amdgpu_dm_plane_get_gfx6_tile_idx(const struct amdgpu_device *adev, + const u32 bpp, + const enum array_mode_values arr) +{ + /* Assume that the microtile mode is DISPLAY. */ + + if (arr == DC_ARRAY_1D_TILED_THIN1) + return 9; + + ASSERT(arr == DC_ARRAY_2D_TILED_THIN1); + + if (adev->family > AMDGPU_FAMILY_SI) + return 10; + + switch (bpp) { + case 8: + return 10; + case 16: + return 11; + default: + return 12; + } +} + +/** + * amdgpu_dm_plane_calc_gfx7_tile_split() - Calculate tile split on GFX7-8 + * + * @adev: amdgpu_device pointer + * @bpp: bits per pixel in the image + * @gb_tile_mode: GB_TILE_MODEn register value for the current tiling mode + * + * Calculate the actual tile split value on GFX7-8, assuming 2D_TILED_THIN1 array mode + * with a non-depth micro tile mode. + * + * On GFX7-8, SAMPLE_SPLIT holds a factor from which the actual tile split bytes + * can be calculated. The TILE_SPLIT field is only used for the depth micro tile mode. + * See CiLib::HwlComputeMacroModeIndex() in addrlib. + */ +static u32 amdgpu_dm_plane_calc_gfx7_tile_split(const struct amdgpu_device *adev, + const u32 bpp, + const u32 gb_tile_mode) +{ + /* Assume 2D_TILED_THIN1 mode with non-DEPTH microtiles */ + const u32 sample_split = (gb_tile_mode >> 25) & 0x3; + const u32 thickness = 1; + const u32 tile_size_pixels = 8 * 8; + const u32 tile_bytes_1x = tile_size_pixels * bpp * thickness / 8; + const u32 sample_split_factor = 1 << sample_split; + + return clamp(tile_bytes_1x * sample_split_factor, + 256, + adev->gfx.config.mem_row_size_in_kb * 1024); +} + +/** + * amdgpu_dm_plane_get_gfx7_macro_tile_idx() - Get macro tile mode index on GFX7-8 + * + * @bpp: bits per pixel in the image + * @tile_split_bytes: actual tile split bytes, see amdgpu_dm_plane_calc_gfx7_tile_split() + * + * Select which macro tiling mode from the table is suitable for the given bits per pixel, + * assuming 2D_TILED_THIN1 array mode and DISPLAY micro tile mode (and no multisampling). + * Note that the tiling table is uAPI and userspace relies on specific modes being + * present at specific indices. + * See CiLib::HwlComputeMacroModeIndex() in addrlib. + */ +static u32 amdgpu_dm_plane_get_gfx7_macro_tile_idx(const u32 bpp, const u32 tile_split_bytes) +{ + const u32 thickness = 1; + const u32 tile_size_pixels = 8 * 8; + const u32 tile_bytes_1x = thickness * tile_size_pixels * bpp / 8; + const u32 tile_bytes = clamp(tile_bytes_1x, 64, tile_split_bytes); + const u32 macro_tile_idx = ilog2(tile_bytes / 64); + + WARN_ON(macro_tile_idx >= 16); + + return macro_tile_idx; +} + +/** + * amdgpu_dm_plane_calc_gfx6_mod() - Calculate a DRM format modifier for GFX6-8 + * + * @adev: amdgpu_device pointer + * @bpp: bits per pixel in the image + * @arr: array mode (aka. tile mode) of the image layout + * + * Select suitable micro and macro tile modes for the given bits per pixel, + * and calculate the corresponding DRM format modifier. + */ +static u64 amdgpu_dm_plane_calc_gfx6_mod(const struct amdgpu_device *adev, + const u32 bpp, + const enum array_mode_values arr) +{ + u32 array_mode, micro_tile_mode, tile_split_bytes; + u32 gb_macrotile_mode, macrotile_idx; + u32 gb_tile_mode, tile_idx; + + u64 modifier_base = + AMD_FMT_MOD | + AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX6) | + AMD_FMT_MOD_SET(TILE, arr) | + AMD_FMT_MOD_SET(MICROTILE, AMD_FMT_MOD_MICROTILE_DISPLAY); + + if (arr < DC_ARRAY_2D_TILED_THIN1) + return modifier_base; + + tile_idx = amdgpu_dm_plane_get_gfx6_tile_idx(adev, bpp, arr); + gb_tile_mode = adev->gfx.config.tile_mode_array[tile_idx]; + array_mode = (gb_tile_mode >> 2) & 0xf; + + if (adev->family == AMDGPU_FAMILY_SI) { + micro_tile_mode = (gb_tile_mode >> 0) & 0x3; + tile_split_bytes = 64 << ((gb_tile_mode >> 11) & 0x7); + } else { + micro_tile_mode = (gb_tile_mode >> 22) & 0x7; + tile_split_bytes = amdgpu_dm_plane_calc_gfx7_tile_split(adev, bpp, gb_tile_mode); + } + + ASSERT(array_mode == arr); + ASSERT(micro_tile_mode == AMD_FMT_MOD_MICROTILE_DISPLAY); + + modifier_base |= + AMD_FMT_MOD_SET(PIPE_CONFIG, (gb_tile_mode >> 6) & 0x1f) | + AMD_FMT_MOD_SET(TILE_SPLIT, ilog2(tile_split_bytes / 64)); + + if (adev->family == AMDGPU_FAMILY_SI) + return modifier_base | + AMD_FMT_MOD_SET(BANK_WIDTH, (gb_tile_mode >> 14) & 0x3) | + AMD_FMT_MOD_SET(BANK_HEIGHT, (gb_tile_mode >> 16) & 0x3) | + AMD_FMT_MOD_SET(MACRO_TILE_ASPECT, (gb_tile_mode >> 18) & 0x3) | + AMD_FMT_MOD_SET(NUM_BANKS, (gb_tile_mode >> 20) & 0x3); + + macrotile_idx = amdgpu_dm_plane_get_gfx7_macro_tile_idx(bpp, tile_split_bytes); + gb_macrotile_mode = adev->gfx.config.macrotile_mode_array[macrotile_idx]; + + return modifier_base | + AMD_FMT_MOD_SET(BANK_WIDTH, (gb_macrotile_mode >> 0) & 0x3) | + AMD_FMT_MOD_SET(BANK_HEIGHT, (gb_macrotile_mode >> 2) & 0x3) | + AMD_FMT_MOD_SET(MACRO_TILE_ASPECT, (gb_macrotile_mode >> 4) & 0x3) | + AMD_FMT_MOD_SET(NUM_BANKS, (gb_macrotile_mode >> 6) & 0x3); +} + +/** + * amdgpu_dm_plane_gfx6_format_mod_supported() - Check if a modifier is supported on GFX6-8 + * + * @adev: amdgpu_device pointer + * @bpp: bits per pixel in the image + * @modifier: the modifier whose support we check + * + * On GFX6-8, not all DRM format modifier can be used with all image formats. + * Check whether the specified modifier is supported with the given bits per pixel value. + */ +static bool amdgpu_dm_plane_gfx6_format_mod_supported(const struct amdgpu_device *adev, + const u32 bpp, + const u64 modifier) +{ + const u32 array_mode = AMD_FMT_MOD_GET(TILE, modifier); + const u32 micro_tile_mode = AMD_FMT_MOD_GET(MICROTILE, modifier); + + if (!IS_AMD_FMT_MOD(modifier)) + return false; + + /* GFX9 and newer format modifiers are not supported on GFX6-8. */ + if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) != AMD_FMT_MOD_TILE_VER_GFX6) + return false; + + /* GFX8 and older format modifiers are not supported on GFX9+. */ + if (adev->family >= AMDGPU_FAMILY_AI) + return false; + + /* GFX6-7 doesn't have any DCC, GFX8 doesn't support displayable DCC. */ + if (AMD_FMT_MOD_GET(DCC, modifier)) + return false; + + /* + * For now, only expose 1D and 2D tiles THIN array modes. + * Linear is already exposed through DRM_FORMAT_MOD_LINEAR. + */ + if (array_mode != AMD_FMT_MOD_TILE_GFX6_1D_TILED_THIN1 && + array_mode != AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1) + return false; + + /* + * For now, only allow DISPLAY micro tile mode. + * THIN, DEPTH and THICK modes are not displayable. + * ROTATED has never been supported by Linux or Mesa. + */ + if (micro_tile_mode != AMD_FMT_MOD_MICROTILE_DISPLAY) + return false; + + /* Verify that the modifier is the same that we'd expose for this bpp */ + return amdgpu_dm_plane_calc_gfx6_mod(adev, bpp, array_mode) == modifier; +} + +/** + * amdgpu_dm_plane_add_gfx6_modifiers() - Expose modifiers for GFX6-8 + * + * @adev: amdgpu_device pointer + * @mods: Pointer to array of format modifiers + * @size: Pointer to size of the array + * @capacity: Pointer to capacity of the array + * + * Calculate a DRM format modifier for macro tiled modes for each supported + * bits per pixel value. Use de-duplication because on some GPUs it may happen + * that different bpp results in the exact same macro tiling mode, depending + * on the tiling table. + * + * Also expose a micro tiled only mode. This is less optimal, but supported. + */ +static void amdgpu_dm_plane_add_gfx6_modifiers(const struct amdgpu_device *adev, + u64 **mods, + u64 *size, + u64 *capacity) +{ + /* 2D tiled displayable */ + amdgpu_dm_plane_add_modifier(mods, size, capacity, + amdgpu_dm_plane_calc_gfx6_mod(adev, 16, DC_ARRAY_2D_TILED_THIN1)); + amdgpu_dm_plane_add_modifier_dedup(mods, size, capacity, + amdgpu_dm_plane_calc_gfx6_mod(adev, 32, DC_ARRAY_2D_TILED_THIN1)); + amdgpu_dm_plane_add_modifier_dedup(mods, size, capacity, + amdgpu_dm_plane_calc_gfx6_mod(adev, 64, DC_ARRAY_2D_TILED_THIN1)); + + /* 1D tiled displayable */ + amdgpu_dm_plane_add_modifier(mods, size, capacity, + amdgpu_dm_plane_calc_gfx6_mod(adev, 0, DC_ARRAY_1D_TILED_THIN1)); +} + static void amdgpu_dm_plane_add_gfx9_modifiers(const struct amdgpu_device *adev, uint64_t **mods, uint64_t *size, @@ -749,12 +1010,6 @@ static void amdgpu_dm_plane_add_gfx12_modifiers(struct amdgpu_device *adev, STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, unsigned int plane_type, uint64_t **mods) { uint64_t size = 0, capacity = 128; - *mods = NULL; - - /* We have not hooked up any pre-GFX9 modifiers. */ - if (adev->family < AMDGPU_FAMILY_AI) - return 0; - *mods = kmalloc_array(capacity, sizeof(uint64_t), GFP_KERNEL); if (plane_type == DRM_PLANE_TYPE_CURSOR) { @@ -764,6 +1019,13 @@ STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *a } switch (adev->family) { + case AMDGPU_FAMILY_SI: + case AMDGPU_FAMILY_CI: + case AMDGPU_FAMILY_KV: + case AMDGPU_FAMILY_VI: + case AMDGPU_FAMILY_CZ: + amdgpu_dm_plane_add_gfx6_modifiers(adev, mods, &size, &capacity); + break; case AMDGPU_FAMILY_AI: case AMDGPU_FAMILY_RV: amdgpu_dm_plane_add_gfx9_modifiers(adev, mods, &size, &capacity); @@ -865,7 +1127,6 @@ int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev, const struct amdgpu_framebuffer *afb, const enum surface_pixel_format format, const enum dc_rotation_angle rotation, - const uint64_t tiling_flags, struct dc_tiling_info *tiling_info, struct plane_size *plane_size, struct dc_plane_dcc_param *dcc, @@ -941,7 +1202,10 @@ int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev, if (ret) return ret; } else { - amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(tiling_info, tiling_flags); + ret = amdgpu_dm_plane_fill_gfx6_tiling_info_from_modifier(tiling_info, + afb->base.modifier); + if (ret) + return ret; } return 0; @@ -1032,7 +1296,6 @@ static int amdgpu_dm_plane_helper_prepare_fb(struct drm_plane *plane, amdgpu_dm_plane_fill_plane_buffer_attributes( adev, afb, plane_state->format, plane_state->rotation, - afb->tiling_flags, &plane_state->tiling_info, &plane_state->plane_size, &plane_state->dcc, &plane_state->address, afb->tmz_surface); @@ -1587,6 +1850,7 @@ STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *pla { struct amdgpu_device *adev = drm_to_adev(plane->dev); const struct drm_format_info *info = drm_format_info(format); + const u32 bpp = drm_format_info_bpp(info, 0); int i; if (!info) @@ -1610,6 +1874,13 @@ STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *pla if (i == plane->modifier_count) return false; + if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) == AMD_FMT_MOD_TILE_VER_GFX6) + return amdgpu_dm_plane_gfx6_format_mod_supported(adev, bpp, modifier); + + /* GFX9+ modifers are not supported on GFX8 and older. */ + if (adev->family < AMDGPU_FAMILY_AI) + return false; + /* GFX12 doesn't have these limitations. */ if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) <= AMD_FMT_MOD_TILE_VER_GFX11) { enum dm_micro_swizzle microtile = amdgpu_dm_plane_modifier_gfx9_swizzle_mode(modifier) & 3; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h index 365e306a52e7..092ade738ce9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h @@ -48,7 +48,6 @@ int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev, const struct amdgpu_framebuffer *afb, const enum surface_pixel_format format, const enum dc_rotation_angle rotation, - const uint64_t tiling_flags, struct dc_tiling_info *tiling_info, struct plane_size *plane_size, struct dc_plane_dcc_param *dcc, @@ -71,8 +70,6 @@ bool amdgpu_dm_plane_is_video_format(uint32_t format); #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size, uint64_t *cap, uint64_t mod); -void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_info *tiling_info, - uint64_t tiling_flags); void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev, struct dc_tiling_info *tiling_info); void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev, @@ -124,5 +121,9 @@ void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane); struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane); void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); +void amdgpu_dm_plane_add_modifier_dedup(uint64_t **mods, uint64_t *size, + uint64_t *cap, uint64_t mod); +int amdgpu_dm_plane_fill_gfx6_tiling_info_from_modifier(struct dc_tiling_info *tiling_info, + uint64_t modifier); #endif #endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c index 0d2e5294d062..3280bfadc6a8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c @@ -33,7 +33,7 @@ #include "amdgpu_dm_irq.h" #include "amdgpu_pm.h" #include "dm_pp_smu.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" #include "amdgpu_dm_pp_smu.h" STATIC_IFN_KUNIT void build_pm_display_cfg( @@ -792,3 +792,55 @@ void dm_pp_get_funcs( } } EXPORT_IF_KUNIT(dm_pp_get_funcs); + +int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev) +{ + int ret = 0; + + /* This interface is for dGPU Navi1x.Linux dc-pplib interface depends + * on window driver dc implementation. + * For Navi1x, clock settings of dcn watermarks are fixed. the settings + * should be passed to smu during boot up and resume from s3. + * boot up: dc calculate dcn watermark clock settings within dc_create, + * dcn20_resource_construct + * then call pplib functions below to pass the settings to smu: + * smu_set_watermarks_for_clock_ranges + * smu_set_watermarks_table + * navi10_set_watermarks_table + * smu_write_watermarks_table + * + * For Renoir, clock settings of dcn watermark are also fixed values. + * dc has implemented different flow for window driver: + * dc_hardware_init / dc_set_power_state + * dcn10_init_hw + * notify_wm_ranges + * set_wm_ranges + * -- Linux + * smu_set_watermarks_for_clock_ranges + * renoir_set_watermarks_table + * smu_write_watermarks_table + * + * For Linux, + * dc_hardware_init -> amdgpu_dm_init + * dc_set_power_state --> dm_resume + * + * therefore, this function apply to navi10/12/14 but not Renoir + * * + */ + switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { + case IP_VERSION(2, 0, 2): + case IP_VERSION(2, 0, 0): + break; + default: + return 0; + } + + ret = amdgpu_dpm_write_watermarks_table(adev); + if (ret) { + drm_err(adev_to_drm(adev), "Failed to update WMTABLE!\n"); + return ret; + } + + return 0; +} +EXPORT_IF_KUNIT(amdgpu_dm_smu_write_watermarks_table); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h index f918eb71f0d1..1d153d0e2f03 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h @@ -11,6 +11,9 @@ struct amd_pp_display_configuration; struct pp_smu_wm_range_sets; struct dm_pp_wm_sets_with_clock_ranges_soc15; +struct amdgpu_device; + +int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev); #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) void build_pm_display_cfg(struct amd_pp_display_configuration *pm_display_cfg, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c index f87de3d18ac0..dbad0a6d3a2b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c @@ -30,7 +30,7 @@ #include "dc.h" #include "amdgpu_dm.h" #include "modules/power/power_helpers.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" STATIC_IFN_KUNIT bool link_supports_psrsu(struct dc_link *link) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c index cf28d50c3b5e..259966703cef 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c @@ -28,7 +28,7 @@ #include "amdgpu.h" #include "amdgpu_dm.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" struct amdgpu_dm_quirks { bool aux_hpd_discon; @@ -178,3 +178,35 @@ void retrieve_dmi_info(struct amdgpu_display_manager *dm) } } EXPORT_IF_KUNIT(retrieve_dmi_info); + +struct amdgpu_stutter_quirk { + u16 chip_vendor; + u16 chip_device; + u16 subsys_vendor; + u16 subsys_device; + u8 revision; +}; + +static const struct amdgpu_stutter_quirk amdgpu_stutter_quirk_list[] = { + /* https://bugzilla.kernel.org/show_bug.cgi?id=214417 */ + { 0x1002, 0x15dd, 0x1002, 0x15dd, 0xc8 }, + { 0, 0, 0, 0, 0 }, +}; + +bool dm_should_disable_stutter(struct pci_dev *pdev) +{ + const struct amdgpu_stutter_quirk *p = amdgpu_stutter_quirk_list; + + while (p && p->chip_device != 0) { + if (pdev->vendor == p->chip_vendor && + pdev->device == p->chip_device && + pdev->subsystem_vendor == p->subsys_vendor && + pdev->subsystem_device == p->subsys_device && + pdev->revision == p->revision) { + return true; + } + ++p; + } + return false; +} +EXPORT_IF_KUNIT(dm_should_disable_stutter); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c index 42e17119461d..ee2e4754b089 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c @@ -31,7 +31,7 @@ #include "modules/power/power_helpers.h" #include "dmub/inc/dmub_cmd.h" #include "dc/inc/link_service.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" /* diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c index 6c0464754ed8..6dbc8d1b4ecd 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c @@ -36,7 +36,7 @@ #include "amdgpu_dm_irq.h" #include "amdgpu_pm.h" #include "amdgpu_dm_trace.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" unsigned long long dm_get_elapse_time_in_ns(struct dc_context *ctx, @@ -96,3 +96,56 @@ bool dm_query_extended_brightness_caps(struct dc_context *ctx, return true; } EXPORT_IF_KUNIT(dm_query_extended_brightness_caps); + +void* +dm_allocate_gpu_mem( + struct amdgpu_device *adev, + enum dc_gpu_mem_alloc_type type, + size_t size, + long long *addr) +{ + struct dal_allocation *da; + u32 domain = (type == DC_MEM_ALLOC_TYPE_GART) ? + AMDGPU_GEM_DOMAIN_GTT : AMDGPU_GEM_DOMAIN_VRAM; + int ret; + + da = kzalloc_obj(*da); + if (!da) + return NULL; + + ret = amdgpu_bo_create_kernel(adev, size, PAGE_SIZE, + domain, &da->bo, + &da->gpu_addr, &da->cpu_ptr); + + *addr = da->gpu_addr; + + if (ret) { + kfree(da); + return NULL; + } + + /* add da to list in dm */ + list_add(&da->list, &adev->dm.da_list); + + return da->cpu_ptr; +} + +void +dm_free_gpu_mem( + struct amdgpu_device *adev, + enum dc_gpu_mem_alloc_type type, + void *pvMem) +{ + struct dal_allocation *da; + + /* walk the da list in DM */ + list_for_each_entry(da, &adev->dm.da_list, list) { + if (pvMem == da->cpu_ptr) { + amdgpu_bo_free_kernel(&da->bo, &da->gpu_addr, &da->cpu_ptr); + list_del(&da->list); + kfree(da); + break; + } + } + +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c index 9e7bad4d6ed0..a7594012a0b2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c @@ -29,7 +29,7 @@ #include "amdgpu.h" #include "amdgpu_dm.h" #include "amdgpu_dm_wb.h" -#include "amdgpu_dm_kunit_helpers.h" +#include "dm_helpers.h" #include "amdgpu_display.h" #include "dc.h" diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile index 1592e8dae1a9..3a51fccc2a5e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile @@ -29,6 +29,8 @@ obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_plane_test.o obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_mst_types_test.o obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_pp_smu_test.o obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_freesync_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_cursor_test.o obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crtc_test.o obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_services_test.o obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_helpers_test.o diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c index 3f0108e9a951..040a7a9c0335 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c @@ -8,7 +8,14 @@ #include <kunit/test.h> #include <linux/device.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_audio_component.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_eld.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/drm_probe_helper.h> #include "dc.h" #include "dc/inc/core_types.h" @@ -17,6 +24,46 @@ #include "amdgpu_mode.h" #include "amdgpu_dm.h" #include "amdgpu_dm_audio.h" +#include "amdgpu_dm_kunit_test_helpers.h" + +static const struct drm_connector_funcs dm_test_audio_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, +}; + +static void dm_test_audio_connector_cleanup(void *data) +{ + drm_connector_cleanup(data); +} + +static struct drm_atomic_commit *dm_test_audio_alloc_atomic_state(struct kunit *test, + unsigned int num_connector, + unsigned int num_crtc) +{ + struct drm_atomic_commit *state; + + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state); + + state->num_connector = num_connector; + if (num_connector) { + state->connectors = kunit_kcalloc(test, num_connector, + sizeof(*state->connectors), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state->connectors); + } + + if (num_crtc) { + state->crtcs = kunit_kcalloc(test, num_crtc, sizeof(*state->crtcs), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state->crtcs); + } + + return state; +} /* Tests for amdgpu_dm_audio_init() */ @@ -392,6 +439,22 @@ static void dm_test_pin_eld_notify(void *audio_ptr, int port, int pipe) dm_test_eld_notify_ptr = audio_ptr; } +static void dm_test_audio_setup_notify_component(struct kunit *test, + struct amdgpu_device *adev) +{ + struct drm_audio_component *acomp; + struct drm_audio_component_audio_ops *audio_ops; + + acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL); + audio_ops = kunit_kzalloc(test, sizeof(*audio_ops), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, acomp); + KUNIT_ASSERT_NOT_NULL(test, audio_ops); + + audio_ops->pin_eld_notify = dm_test_pin_eld_notify; + acomp->audio_ops = audio_ops; + adev->dm.audio_component = acomp; +} + /** * dm_test_eld_notify_invokes_callback - Test ELD notify forwards to hda driver * @test: The KUnit test context @@ -569,6 +632,581 @@ static void dm_test_audio_init_pins_zero_count(struct kunit *test) /* End of tests for amdgpu_dm_audio_init_pins() */ +/* Tests for amdgpu_dm_audio_component_get_eld() */ + +/** + * dm_test_audio_component_get_eld_copies_matching_connector - Test ELD lookup + * @test: The KUnit test context + * + * The bound component callback should find the connector whose audio instance + * matches the requested port, report it enabled, and copy the connector ELD. + */ +static void dm_test_audio_component_get_eld_copies_matching_connector(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_dm_connector *aconnector; + struct drm_connector *wb_connector; + struct drm_audio_component *acomp; + struct device *kdev; + unsigned char buf[DRM_ELD_HEADER_BLOCK_SIZE + 8] = {0}; + bool enabled = false; + int ret; + + kdev = adev->ddev.dev; + KUNIT_ASSERT_NOT_NULL(test, kdev); + acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, acomp); + wb_connector = kunit_kzalloc(test, sizeof(*wb_connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, wb_connector); + KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, wb_connector, + &dm_test_audio_connector_funcs, + DRM_MODE_CONNECTOR_WRITEBACK), 0); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_audio_connector_cleanup, wb_connector), 0); + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, &aconnector->base, + &dm_test_audio_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA), 0); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_audio_connector_cleanup, &aconnector->base), 0); + + mutex_init(&adev->dm.audio_lock); + aconnector->audio_inst = 3; + aconnector->base.eld[DRM_ELD_BASELINE_ELD_LEN] = 2; + aconnector->base.eld[DRM_ELD_VER] = DRM_ELD_VER_CEA861D; + aconnector->base.eld[DRM_ELD_SPEAKER] = DRM_ELD_SPEAKER_FLR; + dev_set_drvdata(kdev, &adev->ddev); + + KUNIT_ASSERT_EQ(test, amdgpu_dm_audio_component_bind(kdev, NULL, acomp), 0); + ret = acomp->ops->get_eld(kdev, 3, 0, &enabled, buf, sizeof(buf)); + + KUNIT_EXPECT_EQ(test, ret, DRM_ELD_HEADER_BLOCK_SIZE + 8); + KUNIT_EXPECT_TRUE(test, enabled); + KUNIT_EXPECT_EQ(test, memcmp(buf, aconnector->base.eld, ret), 0); +} + +/** + * dm_test_audio_component_get_eld_no_match - Test ELD lookup miss + * @test: The KUnit test context + * + * A non-matching connector should leave the audio port disabled and return an + * empty ELD size without copying data. + */ +static void dm_test_audio_component_get_eld_no_match(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_dm_connector *aconnector; + struct drm_audio_component *acomp; + struct device *kdev; + unsigned char buf[DRM_ELD_HEADER_BLOCK_SIZE] = {0x5a}; + bool enabled = true; + int ret; + + kdev = adev->ddev.dev; + KUNIT_ASSERT_NOT_NULL(test, kdev); + acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, acomp); + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, &aconnector->base, + &dm_test_audio_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA), 0); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_audio_connector_cleanup, &aconnector->base), 0); + + mutex_init(&adev->dm.audio_lock); + aconnector->audio_inst = 4; + dev_set_drvdata(kdev, &adev->ddev); + + KUNIT_ASSERT_EQ(test, amdgpu_dm_audio_component_bind(kdev, NULL, acomp), 0); + ret = acomp->ops->get_eld(kdev, 3, 0, &enabled, buf, sizeof(buf)); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_FALSE(test, enabled); + KUNIT_EXPECT_EQ(test, buf[0], 0x5a); +} + +/* Tests for amdgpu_dm_commit_audio() */ + +/** + * dm_test_commit_audio_notifies_removed_connector - Test removal notification + * @test: The KUnit test context + * + * When a connector loses its CRTC, commit_audio should clear its audio + * instance and notify the audio component for the old pin. + */ +static void dm_test_commit_audio_notifies_removed_connector(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; + + state = dm_test_audio_alloc_atomic_state(test, 1, 0); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + + mutex_init(&adev->dm.audio_lock); + dm_test_audio_setup_notify_component(test, adev); + aconnector->audio_inst = 5; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + old_conn_state->crtc = crtc; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + + dm_test_eld_notify_count = 0; + dm_test_eld_notify_port = -1; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, -1); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 5); +} + +/** + * dm_test_commit_audio_notifies_added_connector - Test addition notification + * @test: The KUnit test context + * + * When a modeset enables a connector with a stream status, commit_audio should + * store the stream audio instance and notify the audio component for that pin. + */ +static void dm_test_commit_audio_notifies_added_connector(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; + struct dm_crtc_state *dm_crtc_state; + struct dc_stream_state *stream; + + state = dm_test_audio_alloc_atomic_state(test, 1, 1); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state); + KUNIT_ASSERT_NOT_NULL(test, stream); + + adev->dm.dc = kunit_kzalloc(test, sizeof(*adev->dm.dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc); + adev->dm.dc->current_state = kunit_kzalloc(test, + sizeof(*adev->dm.dc->current_state), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc->current_state); + stream->ctx = kunit_kzalloc(test, sizeof(*stream->ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, stream->ctx); + stream->ctx->dc = adev->dm.dc; + adev->dm.dc->current_state->stream_count = 1; + adev->dm.dc->current_state->streams[0] = stream; + adev->dm.dc->current_state->stream_status[0].audio_inst = 8; + + mutex_init(&adev->dm.audio_lock); + dm_test_audio_setup_notify_component(test, adev); + aconnector->audio_inst = -1; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + crtc->index = 0; + new_conn_state->crtc = crtc; + dm_crtc_state->base.crtc = crtc; + dm_crtc_state->base.mode_changed = true; + dm_crtc_state->stream = stream; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + state->crtcs[0].ptr = crtc; + state->crtcs[0].new_state = &dm_crtc_state->base; + + dm_test_eld_notify_count = 0; + dm_test_eld_notify_port = -1; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 8); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 2); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 8); +} + +/** + * dm_test_commit_audio_skips_writeback_removal - Test writeback removal skip + * @test: The KUnit test context + * + * Writeback connectors do not represent an HDA audio pin, so removal-style + * notifications should leave their audio instance untouched. + */ +static void dm_test_commit_audio_skips_writeback_removal(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; + + state = dm_test_audio_alloc_atomic_state(test, 1, 0); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + + mutex_init(&adev->dm.audio_lock); + dm_test_audio_setup_notify_component(test, adev); + aconnector->audio_inst = 9; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK; + old_conn_state->crtc = crtc; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 9); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0); +} + +/** + * dm_test_commit_audio_skips_without_new_crtc_state - Test missing CRTC state + * @test: The KUnit test context + * + * A connector still attached to the same CRTC should not notify if the atomic + * state does not contain a new CRTC state for that CRTC. + */ +static void dm_test_commit_audio_skips_without_new_crtc_state(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; + + state = dm_test_audio_alloc_atomic_state(test, 1, 1); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + + mutex_init(&adev->dm.audio_lock); + dm_test_audio_setup_notify_component(test, adev); + aconnector->audio_inst = 4; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + crtc->index = 0; + old_conn_state->crtc = crtc; + new_conn_state->crtc = crtc; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 4); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0); +} + +/** + * dm_test_commit_audio_skips_without_stream_status - Test missing DC status + * @test: The KUnit test context + * + * If the new CRTC stream is absent from the current DC state, additions should + * be skipped because there is no audio instance to publish. + */ +static void dm_test_commit_audio_skips_without_stream_status(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; + struct dm_crtc_state *dm_crtc_state; + struct dc_stream_state *stream; + + state = dm_test_audio_alloc_atomic_state(test, 1, 1); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state); + KUNIT_ASSERT_NOT_NULL(test, stream); + + adev->dm.dc = kunit_kzalloc(test, sizeof(*adev->dm.dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc); + adev->dm.dc->current_state = kunit_kzalloc(test, + sizeof(*adev->dm.dc->current_state), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc->current_state); + stream->ctx = kunit_kzalloc(test, sizeof(*stream->ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, stream->ctx); + stream->ctx->dc = adev->dm.dc; + + mutex_init(&adev->dm.audio_lock); + dm_test_audio_setup_notify_component(test, adev); + aconnector->audio_inst = -1; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + crtc->index = 0; + new_conn_state->crtc = crtc; + dm_crtc_state->base.crtc = crtc; + dm_crtc_state->base.mode_changed = true; + dm_crtc_state->stream = stream; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + state->crtcs[0].ptr = crtc; + state->crtcs[0].new_state = &dm_crtc_state->base; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, -1); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1); +} + +/** + * dm_test_commit_audio_skips_detached_connector - Test detached connector skip + * @test: The KUnit test context + * + * A connector that remains detached across the commit should not notify or + * alter the stored audio instance. + */ +static void dm_test_commit_audio_skips_detached_connector(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + + state = dm_test_audio_alloc_atomic_state(test, 1, 0); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + + aconnector->audio_inst = 11; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 11); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0); +} + +/** + * dm_test_commit_audio_skips_without_modeset - Test no-modeset skip + * @test: The KUnit test context + * + * A connector that stays on the same CRTC should not notify if that CRTC does + * not need a modeset. + */ +static void dm_test_commit_audio_skips_without_modeset(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; + struct dm_crtc_state *dm_crtc_state; + + state = dm_test_audio_alloc_atomic_state(test, 1, 1); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state); + + aconnector->audio_inst = 12; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + crtc->index = 0; + old_conn_state->crtc = crtc; + new_conn_state->crtc = crtc; + dm_crtc_state->base.crtc = crtc; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + state->crtcs[0].ptr = crtc; + state->crtcs[0].new_state = &dm_crtc_state->base; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 12); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0); +} + +/** + * dm_test_commit_audio_skips_addition_without_stream - Test NULL stream skip + * @test: The KUnit test context + * + * A modeset still sends the removal-side ELD notification, but the addition + * side must skip if the new DM CRTC state has no stream. + */ +static void dm_test_commit_audio_skips_addition_without_stream(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; + struct dm_crtc_state *dm_crtc_state; + + state = dm_test_audio_alloc_atomic_state(test, 1, 1); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state); + + mutex_init(&adev->dm.audio_lock); + dm_test_audio_setup_notify_component(test, adev); + aconnector->audio_inst = 13; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + crtc->index = 0; + old_conn_state->crtc = crtc; + new_conn_state->crtc = crtc; + dm_crtc_state->base.crtc = crtc; + dm_crtc_state->base.mode_changed = true; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + state->crtcs[0].ptr = crtc; + state->crtcs[0].new_state = &dm_crtc_state->base; + + dm_test_eld_notify_count = 0; + dm_test_eld_notify_port = -1; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, -1); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 13); +} + +/** + * dm_test_commit_audio_skips_writeback_addition - Test writeback addition skip + * @test: The KUnit test context + * + * Even with a valid modeset and stream status, writeback connectors must not + * publish an HDA audio instance. + */ +static void dm_test_commit_audio_skips_writeback_addition(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_crtc *crtc; + struct dm_crtc_state *dm_crtc_state; + struct dc_stream_state *stream; + + state = dm_test_audio_alloc_atomic_state(test, 1, 1); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state); + KUNIT_ASSERT_NOT_NULL(test, stream); + + adev->dm.dc = kunit_kzalloc(test, sizeof(*adev->dm.dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc); + adev->dm.dc->current_state = kunit_kzalloc(test, + sizeof(*adev->dm.dc->current_state), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc->current_state); + stream->ctx = kunit_kzalloc(test, sizeof(*stream->ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, stream->ctx); + stream->ctx->dc = adev->dm.dc; + adev->dm.dc->current_state->stream_count = 1; + adev->dm.dc->current_state->streams[0] = stream; + adev->dm.dc->current_state->stream_status[0].audio_inst = 14; + + mutex_init(&adev->dm.audio_lock); + dm_test_audio_setup_notify_component(test, adev); + aconnector->audio_inst = 15; + aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK; + crtc->index = 0; + new_conn_state->crtc = crtc; + dm_crtc_state->base.crtc = crtc; + dm_crtc_state->base.mode_changed = true; + dm_crtc_state->stream = stream; + state->connectors[0].ptr = &aconnector->base; + state->connectors[0].old_state = old_conn_state; + state->connectors[0].new_state = new_conn_state; + state->crtcs[0].ptr = crtc; + state->crtcs[0].new_state = &dm_crtc_state->base; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_commit_audio(&adev->ddev, state); + + KUNIT_EXPECT_EQ(test, aconnector->audio_inst, 15); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0); +} + static struct kunit_case dm_audio_test_cases[] = { /* amdgpu_dm_audio_init */ KUNIT_CASE(dm_test_audio_init_disabled), @@ -593,6 +1231,19 @@ static struct kunit_case dm_audio_test_cases[] = { KUNIT_CASE(dm_test_eld_notify_no_component), KUNIT_CASE(dm_test_eld_notify_null_audio_ops), KUNIT_CASE(dm_test_eld_notify_null_callback), + /* amdgpu_dm_audio_component_get_eld */ + KUNIT_CASE(dm_test_audio_component_get_eld_copies_matching_connector), + KUNIT_CASE(dm_test_audio_component_get_eld_no_match), + /* amdgpu_dm_commit_audio */ + KUNIT_CASE(dm_test_commit_audio_notifies_removed_connector), + KUNIT_CASE(dm_test_commit_audio_notifies_added_connector), + KUNIT_CASE(dm_test_commit_audio_skips_writeback_removal), + KUNIT_CASE(dm_test_commit_audio_skips_without_new_crtc_state), + KUNIT_CASE(dm_test_commit_audio_skips_without_stream_status), + KUNIT_CASE(dm_test_commit_audio_skips_detached_connector), + KUNIT_CASE(dm_test_commit_audio_skips_without_modeset), + KUNIT_CASE(dm_test_commit_audio_skips_addition_without_stream), + KUNIT_CASE(dm_test_commit_audio_skips_writeback_addition), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c index f55f93747df7..7ca17f803f9d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c @@ -56,14 +56,7 @@ static void setup_test_connector(struct kunit *test, static void setup_test_dm_ddev(struct kunit *test, struct amdgpu_display_manager *dm) { - struct drm_device *ddev; - - ddev = kunit_kzalloc(test, sizeof(*ddev), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ddev); - - INIT_LIST_HEAD(&ddev->mode_config.connector_list); - spin_lock_init(&ddev->mode_config.connector_list_lock); - dm->ddev = ddev; + dm->ddev = dm_kunit_alloc_drm_with_connector_list(test); } /* Tests for dm_find_stream_with_link() */ @@ -333,6 +326,11 @@ static int dm_test_get_backlight_level_error(const struct dc_link *link) return DC_ERROR_UNEXPECTED; } +static void dm_test_unregister_backlight_device(void *data) +{ + backlight_device_unregister(data); +} + static bool dm_test_get_backlight_level_nits(struct dc_link *link, uint32_t *avg, uint32_t *peak) @@ -470,6 +468,52 @@ static void dm_test_register_backlight_device_negative_index(struct kunit *test) KUNIT_EXPECT_NULL(test, adev->dm.backlight_dev[0]); } +/** + * dm_test_register_backlight_device_success - Test native backlight registration + * @test: The KUnit test context + * + * A native backlight device must be registered with the calculated + * properties, and its initial readback must retain the initial brightness. + */ +static void dm_test_register_backlight_device_success(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_dm_connector *aconnector; + struct amdgpu_dm_backlight_caps *caps; + struct dc_link *link = dm_kunit_alloc_link(test); + struct drm_minor *primary; + unsigned int max; + + setup_test_link_service(test, link); + link->dc->link_srv->edp_get_backlight_level = dm_test_get_backlight_level_error; + primary = kunit_kzalloc(test, sizeof(*primary), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, primary); + adev->ddev.primary = primary; + adev->dm.backlight_link[0] = link; + + aconnector = dm_kunit_alloc_connector(test, adev, link); + aconnector->bl_idx = 0; + aconnector->base.kdev = adev->ddev.dev; + caps = &adev->dm.backlight_caps[0]; + caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps->ac_level = 50; + caps->dc_level = 25; + max = 0x101 * AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + + amdgpu_dm_register_backlight_device(aconnector); + + KUNIT_ASSERT_NOT_NULL(test, adev->dm.backlight_dev[0]); + KUNIT_EXPECT_EQ(test, adev->dm.backlight_dev[0]->props.max_brightness, max); + KUNIT_EXPECT_EQ(test, adev->dm.backlight_dev[0]->props.brightness, + DIV_ROUND_CLOSEST(max * caps->ac_level, 100)); + KUNIT_EXPECT_EQ(test, adev->dm.brightness[0], + adev->dm.backlight_dev[0]->props.brightness); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_unregister_backlight_device, + adev->dm.backlight_dev[0]), 0); +} + static struct drm_connector *setup_panel_power_savings_connector(struct kunit *test, struct device **device_out, struct dm_connector_state **state_out) @@ -1868,6 +1912,7 @@ static struct kunit_case dm_backlight_test_cases[] = { KUNIT_CASE(dm_test_backlight_get_brightness_uses_device_index), /* amdgpu_dm_register_backlight_device */ KUNIT_CASE(dm_test_register_backlight_device_negative_index), + KUNIT_CASE(dm_test_register_backlight_device_success), /* panel_power_savings_show / store */ KUNIT_CASE(dm_test_panel_power_savings_show_maps_disable_to_zero), KUNIT_CASE(dm_test_panel_power_savings_show_reports_level), diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c index d64c7da20f2c..8c2f7ab5f8d3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c @@ -7,14 +7,17 @@ #include <kunit/test.h> #include <linux/types.h> +#include <drm/drm_atomic.h> #include <drm/drm_colorop.h> #include <drm/drm_property.h> #include <uapi/drm/drm_mode.h> #include "dc.h" +#include "amdgpu.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" #include "amdgpu_dm_color.h" +#include "amdgpu_dm_kunit_test_helpers.h" /* ---- Tests for amdgpu_dm_fixpt_from_s3132 ---- */ @@ -1373,6 +1376,146 @@ static void dm_test_set_tf_distributed_points_pq(struct kunit *test) } /** + * dm_test_set_legacy_tf_identity - Legacy identity LUT uses the sRGB ROM path + * @test: KUnit test context + */ +static void dm_test_set_legacy_tf_identity(struct kunit *test) +{ + struct drm_color_lut *lut; + struct dc_transfer_func *tf; + int i; + + tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tf); + lut = kunit_kcalloc(test, MAX_COLOR_LEGACY_LUT_ENTRIES, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) { + u16 value = i * MAX_DRM_LUT_VALUE / (MAX_COLOR_LEGACY_LUT_ENTRIES - 1); + + lut[i].red = value; + lut[i].green = value; + lut[i].blue = value; + } + + tf->type = TF_TYPE_PREDEFINED; + tf->tf = TRANSFER_FUNCTION_SRGB; + + KUNIT_EXPECT_EQ(test, + __set_legacy_tf(tf, lut, MAX_COLOR_LEGACY_LUT_ENTRIES, true), + 0); +} + +/** + * dm_test_set_output_tf_linear - Linear output without a LUT calculates degamma + * @test: KUnit test context + */ +static void dm_test_set_output_tf_linear(struct kunit *test) +{ + struct dc_transfer_func *tf; + + tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tf); + tf->type = TF_TYPE_PREDEFINED; + tf->tf = TRANSFER_FUNCTION_LINEAR; + + KUNIT_EXPECT_EQ(test, __set_output_tf(tf, NULL, 0, false), 0); +} + +/** + * dm_test_set_output_tf_32_srgb_rom - sRGB output uses the no-LUT ROM path + * @test: KUnit test context + */ +static void dm_test_set_output_tf_32_srgb_rom(struct kunit *test) +{ + struct dc_transfer_func *tf; + + tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tf); + tf->type = TF_TYPE_PREDEFINED; + tf->tf = TRANSFER_FUNCTION_SRGB; + + KUNIT_EXPECT_EQ(test, __set_output_tf_32(tf, NULL, 0, true), 0); +} + +/** + * dm_test_set_input_tf_srgb - Predefined sRGB input needs no generated curve + * @test: KUnit test context + */ +static void dm_test_set_input_tf_srgb(struct kunit *test) +{ + struct dc_transfer_func *tf; + + tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tf); + tf->type = TF_TYPE_PREDEFINED; + tf->tf = TRANSFER_FUNCTION_SRGB; + + KUNIT_EXPECT_EQ(test, __set_input_tf(NULL, tf, NULL, 0), 0); +} + +/** + * dm_test_set_input_tf_32_srgb - 32-bit input wrapper accepts predefined sRGB + * @test: KUnit test context + */ +static void dm_test_set_input_tf_32_srgb(struct kunit *test) +{ + struct dc_transfer_func *tf; + + tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tf); + tf->type = TF_TYPE_PREDEFINED; + tf->tf = TRANSFER_FUNCTION_SRGB; + + KUNIT_EXPECT_EQ(test, __set_input_tf_32(NULL, tf, NULL, 0), 0); +} + +/** + * dm_test_set_transfer_funcs_with_luts - LUT-backed transfer functions succeed + * @test: KUnit test context + */ +static void dm_test_set_transfer_funcs_with_luts(struct kunit *test) +{ + struct drm_color_lut32 *lut32; + struct drm_color_lut *lut; + struct dc_transfer_func *tf; + + lut = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + lut32 = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, sizeof(*lut32), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut32); + tf = kunit_kzalloc(test, sizeof(*tf), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, tf); + + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = TRANSFER_FUNCTION_LINEAR; + KUNIT_EXPECT_EQ(test, + __set_output_tf(tf, lut, MAX_COLOR_LUT_ENTRIES, false), + 0); + + memset(tf, 0, sizeof(*tf)); + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = TRANSFER_FUNCTION_LINEAR; + KUNIT_EXPECT_EQ(test, + __set_output_tf_32(tf, lut32, MAX_COLOR_LUT_ENTRIES, false), + 0); + + memset(tf, 0, sizeof(*tf)); + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = TRANSFER_FUNCTION_SRGB; + KUNIT_EXPECT_EQ(test, + __set_input_tf(NULL, tf, lut, MAX_COLOR_LUT_ENTRIES), + 0); + + memset(tf, 0, sizeof(*tf)); + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = TRANSFER_FUNCTION_SRGB; + KUNIT_EXPECT_EQ(test, + __set_input_tf_32(NULL, tf, lut32, MAX_COLOR_LUT_ENTRIES), + 0); +} + +/** * dm_test_set_atomic_regamma_bypass - No LUT and linear TF: must take bypass path * @test: KUnit test context */ @@ -1393,6 +1536,23 @@ static void dm_test_set_atomic_regamma_bypass(struct kunit *test) } /** + * dm_test_set_atomic_regamma_srgb - Non-linear TF enables generated regamma + * @test: KUnit test context + */ +static void dm_test_set_atomic_regamma_srgb(struct kunit *test) +{ + struct dc_transfer_func *out_tf; + + out_tf = kunit_kzalloc(test, sizeof(*out_tf), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, out_tf); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_set_atomic_regamma(out_tf, NULL, 0, true, TRANSFER_FUNCTION_SRGB), + 0); + KUNIT_EXPECT_EQ(test, (int)out_tf->type, (int)TF_TYPE_DISTRIBUTED_POINTS); +} + +/** * dm_test_atomic_shaper_lut_bypass - No LUT and linear TF: must take bypass path * @test: KUnit test context */ @@ -1434,6 +1594,28 @@ static void dm_test_atomic_blend_lut_bypass(struct kunit *test) KUNIT_EXPECT_EQ(test, (int)cm->blend_func.tf, (int)TRANSFER_FUNCTION_LINEAR); } +/** + * dm_test_atomic_shaper_blend_srgb - Non-linear TFs enable shaper and blend + * @test: KUnit test context + */ +static void dm_test_atomic_shaper_blend_srgb(struct kunit *test) +{ + struct dc_plane_cm *cm; + + cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cm); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_atomic_shaper_lut(NULL, true, TRANSFER_FUNCTION_SRGB, 0, cm), + 0); + KUNIT_EXPECT_TRUE(test, cm->flags.bits.shaper_enable); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_atomic_blend_lut(NULL, false, TRANSFER_FUNCTION_SRGB, 0, cm), + 0); + KUNIT_EXPECT_TRUE(test, cm->flags.bits.blend_enable); +} + /* ---- Tests for __set_colorop_in_tf_1d_curve ---- */ /** @@ -1527,6 +1709,1175 @@ static void dm_test_set_colorop_in_tf_1d_curve_bypass(struct kunit *test) (int)TRANSFER_FUNCTION_LINEAR); } +/* ---- Tests for amdgpu_dm_init_color_mod ---- */ + +/** + * dm_test_init_color_mod - Smoke test: must initialize x-points without crashing + * @test: KUnit test context + */ +static void dm_test_init_color_mod(struct kunit *test) +{ + amdgpu_dm_init_color_mod(); + KUNIT_SUCCEED(test); +} + +/* ---- Tests for amdgpu_dm_verify_lut3d_size ---- */ + +/** + * dm_test_verify_lut3d_alloc_plane - Allocate a dm_plane_state for lut3d tests + * @test: KUnit test context + * + * Returns: a drm_plane_state pointer embedded in a zeroed dm_plane_state. + */ +static struct drm_plane_state * +dm_test_verify_lut3d_alloc_plane(struct kunit *test) +{ + struct dm_plane_state *dm_plane_state; + + dm_plane_state = kunit_kzalloc(test, sizeof(*dm_plane_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_plane_state); + + return &dm_plane_state->base; +} + +/** + * dm_test_verify_lut3d_alloc_adev - Allocate adev with a DC and given 3D LUT cap + * @test: KUnit test context + * @has_3dlut: value to program into caps.color.dpp.hw_3d_lut + * + * Returns: an amdgpu_device with adev->dm.dc allocated. + */ +static struct amdgpu_device * +dm_test_verify_lut3d_alloc_adev(struct kunit *test, bool has_3dlut) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + + adev->dm.dc = kunit_kzalloc(test, sizeof(*adev->dm.dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev->dm.dc); + adev->dm.dc->caps.color.dpp.hw_3d_lut = has_3dlut; + + return adev; +} + +/** + * dm_test_verify_lut3d_no_luts - No shaper/3D LUT blobs: must succeed + * @test: KUnit test context + */ +static void dm_test_verify_lut3d_no_luts(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_verify_lut3d_alloc_adev(test, false); + struct drm_plane_state *plane_state = dm_test_verify_lut3d_alloc_plane(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut3d_size(adev, plane_state), 0); +} + +/** + * dm_test_verify_lut3d_bad_shaper - Shaper LUT with wrong size: must return -EINVAL + * @test: KUnit test context + */ +static void dm_test_verify_lut3d_bad_shaper(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_verify_lut3d_alloc_adev(test, true); + struct drm_plane_state *plane_state = dm_test_verify_lut3d_alloc_plane(test); + struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); + + /* has_3dlut => expected shaper size is MAX_COLOR_LUT_ENTRIES */ + dm_plane_state->shaper_lut = dm_test_make_lut_blob(test, 128); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut3d_size(adev, plane_state), -EINVAL); +} + +/** + * dm_test_verify_lut3d_bad_lut3d - 3D LUT with wrong size: must return -EINVAL + * @test: KUnit test context + */ +static void dm_test_verify_lut3d_bad_lut3d(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_verify_lut3d_alloc_adev(test, true); + struct drm_plane_state *plane_state = dm_test_verify_lut3d_alloc_plane(test); + struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); + + /* Valid shaper, but wrong 3D LUT size */ + dm_plane_state->shaper_lut = dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES); + dm_plane_state->lut3d = dm_test_make_lut_blob(test, 128); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut3d_size(adev, plane_state), -EINVAL); +} + +/** + * dm_test_verify_lut3d_valid - Correct shaper and 3D LUT sizes: must succeed + * @test: KUnit test context + */ +static void dm_test_verify_lut3d_valid(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_verify_lut3d_alloc_adev(test, true); + struct drm_plane_state *plane_state = dm_test_verify_lut3d_alloc_plane(test); + struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); + const uint32_t lut3d_entries = + MAX_COLOR_3DLUT_SIZE * MAX_COLOR_3DLUT_SIZE * MAX_COLOR_3DLUT_SIZE; + + dm_plane_state->shaper_lut = dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES); + dm_plane_state->lut3d = dm_test_make_lut_blob(test, lut3d_entries); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_verify_lut3d_size(adev, plane_state), 0); +} + +/* ---- Tests for plane colorop helpers ---- */ + +/** + * struct dm_test_colorop_fixture - shared state for plane colorop walk tests + * @adev: backing amdgpu device (provides a real DRM device) + * @state: fabricated atomic state with a single colorop slot + * @colorop: the colorop under test + * @colorop_state: the new state attached to @colorop + * @plane_state: plane state whose ->state points at @state + * @dc_plane_state: DC plane state written by the helpers + */ +struct dm_test_colorop_fixture { + struct amdgpu_device *adev; + struct drm_atomic_commit *state; + struct drm_colorop *colorop; + struct drm_colorop_state *colorop_state; + struct drm_plane_state *plane_state; + struct dc_plane_state *dc_plane_state; +}; + +/** + * dm_test_colorop_setup - build a single-colorop atomic state fixture + * @test: KUnit test context + * @type: colorop type to assign + * + * Fabricates a minimal drm_atomic_commit with one colorop slot so that + * for_each_new_colorop_in_state() finds exactly the colorop under test. + * + * Returns: a populated fixture (by value). + */ +static struct dm_test_colorop_fixture +dm_test_colorop_setup(struct kunit *test, enum drm_colorop_type type) +{ + struct dm_test_colorop_fixture f = {0}; + struct __drm_colorops_state *colorops; + struct dm_plane_state *dm_plane_state; + + f.adev = dm_kunit_alloc_adev(test); + f.adev->ddev.mode_config.num_colorop = 1; + + f.colorop = kunit_kzalloc(test, sizeof(*f.colorop), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.colorop); + f.colorop->dev = &f.adev->ddev; + f.colorop->type = type; + + f.colorop_state = kunit_kzalloc(test, sizeof(*f.colorop_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.colorop_state); + f.colorop_state->colorop = f.colorop; + + colorops = kunit_kcalloc(test, 1, sizeof(*colorops), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, colorops); + colorops[0].ptr = f.colorop; + colorops[0].new_state = f.colorop_state; + + f.state = kunit_kzalloc(test, sizeof(*f.state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.state); + f.state->dev = &f.adev->ddev; + f.state->colorops = colorops; + + dm_plane_state = kunit_kzalloc(test, sizeof(*dm_plane_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_plane_state); + f.plane_state = &dm_plane_state->base; + f.plane_state->state = f.state; + + f.dc_plane_state = kunit_kzalloc(test, sizeof(*f.dc_plane_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.dc_plane_state); + + return f; +} + +/** + * dm_test_colorop_multiplier_applied - Multiplier colorop programs hdr_mult + * @test: KUnit test context + */ +static void dm_test_colorop_multiplier_applied(struct kunit *test) +{ + struct dm_test_colorop_fixture f = + dm_test_colorop_setup(test, DRM_COLOROP_MULTIPLIER); + + /* 1.0 in S31.32 sign-magnitude */ + f.colorop_state->multiplier = 1ULL << 32; + + KUNIT_EXPECT_EQ(test, + __set_dm_plane_colorop_multiplier(f.plane_state, f.dc_plane_state, f.colorop), + 0); + KUNIT_EXPECT_EQ(test, f.dc_plane_state->hdr_mult.value, (long long)(1ULL << 32)); +} + +/** + * dm_test_colorop_multiplier_no_match - Non-multiplier colorop leaves hdr_mult untouched + * @test: KUnit test context + */ +static void dm_test_colorop_multiplier_no_match(struct kunit *test) +{ + struct dm_test_colorop_fixture f = + dm_test_colorop_setup(test, DRM_COLOROP_1D_CURVE); + + f.colorop_state->multiplier = 1ULL << 32; + + KUNIT_EXPECT_EQ(test, + __set_dm_plane_colorop_multiplier(f.plane_state, f.dc_plane_state, f.colorop), + 0); + KUNIT_EXPECT_EQ(test, f.dc_plane_state->hdr_mult.value, 0LL); +} + +/** + * dm_test_colorop_3x4_matrix_applied - CTM 3x4 colorop enables gamut remap + * @test: KUnit test context + */ +static void dm_test_colorop_3x4_matrix_applied(struct kunit *test) +{ + struct dm_test_colorop_fixture f = + dm_test_colorop_setup(test, DRM_COLOROP_CTM_3X4); + struct drm_property_blob *blob; + struct drm_color_ctm_3x4 *ctm; + + ctm = kunit_kzalloc(test, sizeof(*ctm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctm); + ctm->matrix[0] = 1ULL << 32; /* identity diagonal */ + ctm->matrix[5] = 1ULL << 32; + ctm->matrix[10] = 1ULL << 32; + + blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob); + blob->data = ctm; + blob->length = sizeof(struct drm_color_ctm_3x4); + f.colorop_state->data = blob; + + KUNIT_EXPECT_EQ(test, + __set_dm_plane_colorop_3x4_matrix(f.plane_state, f.dc_plane_state, f.colorop), + 0); + KUNIT_EXPECT_TRUE(test, f.dc_plane_state->gamut_remap_matrix.enable_remap); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->input_csc_color_matrix.enable_adjustment); +} + +/** + * dm_test_colorop_3x4_matrix_bad_length - Wrong blob length: must return -EINVAL + * @test: KUnit test context + */ +static void dm_test_colorop_3x4_matrix_bad_length(struct kunit *test) +{ + struct dm_test_colorop_fixture f = + dm_test_colorop_setup(test, DRM_COLOROP_CTM_3X4); + struct drm_property_blob *blob; + + blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob); + blob->data = kunit_kzalloc(test, 8, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob->data); + blob->length = 7; /* not sizeof(struct drm_color_ctm_3x4) */ + f.colorop_state->data = blob; + + KUNIT_EXPECT_EQ(test, + __set_dm_plane_colorop_3x4_matrix(f.plane_state, f.dc_plane_state, f.colorop), + -EINVAL); +} + +/** + * dm_test_colorop_degamma_predefined - Degamma 1D curve programs predefined TF + * @test: KUnit test context + */ +static void dm_test_colorop_degamma_predefined(struct kunit *test) +{ + struct dm_test_colorop_fixture f = + dm_test_colorop_setup(test, DRM_COLOROP_1D_CURVE); + + /* SRGB_EOTF is part of amdgpu_dm_supported_degam_tfs */ + f.colorop_state->curve_1d_type = DRM_COLOROP_1D_CURVE_SRGB_EOTF; + f.colorop_state->bypass = false; + + KUNIT_EXPECT_EQ(test, + __set_dm_plane_colorop_degamma(f.plane_state, f.dc_plane_state, f.colorop), + 0); + KUNIT_EXPECT_EQ(test, + (int)f.dc_plane_state->in_transfer_func.type, + (int)TF_TYPE_PREDEFINED); + KUNIT_EXPECT_EQ(test, + (int)f.dc_plane_state->in_transfer_func.tf, + (int)TRANSFER_FUNCTION_SRGB); +} + +/** + * dm_test_colorop_degamma_no_match - Unsupported degamma curve: must return -EINVAL + * @test: KUnit test context + */ +static void dm_test_colorop_degamma_no_match(struct kunit *test) +{ + struct dm_test_colorop_fixture f = + dm_test_colorop_setup(test, DRM_COLOROP_1D_CURVE); + + /* SRGB_INV_EOTF is a shaper TF, not in amdgpu_dm_supported_degam_tfs */ + f.colorop_state->curve_1d_type = DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF; + + KUNIT_EXPECT_EQ(test, + __set_dm_plane_colorop_degamma(f.plane_state, f.dc_plane_state, f.colorop), + -EINVAL); +} + +/* ---- Tests for CRTC and plane color management update paths ---- */ + +/** + * struct dm_test_color_update_fixture - minimal color update fixture + * @adev: backing amdgpu device + * @state: DRM atomic state with @adev's DRM device + * @crtc_state: DM CRTC state under test + * @stream: DC stream referenced by @crtc_state + * @dm_plane_state: DM plane state under test + * @plane: DRM plane referenced by @dm_plane_state + * @dc_plane_state: DC plane state under test + */ +struct dm_test_color_update_fixture { + struct amdgpu_device *adev; + struct drm_atomic_commit *state; + struct dm_crtc_state *crtc_state; + struct dc_stream_state *stream; + struct dm_plane_state *dm_plane_state; + struct drm_plane *plane; + struct dc_plane_state *dc_plane_state; +}; + +/** + * dm_test_color_update_setup - allocate a minimal color update fixture + * @test: KUnit test context + * + * Returns: a populated fixture with all large DC/DRM state heap-allocated. + */ +static struct dm_test_color_update_fixture +dm_test_color_update_setup(struct kunit *test) +{ + struct dm_test_color_update_fixture f = {0}; + + f.adev = dm_kunit_alloc_adev(test); + f.adev->dm.dc = kunit_kzalloc(test, sizeof(*f.adev->dm.dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.adev->dm.dc); + + f.state = kunit_kzalloc(test, sizeof(*f.state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.state); + f.state->dev = &f.adev->ddev; + + f.stream = kunit_kzalloc(test, sizeof(*f.stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.stream); + + f.crtc_state = kunit_kzalloc(test, sizeof(*f.crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.crtc_state); + f.crtc_state->base.state = f.state; + f.crtc_state->stream = f.stream; + + f.plane = kunit_kzalloc(test, sizeof(*f.plane), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.plane); + f.plane->dev = &f.adev->ddev; + + f.dm_plane_state = kunit_kzalloc(test, sizeof(*f.dm_plane_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.dm_plane_state); + f.dm_plane_state->base.state = f.state; + f.dm_plane_state->base.plane = f.plane; + + f.dc_plane_state = kunit_kzalloc(test, sizeof(*f.dc_plane_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f.dc_plane_state); + + return f; +} + +/** + * dm_test_make_ctm_blob - Allocate a fake drm_property_blob for CTM data + * @test: KUnit test context + * @data: CTM data pointer + * @size: CTM data size in bytes + * + * Returns: a fake property blob pointing at @data. + */ +static struct drm_property_blob * +dm_test_make_ctm_blob(struct kunit *test, void *data, size_t size) +{ + struct drm_property_blob *blob; + + blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob); + blob->data = data; + blob->length = size; + + return blob; +} + +/** + * dm_test_check_crtc_color_mgmt_no_luts - No CRTC LUTs check succeeds + * @test: KUnit test context + */ +static void dm_test_check_crtc_color_mgmt_no_luts(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.crtc_state->cm_has_degamma = true; + f.crtc_state->cm_is_degamma_srgb = true; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_check_crtc_color_mgmt(f.crtc_state, true), 0); + KUNIT_EXPECT_FALSE(test, f.crtc_state->cm_has_degamma); + KUNIT_EXPECT_FALSE(test, f.crtc_state->cm_is_degamma_srgb); +} + +/** + * dm_test_check_crtc_color_mgmt_legacy_regamma - Legacy LUT uses sRGB regamma + * @test: KUnit test context + */ +static void dm_test_check_crtc_color_mgmt_legacy_regamma(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.crtc_state->base.gamma_lut = dm_test_make_lut_blob(test, MAX_COLOR_LEGACY_LUT_ENTRIES); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_check_crtc_color_mgmt(f.crtc_state, true), 0); + KUNIT_EXPECT_TRUE(test, f.crtc_state->cm_is_degamma_srgb); +} + +/** + * dm_test_check_crtc_color_mgmt_degamma - Atomic degamma is mapped to the plane + * @test: KUnit test context + */ +static void dm_test_check_crtc_color_mgmt_degamma(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.crtc_state->base.degamma_lut = + dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_check_crtc_color_mgmt(f.crtc_state, true), 0); + KUNIT_EXPECT_TRUE(test, f.crtc_state->cm_has_degamma); + KUNIT_EXPECT_FALSE(test, f.crtc_state->cm_is_degamma_srgb); +} + +/** + * dm_test_update_crtc_color_mgmt_no_ctm - No CRTC CTM leaves remap bypassed + * @test: KUnit test context + */ +static void dm_test_update_crtc_color_mgmt_no_ctm(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.stream->gamut_remap_matrix.enable_remap = true; + f.stream->csc_color_matrix.enable_adjustment = true; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_update_crtc_color_mgmt(f.crtc_state), 0); + KUNIT_EXPECT_FALSE(test, f.stream->gamut_remap_matrix.enable_remap); + KUNIT_EXPECT_FALSE(test, f.stream->csc_color_matrix.enable_adjustment); +} + +/** + * dm_test_update_crtc_color_mgmt_ctm - CRTC CTM enables stream gamut remap + * @test: KUnit test context + */ +static void dm_test_update_crtc_color_mgmt_ctm(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_color_ctm *ctm; + + ctm = kunit_kzalloc(test, sizeof(*ctm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctm); + ctm->matrix[0] = 1ULL << 32; + ctm->matrix[4] = 1ULL << 32; + ctm->matrix[8] = 1ULL << 32; + f.crtc_state->base.ctm = dm_test_make_ctm_blob(test, ctm, sizeof(*ctm)); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_update_crtc_color_mgmt(f.crtc_state), 0); + KUNIT_EXPECT_TRUE(test, f.stream->gamut_remap_matrix.enable_remap); + KUNIT_EXPECT_FALSE(test, f.stream->csc_color_matrix.enable_adjustment); +} + +/** + * dm_test_update_plane_color_mgmt_bad_lut3d - Bad 3D LUT size fails early + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_bad_lut3d(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = true; + f.dm_plane_state->lut3d = dm_test_make_lut_blob(test, 128); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, + f.dc_plane_state), + -EINVAL); +} + +/** + * dm_test_update_plane_color_mgmt_fallback_defaults - Legacy fallback default path + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_fallback_defaults(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, f.dc_plane_state), + 0); + KUNIT_EXPECT_EQ(test, f.dc_plane_state->hdr_mult.value, 0LL); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.shaper_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.blend_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.lut3d_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->gamut_remap_matrix.enable_remap); +} + +/** + * dm_test_update_plane_color_mgmt_maps_crtc_degamma - CRTC implicit degamma path + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_maps_crtc_degamma(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.crtc_state->cm_is_degamma_srgb = true; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, f.dc_plane_state), + 0); + KUNIT_EXPECT_EQ(test, + (int)f.dc_plane_state->in_transfer_func.type, + (int)TF_TYPE_PREDEFINED); + KUNIT_EXPECT_EQ(test, + (int)f.dc_plane_state->in_transfer_func.tf, + (int)TRANSFER_FUNCTION_SRGB); +} + +/** + * dm_test_update_plane_color_mgmt_maps_crtc_degamma_lut - CRTC LUT maps to plane degamma + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_maps_crtc_degamma_lut(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + int ret; + + f.crtc_state->cm_has_degamma = true; + f.crtc_state->cm_is_degamma_srgb = true; + f.crtc_state->base.degamma_lut = + dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES); + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)f.dc_plane_state->in_transfer_func.type, + (int)TF_TYPE_DISTRIBUTED_POINTS); + KUNIT_EXPECT_EQ(test, (int)f.dc_plane_state->in_transfer_func.tf, + (int)TRANSFER_FUNCTION_SRGB); +} + +/** + * dm_test_update_plane_color_mgmt_maps_bt709_degamma - Video uses BT.709 degamma + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_maps_bt709_degamma(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + int ret; + + f.crtc_state->cm_is_degamma_srgb = true; + f.dc_plane_state->format = SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr; + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)f.dc_plane_state->in_transfer_func.tf, + (int)TRANSFER_FUNCTION_BT709); +} + +/** + * dm_test_update_plane_color_mgmt_plane_degamma_lut - Non-linear plane LUT succeeds + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_plane_degamma_lut(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + struct drm_color_lut *lut; + int ret; + + f.dm_plane_state->degamma_lut = + dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES); + lut = f.dm_plane_state->degamma_lut->data; + lut[0].red = MAX_DRM_LUT_VALUE; + lut[0].green = MAX_DRM_LUT_VALUE; + lut[0].blue = MAX_DRM_LUT_VALUE; + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)f.dc_plane_state->in_transfer_func.type, + (int)TF_TYPE_DISTRIBUTED_POINTS); +} + +/** + * dm_test_update_plane_color_mgmt_rejects_dual_degamma - Reject two degamma stages + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_rejects_dual_degamma(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + struct drm_crtc *crtc; + int ret; + + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, crtc); + crtc->dev = &f.adev->ddev; + f.crtc_state->base.crtc = crtc; + f.crtc_state->cm_has_degamma = true; + f.dm_plane_state->degamma_tf = AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF; + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/** + * dm_test_update_plane_color_mgmt_uses_color_caps - DC context provides color caps + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_uses_color_caps(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct dc_context *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + ctx->dc = f.adev->dm.dc; + f.dc_plane_state->ctx = ctx; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, f.dc_plane_state), + 0); +} + +/** + * dm_test_update_plane_color_mgmt_legacy_luts - Legacy shaper and blend LUTs succeed + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_legacy_luts(struct kunit *test) +{ + const u32 lut3d_entries = + MAX_COLOR_3DLUT_SIZE * MAX_COLOR_3DLUT_SIZE * MAX_COLOR_3DLUT_SIZE; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + int ret; + + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = true; + f.dm_plane_state->shaper_lut = + dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES); + f.dm_plane_state->lut3d = dm_test_make_lut_blob(test, lut3d_entries); + f.dm_plane_state->blend_lut = + dm_test_make_lut_blob(test, MAX_COLOR_LUT_ENTRIES); + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, f.dc_plane_state->cm.flags.bits.shaper_enable); + KUNIT_EXPECT_TRUE(test, f.dc_plane_state->cm.flags.bits.lut3d_enable); + KUNIT_EXPECT_TRUE(test, f.dc_plane_state->cm.flags.bits.blend_enable); +} + +/** + * dm_test_update_plane_color_mgmt_plane_ctm - Plane CTM maps to gamut remap + * @test: KUnit test context + * + * When the plane has a CTM blob, the update path programs the DPP gamut + * remap matrix and enables remapping (and disables the input CSC). + */ +static void dm_test_update_plane_color_mgmt_plane_ctm(struct kunit *test) +{ + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_color_ctm_3x4 *ctm; + + ctm = kunit_kzalloc(test, sizeof(*ctm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctm); + /* Identity-ish entries; values are irrelevant to the branch taken. */ + ctm->matrix[0] = 0x100000000ULL; + ctm->matrix[5] = 0x100000000ULL; + ctm->matrix[10] = 0x100000000ULL; + + f.dm_plane_state->ctm = dm_test_make_ctm_blob(test, ctm, sizeof(*ctm)); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, f.dc_plane_state), + 0); + KUNIT_EXPECT_TRUE(test, f.dc_plane_state->gamut_remap_matrix.enable_remap); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->input_csc_color_matrix.enable_adjustment); +} + +/** + * dm_test_colorop_pipeline_add - append one colorop to a fabricated pipeline + * @test: KUnit test context + * @f: color update fixture that owns the atomic state + * @index: colorop array index to populate + * @type: colorop type + * @curve_1d_type: 1D curve type for DRM_COLOROP_1D_CURVE states + * @bypass: bypass flag for the new colorop state + * + * Returns: the newly allocated colorop. + */ +static struct drm_colorop * +dm_test_colorop_pipeline_add(struct kunit *test, + struct dm_test_color_update_fixture *f, + int index, enum drm_colorop_type type, + enum drm_colorop_curve_1d_type curve_1d_type, + bool bypass) +{ + struct drm_colorop_state *colorop_state; + struct drm_colorop *colorop; + + colorop = kunit_kzalloc(test, sizeof(*colorop), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, colorop); + colorop->dev = &f->adev->ddev; + colorop->type = type; + colorop->size = MAX_COLOR_LUT_ENTRIES; + + colorop_state = kunit_kzalloc(test, sizeof(*colorop_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, colorop_state); + colorop_state->colorop = colorop; + colorop_state->curve_1d_type = curve_1d_type; + colorop_state->bypass = bypass; + + f->state->colorops[index].ptr = colorop; + f->state->colorops[index].new_state = colorop_state; + + return colorop; +} + +/** + * dm_test_colorop_pipeline_setup - build a linked colorop pipeline prefix + * @test: KUnit test context + * @f: color update fixture that owns the atomic state + * @types: colorop types to create + * @curves: curve type for each colorop state + * @bypass: bypass flag for each colorop state + * @count: number of colorops to create + * + * Returns: the first colorop in the linked pipeline. + */ +static struct drm_colorop * +dm_test_colorop_pipeline_setup(struct kunit *test, + struct dm_test_color_update_fixture *f, + const enum drm_colorop_type *types, + const enum drm_colorop_curve_1d_type *curves, + const bool *bypass, + int count) +{ + struct drm_colorop **colorops; + int i; + + f->state->colorops = kunit_kcalloc(test, count, sizeof(*f->state->colorops), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f->state->colorops); + f->adev->ddev.mode_config.num_colorop = count; + + colorops = kunit_kcalloc(test, count, sizeof(*colorops), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, colorops); + + for (i = 0; i < count; i++) + colorops[i] = dm_test_colorop_pipeline_add(test, f, i, types[i], curves[i], bypass[i]); + + for (i = 0; i < count - 1; i++) + colorops[i]->next = colorops[i + 1]; + + return colorops[0]; +} + +/** + * dm_test_update_plane_color_mgmt_colorop_bypass_pipeline - bypassed pipeline succeeds + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_colorop_bypass_pipeline(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_3D_LUT, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { + true, true, true, true, true, true, true, true, + }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = true; + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, ARRAY_SIZE(types)); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, f.dc_plane_state), + 0); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.shaper_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.lut3d_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.blend_enable); +} + +/** + * dm_test_update_plane_color_mgmt_colorop_missing_multiplier - missing second op falls back + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_colorop_missing_multiplier(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { DRM_COLOROP_1D_CURVE }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { true }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, ARRAY_SIZE(types)); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, f.dc_plane_state), + 0); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.shaper_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.blend_enable); +} + +/** + * dm_test_update_plane_color_mgmt_colorop_missing_3x4 - missing third op falls back + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_colorop_missing_3x4(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { true, true }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, ARRAY_SIZE(types)); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, f.dc_plane_state), + 0); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.shaper_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.blend_enable); +} + +/** + * dm_test_update_plane_color_mgmt_colorop_truncated - truncated pipelines fall back + * @test: KUnit test context + * + * Covers each pipeline exit after the 3x4 matrix while all present colorops + * remain bypassed, avoiding the floating-point color calculation paths. + */ +static void dm_test_update_plane_color_mgmt_colorop_truncated(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_3D_LUT, + DRM_COLOROP_1D_CURVE, + }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { + true, true, true, true, true, true, true, + }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct dc_plane_state *dc_plane_state = f.dc_plane_state; + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + struct dm_crtc_state *crtc_state = f.crtc_state; + int count, ret; + + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = true; + + for (count = 3; count <= ARRAY_SIZE(types); count++) { + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, count); + memset(f.dc_plane_state, 0, sizeof(*f.dc_plane_state)); + + ret = amdgpu_dm_update_plane_color_mgmt(crtc_state, plane_state, dc_plane_state); + KUNIT_EXPECT_EQ_MSG(test, ret, 0, "pipeline length %d should fall back", count); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.shaper_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.lut3d_enable); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.blend_enable); + } +} + +/** + * dm_test_update_plane_color_mgmt_colorop_no_3dlut_hw - no 3D LUT skips 3D ops + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_colorop_no_3dlut_hw(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_3D_LUT, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { + true, true, true, true, true, false, true, true, + }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = true; + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, + ARRAY_SIZE(types)); + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = false; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_update_plane_color_mgmt(f.crtc_state, &f.dm_plane_state->base, + f.dc_plane_state), + 0); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.lut3d_enable); +} + +/** + * dm_test_update_plane_color_mgmt_colorop_shaper - enabled shaper TF and LUT succeed + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_colorop_shaper(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_3D_LUT, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { + true, true, true, false, false, true, true, true, + }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_colorop_state *shaper_lut_state; + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + struct drm_property_blob *blob; + int ret; + + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = true; + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, + ARRAY_SIZE(types)); + shaper_lut_state = f.state->colorops[4].new_state; + blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob); + blob->length = MAX_COLOR_LUT_ENTRIES * sizeof(struct drm_color_lut32); + blob->data = kunit_kzalloc(test, blob->length, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob->data); + shaper_lut_state->data = blob; + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, f.dc_plane_state->cm.flags.bits.shaper_enable); + KUNIT_EXPECT_EQ(test, (int)f.dc_plane_state->cm.shaper_func.type, + (int)TF_TYPE_DISTRIBUTED_POINTS); +} + +/** + * dm_test_update_plane_color_mgmt_colorop_3dlut - enabled 3D LUT succeeds + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_colorop_3dlut(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_3D_LUT, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { + true, true, true, true, true, false, true, true, + }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_colorop_state *lut3d_state; + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + struct drm_property_blob *blob; + int ret; + + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = true; + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, + ARRAY_SIZE(types)); + lut3d_state = f.state->colorops[5].new_state; + blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob); + blob->length = 5 * sizeof(struct drm_color_lut32); + blob->data = kunit_kzalloc(test, blob->length, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob->data); + lut3d_state->data = blob; + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, f.dc_plane_state->cm.flags.bits.lut3d_enable); + KUNIT_EXPECT_EQ(test, (int)f.dc_plane_state->cm.shaper_func.type, + (int)TF_TYPE_DISTRIBUTED_POINTS); + KUNIT_EXPECT_EQ(test, (int)f.dc_plane_state->cm.shaper_func.tf, + (int)TRANSFER_FUNCTION_LINEAR); +} + +/** + * dm_test_update_plane_color_mgmt_colorop_3dlut_no_data - empty 3D LUT falls back + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_colorop_3dlut_no_data(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_3D_LUT, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { + true, true, true, true, true, false, true, true, + }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + int ret; + + f.adev->dm.dc->caps.color.dpp.hw_3d_lut = true; + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, + ARRAY_SIZE(types)); + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_FALSE(test, f.dc_plane_state->cm.flags.bits.lut3d_enable); +} + +/** + * dm_test_update_plane_color_mgmt_colorop_blend - enabled blend TF and LUT succeed + * @test: KUnit test context + */ +static void dm_test_update_plane_color_mgmt_colorop_blend(struct kunit *test) +{ + static const enum drm_colorop_type types[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + static const enum drm_colorop_curve_1d_type curves[] = { + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + DRM_COLOROP_1D_CURVE_SRGB_EOTF, + }; + static const bool bypass[] = { true, true, true, false, false }; + struct dm_test_color_update_fixture f = dm_test_color_update_setup(test); + struct drm_colorop_state *blend_lut_state; + struct drm_plane_state *plane_state = &f.dm_plane_state->base; + struct drm_property_blob *blob; + int ret; + + f.dm_plane_state->base.color_pipeline = + dm_test_colorop_pipeline_setup(test, &f, types, curves, bypass, + ARRAY_SIZE(types)); + blend_lut_state = f.state->colorops[4].new_state; + blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob); + blob->length = MAX_COLOR_LUT_ENTRIES * sizeof(struct drm_color_lut32); + blob->data = kunit_kzalloc(test, blob->length, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob->data); + blend_lut_state->data = blob; + + ret = amdgpu_dm_update_plane_color_mgmt(f.crtc_state, plane_state, + f.dc_plane_state); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, f.dc_plane_state->cm.flags.bits.blend_enable); + KUNIT_EXPECT_EQ(test, (int)f.dc_plane_state->cm.blend_func.type, + (int)TF_TYPE_DISTRIBUTED_POINTS); +} + static struct kunit_case dm_color_test_cases[] = { /* amdgpu_dm_fixpt_from_s3132 */ KUNIT_CASE(dm_test_fixpt_from_s3132_zero), @@ -1614,16 +2965,66 @@ static struct kunit_case dm_color_test_cases[] = { /* __set_tf_distributed_points */ KUNIT_CASE(dm_test_set_tf_distributed_points_srgb), KUNIT_CASE(dm_test_set_tf_distributed_points_pq), + /* Transfer-function calculation helpers */ + KUNIT_CASE(dm_test_set_legacy_tf_identity), + KUNIT_CASE(dm_test_set_output_tf_linear), + KUNIT_CASE(dm_test_set_output_tf_32_srgb_rom), + KUNIT_CASE(dm_test_set_input_tf_srgb), + KUNIT_CASE(dm_test_set_input_tf_32_srgb), + KUNIT_CASE(dm_test_set_transfer_funcs_with_luts), /* amdgpu_dm_set_atomic_regamma */ KUNIT_CASE(dm_test_set_atomic_regamma_bypass), + KUNIT_CASE(dm_test_set_atomic_regamma_srgb), /* amdgpu_dm_atomic_shaper_lut */ KUNIT_CASE(dm_test_atomic_shaper_lut_bypass), /* amdgpu_dm_atomic_blend_lut */ KUNIT_CASE(dm_test_atomic_blend_lut_bypass), + KUNIT_CASE(dm_test_atomic_shaper_blend_srgb), /* __set_colorop_in_tf_1d_curve */ KUNIT_CASE(dm_test_set_colorop_in_tf_1d_curve_invalid_type), KUNIT_CASE(dm_test_set_colorop_in_tf_1d_curve_unsupported_curve), KUNIT_CASE(dm_test_set_colorop_in_tf_1d_curve_bypass), + /* amdgpu_dm_init_color_mod */ + KUNIT_CASE(dm_test_init_color_mod), + /* amdgpu_dm_verify_lut3d_size */ + KUNIT_CASE(dm_test_verify_lut3d_no_luts), + KUNIT_CASE(dm_test_verify_lut3d_bad_shaper), + KUNIT_CASE(dm_test_verify_lut3d_bad_lut3d), + KUNIT_CASE(dm_test_verify_lut3d_valid), + /* __set_dm_plane_colorop_multiplier */ + KUNIT_CASE(dm_test_colorop_multiplier_applied), + KUNIT_CASE(dm_test_colorop_multiplier_no_match), + /* __set_dm_plane_colorop_3x4_matrix */ + KUNIT_CASE(dm_test_colorop_3x4_matrix_applied), + KUNIT_CASE(dm_test_colorop_3x4_matrix_bad_length), + /* __set_dm_plane_colorop_degamma */ + KUNIT_CASE(dm_test_colorop_degamma_predefined), + KUNIT_CASE(dm_test_colorop_degamma_no_match), + /* CRTC and plane color management update paths */ + KUNIT_CASE(dm_test_check_crtc_color_mgmt_no_luts), + KUNIT_CASE(dm_test_check_crtc_color_mgmt_legacy_regamma), + KUNIT_CASE(dm_test_check_crtc_color_mgmt_degamma), + KUNIT_CASE(dm_test_update_crtc_color_mgmt_no_ctm), + KUNIT_CASE(dm_test_update_crtc_color_mgmt_ctm), + KUNIT_CASE(dm_test_update_plane_color_mgmt_bad_lut3d), + KUNIT_CASE(dm_test_update_plane_color_mgmt_fallback_defaults), + KUNIT_CASE(dm_test_update_plane_color_mgmt_maps_crtc_degamma), + KUNIT_CASE(dm_test_update_plane_color_mgmt_maps_crtc_degamma_lut), + KUNIT_CASE(dm_test_update_plane_color_mgmt_maps_bt709_degamma), + KUNIT_CASE(dm_test_update_plane_color_mgmt_plane_degamma_lut), + KUNIT_CASE(dm_test_update_plane_color_mgmt_rejects_dual_degamma), + KUNIT_CASE(dm_test_update_plane_color_mgmt_uses_color_caps), + KUNIT_CASE(dm_test_update_plane_color_mgmt_legacy_luts), + KUNIT_CASE(dm_test_update_plane_color_mgmt_plane_ctm), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_bypass_pipeline), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_missing_multiplier), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_missing_3x4), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_truncated), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_no_3dlut_hw), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_shaper), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_3dlut), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_3dlut_no_data), + KUNIT_CASE(dm_test_update_plane_color_mgmt_colorop_blend), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c index 0a5d439f66c3..1a8f02c78c4d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -11,10 +11,14 @@ #include <drm/drm_connector.h> #include <drm/drm_crtc.h> #include <drm/drm_edid.h> +#include <drm/drm_encoder.h> #include <drm/drm_kunit_helpers.h> +#include <drm/drm_managed.h> #include <drm/drm_mode_object.h> +#include <drm/drm_modes.h> #include <drm/drm_property.h> #include <linux/hdmi.h> +#include <linux/i2c.h> #include "dc.h" #include "amdgpu.h" @@ -24,6 +28,7 @@ #include "amdgpu_dm_connector.h" #include "amdgpu_dm_backlight.h" #include "include/grph_object_id.h" +#include "amdgpu_dm_kunit_test_helpers.h" /* Tests for get_subconnector_type() */ @@ -1449,7 +1454,7 @@ static void dm_test_funcs_reset_sets_defaults(struct kunit *test) DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + connector = drmm_kzalloc(drm, sizeof(*connector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, connector); drmm_connector_init(drm, connector, &dm_test_connector_funcs, @@ -1488,7 +1493,7 @@ static void dm_test_funcs_reset_edp_abm_level(struct kunit *test) DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + connector = drmm_kzalloc(drm, sizeof(*connector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, connector); drmm_connector_init(drm, connector, &dm_test_connector_funcs, @@ -1526,7 +1531,7 @@ static void dm_test_funcs_reset_edp_abm_disabled(struct kunit *test) DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + connector = drmm_kzalloc(drm, sizeof(*connector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, connector); drmm_connector_init(drm, connector, &dm_test_connector_funcs, @@ -1568,7 +1573,7 @@ static void dm_test_atomic_dup_state_copies_fields(struct kunit *test) DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + connector = drmm_kzalloc(drm, sizeof(*connector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, connector); drmm_connector_init(drm, connector, &dm_test_connector_funcs, @@ -2359,8 +2364,8 @@ static void setup_panel_type_fixture(struct kunit *test, KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->drm); fixture->adev = drm_to_adev(fixture->drm); - fixture->aconnector = kunit_kzalloc(test, sizeof(*fixture->aconnector), - GFP_KERNEL); + fixture->aconnector = drmm_kzalloc(fixture->drm, sizeof(*fixture->aconnector), + GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->aconnector); fixture->link = kunit_kzalloc(test, sizeof(*fixture->link), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->link); @@ -2554,9 +2559,9 @@ static void dm_test_update_subconnector_dp_with_sink(struct kunit *test) DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + aconnector = drmm_kzalloc(drm, sizeof(*aconnector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, aconnector); - link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + link = drmm_kzalloc(drm, sizeof(*link), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, link); drmm_connector_init(drm, &aconnector->base, &dm_test_connector_funcs, @@ -2598,9 +2603,9 @@ static void dm_test_update_subconnector_dp_no_sink(struct kunit *test) DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + aconnector = drmm_kzalloc(drm, sizeof(*aconnector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, aconnector); - link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + link = drmm_kzalloc(drm, sizeof(*link), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, link); drmm_connector_init(drm, &aconnector->base, &dm_test_connector_funcs, @@ -2641,9 +2646,9 @@ static void dm_test_update_subconnector_non_dp_noop(struct kunit *test) DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + aconnector = drmm_kzalloc(drm, sizeof(*aconnector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, aconnector); - link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + link = drmm_kzalloc(drm, sizeof(*link), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, link); drmm_connector_init(drm, &aconnector->base, &dm_test_connector_funcs, @@ -2657,7 +2662,7 @@ static void dm_test_update_subconnector_non_dp_noop(struct kunit *test) link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER; aconnector->dc_link = link; - aconnector->dc_sink = kunit_kzalloc(test, sizeof(*aconnector->dc_sink), GFP_KERNEL); + aconnector->dc_sink = drmm_kzalloc(drm, sizeof(*aconnector->dc_sink), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, aconnector->dc_sink); update_subconnector_property(aconnector); @@ -2804,7 +2809,7 @@ static struct amdgpu_dm_connector *dm_test_add_connector(struct kunit *test, { struct amdgpu_dm_connector *aconnector; - aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + aconnector = drmm_kzalloc(drm, sizeof(*aconnector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, aconnector); KUNIT_ASSERT_EQ(test, @@ -3103,7 +3108,7 @@ static struct dm_test_panel_ctx *dm_test_panel_ctx_alloc(struct kunit *test) KUNIT_ASSERT_NOT_NULL(test, prop); ctx->drm->mode_config.panel_type_property = prop; - ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + ctx->aconnector = drmm_kzalloc(ctx->drm, sizeof(*ctx->aconnector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); KUNIT_ASSERT_EQ(test, drmm_connector_init(ctx->drm, &ctx->aconnector->base, @@ -3112,7 +3117,7 @@ static struct dm_test_panel_ctx *dm_test_panel_ctx_alloc(struct kunit *test) drm_object_attach_property(&ctx->aconnector->base.base, prop, DRM_MODE_PANEL_TYPE_UNKNOWN); - ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL); + ctx->link = drmm_kzalloc(ctx->drm, sizeof(*ctx->link), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->link); ctx->aconnector->dc_link = ctx->link; @@ -3376,14 +3381,14 @@ static struct dm_test_fill_ctx *dm_test_fill_ctx_alloc(struct kunit *test) DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm); - ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + ctx->aconnector = drmm_kzalloc(ctx->drm, sizeof(*ctx->aconnector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); KUNIT_ASSERT_EQ(test, drmm_connector_init(ctx->drm, &ctx->aconnector->base, &dm_test_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort, NULL), 0); - ctx->conn_state = kunit_kzalloc(test, sizeof(*ctx->conn_state), GFP_KERNEL); + ctx->conn_state = drmm_kzalloc(ctx->drm, sizeof(*ctx->conn_state), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state); ctx->stream = kunit_kzalloc(test, sizeof(*ctx->stream), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->stream); @@ -3604,6 +3609,1659 @@ static void dm_test_fill_stream_aspect_ratio(struct kunit *test) (int)ASPECT_RATIO_16_9); } +/* Tests for create_stream_for_sink() */ + +/* + * Build the inputs for create_stream_for_sink(). The connector is registered + * against a real kunit drm_device so that to_amdgpu_dm_connector() and the drm + * debug helpers resolve. The DC link carries a zeroed dc_context so that + * dc_create_stream_for_sink() can allocate and construct a stream. + * + * By default no dc_sink is attached, so create_stream_for_sink() builds a fake + * VIRTUAL sink. The VIRTUAL signal keeps the DSC, audio and DP/HDMI infoframe + * paths as no-ops, making the exercised behaviour deterministic. + */ +struct dm_test_stream_ctx { + struct drm_device *drm; + struct amdgpu_dm_connector *aconnector; + struct dc_context *dc_ctx; + struct dc_link *link; + struct dm_connector_state *dm_state; + struct drm_display_mode *mode; +}; + +static struct dm_test_stream_ctx *dm_test_stream_ctx_alloc(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx; + struct device *dev; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + ctx->drm = __drm_kunit_helper_alloc_drm_device(test, dev, + sizeof(*ctx->drm), 0, + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm); + + ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + KUNIT_ASSERT_EQ(test, + drmm_connector_init(ctx->drm, &ctx->aconnector->base, + &dm_test_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort, NULL), 0); + + ctx->dc_ctx = kunit_kzalloc(test, sizeof(*ctx->dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->dc_ctx); + + ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link); + ctx->link->ctx = ctx->dc_ctx; + ctx->link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; + + ctx->aconnector->dc_link = ctx->link; + ctx->aconnector->dc_sink = NULL; + + ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state); + ctx->dm_state->scaling = RMX_OFF; + + ctx->mode = kunit_kzalloc(test, sizeof(*ctx->mode), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->mode); + ctx->mode->hdisplay = 1920; + ctx->mode->vdisplay = 1080; + ctx->mode->clock = 148500; + + return ctx; +} + +/** + * dm_test_create_stream_fake_sink_success - Test a stream is built from a fake sink + * @test: The KUnit test context + */ +static void dm_test_create_stream_fake_sink_success(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8); + + KUNIT_ASSERT_NOT_NULL(test, stream); + dc_stream_release(stream); +} + +/** + * dm_test_create_stream_sets_dm_context - Test dm_stream_context points to aconnector + * @test: The KUnit test context + */ +static void dm_test_create_stream_sets_dm_context(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_PTR_EQ(test, stream->dm_stream_context, ctx->aconnector); + dc_stream_release(stream); +} + +/** + * dm_test_create_stream_virtual_signal - Test the fake sink yields a VIRTUAL signal + * @test: The KUnit test context + */ +static void dm_test_create_stream_virtual_signal(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_EQ(test, (int)stream->signal, (int)SIGNAL_TYPE_VIRTUAL); + dc_stream_release(stream); +} + +/** + * dm_test_create_stream_scaling_src - Test the source rect follows the mode + * @test: The KUnit test context + * + * With scaling off the full-screen source viewport matches the requested mode. + */ +static void dm_test_create_stream_scaling_src(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_stream_state *stream; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_EQ(test, (int)stream->src.width, 1920); + KUNIT_EXPECT_EQ(test, (int)stream->src.height, 1080); + dc_stream_release(stream); +} + +/** + * dm_test_create_stream_existing_sink - Test the existing-sink retain path + * @test: The KUnit test context + * + * When the connector already has a dc_sink, create_stream_for_sink() reuses it + * instead of building a fake sink. + */ +static void dm_test_create_stream_existing_sink(struct kunit *test) +{ + struct dm_test_stream_ctx *ctx = dm_test_stream_ctx_alloc(test); + struct dc_sink_init_data sink_init = { 0 }; + struct dc_stream_state *stream; + struct dc_sink *sink; + + sink_init.link = ctx->link; + sink_init.sink_signal = SIGNAL_TYPE_VIRTUAL; + sink = dc_sink_create(&sink_init); + KUNIT_ASSERT_NOT_NULL(test, sink); + sink->sink_signal = SIGNAL_TYPE_VIRTUAL; + + ctx->aconnector->dc_sink = sink; + + stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode, + ctx->dm_state, NULL, 8); + + KUNIT_ASSERT_NOT_NULL(test, stream); + KUNIT_EXPECT_PTR_EQ(test, stream->sink, sink); + + dc_stream_release(stream); + dc_sink_release(sink); +} + +/* Tests for amdgpu_dm_connector_detect() */ + +/* + * A non-DisplayPort connector keeps update_subconnector_property() a no-op and, + * because the kunit thread is not the poll worker, the analog poll branch is + * skipped. That leaves the forced-state and dc_sink presence branches as the + * deterministic behaviour to exercise. + */ +static struct amdgpu_dm_connector *dm_test_detect_connector(struct kunit *test) +{ + struct drm_device *drm = dm_test_alloc_drm(test); + + return dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_HDMIA); +} + +/** + * dm_test_detect_force_on - Test DRM_FORCE_ON reports connected + * @test: The KUnit test context + */ +static void dm_test_detect_force_on(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = dm_test_detect_connector(test); + + aconnector->base.force = DRM_FORCE_ON; + + KUNIT_EXPECT_EQ(test, + (int)amdgpu_dm_connector_detect(&aconnector->base, false), + (int)connector_status_connected); +} + +/** + * dm_test_detect_force_on_digital - Test DRM_FORCE_ON_DIGITAL reports connected + * @test: The KUnit test context + */ +static void dm_test_detect_force_on_digital(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = dm_test_detect_connector(test); + + aconnector->base.force = DRM_FORCE_ON_DIGITAL; + + KUNIT_EXPECT_EQ(test, + (int)amdgpu_dm_connector_detect(&aconnector->base, false), + (int)connector_status_connected); +} + +/** + * dm_test_detect_force_off - Test DRM_FORCE_OFF reports disconnected + * @test: The KUnit test context + */ +static void dm_test_detect_force_off(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = dm_test_detect_connector(test); + + aconnector->base.force = DRM_FORCE_OFF; + + KUNIT_EXPECT_EQ(test, + (int)amdgpu_dm_connector_detect(&aconnector->base, false), + (int)connector_status_disconnected); +} + +/** + * dm_test_detect_sink_present - Test a present dc_sink reports connected + * @test: The KUnit test context + */ +static void dm_test_detect_sink_present(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = dm_test_detect_connector(test); + struct dc_sink *sink; + + sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, sink); + + aconnector->base.force = DRM_FORCE_UNSPECIFIED; + aconnector->dc_sink = sink; + + KUNIT_EXPECT_EQ(test, + (int)amdgpu_dm_connector_detect(&aconnector->base, false), + (int)connector_status_connected); +} + +/** + * dm_test_detect_no_sink - Test a missing dc_sink reports disconnected + * @test: The KUnit test context + */ +static void dm_test_detect_no_sink(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = dm_test_detect_connector(test); + + aconnector->base.force = DRM_FORCE_UNSPECIFIED; + aconnector->dc_sink = NULL; + + KUNIT_EXPECT_EQ(test, + (int)amdgpu_dm_connector_detect(&aconnector->base, false), + (int)connector_status_disconnected); +} + +/* Tests for amdgpu_dm_connector_poll() */ + +/** + * dm_test_poll_dac_load_returns_cached - Test the DAC load detection shortcut + * @test: The KUnit test context + * + * When the previous connection was established by analog DAC load detection and + * polling is not forced, the connector is not re-detected and its cached status + * is returned unchanged. The connector is embedded in an amdgpu_device so that + * drm_to_adev() resolves. + */ +static void dm_test_poll_dac_load_returns_cached(struct kunit *test) +{ + struct amdgpu_device *adev; + struct amdgpu_dm_connector *aconnector; + struct dc_link *link; + struct dc_sink *local_sink; + struct drm_device *drm; + struct device *dev; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*adev), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + adev = drm_to_adev(drm); + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_EQ(test, + drmm_connector_init(drm, &aconnector->base, + &dm_test_connector_funcs, + DRM_MODE_CONNECTOR_VGA, NULL), 0); + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + local_sink = kunit_kzalloc(test, sizeof(*local_sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, local_sink); + + link->local_sink = local_sink; + link->type = dc_connection_analog_load; + aconnector->dc_link = link; + + /* The cached status that the shortcut must return unchanged. */ + aconnector->base.status = connector_status_connected; + + KUNIT_EXPECT_EQ(test, + (int)amdgpu_dm_connector_poll(aconnector, false), + (int)connector_status_connected); +} + +/* Tests for amdgpu_dm_connector_late_register() and _unregister() */ + +/* + * Build an amdgpu_dm_connector embedded in an amdgpu_device so drm_to_adev() + * resolves. A VGA connector keeps amdgpu_dm_should_create_sysfs() false (sysfs + * and DP AUX branches skipped) and bl_idx == -1 turns backlight registration + * into a no-op, leaving the register/unregister bookkeeping safe to exercise. + */ +static struct amdgpu_dm_connector *dm_test_reg_connector(struct kunit *test) +{ + struct amdgpu_device *adev; + struct amdgpu_dm_connector *aconnector; + struct drm_device *drm; + struct device *dev; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*adev), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_EQ(test, + drmm_connector_init(drm, &aconnector->base, + &dm_test_connector_funcs, + DRM_MODE_CONNECTOR_VGA, NULL), 0); + + aconnector->bl_idx = -1; + + return aconnector; +} + +/** + * dm_test_late_register_non_dp_succeeds - Test late_register on a plain connector + * @test: The KUnit test context + * + * With sysfs, backlight and DP AUX registration all skipped, late_register + * completes successfully. + */ +static void dm_test_late_register_non_dp_succeeds(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = dm_test_reg_connector(test); + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_connector_late_register(&aconnector->base), 0); +} + +/** + * dm_test_unregister_non_dp_noop - Test unregister tolerates an unregistered connector + * @test: The KUnit test context + * + * No sysfs group was created, the CEC notifier is NULL and the DP AUX channel + * was never registered, so unregister must be a safe no-op. + */ +static void dm_test_unregister_non_dp_noop(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = dm_test_reg_connector(test); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(aconnector)); + + amdgpu_dm_connector_unregister(&aconnector->base); +} + +/* Tests for amdgpu_dm_connector_destroy() */ + +/* + * amdgpu_dm_connector_destroy() ends with drm_connector_cleanup() followed by + * kfree(connector), so the connector must be initialised with the unmanaged + * drm_connector_init() and allocated with kzalloc() (the function frees it, so + * kunit_kzalloc() would double free at teardown). It is embedded in an + * amdgpu_device so drm_to_adev() resolves and a dc_link carries a dc_context so + * dc_sink_create() works for the sink-release branches. + */ +struct dm_test_destroy_ctx { + struct drm_device *drm; + struct dc_context *dc_ctx; + struct dc_link *link; +}; + +static struct dm_test_destroy_ctx *dm_test_destroy_ctx_alloc(struct kunit *test) +{ + struct dm_test_destroy_ctx *ctx; + struct amdgpu_device *adev; + struct device *dev; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + ctx->drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*adev), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm); + + ctx->dc_ctx = kunit_kzalloc(test, sizeof(*ctx->dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->dc_ctx); + + ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link); + ctx->link->ctx = ctx->dc_ctx; + + return ctx; +} + +/* + * Allocate a connector the destroy path can free. Uses kzalloc() (not + * kunit_kzalloc) and the unmanaged drm_connector_init() because the function + * under test calls drm_connector_cleanup() + kfree(connector). + * + * drm_connector_init() requires funcs->destroy to be set, so a dedicated funcs + * table wires it to amdgpu_dm_connector_destroy() (the test invokes it + * directly; the connector is removed from the device before teardown). + */ +static const struct drm_connector_funcs dm_test_destroy_funcs = { + .reset = amdgpu_dm_connector_funcs_reset, + .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .destroy = amdgpu_dm_connector_destroy, +}; + +static struct amdgpu_dm_connector * +dm_test_destroy_connector(struct kunit *test, struct drm_device *drm) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kzalloc(sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + KUNIT_ASSERT_EQ(test, + drm_connector_init(drm, &aconnector->base, + &dm_test_destroy_funcs, + DRM_MODE_CONNECTOR_VGA), 0); + aconnector->bl_idx = -1; + + return aconnector; +} + +/** + * dm_test_destroy_minimal - Test destroy tears down a bare connector + * @test: The KUnit test context + * + * With no MST, backlight, sinks or registered AUX/CEC, destroy must clean up + * and free the connector without crashing. + */ +static void dm_test_destroy_minimal(struct kunit *test) +{ + struct dm_test_destroy_ctx *ctx = dm_test_destroy_ctx_alloc(test); + struct amdgpu_dm_connector *aconnector = + dm_test_destroy_connector(test, ctx->drm); + + amdgpu_dm_connector_destroy(&aconnector->base); +} + +/** + * dm_test_destroy_releases_dc_sink - Test destroy releases the dc_sink + * @test: The KUnit test context + */ +static void dm_test_destroy_releases_dc_sink(struct kunit *test) +{ + struct dm_test_destroy_ctx *ctx = dm_test_destroy_ctx_alloc(test); + struct amdgpu_dm_connector *aconnector = + dm_test_destroy_connector(test, ctx->drm); + struct dc_sink_init_data sink_init = { 0 }; + struct dc_sink *sink; + + sink_init.link = ctx->link; + sink_init.sink_signal = SIGNAL_TYPE_VIRTUAL; + sink = dc_sink_create(&sink_init); + KUNIT_ASSERT_NOT_NULL(test, sink); + + /* Extra reference so the sink survives destroy for inspection. */ + dc_sink_retain(sink); + aconnector->dc_sink = sink; + + amdgpu_dm_connector_destroy(&aconnector->base); + + KUNIT_EXPECT_EQ(test, (int)kref_read(&sink->refcount), 1); + dc_sink_release(sink); +} + +/** + * dm_test_destroy_releases_dc_em_sink - Test destroy releases the emulated sink + * @test: The KUnit test context + */ +static void dm_test_destroy_releases_dc_em_sink(struct kunit *test) +{ + struct dm_test_destroy_ctx *ctx = dm_test_destroy_ctx_alloc(test); + struct amdgpu_dm_connector *aconnector = + dm_test_destroy_connector(test, ctx->drm); + struct dc_sink_init_data sink_init = { 0 }; + struct dc_sink *sink; + + sink_init.link = ctx->link; + sink_init.sink_signal = SIGNAL_TYPE_VIRTUAL; + sink = dc_sink_create(&sink_init); + KUNIT_ASSERT_NOT_NULL(test, sink); + + dc_sink_retain(sink); + aconnector->dc_em_sink = sink; + + amdgpu_dm_connector_destroy(&aconnector->base); + + KUNIT_EXPECT_EQ(test, (int)kref_read(&sink->refcount), 1); + dc_sink_release(sink); +} + +/* Tests for dm_encoder_helper_disable() */ + +/** + * dm_test_encoder_disable_noop - Test the disable hook is a no-op + * @test: The KUnit test context + * + * dm_encoder_helper_disable() has an empty body; calling it must neither touch + * the encoder nor crash. + */ +static void dm_test_encoder_disable_noop(struct kunit *test) +{ + struct drm_encoder *encoder; + + encoder = kunit_kzalloc(test, sizeof(*encoder), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, encoder); + + dm_encoder_helper_disable(encoder); +} + +/* Tests for dm_encoder_helper_atomic_check() */ + +/* + * dm_encoder_helper_atomic_check() reads back through to_amdgpu_encoder(), + * to_amdgpu_dm_connector() and to_dm_connector_state(), so the encoder, + * connector and connector-state are stacked in their containers and wired + * together through conn_state->connector. + */ +struct dm_test_atomic_check_ctx { + struct drm_device *drm; + struct amdgpu_encoder *aenc; + struct amdgpu_dm_connector *aconnector; + struct dm_connector_state *dm_state; + struct drm_crtc_state *crtc_state; +}; + +static struct dm_test_atomic_check_ctx * +dm_test_atomic_check_ctx_alloc(struct kunit *test, int connector_type) +{ + struct dm_test_atomic_check_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->drm = dm_test_alloc_drm(test); + + ctx->aenc = kunit_kzalloc(test, sizeof(*ctx->aenc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aenc); + ctx->aenc->base.dev = ctx->drm; + + ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + ctx->aconnector->base.connector_type = connector_type; + + ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state); + ctx->dm_state->base.connector = &ctx->aconnector->base; + + ctx->crtc_state = kunit_kzalloc(test, sizeof(*ctx->crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_state); + + return ctx; +} + +/** + * dm_test_atomic_check_edp_native_keeps_scaling - Test native eDP mode is left alone + * @test: The KUnit test context + * + * On an eDP connector whose adjusted mode matches the panel's native mode, + * drm_crtc_helper_mode_valid_fixed() returns MODE_OK so scaling is untouched. + */ +static void dm_test_atomic_check_edp_native_keeps_scaling(struct kunit *test) +{ + struct dm_test_atomic_check_ctx *ctx = + dm_test_atomic_check_ctx_alloc(test, DRM_MODE_CONNECTOR_eDP); + + ctx->aenc->native_mode.hdisplay = 1920; + ctx->aenc->native_mode.vdisplay = 1080; + ctx->crtc_state->adjusted_mode.hdisplay = 1920; + ctx->crtc_state->adjusted_mode.vdisplay = 1080; + ctx->dm_state->scaling = RMX_OFF; + + KUNIT_EXPECT_EQ(test, + dm_encoder_helper_atomic_check(&ctx->aenc->base, + ctx->crtc_state, + &ctx->dm_state->base), 0); + KUNIT_EXPECT_EQ(test, (int)ctx->dm_state->scaling, (int)RMX_OFF); +} + +/** + * dm_test_atomic_check_lvds_non_native_enables_scaling - Test non-native LVDS turns on scaling + * @test: The KUnit test context + * + * On an LVDS connector whose adjusted mode differs from the native mode and is + * currently RMX_OFF, the check enables RMX_ASPECT scaling and still returns 0. + */ +static void dm_test_atomic_check_lvds_non_native_enables_scaling(struct kunit *test) +{ + struct dm_test_atomic_check_ctx *ctx = + dm_test_atomic_check_ctx_alloc(test, DRM_MODE_CONNECTOR_LVDS); + + ctx->aenc->native_mode.hdisplay = 1920; + ctx->aenc->native_mode.vdisplay = 1080; + ctx->crtc_state->adjusted_mode.hdisplay = 1280; + ctx->crtc_state->adjusted_mode.vdisplay = 720; + ctx->dm_state->scaling = RMX_OFF; + + KUNIT_EXPECT_EQ(test, + dm_encoder_helper_atomic_check(&ctx->aenc->base, + ctx->crtc_state, + &ctx->dm_state->base), 0); + KUNIT_EXPECT_EQ(test, (int)ctx->dm_state->scaling, (int)RMX_ASPECT); +} + +/** + * dm_test_atomic_check_non_mst_returns_zero - Test non-MST connectors short-circuit + * @test: The KUnit test context + * + * A non-eDP/LVDS connector with no MST output port hits the early ``return 0`` + * before any topology state is touched. + */ +static void dm_test_atomic_check_non_mst_returns_zero(struct kunit *test) +{ + struct dm_test_atomic_check_ctx *ctx = + dm_test_atomic_check_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + + ctx->aconnector->mst_output_port = NULL; + + KUNIT_EXPECT_EQ(test, + dm_encoder_helper_atomic_check(&ctx->aenc->base, + ctx->crtc_state, + &ctx->dm_state->base), 0); +} + +/* Tests for hdmi_cec_unset_edid() */ + +/** + * dm_test_hdmi_cec_unset_edid_no_notifier - Test the no-notifier no-op path + * @test: The KUnit test context + * + * With aconnector->notifier NULL the function returns early and must not crash. + */ +static void dm_test_hdmi_cec_unset_edid_no_notifier(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + hdmi_cec_unset_edid(aconnector); +} + +/* Tests for create_eml_sink() and handle_edid_mgmt() */ + +/* + * create_eml_sink() reads EDID off the connector's DDC. Forcing the connector + * DRM_FORCE_OFF makes drm_edid_read_ddc() return NULL before touching any i2c + * adapter, exercising the "no EDID" branch without real hardware. aux_mode is + * set so the embedded DP AUX ddc is selected (no i2c adapter pointer needed). + */ +struct dm_test_edid_ctx { + struct drm_device *drm; + struct amdgpu_dm_connector *aconnector; + struct dc_link *link; +}; + +static struct dm_test_edid_ctx * +dm_test_edid_ctx_alloc(struct kunit *test, int connector_type) +{ + struct dm_test_edid_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->drm = dm_test_alloc_drm(test); + ctx->aconnector = dm_test_add_connector(test, ctx->drm, connector_type); + + ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link); + ctx->link->aux_mode = true; + ctx->aconnector->dc_link = ctx->link; + + ctx->aconnector->base.force = DRM_FORCE_OFF; + + return ctx; +} + +/** + * dm_test_create_eml_sink_no_edid - Test the no-EDID branch creates no sink + * @test: The KUnit test context + * + * When no EDID can be read the function logs an error and returns without + * allocating an emulated sink. + */ +static void dm_test_create_eml_sink_no_edid(struct kunit *test) +{ + struct dm_test_edid_ctx *ctx = + dm_test_edid_ctx_alloc(test, DRM_MODE_CONNECTOR_DisplayPort); + + create_eml_sink(ctx->aconnector); + + KUNIT_EXPECT_NULL(test, ctx->aconnector->dc_em_sink); +} + +/** + * dm_test_handle_edid_mgmt_dp_sets_link_caps - Test DP seeds verified link caps + * @test: The KUnit test context + * + * For a DisplayPort link the function primes verified_link_cap before reading + * EDID so a headless force-on connector can still modeset. + */ +static void dm_test_handle_edid_mgmt_dp_sets_link_caps(struct kunit *test) +{ + struct dm_test_edid_ctx *ctx = + dm_test_edid_ctx_alloc(test, DRM_MODE_CONNECTOR_DisplayPort); + + ctx->link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; + + handle_edid_mgmt(ctx->aconnector); + + KUNIT_EXPECT_EQ(test, (int)ctx->link->verified_link_cap.lane_count, + (int)LANE_COUNT_FOUR); + KUNIT_EXPECT_EQ(test, (int)ctx->link->verified_link_cap.link_rate, + (int)LINK_RATE_HIGH2); + KUNIT_EXPECT_NULL(test, ctx->aconnector->dc_em_sink); +} + +/** + * dm_test_handle_edid_mgmt_non_dp_leaves_caps - Test non-DP links keep zeroed caps + * @test: The KUnit test context + * + * A non-DisplayPort link skips the verified_link_cap seeding entirely. + */ +static void dm_test_handle_edid_mgmt_non_dp_leaves_caps(struct kunit *test) +{ + struct dm_test_edid_ctx *ctx = + dm_test_edid_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + + ctx->link->connector_signal = SIGNAL_TYPE_HDMI_TYPE_A; + + handle_edid_mgmt(ctx->aconnector); + + KUNIT_EXPECT_EQ(test, (int)ctx->link->verified_link_cap.lane_count, 0); + KUNIT_EXPECT_EQ(test, (int)ctx->link->verified_link_cap.link_rate, 0); +} + +/* + * Context for the connector funcs / modes tests: a managed DRM device with a + * registered connector and a managed encoder attached to it, so helpers that + * walk connector->encoder relationships resolve correctly. + */ +struct dm_test_modes_ctx { + struct drm_device *drm; + struct amdgpu_dm_connector *aconnector; + struct amdgpu_encoder *aenc; +}; + +static struct dm_test_modes_ctx * +dm_test_modes_ctx_alloc(struct kunit *test, int connector_type) +{ + struct dm_test_modes_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->drm = dm_test_alloc_drm(test); + ctx->aconnector = dm_test_add_connector(test, ctx->drm, connector_type); + + ctx->aenc = kunit_kzalloc(test, sizeof(*ctx->aenc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aenc); + KUNIT_ASSERT_EQ(test, + drmm_encoder_init(ctx->drm, &ctx->aenc->base, NULL, + DRM_MODE_ENCODER_TMDS, NULL), 0); + KUNIT_ASSERT_EQ(test, + drm_connector_attach_encoder(&ctx->aconnector->base, + &ctx->aenc->base), 0); + + return ctx; +} + +/** + * dm_test_funcs_force_no_edid - Test force() leaves drm_edid NULL when no EDID + * @test: The KUnit test context + * + * A headless force-on DisplayPort connector reads no EDID, so the cached + * drm_edid pointer must stay NULL after the force callback runs. + */ +static void dm_test_funcs_force_no_edid(struct kunit *test) +{ + struct dm_test_edid_ctx *ctx = + dm_test_edid_ctx_alloc(test, DRM_MODE_CONNECTOR_DisplayPort); + + amdgpu_dm_connector_funcs_force(&ctx->aconnector->base); + + KUNIT_EXPECT_NULL(test, ctx->aconnector->drm_edid); +} + +/** + * dm_test_validate_stream_null_stream - Test NULL stream returns unexpected + * @test: The KUnit test context + * + * With a NULL stream the validation jumps straight to cleanup without ever + * dereferencing the dc handle and reports DC_ERROR_UNEXPECTED. + */ +static void dm_test_validate_stream_null_stream(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, + (int)dm_validate_stream_and_context(NULL, NULL), + (int)DC_ERROR_UNEXPECTED); +} + +/** + * dm_test_to_encoder_no_encoder - Test connector with no encoder returns NULL + * @test: The KUnit test context + */ +static void dm_test_to_encoder_no_encoder(struct kunit *test) +{ + struct drm_device *drm = dm_test_alloc_drm(test); + struct amdgpu_dm_connector *aconnector = + dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_HDMIA); + + KUNIT_EXPECT_NULL(test, + amdgpu_dm_connector_to_encoder(&aconnector->base)); +} + +/** + * dm_test_to_encoder_returns_attached - Test the attached encoder is returned + * @test: The KUnit test context + */ +static void dm_test_to_encoder_returns_attached(struct kunit *test) +{ + struct dm_test_modes_ctx *ctx = + dm_test_modes_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + + KUNIT_EXPECT_PTR_EQ(test, + amdgpu_dm_connector_to_encoder(&ctx->aconnector->base), + &ctx->aenc->base); +} + +/** + * dm_test_native_mode_no_encoder - Test native mode resolution is a no-op + * @test: The KUnit test context + * + * Without an encoder there is nothing to copy into, so the call must return + * cleanly without dereferencing a NULL encoder. + */ +static void dm_test_native_mode_no_encoder(struct kunit *test) +{ + struct drm_device *drm = dm_test_alloc_drm(test); + struct amdgpu_dm_connector *aconnector = + dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_HDMIA); + + amdgpu_dm_get_native_mode(&aconnector->base); +} + +/** + * dm_test_native_mode_empty_probed_zeroes_clock - Test empty probed list clears mode + * @test: The KUnit test context + * + * With no probed modes there is no preferred mode to copy, so the encoder's + * native mode is memset to zero (clock becomes 0). + */ +static void dm_test_native_mode_empty_probed_zeroes_clock(struct kunit *test) +{ + struct dm_test_modes_ctx *ctx = + dm_test_modes_ctx_alloc(test, DRM_MODE_CONNECTOR_eDP); + + ctx->aenc->native_mode.clock = 148500; + + amdgpu_dm_get_native_mode(&ctx->aconnector->base); + + KUNIT_EXPECT_EQ(test, ctx->aenc->native_mode.clock, 0); +} + +/** + * dm_test_native_mode_copies_preferred - Test the preferred mode is copied + * @test: The KUnit test context + * + * The preferred probed mode is duplicated into the encoder's native mode. + */ +static void dm_test_native_mode_copies_preferred(struct kunit *test) +{ + struct dm_test_modes_ctx *ctx = + dm_test_modes_ctx_alloc(test, DRM_MODE_CONNECTOR_eDP); + struct drm_display_mode *mode; + + mode = drm_mode_create(ctx->drm); + KUNIT_ASSERT_NOT_NULL(test, mode); + mode->type = DRM_MODE_TYPE_PREFERRED; + mode->clock = 148500; + mode->hdisplay = 1920; + mode->vdisplay = 1080; + drm_mode_probed_add(&ctx->aconnector->base, mode); + + amdgpu_dm_get_native_mode(&ctx->aconnector->base); + + KUNIT_EXPECT_EQ(test, ctx->aenc->native_mode.hdisplay, 1920); + KUNIT_EXPECT_EQ(test, ctx->aenc->native_mode.vdisplay, 1080); + KUNIT_EXPECT_EQ(test, ctx->aenc->native_mode.clock, 148500); +} + +/** + * dm_test_create_common_mode_overrides - Test common mode inherits native timing + * @test: The KUnit test context + * + * A new common mode takes its pixel clock and porches from the encoder's + * native mode but overrides the visible resolution and clears PREFERRED. + */ +static void dm_test_create_common_mode_overrides(struct kunit *test) +{ + struct dm_test_modes_ctx *ctx = + dm_test_modes_ctx_alloc(test, DRM_MODE_CONNECTOR_eDP); + struct drm_display_mode *mode; + + ctx->aenc->native_mode.clock = 148500; + ctx->aenc->native_mode.htotal = 2200; + ctx->aenc->native_mode.type = DRM_MODE_TYPE_PREFERRED; + + mode = amdgpu_dm_create_common_mode(&ctx->aenc->base, "800x600", + 800, 600); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_EXPECT_EQ(test, mode->hdisplay, 800); + KUNIT_EXPECT_EQ(test, mode->vdisplay, 600); + KUNIT_EXPECT_EQ(test, mode->clock, 148500); + KUNIT_EXPECT_EQ(test, mode->htotal, 2200); + KUNIT_EXPECT_FALSE(test, mode->type & DRM_MODE_TYPE_PREFERRED); + KUNIT_EXPECT_STREQ(test, mode->name, "800x600"); + + drm_mode_destroy(ctx->drm, mode); +} + +/** + * dm_test_add_common_modes_non_edp_noop - Test non-eDP/LVDS adds no modes + * @test: The KUnit test context + * + * Common scaled modes are only added for eDP/LVDS panels; an HDMI connector + * is left untouched. + */ +static void dm_test_add_common_modes_non_edp_noop(struct kunit *test) +{ + struct dm_test_modes_ctx *ctx = + dm_test_modes_ctx_alloc(test, DRM_MODE_CONNECTOR_HDMIA); + + ctx->aenc->native_mode.hdisplay = 1920; + ctx->aenc->native_mode.vdisplay = 1200; + + amdgpu_dm_connector_add_common_modes(&ctx->aenc->base, + &ctx->aconnector->base); + + KUNIT_EXPECT_EQ(test, ctx->aconnector->num_modes, 0); +} + +/** + * dm_test_add_common_modes_edp_adds - Test eDP adds the smaller common modes + * @test: The KUnit test context + * + * For an eDP panel with a 1920x1200 native mode every common mode strictly + * smaller than the native one is added (10 of the 11 entries). + */ +static void dm_test_add_common_modes_edp_adds(struct kunit *test) +{ + struct dm_test_modes_ctx *ctx = + dm_test_modes_ctx_alloc(test, DRM_MODE_CONNECTOR_eDP); + + ctx->aenc->native_mode.hdisplay = 1920; + ctx->aenc->native_mode.vdisplay = 1200; + + amdgpu_dm_connector_add_common_modes(&ctx->aenc->base, + &ctx->aconnector->base); + + KUNIT_EXPECT_EQ(test, ctx->aconnector->num_modes, 10); +} + +/** + * dm_test_ddc_get_modes_null_edid - Test a NULL EDID resets the mode count + * @test: The KUnit test context + */ +static void dm_test_ddc_get_modes_null_edid(struct kunit *test) +{ + struct drm_device *drm = dm_test_alloc_drm(test); + struct amdgpu_dm_connector *aconnector = + dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_HDMIA); + + aconnector->num_modes = 5; + + amdgpu_dm_connector_ddc_get_modes(&aconnector->base, NULL); + + KUNIT_EXPECT_EQ(test, aconnector->num_modes, 0); +} + +/** + * dm_test_add_fs_modes_no_preferred_mode - Test no preferred mode yields no modes + * @test: The KUnit test context + * + * A writeback connector has no highest-refresh-rate mode, so add_fs_modes() + * cannot build any FreeSync video modes and returns 0. + */ +static void dm_test_add_fs_modes_no_preferred_mode(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK; + + KUNIT_EXPECT_EQ(test, (int)add_fs_modes(aconnector), 0); +} + +/** + * dm_test_add_freesync_modes_null_edid_noop - Test NULL EDID adds no modes + * @test: The KUnit test context + * + * Without an EDID the FreeSync video modes cannot be derived, so the mode + * count is left unchanged. + */ +static void dm_test_add_freesync_modes_null_edid_noop(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->num_modes = 7; + + amdgpu_dm_connector_add_freesync_modes(&aconnector->base, NULL); + + KUNIT_EXPECT_EQ(test, aconnector->num_modes, 7); +} + +/* EDID extension block tag values (avoids pulling in private drm headers). */ +#define DM_TEST_CEA_EXT 0x02 +#define DM_TEST_DISPLAYID_EXT 0x70 + +/** + * dm_test_i2c_func_returns_flags - Test the i2c functionality flags + * @test: The KUnit test context + * + * The algorithm advertises plain I2C plus emulated SMBUS regardless of the + * adapter argument, which it never dereferences. + */ +static void dm_test_i2c_func_returns_flags(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, amdgpu_dm_i2c_func(NULL), + I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL); +} + +/** + * dm_test_i2c_xfer_no_ddc_pin - Test transfers without a DDC pin are rejected + * @test: The KUnit test context + * + * When the backing ddc_service has no ddc_pin the transfer bails out early + * with -EIO before touching the message buffers or the dc handle. + */ +static void dm_test_i2c_xfer_no_ddc_pin(struct kunit *test) +{ + struct amdgpu_i2c_adapter *i2c; + struct ddc_service *ddc; + + i2c = kunit_kzalloc(test, sizeof(*i2c), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, i2c); + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ddc); + + i2c->ddc_service = ddc; + i2c_set_adapdata(&i2c->base, i2c); + + /* ddc->ddc_pin is NULL -> transfer is rejected with -EIO. */ + KUNIT_EXPECT_EQ(test, amdgpu_dm_i2c_xfer(&i2c->base, NULL, 0), -EIO); +} + +/** + * dm_test_get_amd_vsdb_unsupported - Test a zero VSDB version reports no support + * @test: The KUnit test context + */ +static void dm_test_get_amd_vsdb_unsupported(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.display_info.amd_vsdb.version = 0; + aconnector->base.display_info.amd_vsdb.replay_mode = false; + + KUNIT_EXPECT_EQ(test, get_amd_vsdb(aconnector, &vsdb_info), 0); + KUNIT_EXPECT_EQ(test, vsdb_info.amd_vsdb_version, 0); +} + +/** + * dm_test_get_amd_vsdb_supported - Test a non-zero VSDB version is reported + * @test: The KUnit test context + * + * The display info's VSDB version and replay mode are copied out and a + * non-zero version reports support. + */ +static void dm_test_get_amd_vsdb_supported(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.display_info.amd_vsdb.version = 2; + aconnector->base.display_info.amd_vsdb.replay_mode = true; + + KUNIT_EXPECT_EQ(test, get_amd_vsdb(aconnector, &vsdb_info), 1); + KUNIT_EXPECT_EQ(test, vsdb_info.amd_vsdb_version, 2); + KUNIT_EXPECT_TRUE(test, vsdb_info.replay_mode); +} + +/** + * dm_test_parse_hdmi_amd_vsdb_null_edid - Test NULL EDID returns -ENODEV + * @test: The KUnit test context + */ +static void dm_test_parse_hdmi_amd_vsdb_null_edid(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + KUNIT_EXPECT_EQ(test, + parse_hdmi_amd_vsdb(aconnector, NULL, &vsdb_info), + -ENODEV); +} + +/** + * dm_test_parse_hdmi_amd_vsdb_no_extensions - Test EDID without extensions + * @test: The KUnit test context + * + * An EDID that declares no extension blocks has no CEA block to parse. + */ +static void dm_test_parse_hdmi_amd_vsdb_no_extensions(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; + struct edid *edid; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + edid = kunit_kzalloc(test, sizeof(*edid), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, edid); + + edid->extensions = 0; + + KUNIT_EXPECT_EQ(test, + parse_hdmi_amd_vsdb(aconnector, edid, &vsdb_info), + -ENODEV); +} + +/** + * dm_test_parse_hdmi_amd_vsdb_no_cea_ext - Test EDID with no CEA extension + * @test: The KUnit test context + * + * An extension block that is not a CEA block leaves no VSDB to parse. + */ +static void dm_test_parse_hdmi_amd_vsdb_no_cea_ext(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; + struct edid *edid; + u8 *raw; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + /* Base block + one extension block that is NOT a CEA extension. */ + raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, raw); + edid = (struct edid *)raw; + edid->extensions = 1; + raw[EDID_LENGTH] = DM_TEST_DISPLAYID_EXT; + + KUNIT_EXPECT_EQ(test, + parse_hdmi_amd_vsdb(aconnector, edid, &vsdb_info), + -ENODEV); +} + +/** + * dm_test_parse_displayid_vrr_null_edid - Test NULL EDID leaves range untouched + * @test: The KUnit test context + */ +static void dm_test_parse_displayid_vrr_null_edid(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + + parse_edid_displayid_vrr(connector, NULL); + + KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 0); + KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.min_vfreq, 0); +} + +/** + * dm_test_parse_displayid_vrr_no_displayid - Test EDID without a DisplayID ext + * @test: The KUnit test context + * + * Without a DisplayID extension block there is no dynamic range to extract. + */ +static void dm_test_parse_displayid_vrr_no_displayid(struct kunit *test) +{ + struct drm_connector *connector; + struct edid *edid; + u8 *raw; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, raw); + edid = (struct edid *)raw; + edid->extensions = 1; + raw[EDID_LENGTH] = DM_TEST_CEA_EXT; + + parse_edid_displayid_vrr(connector, edid); + + KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 0); +} + +/** + * dm_test_parse_displayid_vrr_sets_range - Test a DisplayID VRR block is parsed + * @test: The KUnit test context + * + * A DisplayID dynamic video timing range descriptor populates the connector's + * monitor refresh range. + */ +static void dm_test_parse_displayid_vrr_sets_range(struct kunit *test) +{ + struct drm_connector *connector; + struct edid *edid; + u8 *raw, *ext; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + raw = kunit_kzalloc(test, 2 * EDID_LENGTH, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, raw); + edid = (struct edid *)raw; + edid->extensions = 1; + + ext = raw + EDID_LENGTH; + ext[0] = DM_TEST_DISPLAYID_EXT; + /* + * DisplayID dynamic video timing range descriptor, parsed from offset + * 1: tag 0x25, flags 0 (single-byte max), payload length 9, then the + * min/max vfreq bytes. + */ + ext[1] = 0x25; + ext[2] = 0x00; + ext[3] = 9; + ext[10] = 40; + ext[11] = 144; + + parse_edid_displayid_vrr(connector, edid); + + KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.min_vfreq, 40); + KUNIT_EXPECT_EQ(test, connector->display_info.monitor_range.max_vfreq, 144); +} + +/** + * dm_test_mode_valid_interlace_rejected - Test interlaced modes are rejected + * @test: The KUnit test context + * + * Interlaced modes are rejected up front with MODE_ERROR before any sink or + * stream validation is attempted. + */ +static void dm_test_mode_valid_interlace_rejected(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode *mode; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + mode = kunit_kzalloc(test, sizeof(*mode), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, mode); + + mode->flags = DRM_MODE_FLAG_INTERLACE; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_connector_mode_valid(&aconnector->base, mode), + MODE_ERROR); +} + +/** + * dm_test_mode_valid_dblscan_rejected - Test doublescan modes are rejected + * @test: The KUnit test context + * + * Doublescan modes are rejected up front with MODE_ERROR. + */ +static void dm_test_mode_valid_dblscan_rejected(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode *mode; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + mode = kunit_kzalloc(test, sizeof(*mode), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, mode); + + mode->flags = DRM_MODE_FLAG_DBLSCAN; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_connector_mode_valid(&aconnector->base, mode), + MODE_ERROR); +} + +/** + * dm_test_hdmi_cec_set_edid_no_notifier - Test the no-notifier no-op path + * @test: The KUnit test context + * + * With aconnector->notifier NULL the function returns early and must not crash. + */ +static void dm_test_hdmi_cec_set_edid_no_notifier(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + amdgpu_dm_hdmi_cec_set_edid(aconnector); +} + +/** + * dm_test_s3_handle_hdmi_cec_suspend - Test the suspend pass over connectors + * @test: The KUnit test context + * + * Suspend iterates all connectors, skipping writeback ones and calling the + * unset path on the rest; with NULL notifiers this is a crash-free no-op. + */ +static void dm_test_s3_handle_hdmi_cec_suspend(struct kunit *test) +{ + struct drm_device *drm = dm_test_alloc_drm(test); + + dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_WRITEBACK); + dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_HDMIA); + + amdgpu_dm_s3_handle_hdmi_cec(drm, true); +} + +/** + * dm_test_s3_handle_hdmi_cec_resume - Test the resume pass over connectors + * @test: The KUnit test context + * + * Resume iterates all connectors, skipping writeback ones and calling the set + * path on the rest; with NULL notifiers this is a crash-free no-op. + */ +static void dm_test_s3_handle_hdmi_cec_resume(struct kunit *test) +{ + struct drm_device *drm = dm_test_alloc_drm(test); + + dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_WRITEBACK); + dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_HDMIA); + + amdgpu_dm_s3_handle_hdmi_cec(drm, false); +} + +/** + * dm_test_create_validate_stream_null_dm_state - Test NULL state returns NULL + * @test: The KUnit test context + * + * Without a connector state there is nothing to validate against, so the + * helper bails out with NULL before touching the dc handle. + */ +static void dm_test_create_validate_stream_null_dm_state(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + KUNIT_EXPECT_NULL(test, + amdgpu_dm_create_validate_stream_for_sink(&aconnector->base, + NULL, NULL, NULL)); +} + +/** + * dm_test_update_after_detect_mst_noop - Test MST connectors are left to drm_mst + * @test: The KUnit test context + * + * An MST connector is handled by the drm_mst framework, so the function + * returns immediately and never dereferences the (NULL) dc_link. + */ +static void dm_test_update_after_detect_mst_noop(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->mst_mgr.mst_state = true; + + amdgpu_dm_update_connector_after_detect(aconnector); +} + +/** + * dm_test_update_after_detect_sink_unchanged - Test the short-pulse no-op path + * @test: The KUnit test context + * + * When the link reports no local sink and the connector already has no + * dc_sink, the "sink didn't change" path returns without touching DC. + */ +static void dm_test_update_after_detect_sink_unchanged(struct kunit *test) +{ + struct drm_device *drm = dm_test_alloc_drm(test); + struct amdgpu_dm_connector *aconnector; + struct dc_link *link; + + aconnector = dm_test_add_connector(test, drm, DRM_MODE_CONNECTOR_HDMIA); + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + aconnector->dc_link = link; + + /* link->local_sink and aconnector->dc_sink are both NULL. */ + amdgpu_dm_update_connector_after_detect(aconnector); + + KUNIT_EXPECT_NULL(test, aconnector->dc_sink); +} + +/* Tests for amdgpu_dm_update_stream_scaling_settings() */ + +/** + * dm_test_update_scaling_null_mode - Test NULL mode leaves the stream rects untouched + * @test: The KUnit test context + */ +static void dm_test_update_scaling_null_mode(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); + + stream->timing.h_addressable = 1920; + stream->timing.v_addressable = 1080; + + amdgpu_dm_update_stream_scaling_settings(&adev->ddev, NULL, NULL, stream); + + /* NULL mode: early return before touching src/dst */ + KUNIT_EXPECT_EQ(test, stream->src.width, 0); + KUNIT_EXPECT_EQ(test, stream->dst.width, 0); +} + +/** + * dm_test_update_scaling_fullscreen_default - Test full-screen default with no dm_state + * @test: The KUnit test context + */ +static void dm_test_update_scaling_fullscreen_default(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); + struct drm_display_mode mode = { 0 }; + + mode.hdisplay = 1920; + mode.vdisplay = 1080; + stream->timing.h_addressable = 2560; + stream->timing.v_addressable = 1440; + + amdgpu_dm_update_stream_scaling_settings(&adev->ddev, &mode, NULL, stream); + + /* src = mode, dst = timing addressable, no centering without dm_state */ + KUNIT_EXPECT_EQ(test, stream->src.width, 1920); + KUNIT_EXPECT_EQ(test, stream->src.height, 1080); + KUNIT_EXPECT_EQ(test, stream->dst.width, 2560); + KUNIT_EXPECT_EQ(test, stream->dst.height, 1440); + KUNIT_EXPECT_EQ(test, stream->dst.x, 0); + KUNIT_EXPECT_EQ(test, stream->dst.y, 0); +} + +/** + * dm_test_update_scaling_rmx_full - Test RMX_FULL keeps a full-size, centered dst + * @test: The KUnit test context + */ +static void dm_test_update_scaling_rmx_full(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); + struct dm_connector_state *dm_state; + struct drm_display_mode mode = { 0 }; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + + mode.hdisplay = 1280; + mode.vdisplay = 720; + stream->timing.h_addressable = 1920; + stream->timing.v_addressable = 1080; + dm_state->scaling = RMX_FULL; + + amdgpu_dm_update_stream_scaling_settings(&adev->ddev, &mode, dm_state, stream); + + /* RMX_FULL: dst stays full addressable, offset 0 */ + KUNIT_EXPECT_EQ(test, stream->dst.width, 1920); + KUNIT_EXPECT_EQ(test, stream->dst.height, 1080); + KUNIT_EXPECT_EQ(test, stream->dst.x, 0); + KUNIT_EXPECT_EQ(test, stream->dst.y, 0); +} + +/** + * dm_test_update_scaling_rmx_aspect_pillarbox - Test RMX_ASPECT preserves aspect ratio + * @test: The KUnit test context + */ +static void dm_test_update_scaling_rmx_aspect_pillarbox(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); + struct dm_connector_state *dm_state; + struct drm_display_mode mode = { 0 }; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + + /* 4:3 source on a 16:9 panel -> pillarboxed */ + mode.hdisplay = 1024; + mode.vdisplay = 768; + stream->timing.h_addressable = 1920; + stream->timing.v_addressable = 1080; + dm_state->scaling = RMX_ASPECT; + + amdgpu_dm_update_stream_scaling_settings(&adev->ddev, &mode, dm_state, stream); + + /* + * src.width*dst.height (1024*1080) < src.height*dst.width (768*1920): + * width scaled to src.width*dst.height/src.height = 1440, height stays + * 1080, centered horizontally at (1920-1440)/2 = 240. + */ + KUNIT_EXPECT_EQ(test, stream->dst.width, 1440); + KUNIT_EXPECT_EQ(test, stream->dst.height, 1080); + KUNIT_EXPECT_EQ(test, stream->dst.x, 240); + KUNIT_EXPECT_EQ(test, stream->dst.y, 0); +} + +/** + * dm_test_update_scaling_rmx_aspect_letterbox - Test RMX_ASPECT letterboxes wide sources + * @test: The KUnit test context + */ +static void dm_test_update_scaling_rmx_aspect_letterbox(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); + struct dm_connector_state *dm_state; + struct drm_display_mode mode = { 0 }; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + + /* 16:9 source on a 4:3 panel -> letterboxed */ + mode.hdisplay = 1920; + mode.vdisplay = 1080; + stream->timing.h_addressable = 1024; + stream->timing.v_addressable = 768; + dm_state->scaling = RMX_ASPECT; + + amdgpu_dm_update_stream_scaling_settings(&adev->ddev, &mode, dm_state, stream); + + KUNIT_EXPECT_EQ(test, stream->dst.width, 1024); + KUNIT_EXPECT_EQ(test, stream->dst.height, 576); + KUNIT_EXPECT_EQ(test, stream->dst.x, 0); + KUNIT_EXPECT_EQ(test, stream->dst.y, 96); +} + +/** + * dm_test_update_scaling_rmx_center - Test RMX_CENTER centers a 1:1 dst + * @test: The KUnit test context + */ +static void dm_test_update_scaling_rmx_center(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); + struct dm_connector_state *dm_state; + struct drm_display_mode mode = { 0 }; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + + mode.hdisplay = 1280; + mode.vdisplay = 720; + stream->timing.h_addressable = 1920; + stream->timing.v_addressable = 1080; + dm_state->scaling = RMX_CENTER; + + amdgpu_dm_update_stream_scaling_settings(&adev->ddev, &mode, dm_state, stream); + + /* RMX_CENTER: dst = src, centered on the addressable area */ + KUNIT_EXPECT_EQ(test, stream->dst.width, 1280); + KUNIT_EXPECT_EQ(test, stream->dst.height, 720); + KUNIT_EXPECT_EQ(test, stream->dst.x, 320); + KUNIT_EXPECT_EQ(test, stream->dst.y, 180); +} + +/** + * dm_test_update_scaling_underscan - Test underscan borders shrink and offset dst + * @test: The KUnit test context + */ +static void dm_test_update_scaling_underscan(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); + struct dm_connector_state *dm_state; + struct drm_display_mode mode = { 0 }; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + + mode.hdisplay = 1920; + mode.vdisplay = 1080; + stream->timing.h_addressable = 1920; + stream->timing.v_addressable = 1080; + dm_state->scaling = RMX_FULL; + dm_state->underscan_enable = true; + dm_state->underscan_hborder = 64; + dm_state->underscan_vborder = 32; + + amdgpu_dm_update_stream_scaling_settings(&adev->ddev, &mode, dm_state, stream); + + /* Full dst, then underscan: x/y += border/2, width/height -= border */ + KUNIT_EXPECT_EQ(test, stream->dst.x, 32); + KUNIT_EXPECT_EQ(test, stream->dst.y, 16); + KUNIT_EXPECT_EQ(test, stream->dst.width, 1856); + KUNIT_EXPECT_EQ(test, stream->dst.height, 1048); +} + static struct kunit_case amdgpu_dm_connector_tests[] = { /* get_subconnector_type */ KUNIT_CASE(dm_test_subconnector_type_none), @@ -3798,6 +5456,99 @@ static struct kunit_case amdgpu_dm_connector_tests[] = { KUNIT_CASE(dm_test_fill_stream_color_depth_requested_bpc), KUNIT_CASE(dm_test_fill_stream_content_type), KUNIT_CASE(dm_test_fill_stream_aspect_ratio), + /* create_stream_for_sink */ + KUNIT_CASE(dm_test_create_stream_fake_sink_success), + KUNIT_CASE(dm_test_create_stream_sets_dm_context), + KUNIT_CASE(dm_test_create_stream_virtual_signal), + KUNIT_CASE(dm_test_create_stream_scaling_src), + KUNIT_CASE(dm_test_create_stream_existing_sink), + /* amdgpu_dm_connector_detect */ + KUNIT_CASE(dm_test_detect_force_on), + KUNIT_CASE(dm_test_detect_force_on_digital), + KUNIT_CASE(dm_test_detect_force_off), + KUNIT_CASE(dm_test_detect_sink_present), + KUNIT_CASE(dm_test_detect_no_sink), + /* amdgpu_dm_connector_poll */ + KUNIT_CASE(dm_test_poll_dac_load_returns_cached), + /* amdgpu_dm_connector_late_register */ + KUNIT_CASE(dm_test_late_register_non_dp_succeeds), + /* amdgpu_dm_connector_unregister */ + KUNIT_CASE(dm_test_unregister_non_dp_noop), + /* amdgpu_dm_connector_destroy */ + KUNIT_CASE(dm_test_destroy_minimal), + KUNIT_CASE(dm_test_destroy_releases_dc_sink), + KUNIT_CASE(dm_test_destroy_releases_dc_em_sink), + /* dm_encoder_helper_disable */ + KUNIT_CASE(dm_test_encoder_disable_noop), + /* dm_encoder_helper_atomic_check */ + KUNIT_CASE(dm_test_atomic_check_edp_native_keeps_scaling), + KUNIT_CASE(dm_test_atomic_check_lvds_non_native_enables_scaling), + KUNIT_CASE(dm_test_atomic_check_non_mst_returns_zero), + /* hdmi_cec_unset_edid */ + KUNIT_CASE(dm_test_hdmi_cec_unset_edid_no_notifier), + /* create_eml_sink */ + KUNIT_CASE(dm_test_create_eml_sink_no_edid), + /* handle_edid_mgmt */ + KUNIT_CASE(dm_test_handle_edid_mgmt_dp_sets_link_caps), + KUNIT_CASE(dm_test_handle_edid_mgmt_non_dp_leaves_caps), + /* amdgpu_dm_connector_funcs_force */ + KUNIT_CASE(dm_test_funcs_force_no_edid), + /* dm_validate_stream_and_context */ + KUNIT_CASE(dm_test_validate_stream_null_stream), + /* amdgpu_dm_connector_to_encoder */ + KUNIT_CASE(dm_test_to_encoder_no_encoder), + KUNIT_CASE(dm_test_to_encoder_returns_attached), + /* amdgpu_dm_get_native_mode */ + KUNIT_CASE(dm_test_native_mode_no_encoder), + KUNIT_CASE(dm_test_native_mode_empty_probed_zeroes_clock), + KUNIT_CASE(dm_test_native_mode_copies_preferred), + /* amdgpu_dm_create_common_mode */ + KUNIT_CASE(dm_test_create_common_mode_overrides), + /* amdgpu_dm_connector_add_common_modes */ + KUNIT_CASE(dm_test_add_common_modes_non_edp_noop), + KUNIT_CASE(dm_test_add_common_modes_edp_adds), + /* amdgpu_dm_connector_ddc_get_modes */ + KUNIT_CASE(dm_test_ddc_get_modes_null_edid), + /* add_fs_modes */ + KUNIT_CASE(dm_test_add_fs_modes_no_preferred_mode), + /* amdgpu_dm_connector_add_freesync_modes */ + KUNIT_CASE(dm_test_add_freesync_modes_null_edid_noop), + /* amdgpu_dm_i2c_func */ + KUNIT_CASE(dm_test_i2c_func_returns_flags), + /* amdgpu_dm_i2c_xfer */ + KUNIT_CASE(dm_test_i2c_xfer_no_ddc_pin), + /* get_amd_vsdb */ + KUNIT_CASE(dm_test_get_amd_vsdb_unsupported), + KUNIT_CASE(dm_test_get_amd_vsdb_supported), + /* parse_hdmi_amd_vsdb */ + KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_null_edid), + KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_no_extensions), + KUNIT_CASE(dm_test_parse_hdmi_amd_vsdb_no_cea_ext), + /* parse_edid_displayid_vrr */ + KUNIT_CASE(dm_test_parse_displayid_vrr_null_edid), + KUNIT_CASE(dm_test_parse_displayid_vrr_no_displayid), + KUNIT_CASE(dm_test_parse_displayid_vrr_sets_range), + /* amdgpu_dm_connector_mode_valid */ + KUNIT_CASE(dm_test_mode_valid_interlace_rejected), + KUNIT_CASE(dm_test_mode_valid_dblscan_rejected), + /* amdgpu_dm_hdmi_cec_set_edid */ + KUNIT_CASE(dm_test_hdmi_cec_set_edid_no_notifier), + /* amdgpu_dm_s3_handle_hdmi_cec */ + KUNIT_CASE(dm_test_s3_handle_hdmi_cec_suspend), + KUNIT_CASE(dm_test_s3_handle_hdmi_cec_resume), + /* amdgpu_dm_create_validate_stream_for_sink */ + KUNIT_CASE(dm_test_create_validate_stream_null_dm_state), + /* amdgpu_dm_update_connector_after_detect */ + KUNIT_CASE(dm_test_update_after_detect_mst_noop), + KUNIT_CASE(dm_test_update_after_detect_sink_unchanged), + /* amdgpu_dm_update_stream_scaling_settings */ + KUNIT_CASE(dm_test_update_scaling_null_mode), + KUNIT_CASE(dm_test_update_scaling_fullscreen_default), + KUNIT_CASE(dm_test_update_scaling_rmx_full), + KUNIT_CASE(dm_test_update_scaling_rmx_aspect_pillarbox), + KUNIT_CASE(dm_test_update_scaling_rmx_aspect_letterbox), + KUNIT_CASE(dm_test_update_scaling_rmx_center), + KUNIT_CASE(dm_test_update_scaling_underscan), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c index a6fd3a6fd803..29a513dab9cd 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c @@ -7,7 +7,185 @@ #include <kunit/test.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_modeset_lock.h> +#include <drm/drm_probe_helper.h> + +#include "dc.h" +#include "core_types.h" +#include "logger_types.h" +#include "opp.h" +#include "timing_generator.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" #include "amdgpu_dm_crc.h" +#include "amdgpu_dm_kunit_test_helpers.h" + +struct dm_test_crc_dc_fixture { + struct dc *dc; + struct dc_context *dc_ctx; + struct dc_state *dc_state; + struct dc_stream_state *stream; + struct dc_link *link; + struct timing_generator *tg; + struct output_pixel_processor *opp; + struct dal_logger *logger; + struct dm_crtc_state *dm_state; + struct crc_params crc_params; + enum dc_dynamic_expansion dyn_expansion; + enum dc_dither_option dither_option; + uint32_t crc_r; + uint32_t crc_g; + uint32_t crc_b; + bool configure_crc_called; + bool dyn_expansion_called; + bool bit_depth_reduction_called; + bool configure_crc_return; + bool get_crc_called; + bool get_crc_return; +}; + +static struct dm_test_crc_dc_fixture *dm_test_crc_dc_ctx; + +static bool dm_test_configure_crc(struct timing_generator *tg, + const struct crc_params *params) +{ + if (!dm_test_crc_dc_ctx) + return false; + + dm_test_crc_dc_ctx->configure_crc_called = true; + dm_test_crc_dc_ctx->crc_params = *params; + + return dm_test_crc_dc_ctx->configure_crc_return; +} + +static bool dm_test_get_crc(struct timing_generator *tg, uint8_t idx, + uint32_t *r_cr, uint32_t *g_y, uint32_t *b_cb) +{ + if (!dm_test_crc_dc_ctx) + return false; + + dm_test_crc_dc_ctx->get_crc_called = true; + *r_cr = dm_test_crc_dc_ctx->crc_r; + *g_y = dm_test_crc_dc_ctx->crc_g; + *b_cb = dm_test_crc_dc_ctx->crc_b; + + return dm_test_crc_dc_ctx->get_crc_return; +} + +static void dm_test_opp_set_dyn_expansion(struct output_pixel_processor *opp, + enum dc_color_space color_sp, + enum dc_color_depth color_dpth, + enum signal_type signal) +{ + if (!dm_test_crc_dc_ctx) + return; + + dm_test_crc_dc_ctx->dyn_expansion_called = true; + dm_test_crc_dc_ctx->dyn_expansion = opp->dyn_expansion; +} + +static void dm_test_opp_program_bit_depth_reduction(struct output_pixel_processor *opp, + const struct bit_depth_reduction_params *params) +{ + if (!dm_test_crc_dc_ctx) + return; + + dm_test_crc_dc_ctx->bit_depth_reduction_called = true; +} + +static const struct timing_generator_funcs dm_test_tg_funcs = { + .configure_crc = dm_test_configure_crc, + .get_crc = dm_test_get_crc, +}; + +static const struct opp_funcs dm_test_opp_funcs = { + .opp_set_dyn_expansion = dm_test_opp_set_dyn_expansion, + .opp_program_bit_depth_reduction = dm_test_opp_program_bit_depth_reduction, +}; + +static struct dm_test_crc_dc_fixture *dm_test_alloc_crc_dc_fixture(struct kunit *test, + struct amdgpu_device *adev) +{ + struct dm_test_crc_dc_fixture *fixture; + struct pipe_ctx *pipe; + + fixture = kunit_kzalloc(test, sizeof(*fixture), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fixture); + + fixture->dm_state = kunit_kzalloc(test, sizeof(*fixture->dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fixture->dm_state); + fixture->dc = kunit_kzalloc(test, sizeof(*fixture->dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fixture->dc); + fixture->dc_ctx = kunit_kzalloc(test, sizeof(*fixture->dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fixture->dc_ctx); + fixture->dc_state = kunit_kzalloc(test, sizeof(*fixture->dc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fixture->dc_state); + fixture->tg = kunit_kzalloc(test, sizeof(*fixture->tg), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fixture->tg); + fixture->opp = kunit_kzalloc(test, sizeof(*fixture->opp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fixture->opp); + fixture->logger = kunit_kzalloc(test, sizeof(*fixture->logger), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fixture->logger); + fixture->link = dm_kunit_alloc_link(test); + fixture->stream = dm_kunit_alloc_stream(test, fixture->link); + + mutex_init(&adev->dm.dc_lock); + adev->dm.dc = fixture->dc; + fixture->dc->ctx = fixture->dc_ctx; + fixture->dc->current_state = fixture->dc_state; + fixture->dc_ctx->dc = fixture->dc; + fixture->dc_ctx->logger = fixture->logger; + fixture->link->dc = fixture->dc; + fixture->stream->ctx = fixture->dc_ctx; + fixture->stream->link = fixture->link; + fixture->stream->timing.h_addressable = 1920; + fixture->stream->timing.v_addressable = 1080; + fixture->configure_crc_return = true; + fixture->tg->funcs = &dm_test_tg_funcs; + fixture->opp->funcs = &dm_test_opp_funcs; + fixture->dm_state->stream = fixture->stream; + + pipe = &fixture->dc_state->res_ctx.pipe_ctx[0]; + pipe->stream = fixture->stream; + pipe->pipe_idx = 0; + pipe->stream_res.tg = fixture->tg; + pipe->stream_res.opp = fixture->opp; + + return fixture; +} + +static struct amdgpu_crtc *dm_test_alloc_crc_crtc(struct kunit *test, + struct amdgpu_device *adev) +{ + struct amdgpu_crtc *acrtc; + + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, acrtc); + + acrtc->base.dev = &adev->ddev; + drm_modeset_lock_init(&acrtc->base.mutex); + spin_lock_init(&acrtc->base.commit_lock); + INIT_LIST_HEAD(&acrtc->base.commit_list); + + return acrtc; +} + +static const struct drm_connector_funcs dm_test_crc_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, +}; + +static void dm_test_crc_connector_cleanup(void *data) +{ + drm_connector_cleanup(data); +} static void dm_test_parse_crc_source_none(struct kunit *test) { @@ -96,6 +274,532 @@ static void dm_test_is_valid_crc_source(struct kunit *test) } /** + * dm_test_crtc_get_crc_sources() - Test available CRC source strings. + * @test: KUnit test context. + * + * Verifies that amdgpu_dm_crtc_get_crc_sources() returns the static source + * list and reports all debugfs source names. + */ +static void dm_test_crtc_get_crc_sources(struct kunit *test) +{ + const char *const *sources; + size_t count = 0; + + sources = amdgpu_dm_crtc_get_crc_sources(NULL, &count); + + KUNIT_ASSERT_NOT_NULL(test, sources); + KUNIT_EXPECT_EQ(test, count, 6); + KUNIT_EXPECT_STREQ(test, sources[0], "none"); + KUNIT_EXPECT_STREQ(test, sources[1], "crtc"); + KUNIT_EXPECT_STREQ(test, sources[2], "crtc dither"); + KUNIT_EXPECT_STREQ(test, sources[3], "dprx"); + KUNIT_EXPECT_STREQ(test, sources[4], "dprx dither"); + KUNIT_EXPECT_STREQ(test, sources[5], "auto"); +} + +/** + * dm_test_crtc_verify_crc_source_valid() - Test valid CRC source verification. + * @test: KUnit test context. + * + * Verifies that valid source strings return success and request three CRC + * values. + */ +static void dm_test_crtc_verify_crc_source_valid(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + size_t values_cnt = 0; + int ret; + + ret = amdgpu_dm_crtc_verify_crc_source(&acrtc->base, "crtc", &values_cnt); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, values_cnt, 3); +} + +/** + * dm_test_crtc_verify_crc_source_invalid() - Test invalid CRC source verification. + * @test: KUnit test context. + * + * Verifies that invalid source strings are rejected without changing the + * caller-provided values count. + */ +static void dm_test_crtc_verify_crc_source_invalid(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + size_t values_cnt = 7; + int ret; + + ret = amdgpu_dm_crtc_verify_crc_source(&acrtc->base, "bad", &values_cnt); + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + KUNIT_EXPECT_EQ(test, values_cnt, 7); +} + +/** + * dm_test_crtc_configure_crc_source_no_stream() - Test missing stream handling. + * @test: KUnit test context. + * + * Verifies that configuration is deferred/rejected before any DC access when + * the CRTC state does not have a stream. + */ +static void dm_test_crtc_configure_crc_source_no_stream(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_crtc_state *dm_state; + int ret; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + + ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, dm_state, + AMDGPU_DM_PIPE_CRC_SOURCE_CRTC); + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/** + * dm_test_crtc_configure_crc_source_dprx() - Test DPRX configure path. + * @test: KUnit test context. + * + * Verifies that a DPRX source can be configured with an empty DC resource + * state, covering the non-CRTC path that only updates dither/dynamic expansion. + */ +static void dm_test_crtc_configure_crc_source_dprx(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_test_crc_dc_fixture *fixture; + int ret; + + fixture = dm_test_alloc_crc_dc_fixture(test, adev); + dm_test_crc_dc_ctx = fixture; + + ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX); + dm_test_crc_dc_ctx = NULL; + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_FALSE(test, fixture->configure_crc_called); + KUNIT_EXPECT_TRUE(test, fixture->dyn_expansion_called); + KUNIT_EXPECT_EQ(test, fixture->dyn_expansion, DYN_EXPANSION_DISABLE); + KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called); +} + +/** + * dm_test_crtc_configure_crc_source_dprx_dither() - Test DPRX dither path. + * @test: KUnit test context. + * + * Verifies that a DPRX dither source reaches the default dither/dynamic + * expansion path without requiring timing-generator callbacks. + */ +static void dm_test_crtc_configure_crc_source_dprx_dither(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_test_crc_dc_fixture *fixture; + int ret; + + fixture = dm_test_alloc_crc_dc_fixture(test, adev); + dm_test_crc_dc_ctx = fixture; + + ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER); + dm_test_crc_dc_ctx = NULL; + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_FALSE(test, fixture->configure_crc_called); + KUNIT_EXPECT_TRUE(test, fixture->dyn_expansion_called); + KUNIT_EXPECT_EQ(test, fixture->dyn_expansion, DYN_EXPANSION_AUTO); + KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called); +} + +/** + * dm_test_crtc_configure_crc_source_crtc() - Test CRTC enable path. + * @test: KUnit test context. + * + * Verifies that a CRTC source enables DC CRC capture and disables dither. + */ +static void dm_test_crtc_configure_crc_source_crtc(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_test_crc_dc_fixture *fixture; + int ret; + + fixture = dm_test_alloc_crc_dc_fixture(test, adev); + dm_test_crc_dc_ctx = fixture; + + ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state, + AMDGPU_DM_PIPE_CRC_SOURCE_CRTC); + dm_test_crc_dc_ctx = NULL; + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, fixture->configure_crc_called); + KUNIT_EXPECT_TRUE(test, fixture->crc_params.enable); + KUNIT_EXPECT_TRUE(test, fixture->crc_params.continuous_mode); + KUNIT_EXPECT_TRUE(test, fixture->crc_params.reset); + KUNIT_EXPECT_EQ(test, fixture->crc_params.windowa_x_end, 1920); + KUNIT_EXPECT_EQ(test, fixture->crc_params.windowa_y_end, 1080); + KUNIT_EXPECT_TRUE(test, fixture->dyn_expansion_called); + KUNIT_EXPECT_EQ(test, fixture->dyn_expansion, DYN_EXPANSION_DISABLE); + KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called); +} + +/** + * dm_test_crtc_configure_crc_source_crtc_dcn36_poly() - Test CRC poly select. + * @test: KUnit test context. + * + * Verifies that DCN3.6+ configurations use the CRTC-selected CRC polynomial. + */ +static void dm_test_crtc_configure_crc_source_crtc_dcn36_poly(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_test_crc_dc_fixture *fixture; + int ret; + + fixture = dm_test_alloc_crc_dc_fixture(test, adev); + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 6, 0); + acrtc->dm_irq_params.crc_poly_mode = CRC_POLY_MODE_32; + dm_test_crc_dc_ctx = fixture; + + ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state, + AMDGPU_DM_PIPE_CRC_SOURCE_CRTC); + dm_test_crc_dc_ctx = NULL; + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, fixture->configure_crc_called); + KUNIT_EXPECT_EQ(test, fixture->crc_params.crc_poly_mode, CRC_POLY_MODE_32); +} + +/** + * dm_test_crtc_configure_crc_source_crtc_configure_fails() - Test failure path. + * @test: KUnit test context. + * + * Verifies that a DC CRC configuration failure is reported as -EINVAL and + * stops before dither/dynamic expansion programming. + */ +static void dm_test_crtc_configure_crc_source_crtc_configure_fails(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_test_crc_dc_fixture *fixture; + int ret; + + fixture = dm_test_alloc_crc_dc_fixture(test, adev); + fixture->configure_crc_return = false; + dm_test_crc_dc_ctx = fixture; + + ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state, + AMDGPU_DM_PIPE_CRC_SOURCE_CRTC); + dm_test_crc_dc_ctx = NULL; + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + KUNIT_EXPECT_TRUE(test, fixture->configure_crc_called); + KUNIT_EXPECT_FALSE(test, fixture->dyn_expansion_called); + KUNIT_EXPECT_FALSE(test, fixture->bit_depth_reduction_called); +} + +/** + * dm_test_crtc_configure_crc_source_none() - Test CRC disable path. + * @test: KUnit test context. + * + * Verifies that source NONE disables DC CRC capture and restores default + * dither/dynamic expansion. + */ +static void dm_test_crtc_configure_crc_source_none(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_test_crc_dc_fixture *fixture; + int ret; + + fixture = dm_test_alloc_crc_dc_fixture(test, adev); + dm_test_crc_dc_ctx = fixture; + + ret = amdgpu_dm_crtc_configure_crc_source(&acrtc->base, fixture->dm_state, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE); + dm_test_crc_dc_ctx = NULL; + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_TRUE(test, fixture->configure_crc_called); + KUNIT_EXPECT_FALSE(test, fixture->crc_params.enable); + KUNIT_EXPECT_FALSE(test, fixture->crc_params.continuous_mode); + KUNIT_EXPECT_TRUE(test, fixture->crc_params.reset); + KUNIT_EXPECT_TRUE(test, fixture->dyn_expansion_called); + KUNIT_EXPECT_EQ(test, fixture->dyn_expansion, DYN_EXPANSION_AUTO); + KUNIT_EXPECT_TRUE(test, fixture->bit_depth_reduction_called); +} + +/** + * dm_test_crtc_set_crc_source_invalid() - Test invalid source guard. + * @test: KUnit test context. + * + * Verifies that amdgpu_dm_crtc_set_crc_source() rejects invalid source names + * before taking modeset locks, vblank references, or touching DC state. + */ +static void dm_test_crtc_set_crc_source_invalid(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + int ret; + + ret = amdgpu_dm_crtc_set_crc_source(&acrtc->base, "invalid"); + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/** + * dm_test_crtc_set_crc_source_none_no_stream() - Test valid source no-stream exit. + * @test: KUnit test context. + * + * Verifies that a valid NONE request enters the set-source body, reads the + * current CRC state, and exits cleanly when configuration is deferred because + * no stream is attached. + */ +static void dm_test_crtc_set_crc_source_none_no_stream(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_crtc_state *dm_state; + int ret; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + acrtc->base.state = &dm_state->base; + acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE; + + ret = amdgpu_dm_crtc_set_crc_source(&acrtc->base, "none"); + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + KUNIT_EXPECT_EQ(test, acrtc->dm_irq_params.crc_src, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE); + KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 0); +} + +/** + * dm_test_crtc_set_crc_source_none_commit() - Test set-source with pending commit. + * @test: KUnit test context. + * + * Verifies that a pending CRTC commit is acquired and waited on (already + * completed here so the wait returns immediately), then released during + * cleanup. Configuration is still deferred because no stream is attached. + */ +static void dm_test_crtc_set_crc_source_none_commit(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_crtc_state *dm_state; + struct drm_crtc_commit *commit; + int ret; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + acrtc->base.state = &dm_state->base; + acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE; + + commit = kunit_kzalloc(test, sizeof(*commit), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, commit); + kref_init(&commit->ref); + init_completion(&commit->hw_done); + /* Mark the commit finished so the wait returns immediately. */ + complete_all(&commit->hw_done); + list_add_tail(&commit->commit_entry, &acrtc->base.commit_list); + + ret = amdgpu_dm_crtc_set_crc_source(&acrtc->base, "none"); + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 0); +} + +/** + * dm_test_crtc_set_crc_source_dprx_no_connector() - Test DPRX with no match. + * @test: KUnit test context. + * + * Verifies that requesting a DPRX source walks the connector list and returns + * -EINVAL when no matching DP connector is attached to the CRTC. A stateless + * connector and a writeback connector exercise both connector filter branches. + */ +static void dm_test_crtc_set_crc_source_dprx_no_connector(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct drm_connector *dp_conn; + struct drm_connector *wb_conn; + struct dm_crtc_state *dm_state; + int ret; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + acrtc->base.state = &dm_state->base; + acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE; + + /* Stateless DP connector: skipped by the !state filter. */ + dp_conn = kunit_kzalloc(test, sizeof(*dp_conn), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dp_conn); + KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, dp_conn, + &dm_test_crc_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort), 0); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_crc_connector_cleanup, dp_conn), 0); + + /* Writeback connector bound to this CRTC: skipped by the WB filter. */ + wb_conn = kunit_kzalloc(test, sizeof(*wb_conn), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, wb_conn); + KUNIT_ASSERT_EQ(test, drm_connector_init(&adev->ddev, wb_conn, + &dm_test_crc_connector_funcs, + DRM_MODE_CONNECTOR_WRITEBACK), 0); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, + dm_test_crc_connector_cleanup, wb_conn), 0); + drm_atomic_helper_connector_reset(wb_conn); + KUNIT_ASSERT_NOT_NULL(test, wb_conn->state); + wb_conn->state->crtc = &acrtc->base; + /* + * __drm_atomic_helper_connector_destroy_state() drops a connector + * reference when state->crtc is set. Balance it here since the CRTC is + * assigned directly rather than via drm_atomic_set_crtc_for_connector(), + * otherwise cleanup would drop the connector to zero and schedule an + * async free on the system workqueue. + */ + drm_connector_get(wb_conn); + + ret = amdgpu_dm_crtc_set_crc_source(&acrtc->base, "dprx"); + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + KUNIT_EXPECT_EQ(test, acrtc->dm_irq_params.crc_src, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE); +} + +/** + * dm_test_crtc_handle_crc_irq_early_returns() - Test null/missing state exits. + * @test: KUnit test context. + * + * Verifies that the CRC IRQ handler safely ignores incomplete CRTC objects. + */ +static void dm_test_crtc_handle_crc_irq_early_returns(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_crtc_state *dm_state; + + amdgpu_dm_crtc_handle_crc_irq(NULL); + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + acrtc->base.state = &dm_state->base; + + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 0); +} + +/** + * dm_test_crtc_handle_crc_irq_disabled_source() - Test disabled source exit. + * @test: KUnit test context. + * + * Verifies that a present stream does not advance the skip counter when CRC + * capture is disabled. + */ +static void dm_test_crtc_handle_crc_irq_disabled_source(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_crtc_state *dm_state; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + dm_state->stream = dm_kunit_alloc_stream(test, NULL); + acrtc->base.state = &dm_state->base; + acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_NONE; + + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + + KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 0); +} + +/** + * dm_test_crtc_handle_crc_irq_skips_initial_frames() - Test initial skip logic. + * @test: KUnit test context. + * + * Verifies that the first two enabled CRC IRQs only increment crc_skip_count, + * avoiding the later DC CRC read path. + */ +static void dm_test_crtc_handle_crc_irq_skips_initial_frames(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_crtc_state *dm_state; + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + dm_state->stream = dm_kunit_alloc_stream(test, NULL); + acrtc->base.state = &dm_state->base; + acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_CRTC; + + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 1); + + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + KUNIT_EXPECT_EQ(test, dm_state->crc_skip_count, 2); +} + +/** + * dm_test_crtc_handle_crc_irq_dprx_after_skip() - Test DPRX post-skip exit. + * @test: KUnit test context. + * + * Verifies that enabled non-CRTC CRC sources do not call into DC CRC reads + * after the initial skip window. + */ +static void dm_test_crtc_handle_crc_irq_dprx_after_skip(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_test_crc_dc_fixture *fixture; + + fixture = dm_test_alloc_crc_dc_fixture(test, adev); + fixture->dm_state->crc_skip_count = 2; + acrtc->base.state = &fixture->dm_state->base; + acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_DPRX; + dm_test_crc_dc_ctx = fixture; + + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + dm_test_crc_dc_ctx = NULL; + + KUNIT_EXPECT_FALSE(test, fixture->get_crc_called); + KUNIT_EXPECT_EQ(test, fixture->dm_state->crc_skip_count, 2); +} + +/** + * dm_test_crtc_handle_crc_irq_get_crc_fails() - Test failed DC CRC read. + * @test: KUnit test context. + * + * Verifies that the IRQ handler exits after dc_stream_get_crc() returns false, + * before attempting to deliver a DRM CRC entry. + */ +static void dm_test_crtc_handle_crc_irq_get_crc_fails(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc = dm_test_alloc_crc_crtc(test, adev); + struct dm_test_crc_dc_fixture *fixture; + + fixture = dm_test_alloc_crc_dc_fixture(test, adev); + fixture->dm_state->crc_skip_count = 2; + fixture->get_crc_return = false; + acrtc->base.state = &fixture->dm_state->base; + acrtc->dm_irq_params.crc_src = AMDGPU_DM_PIPE_CRC_SOURCE_CRTC; + dm_test_crc_dc_ctx = fixture; + + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + dm_test_crc_dc_ctx = NULL; + + KUNIT_EXPECT_TRUE(test, fixture->get_crc_called); + KUNIT_EXPECT_EQ(test, fixture->dm_state->crc_skip_count, 2); +} + +/** * dm_test_need_dp_aux() - Test dm_need_dp_aux(). * @test: KUnit test context. * @@ -222,6 +926,30 @@ static struct kunit_case dm_crc_test_cases[] = { KUNIT_CASE(dm_test_need_crc_dither), /* amdgpu_dm_is_valid_crc_source() */ KUNIT_CASE(dm_test_is_valid_crc_source), + /* amdgpu_dm_crtc_get_crc_sources() */ + KUNIT_CASE(dm_test_crtc_get_crc_sources), + /* amdgpu_dm_crtc_verify_crc_source() */ + KUNIT_CASE(dm_test_crtc_verify_crc_source_valid), + KUNIT_CASE(dm_test_crtc_verify_crc_source_invalid), + /* amdgpu_dm_crtc_configure_crc_source() */ + KUNIT_CASE(dm_test_crtc_configure_crc_source_no_stream), + KUNIT_CASE(dm_test_crtc_configure_crc_source_dprx), + KUNIT_CASE(dm_test_crtc_configure_crc_source_dprx_dither), + KUNIT_CASE(dm_test_crtc_configure_crc_source_crtc), + KUNIT_CASE(dm_test_crtc_configure_crc_source_crtc_dcn36_poly), + KUNIT_CASE(dm_test_crtc_configure_crc_source_crtc_configure_fails), + KUNIT_CASE(dm_test_crtc_configure_crc_source_none), + /* amdgpu_dm_crtc_set_crc_source() */ + KUNIT_CASE(dm_test_crtc_set_crc_source_invalid), + KUNIT_CASE(dm_test_crtc_set_crc_source_none_no_stream), + KUNIT_CASE(dm_test_crtc_set_crc_source_none_commit), + KUNIT_CASE(dm_test_crtc_set_crc_source_dprx_no_connector), + /* amdgpu_dm_crtc_handle_crc_irq() */ + KUNIT_CASE(dm_test_crtc_handle_crc_irq_early_returns), + KUNIT_CASE(dm_test_crtc_handle_crc_irq_disabled_source), + KUNIT_CASE(dm_test_crtc_handle_crc_irq_skips_initial_frames), + KUNIT_CASE(dm_test_crtc_handle_crc_irq_dprx_after_skip), + KUNIT_CASE(dm_test_crtc_handle_crc_irq_get_crc_fails), /* dm_need_dp_aux() */ KUNIT_CASE(dm_test_need_dp_aux), /* dm_crc_source_should_start_dprx() */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c index 0edaf969f16b..ac21bbec92ab 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c @@ -296,13 +296,9 @@ static void dm_test_crtc_is_headless_null_adev(struct kunit *test) static void dm_test_crtc_is_headless_no_connectors(struct kunit *test) { struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); - struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_device *dev = dm_kunit_alloc_drm_with_connector_list(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); - - INIT_LIST_HEAD(&dev->mode_config.connector_list); - spin_lock_init(&dev->mode_config.connector_list_lock); adev->dm.ddev = dev; KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(adev)); @@ -315,15 +311,11 @@ static void dm_test_crtc_is_headless_no_connectors(struct kunit *test) static void dm_test_crtc_is_headless_writeback_only(struct kunit *test) { struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); - struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_device *dev = dm_kunit_alloc_drm_with_connector_list(test); struct drm_connector *wb = kunit_kzalloc(test, sizeof(*wb), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wb); - - INIT_LIST_HEAD(&dev->mode_config.connector_list); - spin_lock_init(&dev->mode_config.connector_list_lock); adev->dm.ddev = dev; dm_test_add_connector(dev, wb, DRM_MODE_CONNECTOR_WRITEBACK, @@ -339,15 +331,11 @@ static void dm_test_crtc_is_headless_writeback_only(struct kunit *test) static void dm_test_crtc_is_headless_disconnected_display(struct kunit *test) { struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); - struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_device *dev = dm_kunit_alloc_drm_with_connector_list(test); struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display); - - INIT_LIST_HEAD(&dev->mode_config.connector_list); - spin_lock_init(&dev->mode_config.connector_list_lock); adev->dm.ddev = dev; dm_test_add_connector(dev, display, DRM_MODE_CONNECTOR_HDMIA, @@ -363,15 +351,11 @@ static void dm_test_crtc_is_headless_disconnected_display(struct kunit *test) static void dm_test_crtc_is_headless_connected_display(struct kunit *test) { struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); - struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_device *dev = dm_kunit_alloc_drm_with_connector_list(test); struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display); - - INIT_LIST_HEAD(&dev->mode_config.connector_list); - spin_lock_init(&dev->mode_config.connector_list_lock); adev->dm.ddev = dev; dm_test_add_connector(dev, display, DRM_MODE_CONNECTOR_HDMIA, @@ -387,17 +371,13 @@ static void dm_test_crtc_is_headless_connected_display(struct kunit *test) static void dm_test_crtc_is_headless_mixed_connectors(struct kunit *test) { struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); - struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_device *dev = dm_kunit_alloc_drm_with_connector_list(test); struct drm_connector *wb = kunit_kzalloc(test, sizeof(*wb), GFP_KERNEL); struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wb); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display); - - INIT_LIST_HEAD(&dev->mode_config.connector_list); - spin_lock_init(&dev->mode_config.connector_list_lock); adev->dm.ddev = dev; dm_test_add_connector(dev, wb, DRM_MODE_CONNECTOR_WRITEBACK, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_cursor_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_cursor_test.c new file mode 100644 index 000000000000..3da76794ece9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_cursor_test.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_cursor.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <drm/drm_atomic.h> +#include <drm/drm_blend.h> +#include <drm/drm_crtc.h> +#include <drm/drm_plane.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_cursor.h" + +/* Tests for amdgpu_dm_should_update_native_cursor() */ + +/** + * dm_test_should_update_native_cursor_without_crtc - Test NULL crtc cases update native cursor + * @test: The KUnit test context + */ +static void dm_test_should_update_native_cursor_without_crtc(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_update_native_cursor(NULL, NULL, NULL, false)); + KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_update_native_cursor(NULL, NULL, NULL, true)); +} + +/** + * dm_test_should_update_native_cursor_disable_native - Test disable path reads old crtc cursor mode + * @test: The KUnit test context + */ +static void dm_test_should_update_native_cursor_disable_native(struct kunit *test) +{ + struct dm_crtc_state *dm_crtc_state; + struct drm_atomic_commit *state; + struct drm_crtc *crtc; + + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state); + + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, crtc); + + dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state); + + state->crtcs = kunit_kzalloc(test, sizeof(*state->crtcs), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state->crtcs); + + crtc->index = 0; + dm_crtc_state->cursor_mode = DM_CURSOR_NATIVE_MODE; + state->crtcs[0].old_state = &dm_crtc_state->base; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_should_update_native_cursor(state, crtc, NULL, false)); +} + +/** + * dm_test_should_update_native_cursor_enable_overlay - Test enable path reads new crtc cursor mode + * @test: The KUnit test context + */ +static void dm_test_should_update_native_cursor_enable_overlay(struct kunit *test) +{ + struct dm_crtc_state *dm_crtc_state; + struct drm_atomic_commit *state; + struct drm_crtc *crtc; + + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state); + + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, crtc); + + dm_crtc_state = kunit_kzalloc(test, sizeof(*dm_crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_crtc_state); + + state->crtcs = kunit_kzalloc(test, sizeof(*state->crtcs), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state->crtcs); + + crtc->index = 0; + dm_crtc_state->cursor_mode = DM_CURSOR_OVERLAY_MODE; + state->crtcs[0].new_state = &dm_crtc_state->base; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_should_update_native_cursor(state, NULL, crtc, true)); +} + +/* Tests for dm_get_oriented_plane_size() */ + +/** + * dm_test_oriented_plane_size_rotate_0 - Test Oriented plane size rotate 0 + * @test: The KUnit test context + */ +static void dm_test_oriented_plane_size_rotate_0(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int src_w = 0; + int src_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_0; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + + dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); + + KUNIT_EXPECT_EQ(test, src_w, 1920); + KUNIT_EXPECT_EQ(test, src_h, 1080); +} + +/** + * dm_test_oriented_plane_size_rotate_90 - Test Oriented plane size rotate 90 + * @test: The KUnit test context + */ +static void dm_test_oriented_plane_size_rotate_90(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int src_w = 0; + int src_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_90; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + + dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); + + KUNIT_EXPECT_EQ(test, src_w, 1080); + KUNIT_EXPECT_EQ(test, src_h, 1920); +} + +/** + * dm_test_oriented_plane_size_rotate_180 - Test Oriented plane size rotate 180 + * @test: The KUnit test context + */ +static void dm_test_oriented_plane_size_rotate_180(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int src_w = 0; + int src_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_180; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + + dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); + + KUNIT_EXPECT_EQ(test, src_w, 1920); + KUNIT_EXPECT_EQ(test, src_h, 1080); +} + +/** + * dm_test_oriented_plane_size_rotate_270 - Test Oriented plane size rotate 270 + * @test: The KUnit test context + */ +static void dm_test_oriented_plane_size_rotate_270(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int src_w = 0; + int src_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_270; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + + dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); + + KUNIT_EXPECT_EQ(test, src_w, 1080); + KUNIT_EXPECT_EQ(test, src_h, 1920); +} + +/* Tests for dm_get_plane_scale() */ + +/** + * dm_test_get_plane_scale_identity - Test Get plane scale identity + * @test: The KUnit test context + */ +static void dm_test_get_plane_scale_identity(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int scale_w = 0; + int scale_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_0; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + plane_state.crtc_w = 1920; + plane_state.crtc_h = 1080; + + dm_get_plane_scale(&plane_state, &scale_w, &scale_h); + + KUNIT_EXPECT_EQ(test, scale_w, 1000); + KUNIT_EXPECT_EQ(test, scale_h, 1000); +} + +/** + * dm_test_get_plane_scale_rotate_90_identity - Test Get plane scale rotate 90 identity + * @test: The KUnit test context + */ +static void dm_test_get_plane_scale_rotate_90_identity(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int scale_w = 0; + int scale_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_90; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + plane_state.crtc_w = 1080; + plane_state.crtc_h = 1920; + + dm_get_plane_scale(&plane_state, &scale_w, &scale_h); + + KUNIT_EXPECT_EQ(test, scale_w, 1000); + KUNIT_EXPECT_EQ(test, scale_h, 1000); +} + +/** + * dm_test_get_plane_scale_zero_src_width - Test Get plane scale zero src width + * @test: The KUnit test context + */ +static void dm_test_get_plane_scale_zero_src_width(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int scale_w = 0; + int scale_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_0; + plane_state.src_w = 0; + plane_state.src_h = 1080 << 16; + plane_state.crtc_w = 100; + plane_state.crtc_h = 200; + + dm_get_plane_scale(&plane_state, &scale_w, &scale_h); + + KUNIT_EXPECT_EQ(test, scale_w, 0); + KUNIT_EXPECT_EQ(test, scale_h, 185); +} + +static struct kunit_case amdgpu_dm_cursor_tests[] = { + /* amdgpu_dm_should_update_native_cursor */ + KUNIT_CASE(dm_test_should_update_native_cursor_without_crtc), + KUNIT_CASE(dm_test_should_update_native_cursor_disable_native), + KUNIT_CASE(dm_test_should_update_native_cursor_enable_overlay), + /* dm_get_oriented_plane_size */ + KUNIT_CASE(dm_test_oriented_plane_size_rotate_0), + KUNIT_CASE(dm_test_oriented_plane_size_rotate_90), + KUNIT_CASE(dm_test_oriented_plane_size_rotate_180), + KUNIT_CASE(dm_test_oriented_plane_size_rotate_270), + /* dm_get_plane_scale */ + KUNIT_CASE(dm_test_get_plane_scale_identity), + KUNIT_CASE(dm_test_get_plane_scale_rotate_90_identity), + KUNIT_CASE(dm_test_get_plane_scale_zero_src_width), + {} +}; + +static struct kunit_suite amdgpu_dm_cursor_test_suite = { + .name = "amdgpu_dm_cursor", + .test_cases = amdgpu_dm_cursor_tests, +}; + +kunit_test_suite(amdgpu_dm_cursor_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_cursor"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c index bf90ccfbf431..ead43eeb38c7 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c @@ -6,14 +6,20 @@ */ #include <kunit/test.h> +#include <linux/firmware.h> #include "dc.h" #include "dc/inc/core_types.h" +#include "dc/inc/hw/dmcu.h" +#include "dc/inc/hw/abm.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" +#include "dm_services.h" #include "dmub/dmub_srv.h" #include "amdgpu_dm_dmub.h" +#define DM_TEST_FW_SIZE 512 + /* Tests for dm_register_dmub_notify_callback() */ static void dummy_callback(struct amdgpu_device *adev, @@ -21,6 +27,99 @@ static void dummy_callback(struct amdgpu_device *adev, { } +static bool dm_test_dmub_supported(struct dmub_srv *dmub) +{ + return true; +} + +static bool dm_test_dmub_unsupported(struct dmub_srv *dmub) +{ + return false; +} + +static bool dm_test_dmub_hw_initialized(struct dmub_srv *dmub) +{ + return true; +} + +static union dmub_fw_boot_status dm_test_dmub_fw_ready(struct dmub_srv *dmub) +{ + union dmub_fw_boot_status status = { 0 }; + + status.bits.dal_fw = 1; + status.bits.mailbox_rdy = 1; + return status; +} + +static union dmub_fw_boot_status dm_test_dmub_fw_not_ready(struct dmub_srv *dmub) +{ + union dmub_fw_boot_status status = { 0 }; + + return status; +} + +static void dm_test_dmub_init_reg_offsets(struct dmub_srv *dmub, + struct dc_context *ctx) +{ +} + +static bool dm_test_dmcu_init(struct dmcu *dmcu) +{ + return true; +} + +static bool dm_test_dmcu_is_initialized(struct dmcu *dmcu) +{ + return true; +} + +static const struct dmcu_funcs dm_test_dmcu_funcs = { + .dmcu_init = dm_test_dmcu_init, + .is_dmcu_initialized = dm_test_dmcu_is_initialized, +}; + +static struct dmub_srv *dm_test_alloc_dmub_srv(struct kunit *test) +{ + struct dmub_srv *dmub_srv; + + dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv); + + dmub_srv->sw_init = true; + dmub_srv->hw_init = true; + dmub_srv->power_state = DMUB_POWER_STATE_D0; + dmub_srv->hw_funcs.is_supported = dm_test_dmub_supported; + dmub_srv->hw_funcs.is_hw_init = dm_test_dmub_hw_initialized; + dmub_srv->hw_funcs.get_fw_status = dm_test_dmub_fw_ready; + dmub_srv->hw_funcs.init_reg_offsets = dm_test_dmub_init_reg_offsets; + + return dmub_srv; +} + +static const struct firmware *dm_test_alloc_dmub_fw(struct kunit *test) +{ + struct dmcub_firmware_header_v1_0 *hdr; + struct firmware *fw; + u8 *data; + + fw = kunit_kzalloc(test, sizeof(*fw), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fw); + + data = kunit_kzalloc(test, DM_TEST_FW_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, data); + + hdr = (struct dmcub_firmware_header_v1_0 *)data; + hdr->header.ucode_array_offset_bytes = cpu_to_le32(0); + hdr->header.ucode_version = cpu_to_le32(DMUB_FW_VERSION(9, 9, 9)); + hdr->inst_const_bytes = cpu_to_le32(PSP_HEADER_BYTES_256); + hdr->bss_data_bytes = cpu_to_le32(0); + + fw->size = DM_TEST_FW_SIZE; + fw->data = data; + + return fw; +} + /** * dm_test_register_dmub_notify_callback_null_callback - Test null callback is rejected * @test: The KUnit test context @@ -297,6 +396,22 @@ static void dm_test_dmub_aux_fused_io_callback_max_ddc_line(struct kunit *test) KUNIT_EXPECT_EQ(test, reply_ddc_line, notify_ddc_line); } +/** + * dm_test_dmub_aux_fused_io_callback_null_args - Test the NULL-argument guard + * @test: The KUnit test context + * + * Passing a NULL device triggers the defensive guard (an ASSERT that maps to + * WARN_ON_ONCE in this build) and returns early without dereferencing the + * arguments. The call must not crash. + */ +static void dm_test_dmub_aux_fused_io_callback_null_args(struct kunit *test) +{ + struct dmub_notification notify = {}; + + /* Must not crash; guard hits ASSERT (WARN_ON_ONCE) and returns. */ + dm_dmub_aux_fused_io_callback(NULL, ¬ify); +} + /* Tests for dm_get_default_ips_mode() */ /** @@ -395,6 +510,7 @@ static void dm_test_get_default_ips_mode_newer_default(struct kunit *test) static struct amdgpu_device *dm_test_alloc_adev_with_dc(struct kunit *test) { struct amdgpu_device *adev; + struct dc_context *ctx; struct dc *dc; struct resource_pool *res_pool; @@ -407,12 +523,47 @@ static struct amdgpu_device *dm_test_alloc_adev_with_dc(struct kunit *test) res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res_pool); + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + dc->res_pool = res_pool; + dc->ctx = ctx; + ctx->dc = dc; + ctx->driver_context = adev; adev->dm.dc = dc; return adev; } +static struct amdgpu_device *dm_test_alloc_adev_with_dmub(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dmub_srv_fb_info *fb_info; + int i; + + adev = dm_test_alloc_adev_with_dc(test); + + fb_info = kunit_kzalloc(test, sizeof(*fb_info), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fb_info); + + fb_info->num_fb = DMUB_WINDOW_TOTAL; + for (i = 0; i < DMUB_WINDOW_TOTAL; i++) { + fb_info->fb[i].size = PAGE_SIZE; + fb_info->fb[i].cpu_addr = kunit_kzalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fb_info->fb[i].cpu_addr); + } + + adev->dm.dmub_srv = dm_test_alloc_dmub_srv(test); + adev->dm.dmub_fb_info = fb_info; + adev->dm.dmub_fw = dm_test_alloc_dmub_fw(test); + adev->bios = kunit_kzalloc(test, 4, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->bios); + adev->bios_size = 4; + adev->dm.fw_inst_size = 0; + + return adev; +} + /** * dm_test_dmub_hw_init_no_dmub_srv - Test hw init returns 0 when DMUB unsupported * @test: The KUnit test context @@ -476,6 +627,177 @@ static void dm_test_dmub_hw_init_no_firmware(struct kunit *test) KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), -EINVAL); } +/** + * dm_test_dmub_hw_init_success_fake_dmub - Test hw init with a fake DMUB service + * @test: The KUnit test context + * + * With fake DMUB callbacks and preallocated framebuffer windows, the init path + * should reach DMUB service initialization without real register access. + */ +static void dm_test_dmub_hw_init_success_fake_dmub(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); + KUNIT_EXPECT_TRUE(test, adev->dm.dmub_srv->hw_init); + KUNIT_EXPECT_NOT_NULL(test, adev->dm.dc->ctx->dmub_srv); +} + +/** + * dm_test_dmub_hw_init_no_hw_support - Test hw init returns 0 when HW is unsupported + * @test: The KUnit test context + * + * When the DMUB service reports no hardware support, dm_dmub_hw_init() should + * log and return 0 without initializing the DMUB hardware. + */ +static void dm_test_dmub_hw_init_no_hw_support(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + adev->dm.dmub_srv->hw_funcs.is_supported = dm_test_dmub_unsupported; + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); + KUNIT_EXPECT_NULL(test, adev->dm.dc->ctx->dmub_srv); +} + +/** + * dm_test_dmub_hw_init_bss_data - Test hw init copies BSS data into FB memory + * @test: The KUnit test context + * + * When the DMUB firmware declares a non-zero BSS data size, dm_dmub_hw_init() + * should copy that region into the BSS framebuffer window. + */ +static void dm_test_dmub_hw_init_bss_data(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + struct dmcub_firmware_header_v1_0 *hdr; + + hdr = (struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data; + hdr->bss_data_bytes = cpu_to_le32(16); + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); + KUNIT_EXPECT_TRUE(test, adev->dm.dmub_srv->hw_init); +} + +/** + * dm_test_dmub_hw_init_hw_init_fails - Test hw init returns -EINVAL on DMUB init failure + * @test: The KUnit test context + * + * A framebuffer-info window count below the required total makes + * dmub_srv_hw_init() reject the request, so dm_dmub_hw_init() logs and + * returns -EINVAL. (The rejection path emits a one-time WARN via ASSERT.) + */ +static void dm_test_dmub_hw_init_hw_init_fails(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + adev->dm.dmub_fb_info->num_fb = 0; + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), -EINVAL); +} + +/** + * dm_test_dmub_hw_init_auto_load_timeout - Test hw init tolerates an auto-load timeout + * @test: The KUnit test context + * + * When the DMUB firmware never reports ready, dmub_srv_wait_for_auto_load() + * times out; dm_dmub_hw_init() only warns and still completes successfully. + */ +static void dm_test_dmub_hw_init_auto_load_timeout(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + adev->dm.dmub_srv->hw_funcs.get_fw_status = dm_test_dmub_fw_not_ready; + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); + KUNIT_EXPECT_NOT_NULL(test, adev->dm.dc->ctx->dmub_srv); +} + +/** + * dm_test_dmub_hw_init_apu_dpia_dcn35 - Test hw init APU DPIA and DCN35 hw params + * @test: The KUnit test context + * + * On a DCN3.5 APU with a USB4 DPIA link, dm_dmub_hw_init() should populate the + * DPIA hw params and the DCN3.5 IPS-sequential hw params before initializing + * the fake DMUB service. + */ +static void dm_test_dmub_hw_init_apu_dpia_dcn35(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 0); + adev->dm.dc->caps.is_apu = true; + adev->dm.dc->res_pool->usb4_dpia_count = 1; + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); + KUNIT_EXPECT_TRUE(test, adev->dm.dmub_srv->hw_init); +} + +/** + * dm_test_dmub_hw_init_sanity_checks_dcn31 - Test hw init enables DCN31 sanity checks + * @test: The KUnit test context + * + * On DCN3.1.2 with a DMCUB firmware version in the affected range, + * dm_dmub_hw_init() should enable the DC sanity-check debug flag. + */ +static void dm_test_dmub_hw_init_sanity_checks_dcn31(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 2); + adev->dm.dmcub_fw_version = DMUB_FW_VERSION(4, 0, 10); + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); + KUNIT_EXPECT_TRUE(test, adev->dm.dc->debug.sanity_checks); +} + +/** + * dm_test_dmub_hw_init_sanity_checks_dcn314 - Test hw init enables DCN314 sanity checks + * @test: The KUnit test context + * + * On DCN3.1.4 with a DMCUB firmware version in the affected range, + * dm_dmub_hw_init() should enable the DC sanity-check debug flag. + */ +static void dm_test_dmub_hw_init_sanity_checks_dcn314(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 1, 4); + adev->dm.dmcub_fw_version = DMUB_FW_VERSION(4, 0, 10); + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); + KUNIT_EXPECT_TRUE(test, adev->dm.dc->debug.sanity_checks); +} + +/** + * dm_test_dmub_hw_init_dmcu_abm - Test hw init initializes DMCU and ABM when present + * @test: The KUnit test context + * + * When the resource pool exposes a DMCU and ABM, dm_dmub_hw_init() should + * program the PSP version, invoke the DMCU init callback, and record the + * running state reported by the DMCU. + */ +static void dm_test_dmub_hw_init_dmcu_abm(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + struct dmcu *dmcu; + struct abm *abm; + + dmcu = kunit_kzalloc(test, sizeof(*dmcu), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmcu); + + abm = kunit_kzalloc(test, sizeof(*abm), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, abm); + + dmcu->funcs = &dm_test_dmcu_funcs; + dmcu->psp_version = 0x12345678; + adev->dm.dc->res_pool->dmcu = dmcu; + adev->dm.dc->res_pool->abm = abm; + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); + KUNIT_EXPECT_TRUE(test, abm->dmcu_is_running); +} + /* Tests for dm_dmub_hw_resume() */ /** @@ -495,6 +817,79 @@ static void dm_test_dmub_hw_resume_no_dmub_srv(struct kunit *test) dm_dmub_hw_resume(adev); } +/** + * dm_test_dmub_hw_resume_initialized_dmub - Test resume waits for initialized DMUB + * @test: The KUnit test context + * + * When the fake DMUB service reports hardware already initialized, resume + * should only wait for firmware readiness and skip full reinitialization. + */ +static void dm_test_dmub_hw_resume_initialized_dmub(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test); + + adev->dm.dmub_srv = dm_test_alloc_dmub_srv(test); + + /* Must not crash. */ + dm_dmub_hw_resume(adev); +} + +/** + * dm_test_dmub_hw_resume_full_init - Test resume performs full init when uninitialized + * @test: The KUnit test context + * + * When the fake DMUB service reports hardware not yet initialized, resume + * should continue into a full dm_dmub_hw_init() and create the DC DMUB + * server. + */ +static void dm_test_dmub_hw_resume_full_init(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + adev->dm.dmub_srv->hw_init = false; + + dm_dmub_hw_resume(adev); + + KUNIT_EXPECT_NOT_NULL(test, adev->dm.dc->ctx->dmub_srv); +} + +/** + * dm_test_dmub_hw_resume_init_check_failed - Test resume handles a failed init check + * @test: The KUnit test context + * + * When the DMUB service is not software-initialized, the init-state query + * fails and resume continues into dm_dmub_hw_init(), which also fails; the + * call must warn and return without crashing. + */ +static void dm_test_dmub_hw_resume_init_check_failed(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test); + + adev->dm.dmub_srv->sw_init = false; + + /* Must not crash. */ + dm_dmub_hw_resume(adev); +} + +/** + * dm_test_dmub_hw_resume_auto_load_timeout - Test resume tolerates an auto-load timeout + * @test: The KUnit test context + * + * When the DMUB reports hardware already initialized but the firmware never + * signals ready, resume's auto-load wait times out and only warns; the call + * must not crash. + */ +static void dm_test_dmub_hw_resume_auto_load_timeout(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test); + + adev->dm.dmub_srv = dm_test_alloc_dmub_srv(test); + adev->dm.dmub_srv->hw_funcs.get_fw_status = dm_test_dmub_fw_not_ready; + + /* Must not crash; auto-load times out and only warns. */ + dm_dmub_hw_resume(adev); +} + /* Tests for dm_dmub_sw_init() */ /** @@ -537,6 +932,421 @@ static void dm_test_init_microcode_unsupported_asic(struct kunit *test) KUNIT_EXPECT_EQ(test, dm_init_microcode(adev), 0); } +/* Tests for dm_dmub_get_vbios_bounding_box() */ + +/** + * dm_test_dmub_get_vbios_bounding_box_default_null - Test default IP version returns NULL + * @test: The KUnit test context + * + * For an IP version without a bounding-box size mapping, the switch falls + * through to the default case and dm_dmub_get_vbios_bounding_box() returns + * NULL without allocating GPU memory or issuing GPINT commands. + */ +static void dm_test_dmub_get_vbios_bounding_box_default_null(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 0); + + KUNIT_EXPECT_NULL(test, dm_dmub_get_vbios_bounding_box(adev)); +} + +/* Tests for dm_execute_dmub_cmd() */ + +/** + * dm_test_execute_dmub_cmd_null_dmub_srv - Test command execution fails without DMUB service + * @test: The KUnit test context + * + * With no DC DMUB service on the context, dc_dmub_srv_cmd_run() returns false + * and dm_execute_dmub_cmd() propagates that failure. + */ +static void dm_test_execute_dmub_cmd_null_dmub_srv(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_context *ctx; + union dmub_rb_cmd *cmd; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + cmd = kunit_kzalloc(test, sizeof(*cmd), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, cmd); + + spin_lock_init(&adev->dm.dmub_lock); + ctx->driver_context = adev; + ctx->dmub_srv = NULL; + + KUNIT_EXPECT_FALSE(test, + dm_execute_dmub_cmd(ctx, cmd, DM_DMUB_WAIT_TYPE_NO_WAIT)); +} + +/* Tests for amdgpu_dm_process_dmub_aux_transfer_sync() */ + +/** + * dm_test_process_dmub_aux_transfer_sync_engine_acquire - Test AUX transfer engine-acquire failure + * @test: The KUnit test context + * + * With dc->link_count == 0, dc_process_dmub_aux_transfer_async() rejects the + * link index and amdgpu_dm_process_dmub_aux_transfer_sync() reports an + * engine-acquire error and returns -1. + */ +static void dm_test_process_dmub_aux_transfer_sync_engine_acquire(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_context *ctx; + struct dc *dc; + struct aux_payload *payload; + struct dmub_notification *notify; + enum aux_return_code_type result = AUX_RET_SUCCESS; + int ret; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc); + + payload = kunit_kzalloc(test, sizeof(*payload), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload); + + notify = kunit_kzalloc(test, sizeof(*notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, notify); + + dc->link_count = 0; + ctx->dc = dc; + ctx->driver_context = adev; + adev->dm.dmub_notify = notify; + mutex_init(&adev->dm.dpia_aux_lock); + init_completion(&adev->dm.dmub_aux_transfer_done); + + ret = amdgpu_dm_process_dmub_aux_transfer_sync(ctx, 0, payload, &result); + + KUNIT_EXPECT_EQ(test, ret, -1); + KUNIT_EXPECT_EQ(test, result, AUX_RET_ERROR_ENGINE_ACQUIRE); +} + +/** + * dm_test_process_dmub_aux_transfer_sync_protocol_error - Test AUX protocol error result + * @test: The KUnit test context + * + * With the completion pre-signaled and a fake DC DMUB service that rejects the + * command after construction, the sync helper should propagate the notification + * result without waiting for real firmware. + */ +static void dm_test_process_dmub_aux_transfer_sync_protocol_error(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_context *ctx; + struct dc_context *dc_ctx; + struct dc *dc; + struct dc_link *link; + struct ddc_service *ddc; + struct aux_payload *payload; + struct dmub_notification *notify; + enum aux_return_code_type result = AUX_RET_SUCCESS; + int ret; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + dc_ctx = kunit_kzalloc(test, sizeof(*dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_ctx); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc); + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ddc); + + payload = kunit_kzalloc(test, sizeof(*payload), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload); + + notify = kunit_kzalloc(test, sizeof(*notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, notify); + + link->ddc = ddc; + dc->ctx = dc_ctx; + dc->link_count = 1; + dc->links[0] = link; + dc_ctx->dc = dc; + dc_ctx->driver_context = adev; + dc_ctx->dmub_srv = NULL; + ctx->dc = dc; + ctx->driver_context = adev; + spin_lock_init(&adev->dm.dmub_lock); + adev->dm.dmub_notify = notify; + mutex_init(&adev->dm.dpia_aux_lock); + init_completion(&adev->dm.dmub_aux_transfer_done); + complete(&adev->dm.dmub_aux_transfer_done); + notify->result = AUX_RET_ERROR_PROTOCOL_ERROR; + + ret = amdgpu_dm_process_dmub_aux_transfer_sync(ctx, 0, payload, &result); + + KUNIT_EXPECT_EQ(test, ret, -1); + KUNIT_EXPECT_EQ(test, result, AUX_RET_ERROR_PROTOCOL_ERROR); +} + +/** + * dm_test_process_dmub_aux_transfer_sync_copies_data - Test AUX reply data copy + * @test: The KUnit test context + * + * On a successful notification, the sync helper should copy the bounded reply + * data and report the high-nibble command reply when present. + */ +static void dm_test_process_dmub_aux_transfer_sync_copies_data(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_context *ctx; + struct dc_context *dc_ctx; + struct dc *dc; + struct dc_link *link; + struct ddc_service *ddc; + struct aux_payload *payload; + struct dmub_notification *notify; + enum aux_return_code_type result = AUX_RET_ERROR_UNKNOWN; + u8 data[4] = { 0 }; + u8 reply = 0; + int ret; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + dc_ctx = kunit_kzalloc(test, sizeof(*dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_ctx); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc); + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ddc); + + payload = kunit_kzalloc(test, sizeof(*payload), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload); + + notify = kunit_kzalloc(test, sizeof(*notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, notify); + + link->ddc = ddc; + dc->ctx = dc_ctx; + dc->link_count = 1; + dc->links[0] = link; + dc_ctx->dc = dc; + dc_ctx->driver_context = adev; + dc_ctx->dmub_srv = NULL; + ctx->dc = dc; + ctx->driver_context = adev; + spin_lock_init(&adev->dm.dmub_lock); + adev->dm.dmub_notify = notify; + mutex_init(&adev->dm.dpia_aux_lock); + init_completion(&adev->dm.dmub_aux_transfer_done); + complete(&adev->dm.dmub_aux_transfer_done); + payload->data = data; + payload->reply = &reply; + payload->length = sizeof(data); + notify->result = AUX_RET_SUCCESS; + notify->aux_reply.command = 0xA4; + notify->aux_reply.length = 3; + notify->aux_reply.data[0] = 0x11; + notify->aux_reply.data[1] = 0x22; + notify->aux_reply.data[2] = 0x33; + + ret = amdgpu_dm_process_dmub_aux_transfer_sync(ctx, 0, payload, &result); + + KUNIT_EXPECT_EQ(test, ret, 3); + KUNIT_EXPECT_EQ(test, result, AUX_RET_SUCCESS); + KUNIT_EXPECT_EQ(test, reply, 0xA); + KUNIT_EXPECT_EQ(test, data[0], 0x11); + KUNIT_EXPECT_EQ(test, data[1], 0x22); + KUNIT_EXPECT_EQ(test, data[2], 0x33); +} + +/** + * dm_test_process_dmub_aux_transfer_sync_zero_length - Test AUX reply with no data + * @test: The KUnit test context + * + * On a successful notification whose reply carries no data, the sync helper + * takes the zero-length branch and returns the reply length (0) without + * copying any payload data. + */ +static void dm_test_process_dmub_aux_transfer_sync_zero_length(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_context *ctx; + struct dc_context *dc_ctx; + struct dc *dc; + struct dc_link *link; + struct ddc_service *ddc; + struct aux_payload *payload; + struct dmub_notification *notify; + enum aux_return_code_type result = AUX_RET_ERROR_UNKNOWN; + u8 data[4] = { 0 }; + u8 reply = 0; + int ret; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + dc_ctx = kunit_kzalloc(test, sizeof(*dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_ctx); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc); + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ddc); + + payload = kunit_kzalloc(test, sizeof(*payload), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload); + + notify = kunit_kzalloc(test, sizeof(*notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, notify); + + link->ddc = ddc; + dc->ctx = dc_ctx; + dc->link_count = 1; + dc->links[0] = link; + dc_ctx->dc = dc; + dc_ctx->driver_context = adev; + dc_ctx->dmub_srv = NULL; + ctx->dc = dc; + ctx->driver_context = adev; + spin_lock_init(&adev->dm.dmub_lock); + adev->dm.dmub_notify = notify; + mutex_init(&adev->dm.dpia_aux_lock); + init_completion(&adev->dm.dmub_aux_transfer_done); + complete(&adev->dm.dmub_aux_transfer_done); + payload->data = data; + payload->reply = &reply; + payload->length = sizeof(data); + notify->result = AUX_RET_SUCCESS; + notify->aux_reply.command = 0x03; + notify->aux_reply.length = 0; + + ret = amdgpu_dm_process_dmub_aux_transfer_sync(ctx, 0, payload, &result); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, result, AUX_RET_SUCCESS); + KUNIT_EXPECT_EQ(test, reply, 0x3); +} + +/* Tests for amdgpu_dm_process_dmub_set_config_sync() */ + +/** + * dm_test_process_dmub_set_config_sync_unknown_error - Test SET_CONFIG completes with unknown error + * @test: The KUnit test context + * + * With no DC DMUB service, dc_process_dmub_set_config_async() cannot reach the + * firmware and reports the command as completed with SET_CONFIG_UNKNOWN_ERROR, + * so amdgpu_dm_process_dmub_set_config_sync() returns 0 with that status. + */ +static void dm_test_process_dmub_set_config_sync_unknown_error(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_context *ctx; + struct dc_context *dc_ctx; + struct dc *dc; + struct dc_link *link; + struct set_config_cmd_payload *payload; + struct dmub_notification *notify; + enum set_config_status result = SET_CONFIG_PENDING; + int ret; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + dc_ctx = kunit_kzalloc(test, sizeof(*dc_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_ctx); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc); + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + payload = kunit_kzalloc(test, sizeof(*payload), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload); + + notify = kunit_kzalloc(test, sizeof(*notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, notify); + + dc->ctx = dc_ctx; + dc_ctx->dmub_srv = NULL; + dc->links[0] = link; + ctx->dc = dc; + ctx->driver_context = adev; + adev->dm.dmub_notify = notify; + mutex_init(&adev->dm.dpia_aux_lock); + init_completion(&adev->dm.dmub_aux_transfer_done); + + ret = amdgpu_dm_process_dmub_set_config_sync(ctx, 0, payload, &result); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, result, SET_CONFIG_UNKNOWN_ERROR); +} + +/* Tests for abort_fused_io() */ + +/** + * dm_test_abort_fused_io_no_dmub_srv - Test fused IO abort is a safe no-op without DMUB service + * @test: The KUnit test context + * + * abort_fused_io() builds an abort command and submits it via + * dm_execute_dmub_cmd(); with no DC DMUB service the submission fails + * silently and the call must not crash. + */ +static void dm_test_abort_fused_io_no_dmub_srv(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_context *ctx; + struct dmub_cmd_fused_request *req; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); + + req = kunit_kzalloc(test, sizeof(*req), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, req); + + spin_lock_init(&adev->dm.dmub_lock); + ctx->driver_context = adev; + ctx->dmub_srv = NULL; + + /* Must not crash. */ + abort_fused_io(ctx, req); +} + static struct kunit_case amdgpu_dm_dmub_tests[] = { /* dm_register_dmub_notify_callback() */ KUNIT_CASE(dm_test_register_dmub_notify_callback_null_callback), @@ -551,6 +1361,7 @@ static struct kunit_case amdgpu_dm_dmub_tests[] = { /* dm_dmub_aux_fused_io_callback() */ KUNIT_CASE(dm_test_dmub_aux_fused_io_callback_copies_reply_and_completes), KUNIT_CASE(dm_test_dmub_aux_fused_io_callback_max_ddc_line), + KUNIT_CASE(dm_test_dmub_aux_fused_io_callback_null_args), /* dm_get_default_ips_mode() */ KUNIT_CASE(dm_test_get_default_ips_mode_dcn35), KUNIT_CASE(dm_test_get_default_ips_mode_dcn351), @@ -561,12 +1372,38 @@ static struct kunit_case amdgpu_dm_dmub_tests[] = { KUNIT_CASE(dm_test_dmub_hw_init_no_dmub_srv), KUNIT_CASE(dm_test_dmub_hw_init_no_fb_info), KUNIT_CASE(dm_test_dmub_hw_init_no_firmware), + KUNIT_CASE(dm_test_dmub_hw_init_success_fake_dmub), + KUNIT_CASE(dm_test_dmub_hw_init_no_hw_support), + KUNIT_CASE(dm_test_dmub_hw_init_bss_data), + KUNIT_CASE(dm_test_dmub_hw_init_hw_init_fails), + KUNIT_CASE(dm_test_dmub_hw_init_auto_load_timeout), + KUNIT_CASE(dm_test_dmub_hw_init_apu_dpia_dcn35), + KUNIT_CASE(dm_test_dmub_hw_init_sanity_checks_dcn31), + KUNIT_CASE(dm_test_dmub_hw_init_sanity_checks_dcn314), + KUNIT_CASE(dm_test_dmub_hw_init_dmcu_abm), /* dm_dmub_hw_resume() */ KUNIT_CASE(dm_test_dmub_hw_resume_no_dmub_srv), + KUNIT_CASE(dm_test_dmub_hw_resume_initialized_dmub), + KUNIT_CASE(dm_test_dmub_hw_resume_full_init), + KUNIT_CASE(dm_test_dmub_hw_resume_init_check_failed), + KUNIT_CASE(dm_test_dmub_hw_resume_auto_load_timeout), /* dm_dmub_sw_init() */ KUNIT_CASE(dm_test_dmub_sw_init_unsupported_asic), /* dm_init_microcode() */ KUNIT_CASE(dm_test_init_microcode_unsupported_asic), + /* dm_dmub_get_vbios_bounding_box() */ + KUNIT_CASE(dm_test_dmub_get_vbios_bounding_box_default_null), + /* dm_execute_dmub_cmd() */ + KUNIT_CASE(dm_test_execute_dmub_cmd_null_dmub_srv), + /* amdgpu_dm_process_dmub_aux_transfer_sync() */ + KUNIT_CASE(dm_test_process_dmub_aux_transfer_sync_engine_acquire), + KUNIT_CASE(dm_test_process_dmub_aux_transfer_sync_protocol_error), + KUNIT_CASE(dm_test_process_dmub_aux_transfer_sync_copies_data), + KUNIT_CASE(dm_test_process_dmub_aux_transfer_sync_zero_length), + /* amdgpu_dm_process_dmub_set_config_sync() */ + KUNIT_CASE(dm_test_process_dmub_set_config_sync_unknown_error), + /* abort_fused_io() */ + KUNIT_CASE(dm_test_abort_fused_io_no_dmub_srv), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c new file mode 100644 index 000000000000..0ae636dcbbe4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c @@ -0,0 +1,453 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_freesync.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <drm/drm_atomic.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_modes.h> + +#include "dc.h" +#include "inc/core_types.h" +#include "amd_shared.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_freesync.h" +#include "amdgpu_dm_kunit_test_helpers.h" + +/* Tests for amdgpu_dm_is_timing_unchanged_for_freesync() */ + +/** + * dm_test_timing_unchanged_null_args - Test NULL crtc states return false + * @test: The KUnit test context + */ +static void dm_test_timing_unchanged_null_args(struct kunit *test) +{ + struct drm_crtc_state crtc_state = { 0 }; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_is_timing_unchanged_for_freesync(NULL, &crtc_state)); + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_is_timing_unchanged_for_freesync(&crtc_state, NULL)); +} + +/** + * dm_test_timing_unchanged_identical_modes - Test identical modes are not "unchanged" + * @test: The KUnit test context + * + * The helper only returns true when vtotal/vsync shift (vrr) while the rest + * of the timing stays fixed, so identical modes must return false. + */ +static void dm_test_timing_unchanged_identical_modes(struct kunit *test) +{ + struct drm_crtc_state old_state = { 0 }; + struct drm_crtc_state new_state = { 0 }; + + old_state.mode.clock = 148500; + old_state.mode.hdisplay = 1920; + old_state.mode.vdisplay = 1080; + old_state.mode.htotal = 2200; + old_state.mode.vtotal = 1125; + new_state.mode = old_state.mode; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_is_timing_unchanged_for_freesync(&old_state, &new_state)); +} + +/** + * dm_test_timing_unchanged_vrr_shift - Test vrr-style vtotal/vsync shift is detected + * @test: The KUnit test context + */ +static void dm_test_timing_unchanged_vrr_shift(struct kunit *test) +{ + struct drm_crtc_state old_state = { 0 }; + struct drm_crtc_state new_state = { 0 }; + + old_state.mode.clock = 148500; + old_state.mode.hdisplay = 1920; + old_state.mode.vdisplay = 1080; + old_state.mode.htotal = 2200; + old_state.mode.vtotal = 1125; + old_state.mode.hsync_start = 2008; + old_state.mode.vsync_start = 1084; + old_state.mode.hsync_end = 2052; + old_state.mode.vsync_end = 1089; + + /* Same horizontal timing, vertical totals/sync shifted by 125 lines */ + new_state.mode = old_state.mode; + new_state.mode.vtotal = 1250; + new_state.mode.vsync_start = 1209; + new_state.mode.vsync_end = 1214; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_is_timing_unchanged_for_freesync(&old_state, &new_state)); +} + +/** + * dm_test_timing_unchanged_clock_changed - Test pixel clock change returns false + * @test: The KUnit test context + */ +static void dm_test_timing_unchanged_clock_changed(struct kunit *test) +{ + struct drm_crtc_state old_state = { 0 }; + struct drm_crtc_state new_state = { 0 }; + + old_state.mode.clock = 148500; + old_state.mode.htotal = 2200; + old_state.mode.vtotal = 1125; + old_state.mode.vsync_start = 1084; + old_state.mode.vsync_end = 1089; + + new_state.mode = old_state.mode; + new_state.mode.clock = 297000; + new_state.mode.vtotal = 1250; + new_state.mode.vsync_start = 1209; + new_state.mode.vsync_end = 1214; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_is_timing_unchanged_for_freesync(&old_state, &new_state)); +} + +/* Tests for amdgpu_dm_set_freesync_fixed_config() */ + +/** + * dm_test_set_freesync_fixed_config_60hz - Test fixed refresh computed for 1080p60 + * @test: The KUnit test context + */ +static void dm_test_set_freesync_fixed_config_60hz(struct kunit *test) +{ + struct dm_crtc_state dm_crtc_state = { 0 }; + + dm_crtc_state.base.mode.clock = 148500; + dm_crtc_state.base.mode.htotal = 2200; + dm_crtc_state.base.mode.vtotal = 1125; + + amdgpu_dm_set_freesync_fixed_config(&dm_crtc_state); + + KUNIT_EXPECT_EQ(test, (int)dm_crtc_state.freesync_config.state, + (int)VRR_STATE_ACTIVE_FIXED); + /* 148500 kHz / (2200 * 1125) = 60 Hz = 60000000 uHz */ + KUNIT_EXPECT_EQ(test, dm_crtc_state.freesync_config.fixed_refresh_in_uhz, + 60000000U); +} + +/* Tests for amdgpu_dm_is_dc_timing_adjust_needed() */ + +/** + * dm_test_dc_timing_adjust_pending - Test a pending hw timing adjust forces true + * @test: The KUnit test context + */ +static void dm_test_dc_timing_adjust_pending(struct kunit *test) +{ + struct dm_crtc_state *old_state, *new_state; + struct dc_stream_state *stream; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + new_state->stream = stream; + stream->adjust.timing_adjust_pending = 1; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state)); +} + +/** + * dm_test_dc_timing_adjust_active_fixed - Test VRR active-fixed forces true + * @test: The KUnit test context + */ +static void dm_test_dc_timing_adjust_active_fixed(struct kunit *test) +{ + struct dm_crtc_state *old_state, *new_state; + struct dc_stream_state *stream; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + new_state->stream = stream; + new_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state)); +} + +/** + * dm_test_dc_timing_adjust_vrr_toggle - Test a change in vrr active state forces true + * @test: The KUnit test context + */ +static void dm_test_dc_timing_adjust_vrr_toggle(struct kunit *test) +{ + struct dm_crtc_state *old_state, *new_state; + struct dc_stream_state *stream; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + new_state->stream = stream; + old_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE; + new_state->freesync_config.state = VRR_STATE_INACTIVE; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state)); +} + +/** + * dm_test_dc_timing_adjust_not_needed - Test steady-state timing needs no adjust + * @test: The KUnit test context + */ +static void dm_test_dc_timing_adjust_not_needed(struct kunit *test) +{ + struct dm_crtc_state *old_state, *new_state; + struct dc_stream_state *stream; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + new_state->stream = stream; + old_state->freesync_config.state = VRR_STATE_INACTIVE; + new_state->freesync_config.state = VRR_STATE_INACTIVE; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_dc_timing_adjust_needed(old_state, new_state)); +} + +/* Tests for amdgpu_dm_get_freesync_config_for_crtc() */ + +struct dm_test_freesync_ctx { + struct amdgpu_dm_connector *aconnector; + struct dm_crtc_state *crtc_state; + struct dm_connector_state *conn_state; + struct dc_stream_state *stream; +}; + +static struct dm_test_freesync_ctx *dm_test_freesync_ctx_alloc(struct kunit *test) +{ + struct dm_test_freesync_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + ctx->crtc_state = kunit_kzalloc(test, sizeof(*ctx->crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->crtc_state); + ctx->conn_state = kunit_kzalloc(test, sizeof(*ctx->conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state); + ctx->stream = dm_kunit_alloc_stream(test, NULL); + + ctx->conn_state->base.connector = &ctx->aconnector->base; + ctx->aconnector->base.connector_type = DRM_MODE_CONNECTOR_DisplayPort; + ctx->crtc_state->stream = ctx->stream; + + /* 1080p60 timing so drm_mode_vrefresh() == 60 */ + ctx->crtc_state->base.mode.clock = 148500; + ctx->crtc_state->base.mode.htotal = 2200; + ctx->crtc_state->base.mode.vtotal = 1125; + + return ctx; +} + +/** + * dm_test_freesync_config_writeback - Test writeback connector is left untouched + * @test: The KUnit test context + */ +static void dm_test_freesync_config_writeback(struct kunit *test) +{ + struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test); + + ctx->aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK; + ctx->conn_state->freesync_capable = true; + ctx->aconnector->min_vfreq = 48; + ctx->aconnector->max_vfreq = 120; + ctx->crtc_state->vrr_supported = true; /* sentinel: must stay set */ + + amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state); + + /* Writeback: early return leaves vrr_supported sentinel untouched */ + KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported); +} + +/** + * dm_test_freesync_config_not_capable - Test a non-freesync sink reports UNSUPPORTED + * @test: The KUnit test context + */ +static void dm_test_freesync_config_not_capable(struct kunit *test) +{ + struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test); + + ctx->conn_state->freesync_capable = false; + ctx->aconnector->min_vfreq = 48; + ctx->aconnector->max_vfreq = 120; + + amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state); + + KUNIT_EXPECT_FALSE(test, ctx->crtc_state->vrr_supported); + KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state, + (int)VRR_STATE_UNSUPPORTED); +} + +/** + * dm_test_freesync_config_out_of_range - Test a refresh outside the range is UNSUPPORTED + * @test: The KUnit test context + */ +static void dm_test_freesync_config_out_of_range(struct kunit *test) +{ + struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test); + + ctx->conn_state->freesync_capable = true; + ctx->aconnector->min_vfreq = 90; /* 60 < 90 -> out of range */ + ctx->aconnector->max_vfreq = 120; + + amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state); + + KUNIT_EXPECT_FALSE(test, ctx->crtc_state->vrr_supported); + KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state, + (int)VRR_STATE_UNSUPPORTED); +} + +/** + * dm_test_freesync_config_active_variable - Test vrr_enabled yields ACTIVE_VARIABLE + * @test: The KUnit test context + */ +static void dm_test_freesync_config_active_variable(struct kunit *test) +{ + struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test); + + ctx->conn_state->freesync_capable = true; + ctx->aconnector->min_vfreq = 48; + ctx->aconnector->max_vfreq = 120; + ctx->crtc_state->base.vrr_enabled = true; + + amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state); + + KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported); + KUNIT_EXPECT_TRUE(test, ctx->stream->ignore_msa_timing_param); + KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state, + (int)VRR_STATE_ACTIVE_VARIABLE); + KUNIT_EXPECT_EQ(test, ctx->crtc_state->freesync_config.min_refresh_in_uhz, + 48000000U); + KUNIT_EXPECT_EQ(test, ctx->crtc_state->freesync_config.max_refresh_in_uhz, + 120000000U); + KUNIT_EXPECT_TRUE(test, ctx->crtc_state->freesync_config.vsif_supported); + KUNIT_EXPECT_TRUE(test, ctx->crtc_state->freesync_config.btr); +} + +/** + * dm_test_freesync_config_inactive - Test supported-but-off yields INACTIVE + * @test: The KUnit test context + */ +static void dm_test_freesync_config_inactive(struct kunit *test) +{ + struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test); + + ctx->conn_state->freesync_capable = true; + ctx->aconnector->min_vfreq = 48; + ctx->aconnector->max_vfreq = 120; + ctx->crtc_state->base.vrr_enabled = false; + + amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state); + + KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported); + KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state, + (int)VRR_STATE_INACTIVE); +} + +/** + * dm_test_freesync_config_active_fixed - Test freesync-video mode yields ACTIVE_FIXED + * @test: The KUnit test context + */ +static void dm_test_freesync_config_active_fixed(struct kunit *test) +{ + struct dm_test_freesync_ctx *ctx = dm_test_freesync_ctx_alloc(test); + + ctx->conn_state->freesync_capable = true; + ctx->aconnector->min_vfreq = 48; + ctx->aconnector->max_vfreq = 120; + /* Pre-set fixed state selects the freesync-video (fixed) path */ + ctx->crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; + ctx->crtc_state->freesync_config.fixed_refresh_in_uhz = 60000000; + ctx->crtc_state->base.vrr_enabled = true; /* ignored on the fixed path */ + + amdgpu_dm_get_freesync_config_for_crtc(ctx->crtc_state, ctx->conn_state); + + KUNIT_EXPECT_TRUE(test, ctx->crtc_state->vrr_supported); + KUNIT_EXPECT_EQ(test, (int)ctx->crtc_state->freesync_config.state, + (int)VRR_STATE_ACTIVE_FIXED); + KUNIT_EXPECT_EQ(test, ctx->crtc_state->freesync_config.fixed_refresh_in_uhz, + 60000000U); +} + +/* Tests for amdgpu_dm_reset_freesync_config_for_crtc() */ + +/** + * dm_test_reset_freesync_config - Test reset clears vrr support and info packet + * @test: The KUnit test context + */ +static void dm_test_reset_freesync_config(struct kunit *test) +{ + struct dm_crtc_state *crtc_state; + + crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, crtc_state); + + crtc_state->vrr_supported = true; + crtc_state->vrr_infopacket.valid = true; + + amdgpu_dm_reset_freesync_config_for_crtc(crtc_state); + + KUNIT_EXPECT_FALSE(test, crtc_state->vrr_supported); + KUNIT_EXPECT_FALSE(test, crtc_state->vrr_infopacket.valid); +} + +static struct kunit_case amdgpu_dm_freesync_tests[] = { + /* amdgpu_dm_is_timing_unchanged_for_freesync */ + KUNIT_CASE(dm_test_timing_unchanged_null_args), + KUNIT_CASE(dm_test_timing_unchanged_identical_modes), + KUNIT_CASE(dm_test_timing_unchanged_vrr_shift), + KUNIT_CASE(dm_test_timing_unchanged_clock_changed), + /* amdgpu_dm_set_freesync_fixed_config */ + KUNIT_CASE(dm_test_set_freesync_fixed_config_60hz), + /* amdgpu_dm_is_dc_timing_adjust_needed */ + KUNIT_CASE(dm_test_dc_timing_adjust_pending), + KUNIT_CASE(dm_test_dc_timing_adjust_active_fixed), + KUNIT_CASE(dm_test_dc_timing_adjust_vrr_toggle), + KUNIT_CASE(dm_test_dc_timing_adjust_not_needed), + /* amdgpu_dm_get_freesync_config_for_crtc */ + KUNIT_CASE(dm_test_freesync_config_writeback), + KUNIT_CASE(dm_test_freesync_config_not_capable), + KUNIT_CASE(dm_test_freesync_config_out_of_range), + KUNIT_CASE(dm_test_freesync_config_active_variable), + KUNIT_CASE(dm_test_freesync_config_inactive), + KUNIT_CASE(dm_test_freesync_config_active_fixed), + /* amdgpu_dm_reset_freesync_config_for_crtc */ + KUNIT_CASE(dm_test_reset_freesync_config), + {} +}; + +static struct kunit_suite amdgpu_dm_freesync_test_suite = { + .name = "amdgpu_dm_freesync", + .test_cases = amdgpu_dm_freesync_tests, +}; + +kunit_test_suite(amdgpu_dm_freesync_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_freesync"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c index dbcff92672e6..cfdcc44581b1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c @@ -7,8 +7,18 @@ #include <kunit/test.h> #include <linux/workqueue.h> +#include <linux/kobject.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/i2c.h> +#include <drm/display/drm_dp_helper.h> + +#include "amdgpu.h" +#include "amdgpu_dm.h" #include "amdgpu_dm_hdcp.h" +#include "amdgpu_dm_kunit_test_helpers.h" +#include "hdcp_psp.h" static void dummy_work_fn(struct work_struct *work) {} @@ -463,7 +473,8 @@ static void dm_test_process_output_watchdog_stop_and_needed_requeues(struct kuni * @test: KUnit test context for managed allocation * * Allocates a minimal hdcp_workqueue with property_update_work initialised - * so event_property_update() can resolve container_of() safely. + * so event_property_update() can resolve container_of() safely. The mutex is + * initialised as well because the connected path takes guard(mutex). */ static struct hdcp_workqueue *alloc_test_workqueue_for_property_update(struct kunit *test) { @@ -472,12 +483,42 @@ static struct hdcp_workqueue *alloc_test_workqueue_for_property_update(struct ku work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, work); + mutex_init(&work->mutex); INIT_WORK(&work->property_update_work, dummy_work_fn); return work; } /** + * alloc_update_connector - connector for event_property_update() tests + * @test: KUnit test context for managed allocation + * @status: drm connector detection status to assign + * @conn_state: drm_connector_state to attach (may be NULL) + * @dev: drm_device to attach as connector->dev (may be NULL) + * + * Allocates an amdgpu_dm_connector wired for the traversal in + * event_property_update(). The caller supplies the state and device so the + * various skip branches (disconnected, no state, no device) and the fully + * connected path can all be exercised. + */ +static struct amdgpu_dm_connector *alloc_update_connector(struct kunit *test, + enum drm_connector_status status, + struct drm_connector_state *conn_state, + struct drm_device *dev) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.status = status; + aconnector->base.state = conn_state; + aconnector->base.dev = dev; + + return aconnector; +} + +/** * dm_test_event_property_update_skips_null_connector - null connector is ignored * @test: KUnit test context * @@ -497,8 +538,517 @@ static void dm_test_event_property_update_skips_null_connector(struct kunit *tes KUNIT_EXPECT_EQ(test, work->encryption_status[0], before); } +/** + * dm_test_event_property_update_skips_disconnected - disconnected is skipped + * @test: KUnit test context + * + * A connector whose status is not connector_status_connected must be skipped + * before any modeset lock is taken, leaving encryption_status untouched. + */ +static void dm_test_event_property_update_skips_disconnected(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_update(test); + struct drm_connector_state *conn_state; + + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + work->aconnector[1] = alloc_update_connector(test, connector_status_disconnected, + conn_state, NULL); + work->encryption_status[1] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + + event_property_update(&work->property_update_work); + + KUNIT_EXPECT_EQ(test, work->encryption_status[1], + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON); +} + +/** + * dm_test_event_property_update_skips_null_state - missing state is skipped + * @test: KUnit test context + * + * A connected connector without a drm_connector_state must be skipped before + * the modeset lock, leaving encryption_status unchanged. + */ +static void dm_test_event_property_update_skips_null_state(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_update(test); + + work->aconnector[2] = alloc_update_connector(test, connector_status_connected, + NULL, NULL); + work->encryption_status[2] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + + event_property_update(&work->property_update_work); + + KUNIT_EXPECT_EQ(test, work->encryption_status[2], + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON); +} + +/** + * dm_test_event_property_update_skips_null_dev - missing device is skipped + * @test: KUnit test context + * + * A connected connector with state but no drm_device must be skipped before + * the modeset lock, leaving encryption_status unchanged. + */ +static void dm_test_event_property_update_skips_null_dev(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_update(test); + struct drm_connector_state *conn_state; + + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + work->aconnector[3] = alloc_update_connector(test, connector_status_connected, + conn_state, NULL); + work->encryption_status[3] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + + event_property_update(&work->property_update_work); + + KUNIT_EXPECT_EQ(test, work->encryption_status[3], + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON); +} + +/** + * dm_test_event_property_update_desired_when_off - HDCP off maps to DESIRED + * @test: KUnit test context + * + * A fully connected display with HDCP_OFF encryption drives the connected + * path: the modeset lock is taken, hdcp_get_content_protection_from_status() + * reports DRM_MODE_CONTENT_PROTECTION_DESIRED and + * drm_hdcp_update_content_protection() is called. The connector state is + * pre-set to DESIRED so the value is unchanged (no sysfs event) and the deep + * path completes cleanly. + */ +static void dm_test_event_property_update_desired_when_off(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_update(test); + struct drm_connector_state *conn_state; + + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + + work->aconnector[0] = alloc_update_connector(test, connector_status_connected, + conn_state, &adev->ddev); + work->encryption_status[0] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; + + event_property_update(&work->property_update_work); + + KUNIT_EXPECT_EQ(test, conn_state->content_protection, + (unsigned int)DRM_MODE_CONTENT_PROTECTION_DESIRED); + KUNIT_EXPECT_EQ(test, work->encryption_status[0], + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF); +} + +/** + * dm_test_event_property_update_enabled_when_encrypted - encrypted maps to ENABLED + * @test: KUnit test context + * + * A fully connected display with TYPE0 content and HDCP1 encryption drives + * the connected path where hdcp_get_content_protection_from_status() reports + * DRM_MODE_CONTENT_PROTECTION_ENABLED. The connector state is pre-set to + * ENABLED so drm_hdcp_update_content_protection() leaves it unchanged. + */ +static void dm_test_event_property_update_enabled_when_encrypted(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_update(test); + struct drm_connector_state *conn_state; + + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + conn_state->hdcp_content_type = DRM_MODE_HDCP_CONTENT_TYPE0; + conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; + + work->aconnector[0] = alloc_update_connector(test, connector_status_connected, + conn_state, &adev->ddev); + work->encryption_status[0] = MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON; + + event_property_update(&work->property_update_work); + + KUNIT_EXPECT_EQ(test, conn_state->content_protection, + (unsigned int)DRM_MODE_CONTENT_PROTECTION_ENABLED); + KUNIT_EXPECT_EQ(test, work->encryption_status[0], + MOD_HDCP_ENCRYPTION_STATUS_HDCP1_ON); +} + /* End of tests for event_property_update() */ +/* Tests for event_callback() */ + +/** + * alloc_test_workqueue_for_callback - workqueue ready for event_callback() + * @test: KUnit test context for managed allocation + * + * Allocates a minimal hdcp_workqueue with its mutex and the three delayed + * works initialised, as required by the guard(mutex), cancel_delayed_work() + * and process_output() usage inside event_callback(). + */ +static struct hdcp_workqueue *alloc_test_workqueue_for_callback(struct kunit *test) +{ + struct hdcp_workqueue *work; + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + + mutex_init(&work->mutex); + INIT_DELAYED_WORK(&work->callback_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn); + + return work; +} + +/** + * dm_test_event_callback_cancels_callback_dwork - callback work is cancelled + * @test: KUnit test context + * + * event_callback() must cancel a previously scheduled callback_dwork. With + * no active hdcp display, mod_hdcp_process_event() leaves output cleared so + * the callback is not requeued and callback_dwork ends up not pending. + */ +static void dm_test_event_callback_cancels_callback_dwork(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_callback(test); + + /* Pre-schedule callback_dwork with a long delay so it won't fire. */ + schedule_delayed_work(&work->callback_dwork, msecs_to_jiffies(10000)); + KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->callback_dwork)); + + event_callback(&work->callback_dwork.work); + + KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork)); + + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/** + * dm_test_event_callback_schedules_property_validate - process_output() runs + * @test: KUnit test context + * + * event_callback() finishes by calling process_output(), which always + * enqueues property_validate_dwork with delay=0. Verifying it is pending + * proves event_callback() reached process_output() and released the mutex. + */ +static void dm_test_event_callback_schedules_property_validate(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_callback(test); + + event_callback(&work->callback_dwork.work); + + KUNIT_EXPECT_TRUE(test, work_pending(&work->property_validate_dwork.work)); + /* Mutex must be released after the guard scope exits. */ + KUNIT_EXPECT_FALSE(test, mutex_is_locked(&work->mutex)); + + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/* End of tests for event_callback() */ + +/* Tests for event_property_validate() */ + +/** + * alloc_test_workqueue_for_property_validate - workqueue for validate tests + * @test: KUnit test context for managed allocation + * + * Allocates a minimal hdcp_workqueue with its mutex and property_update_work + * initialised, as required by the guard(mutex) and schedule_work() usage + * inside event_property_validate(). + */ +static struct hdcp_workqueue *alloc_test_workqueue_for_property_validate(struct kunit *test) +{ + struct hdcp_workqueue *work; + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + + mutex_init(&work->mutex); + INIT_WORK(&work->property_update_work, dummy_work_fn); + + return work; +} + +/** + * alloc_validate_connector - connector for event_property_validate() tests + * @test: KUnit test context for managed allocation + * @index: drm connector index to assign + * @status: drm connector detection status to assign + * @with_state: whether to attach a zeroed drm_connector_state + * + * Allocates an amdgpu_dm_connector sufficient for the traversal in + * event_property_validate(). mod_hdcp_query_display() has no active display + * so it returns early, leaving the pre-set HDCP_OFF query untouched. + */ +static struct amdgpu_dm_connector *alloc_validate_connector(struct kunit *test, + unsigned int index, + enum drm_connector_status status, + bool with_state) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.index = index; + aconnector->base.status = status; + + if (with_state) { + struct drm_connector_state *conn_state; + + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + aconnector->base.state = conn_state; + } + + return aconnector; +} + +/** + * dm_test_event_property_validate_skips_null_connector - null entries ignored + * @test: KUnit test context + * + * With every aconnector entry NULL, event_property_validate() must not + * schedule property_update_work and must leave encryption_status untouched. + */ +static void dm_test_event_property_validate_skips_null_connector(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_validate(test); + + work->encryption_status[0] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + + event_property_validate(&work->property_validate_dwork.work); + + KUNIT_EXPECT_FALSE(test, work_pending(&work->property_update_work)); + KUNIT_EXPECT_EQ(test, work->encryption_status[0], + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON); +} + +/** + * dm_test_event_property_validate_skips_disconnected - disconnected is skipped + * @test: KUnit test context + * + * A connector whose status is not connector_status_connected must be + * skipped, leaving encryption_status unchanged and no work scheduled. + */ +static void dm_test_event_property_validate_skips_disconnected(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_validate(test); + + work->aconnector[1] = alloc_validate_connector(test, 1, + connector_status_disconnected, true); + work->encryption_status[1] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + + event_property_validate(&work->property_validate_dwork.work); + + KUNIT_EXPECT_FALSE(test, work_pending(&work->property_update_work)); + KUNIT_EXPECT_EQ(test, work->encryption_status[1], + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON); +} + +/** + * dm_test_event_property_validate_skips_null_state - missing state is skipped + * @test: KUnit test context + * + * A connected connector without a drm_connector_state must be skipped + * before the query, leaving encryption_status unchanged. + */ +static void dm_test_event_property_validate_skips_null_state(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_validate(test); + + work->aconnector[2] = alloc_validate_connector(test, 2, + connector_status_connected, false); + work->encryption_status[2] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + + event_property_validate(&work->property_validate_dwork.work); + + KUNIT_EXPECT_FALSE(test, work_pending(&work->property_update_work)); + KUNIT_EXPECT_EQ(test, work->encryption_status[2], + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON); +} + +/** + * dm_test_event_property_validate_updates_on_status_change - change triggers update + * @test: KUnit test context + * + * For a connected connector with state, the query returns HDCP_OFF (no + * active hdcp display). When the stored encryption_status differs, it must + * be updated to HDCP_OFF and property_update_work must be scheduled. + */ +static void dm_test_event_property_validate_updates_on_status_change(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_validate(test); + + work->aconnector[3] = alloc_validate_connector(test, 3, + connector_status_connected, true); + work->encryption_status[3] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + + event_property_validate(&work->property_validate_dwork.work); + + KUNIT_EXPECT_EQ(test, work->encryption_status[3], + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF); + KUNIT_EXPECT_TRUE(test, work_pending(&work->property_update_work)); + + cancel_work_sync(&work->property_update_work); +} + +/** + * dm_test_event_property_validate_no_update_when_unchanged - no change, no work + * @test: KUnit test context + * + * For a connected connector with state whose stored encryption_status is + * already HDCP_OFF (matching the query result), event_property_validate() + * must not reschedule property_update_work. + */ +static void dm_test_event_property_validate_no_update_when_unchanged(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_property_validate(test); + + work->aconnector[4] = alloc_validate_connector(test, 4, + connector_status_connected, true); + work->encryption_status[4] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; + + event_property_validate(&work->property_validate_dwork.work); + + KUNIT_EXPECT_EQ(test, work->encryption_status[4], + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF); + KUNIT_EXPECT_FALSE(test, work_pending(&work->property_update_work)); +} + +/* End of tests for event_property_validate() */ + +/* Tests for event_watchdog_timer() */ + +/** + * dm_test_event_watchdog_timer_cancels_watchdog_dwork - watchdog work is cancelled + * @test: KUnit test context + * + * event_watchdog_timer() must cancel a previously scheduled + * watchdog_timer_dwork. With no active hdcp display, + * mod_hdcp_process_event() leaves output cleared so the watchdog is not + * requeued and watchdog_timer_dwork ends up not pending. + */ +static void dm_test_event_watchdog_timer_cancels_watchdog_dwork(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_callback(test); + + /* Pre-schedule watchdog_timer_dwork with a long delay so it won't fire. */ + schedule_delayed_work(&work->watchdog_timer_dwork, msecs_to_jiffies(10000)); + KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork)); + + event_watchdog_timer(&work->watchdog_timer_dwork.work); + + KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork)); + + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/** + * dm_test_event_watchdog_timer_schedules_property_validate - process_output() runs + * @test: KUnit test context + * + * event_watchdog_timer() finishes by calling process_output(), which always + * enqueues property_validate_dwork with delay=0. Verifying it is pending + * proves the handler reached process_output() and released the mutex. + */ +static void dm_test_event_watchdog_timer_schedules_property_validate(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_callback(test); + + event_watchdog_timer(&work->watchdog_timer_dwork.work); + + KUNIT_EXPECT_TRUE(test, work_pending(&work->property_validate_dwork.work)); + /* Mutex must be released after the guard scope exits. */ + KUNIT_EXPECT_FALSE(test, mutex_is_locked(&work->mutex)); + + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/* End of tests for event_watchdog_timer() */ + +/* Tests for event_cpirq() */ + +/** + * alloc_test_workqueue_for_cpirq - workqueue ready for event_cpirq() + * @test: KUnit test context for managed allocation + * + * Allocates a minimal hdcp_workqueue with its mutex, cpirq_work and the + * three delayed works initialised, as required by the guard(mutex) and + * process_output() usage inside event_cpirq(). + */ +static struct hdcp_workqueue *alloc_test_workqueue_for_cpirq(struct kunit *test) +{ + struct hdcp_workqueue *work; + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + + mutex_init(&work->mutex); + INIT_WORK(&work->cpirq_work, dummy_work_fn); + INIT_DELAYED_WORK(&work->callback_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn); + + return work; +} + +/** + * dm_test_event_cpirq_schedules_property_validate - process_output() runs + * @test: KUnit test context + * + * event_cpirq() finishes by calling process_output(), which always + * enqueues property_validate_dwork with delay=0. Verifying it is pending + * proves the handler reached process_output() and released the mutex. + */ +static void dm_test_event_cpirq_schedules_property_validate(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_cpirq(test); + + event_cpirq(&work->cpirq_work); + + KUNIT_EXPECT_TRUE(test, work_pending(&work->property_validate_dwork.work)); + /* Mutex must be released after the guard scope exits. */ + KUNIT_EXPECT_FALSE(test, mutex_is_locked(&work->mutex)); + + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/** + * dm_test_event_cpirq_leaves_callback_and_watchdog_idle - only validate is queued + * @test: KUnit test context + * + * With no active hdcp display, mod_hdcp_process_event() leaves output + * cleared, so event_cpirq() must not schedule callback_dwork or + * watchdog_timer_dwork; only property_validate_dwork is enqueued. + */ +static void dm_test_event_cpirq_leaves_callback_and_watchdog_idle(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_for_cpirq(test); + + event_cpirq(&work->cpirq_work); + + KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork)); + KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork)); + + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/* End of tests for event_cpirq() */ + /* Tests for hdcp_handle_cpirq() */ /** @@ -630,8 +1180,1337 @@ static void dm_test_hdcp_create_workqueue_zero_max_links_returns_null(struct kun KUNIT_EXPECT_PTR_EQ(test, work, NULL); } +/** + * dm_test_hdcp_create_workqueue_initializes_work - success path wires everything up + * @test: KUnit test context + * + * With a single link, hdcp_create_workqueue() must allocate the workqueue and + * both SRM buffers, record max_link, publish the cp_psp callbacks and handle, + * and point every link's psp handle at the device psp. A non-matching + * dce_version must leave dtm_v3_supported clear. hdcp_destroy() is used to + * tear the workqueue down (it also removes the SRM sysfs file created on the + * device kobject). + */ +static void dm_test_hdcp_create_workqueue_initializes_work(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct cp_psp cp_psp = {0}; + struct dc *dc = dm_kunit_alloc_dc_with_ctx(test); + struct hdcp_workqueue *work; + + /* sysfs_create_bin_file() needs a real device kobject. */ + adev->dev = adev->ddev.dev; + dc->caps.max_links = 1; + /* DCE_VERSION_UNKNOWN (0) is not in the DTM v3 list. */ + dc->ctx->dce_version = DCE_VERSION_UNKNOWN; + + work = hdcp_create_workqueue(adev, &cp_psp, dc); + + KUNIT_ASSERT_NOT_NULL(test, work); + KUNIT_EXPECT_EQ(test, work->max_link, 1); + KUNIT_EXPECT_NOT_NULL(test, work->srm); + KUNIT_EXPECT_NOT_NULL(test, work->srm_temp); + KUNIT_EXPECT_PTR_EQ(test, (void *)cp_psp.funcs.update_stream_config, + (void *)update_config); + KUNIT_EXPECT_PTR_EQ(test, (void *)cp_psp.funcs.enable_assr, + (void *)enable_assr); + KUNIT_EXPECT_PTR_EQ(test, cp_psp.handle, work); + KUNIT_EXPECT_PTR_EQ(test, work[0].hdcp.config.psp.handle, &adev->psp); + KUNIT_EXPECT_EQ(test, work[0].hdcp.config.psp.caps.dtm_v3_supported, 0); + + hdcp_destroy(&adev->dev->kobj, work); +} + +/** + * dm_test_hdcp_create_workqueue_sets_dtm_v3_for_dcn31 - DCN 3.1 enables DTM v3 + * @test: KUnit test context + * + * On a DCN 3.1 device, hdcp_create_workqueue() must set the per-link + * dtm_v3_supported cap; an unmatched dce_version must leave it clear. + */ +static void dm_test_hdcp_create_workqueue_sets_dtm_v3_for_dcn31(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct cp_psp cp_psp = {0}; + struct dc *dc = dm_kunit_alloc_dc_with_ctx(test); + struct hdcp_workqueue *work; + + adev->dev = adev->ddev.dev; + dc->caps.max_links = 1; + dc->ctx->dce_version = DCN_VERSION_3_1; + + work = hdcp_create_workqueue(adev, &cp_psp, dc); + + KUNIT_ASSERT_NOT_NULL(test, work); + KUNIT_EXPECT_EQ(test, work[0].hdcp.config.psp.caps.dtm_v3_supported, 1); + + hdcp_destroy(&adev->dev->kobj, work); +} + +/** + * dm_test_hdcp_create_workqueue_initializes_all_links - loop covers every link + * @test: KUnit test context + * + * With more than one link, hdcp_create_workqueue() must run its init loop for + * every entry: each link records max_link, points its psp handle at the device + * psp and gets the DTM v3 cap set for a matching dce_version. + */ +static void dm_test_hdcp_create_workqueue_initializes_all_links(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct cp_psp cp_psp = {0}; + struct dc *dc = dm_kunit_alloc_dc_with_ctx(test); + struct hdcp_workqueue *work; + int i; + + adev->dev = adev->ddev.dev; + dc->caps.max_links = 3; + dc->ctx->dce_version = DCN_VERSION_3_1; + + work = hdcp_create_workqueue(adev, &cp_psp, dc); + + KUNIT_ASSERT_NOT_NULL(test, work); + KUNIT_EXPECT_EQ(test, work->max_link, 3); + for (i = 0; i < 3; i++) { + KUNIT_EXPECT_PTR_EQ(test, work[i].hdcp.config.psp.handle, + &adev->psp); + KUNIT_EXPECT_EQ(test, work[i].hdcp.config.psp.caps.dtm_v3_supported, 1); + } + + hdcp_destroy(&adev->dev->kobj, work); +} + /* End of tests for hdcp_create_workqueue() */ +/* Tests for hdcp_destroy() */ + +static ssize_t test_srm_bin_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *bin_attr, char *buffer, + loff_t pos, size_t count) +{ + return 0; +} + +static ssize_t test_srm_bin_write(struct file *filp, struct kobject *kobj, + const struct bin_attribute *bin_attr, char *buffer, + loff_t pos, size_t count) +{ + return count; +} + +/** + * setup_destroy_sysfs - create a kobject with the SRM bin file attached + * @test: KUnit test context + * @work: workqueue whose attr will be registered + * + * hdcp_destroy() calls sysfs_remove_bin_file() on the first entry's attr, so + * a real kobject with the bin file created is required. Returns the kobject, + * which the caller must kobject_put() after hdcp_destroy() has run. + */ +static struct kobject *setup_destroy_sysfs(struct kunit *test, + struct hdcp_workqueue *work) +{ + struct kobject *kobj; + int ret; + + kobj = kobject_create_and_add("amdgpu_dm_hdcp_test", NULL); + KUNIT_ASSERT_NOT_NULL(test, kobj); + + sysfs_bin_attr_init(&work->attr); + work->attr.attr.name = "hdcp_srm"; + work->attr.attr.mode = 0664; + work->attr.size = 16; + work->attr.read = test_srm_bin_read; + work->attr.write = test_srm_bin_write; + + ret = sysfs_create_bin_file(kobj, &work->attr); + KUNIT_ASSERT_EQ(test, ret, 0); + + return kobj; +} + +/** + * dm_test_hdcp_destroy_frees_and_removes_sysfs - full teardown path + * @test: KUnit test context + * + * hdcp_destroy() must cancel every link's delayed works, remove the SRM + * sysfs bin file and free srm, srm_temp and the workqueue itself. The + * workqueue and SRM buffers use kzalloc() (not kunit-managed) because + * hdcp_destroy() frees them; KASAN/kmemleak validate there is no leak or + * use-after-free. + */ +static void dm_test_hdcp_destroy_frees_and_removes_sysfs(struct kunit *test) +{ + struct hdcp_workqueue *work; + struct kobject *kobj; + + work = kzalloc_obj(*work); + KUNIT_ASSERT_NOT_NULL(test, work); + + work->max_link = 1; + INIT_DELAYED_WORK(&work->callback_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn); + + work->srm = kzalloc(16, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work->srm); + work->srm_temp = kzalloc(16, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work->srm_temp); + + kobj = setup_destroy_sysfs(test, work); + + /* Pre-schedule a delayed work to exercise the cancel path. */ + schedule_delayed_work(&work->callback_dwork, msecs_to_jiffies(10000)); + KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->callback_dwork)); + + hdcp_destroy(kobj, work); + + /* work is freed by hdcp_destroy(); only the kobject remains. */ + kobject_put(kobj); +} + +/** + * dm_test_hdcp_destroy_zero_links_null_srm - teardown with no links or SRM + * @test: KUnit test context + * + * With max_link == 0 the cancel loop is skipped, and NULL srm/srm_temp make + * the kfree() calls no-ops. hdcp_destroy() must still remove the sysfs bin + * file and free the workqueue without crashing. + */ +static void dm_test_hdcp_destroy_zero_links_null_srm(struct kunit *test) +{ + struct hdcp_workqueue *work; + struct kobject *kobj; + + work = kzalloc_obj(*work); + KUNIT_ASSERT_NOT_NULL(test, work); + + work->max_link = 0; + work->srm = NULL; + work->srm_temp = NULL; + + kobj = setup_destroy_sysfs(test, work); + + hdcp_destroy(kobj, work); + + kobject_put(kobj); +} + +/* End of tests for hdcp_destroy() */ + +/* Tests for link_lock() */ + +/** + * dm_test_link_lock_locks_and_unlocks_all_links - lock/unlock spans every link + * @test: KUnit test context + * + * link_lock() should acquire the mutex of every entry from 0 to max_link + * when locking, and release all of them when unlocking. A subsequent + * lock/unlock cycle must succeed, proving the mutexes were left released. + */ +static void dm_test_link_lock_locks_and_unlocks_all_links(struct kunit *test) +{ + const int num_links = 3; + struct hdcp_workqueue *work; + int i; + + work = kunit_kcalloc(test, num_links, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + + /* max_link is read from the first element. */ + work[0].max_link = num_links; + for (i = 0; i < num_links; i++) + mutex_init(&work[i].mutex); + + link_lock(work, true); + for (i = 0; i < num_links; i++) + KUNIT_EXPECT_TRUE(test, mutex_is_locked(&work[i].mutex)); + + link_lock(work, false); + for (i = 0; i < num_links; i++) + KUNIT_EXPECT_FALSE(test, mutex_is_locked(&work[i].mutex)); + + /* Mutexes must be re-acquirable after being released. */ + link_lock(work, true); + for (i = 0; i < num_links; i++) + KUNIT_EXPECT_TRUE(test, mutex_is_locked(&work[i].mutex)); + link_lock(work, false); +} + +/** + * dm_test_link_lock_zero_links_is_noop - zero max_link touches no mutexes + * @test: KUnit test context + * + * When max_link is zero, link_lock() must not touch any mutex and simply + * return, leaving the (single) entry's mutex unlocked. + */ +static void dm_test_link_lock_zero_links_is_noop(struct kunit *test) +{ + struct hdcp_workqueue *work; + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + + mutex_init(&work->mutex); + work->max_link = 0; + + link_lock(work, true); + + KUNIT_EXPECT_FALSE(test, mutex_is_locked(&work->mutex)); +} + +/* End of tests for link_lock() */ + +/* Tests for psp_get_srm() and psp_set_srm() */ + +/** + * dm_test_psp_get_srm_uninitialized_returns_null - GET fails when TA not initialized + * @test: KUnit test context + * + * When the HDCP TA context is not initialized, psp_get_srm() must take the + * guard path and return NULL without touching the output parameters or + * invoking the (real) firmware path. + */ +static void dm_test_psp_get_srm_uninitialized_returns_null(struct kunit *test) +{ + struct psp_context *psp; + uint32_t srm_version = 0xdead; + uint32_t srm_size = 0xbeef; + uint8_t *srm; + + psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, psp); + + /* kzalloc leaves hdcp_context.context.initialized == false */ + srm = psp_get_srm(psp, &srm_version, &srm_size); + + KUNIT_EXPECT_PTR_EQ(test, srm, NULL); + /* Output parameters must be left untouched on the guard path. */ + KUNIT_EXPECT_EQ(test, srm_version, 0xdead); + KUNIT_EXPECT_EQ(test, srm_size, 0xbeef); +} + +/** + * dm_test_psp_set_srm_uninitialized_returns_einval - SET fails when TA not initialized + * @test: KUnit test context + * + * When the HDCP TA context is not initialized, psp_set_srm() must take the + * guard path and return -EINVAL without updating srm_version or invoking + * the (real) firmware path. + */ +static void dm_test_psp_set_srm_uninitialized_returns_einval(struct kunit *test) +{ + struct psp_context *psp; + uint32_t srm_version = 0xdead; + u8 srm_buf[4] = {0}; + int ret; + + psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, psp); + + /* kzalloc leaves hdcp_context.context.initialized == false */ + ret = psp_set_srm(psp, srm_buf, sizeof(srm_buf), &srm_version); + + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + /* srm_version must be left untouched on the guard path. */ + KUNIT_EXPECT_EQ(test, srm_version, 0xdead); +} + +/** + * dm_test_psp_set_srm_initialized_stages_command - initialized path builds the command + * @test: KUnit test context + * + * With an initialized TA and the SR-IOV VF bypass, psp_hdcp_invoke() is a + * no-op, so the shared command buffer keeps the values psp_set_srm() staged. + * The function must copy the SRM into the SET_SRM in-message, record its size + * and command id, and then fail the response validation (the zeroed reply has + * valid_signature == 0), returning -EINVAL without updating srm_version. + */ +static void dm_test_psp_set_srm_initialized_stages_command(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct ta_hdcp_shared_memory *hdcp_cmd; + struct psp_context *psp; + uint32_t srm_version = 0xdead; + u8 srm_buf[4] = {0x1, 0x2, 0x3, 0x4}; + int ret; + + KUNIT_ASSERT_NOT_NULL(test, adev); + + psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, psp); + hdcp_cmd = kunit_kzalloc(test, sizeof(*hdcp_cmd), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, hdcp_cmd); + + psp->adev = adev; + psp->hdcp_context.context.initialized = true; + psp->hdcp_context.context.mem_context.shared_buf = (uint8_t *)hdcp_cmd; + + /* SR-IOV VF makes psp_hdcp_invoke() return early without firmware. */ + adev->virt.caps |= AMDGPU_SRIOV_CAPS_IS_VF; + + ret = psp_set_srm(psp, srm_buf, sizeof(srm_buf), &srm_version); + + /* Response validation fails (valid_signature == 0 in the zeroed reply). */ + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + KUNIT_EXPECT_EQ(test, srm_version, 0xdead); + /* The initialized path must have staged the SET_SRM command. */ + KUNIT_EXPECT_EQ(test, hdcp_cmd->cmd_id, TA_HDCP_COMMAND__HDCP_SET_SRM); + KUNIT_EXPECT_EQ(test, hdcp_cmd->in_msg.hdcp_set_srm.srm_buf_size, + (uint32_t)sizeof(srm_buf)); + KUNIT_EXPECT_MEMEQ(test, hdcp_cmd->in_msg.hdcp_set_srm.srm_buf, srm_buf, + sizeof(srm_buf)); +} + +/* End of tests for psp_get_srm() and psp_set_srm() */ + +/* Tests for srm_data_write() and srm_data_read() */ + +/** + * dm_test_srm_data_write_uninitialized_ta_keeps_srm - write with TA not initialized + * @test: KUnit test context + * + * srm_data_write() always copies the incoming buffer into work->srm_temp and + * returns the byte count. When the HDCP TA is not initialized, psp_set_srm() + * fails, so the committed SRM (work->srm / work->srm_size) must stay untouched. + */ +static void dm_test_srm_data_write_uninitialized_ta_keeps_srm(struct kunit *test) +{ + struct hdcp_workqueue *work; + struct psp_context *psp; + u8 buf[4] = {0xAA, 0xBB, 0xCC, 0xDD}; + ssize_t ret; + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, psp); + + work->max_link = 1; + mutex_init(&work->mutex); + /* kzalloc leaves hdcp_context.context.initialized == false */ + work->hdcp.config.psp.handle = psp; + work->srm_temp = kunit_kzalloc(test, PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work->srm_temp); + work->srm = kunit_kzalloc(test, PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work->srm); + + ret = srm_data_write(NULL, NULL, &work->attr, buf, 0, sizeof(buf)); + + KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buf)); + /* Incoming data is always staged into srm_temp. */ + KUNIT_EXPECT_MEMEQ(test, work->srm_temp, buf, sizeof(buf)); + /* psp_set_srm() failed, so the committed SRM must be unchanged. */ + KUNIT_EXPECT_EQ(test, work->srm_size, 0u); +} + +/** + * dm_test_srm_data_read_uninitialized_ta_returns_einval - read with TA not initialized + * @test: KUnit test context + * + * When the HDCP TA is not initialized, psp_get_srm() returns NULL, so + * srm_data_read() must take the error path and return -EINVAL. + */ +static void dm_test_srm_data_read_uninitialized_ta_returns_einval(struct kunit *test) +{ + struct hdcp_workqueue *work; + struct psp_context *psp; + u8 buf[4]; + ssize_t ret; + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, psp); + + work->max_link = 1; + mutex_init(&work->mutex); + /* kzalloc leaves hdcp_context.context.initialized == false */ + work->hdcp.config.psp.handle = psp; + + ret = srm_data_read(NULL, NULL, &work->attr, buf, 0, sizeof(buf)); + + KUNIT_EXPECT_EQ(test, ret, (ssize_t)-EINVAL); +} + +/** + * dm_test_srm_data_read_empty_srm_returns_zero - read of an empty SRM + * @test: KUnit test context + * + * With an initialized TA and the SR-IOV VF bypass, psp_hdcp_invoke() is a + * no-op and the zeroed shared buffer yields a SUCCESS status with srm_size 0. + * psp_get_srm() then returns a non-NULL (empty) buffer, so srm_data_read() + * takes the "nothing left to copy" path and returns 0. + */ +static void dm_test_srm_data_read_empty_srm_returns_zero(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct ta_hdcp_shared_memory *hdcp_cmd; + struct hdcp_workqueue *work; + struct psp_context *psp; + u8 buf[4]; + ssize_t ret; + + KUNIT_ASSERT_NOT_NULL(test, adev); + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, psp); + hdcp_cmd = kunit_kzalloc(test, sizeof(*hdcp_cmd), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, hdcp_cmd); + + work->max_link = 1; + mutex_init(&work->mutex); + psp->adev = adev; + psp->hdcp_context.context.initialized = true; + psp->hdcp_context.context.mem_context.shared_buf = (uint8_t *)hdcp_cmd; + work->hdcp.config.psp.handle = psp; + + /* SR-IOV VF makes psp_hdcp_invoke() return early without firmware. */ + adev->virt.caps |= AMDGPU_SRIOV_CAPS_IS_VF; + + ret = srm_data_read(NULL, NULL, &work->attr, buf, 0, sizeof(buf)); + + KUNIT_EXPECT_EQ(test, ret, 0); +} + +/* End of tests for srm_data_write() and srm_data_read() */ + +/* Tests for lp_write_i2c() / lp_read_i2c() / lp_write_dpcd() / lp_read_dpcd() */ + +/* Defined further below with the display helper tests. */ +static struct amdgpu_dm_connector *alloc_test_connector(struct kunit *test, + unsigned int index); + +/* + * Recording fakes for the DDC layer. The lp_* wrappers build i2c/DPCD + * transactions and forward them through dm_helpers_*, which end up calling + * i2c_transfer() / drm_dp_dpcd_*(). These fakes capture the resulting + * messages so the tests can assert what the wrappers built, without touching + * real hardware. KUnit runs cases sequentially, so file-scope capture state + * is reset at the start of each test. + */ +#define FAKE_DDC_MAX_MSGS 4 + +static struct fake_i2c_capture { + int num; + struct i2c_msg msgs[FAKE_DDC_MAX_MSGS]; +} fake_i2c_cap; + +static int fake_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + int i; + + fake_i2c_cap.num = num; + for (i = 0; i < num && i < FAKE_DDC_MAX_MSGS; i++) + fake_i2c_cap.msgs[i] = msgs[i]; + + return num; +} + +static u32 fake_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static const struct i2c_algorithm fake_i2c_algo = { + .master_xfer = fake_i2c_master_xfer, + .functionality = fake_i2c_functionality, +}; + +static void fake_i2c_lock_bus(struct i2c_adapter *adap, unsigned int flags) {} +static int fake_i2c_trylock_bus(struct i2c_adapter *adap, unsigned int flags) +{ + return 1; +} +static void fake_i2c_unlock_bus(struct i2c_adapter *adap, unsigned int flags) {} + +static const struct i2c_lock_operations fake_i2c_lock_ops = { + .lock_bus = fake_i2c_lock_bus, + .trylock_bus = fake_i2c_trylock_bus, + .unlock_bus = fake_i2c_unlock_bus, +}; + +static struct fake_aux_capture { + int calls; + u8 request; + unsigned int address; + size_t size; +} fake_aux_cap; + +static ssize_t fake_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + fake_aux_cap.calls++; + fake_aux_cap.request = msg->request; + fake_aux_cap.address = msg->address; + fake_aux_cap.size = msg->size; + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + + return msg->size; +} + +/** + * alloc_test_ddc_link - connector/link wired to the recording i2c + aux fakes + * @test: KUnit test context for managed allocation + * + * Builds an amdgpu_dm_connector with a fake i2c adapter and a fake DP aux, and + * points link->priv at the connector so dm_helpers_* find it. Returns the + * dc_link that the lp_* wrappers take as their opaque handle. + */ +static struct dc_link *alloc_test_ddc_link(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 0); + struct amdgpu_i2c_adapter *i2c; + struct dc_link *link; + + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + i2c = kunit_kzalloc(test, sizeof(*i2c), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, i2c); + i2c->base.algo = &fake_i2c_algo; + i2c->base.lock_ops = &fake_i2c_lock_ops; + aconnector->i2c = i2c; + + mutex_init(&aconnector->dm_dp_aux.aux.hw_mutex); + aconnector->dm_dp_aux.aux.transfer = fake_aux_transfer; + /* Skip the DPCD "throw away" probe read so we capture only our access. */ + aconnector->dm_dp_aux.aux.dpcd_probe_disabled = true; + + link = aconnector->dc_link; + link->priv = aconnector; + + return link; +} + +/** + * dm_test_lp_write_i2c_builds_single_write_payload - write builds one i2c msg + * @test: KUnit test context + * + * lp_write_i2c() must forward a single write payload carrying the address, + * length and data buffer unchanged. + */ +static void dm_test_lp_write_i2c_builds_single_write_payload(struct kunit *test) +{ + struct dc_link *link = alloc_test_ddc_link(test); + u8 data[3] = {0x11, 0x22, 0x33}; + bool ok; + + memset(&fake_i2c_cap, 0, sizeof(fake_i2c_cap)); + + ok = lp_write_i2c(link, 0x3a, data, sizeof(data)); + + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_ASSERT_EQ(test, fake_i2c_cap.num, 1); + /* write => flags without I2C_M_RD */ + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[0].flags, 0); + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[0].addr, 0x3a); + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[0].len, (int)sizeof(data)); + KUNIT_EXPECT_PTR_EQ(test, fake_i2c_cap.msgs[0].buf, (void *)data); +} + +/** + * dm_test_lp_read_i2c_builds_offset_then_read - read builds offset + read msgs + * @test: KUnit test context + * + * lp_read_i2c() must build a 1-byte write of the offset followed by a + * size-byte read into the caller buffer, both at the same address. + */ +static void dm_test_lp_read_i2c_builds_offset_then_read(struct kunit *test) +{ + struct dc_link *link = alloc_test_ddc_link(test); + u8 data[4]; + bool ok; + + memset(&fake_i2c_cap, 0, sizeof(fake_i2c_cap)); + + ok = lp_read_i2c(link, 0x50, 0x07, data, sizeof(data)); + + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_ASSERT_EQ(test, fake_i2c_cap.num, 2); + /* first: 1-byte write of the offset */ + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[0].flags, 0); + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[0].addr, 0x50); + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[0].len, 1); + /* second: size-byte read into the caller buffer */ + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[1].flags, I2C_M_RD); + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[1].addr, 0x50); + KUNIT_EXPECT_EQ(test, (int)fake_i2c_cap.msgs[1].len, (int)sizeof(data)); + KUNIT_EXPECT_PTR_EQ(test, fake_i2c_cap.msgs[1].buf, (void *)data); +} + +/** + * dm_test_lp_write_dpcd_forwards_native_write - write forwards a native write + * @test: KUnit test context + * + * lp_write_dpcd() must issue a single DP_AUX_NATIVE_WRITE at the requested + * address for the requested size. + */ +static void dm_test_lp_write_dpcd_forwards_native_write(struct kunit *test) +{ + struct dc_link *link = alloc_test_ddc_link(test); + u8 data[2] = {0xDE, 0xAD}; + bool ok; + + memset(&fake_aux_cap, 0, sizeof(fake_aux_cap)); + + ok = lp_write_dpcd(link, 0x68000, data, sizeof(data)); + + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, fake_aux_cap.calls, 1); + KUNIT_EXPECT_EQ(test, (int)fake_aux_cap.request, DP_AUX_NATIVE_WRITE); + KUNIT_EXPECT_EQ(test, fake_aux_cap.address, 0x68000u); + KUNIT_EXPECT_EQ(test, (int)fake_aux_cap.size, (int)sizeof(data)); +} + +/** + * dm_test_lp_read_dpcd_forwards_native_read - read forwards a native read + * @test: KUnit test context + * + * lp_read_dpcd() must issue a single DP_AUX_NATIVE_READ at the requested + * address for the requested size. + */ +static void dm_test_lp_read_dpcd_forwards_native_read(struct kunit *test) +{ + struct dc_link *link = alloc_test_ddc_link(test); + u8 data[4]; + bool ok; + + memset(&fake_aux_cap, 0, sizeof(fake_aux_cap)); + + ok = lp_read_dpcd(link, 0x00220, data, sizeof(data)); + + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, fake_aux_cap.calls, 1); + KUNIT_EXPECT_EQ(test, (int)fake_aux_cap.request, DP_AUX_NATIVE_READ); + KUNIT_EXPECT_EQ(test, fake_aux_cap.address, 0x00220u); + KUNIT_EXPECT_EQ(test, (int)fake_aux_cap.size, (int)sizeof(data)); +} + +/** + * dm_test_lp_write_i2c_no_connector_returns_false - missing connector fails + * @test: KUnit test context + * + * When link->priv has no connector, dm_helpers_submit_i2c() cannot proceed, + * so lp_write_i2c() must report failure without invoking the adapter. + */ +static void dm_test_lp_write_i2c_no_connector_returns_false(struct kunit *test) +{ + struct dc_link *link = alloc_test_ddc_link(test); + u8 data[2] = {0x01, 0x02}; + bool ok; + + link->priv = NULL; + memset(&fake_i2c_cap, 0, sizeof(fake_i2c_cap)); + + ok = lp_write_i2c(link, 0x3a, data, sizeof(data)); + + KUNIT_EXPECT_FALSE(test, ok); + KUNIT_EXPECT_EQ(test, fake_i2c_cap.num, 0); +} + +/** + * dm_test_lp_read_dpcd_no_connector_returns_false - missing connector fails + * @test: KUnit test context + * + * When link->priv has no connector, dm_helpers_dp_read_dpcd() cannot proceed, + * so lp_read_dpcd() must report failure without invoking the aux transfer. + */ +static void dm_test_lp_read_dpcd_no_connector_returns_false(struct kunit *test) +{ + struct dc_link *link = alloc_test_ddc_link(test); + u8 data[4]; + bool ok; + + link->priv = NULL; + memset(&fake_aux_cap, 0, sizeof(fake_aux_cap)); + + ok = lp_read_dpcd(link, 0x00220, data, sizeof(data)); + + KUNIT_EXPECT_FALSE(test, ok); + KUNIT_EXPECT_EQ(test, fake_aux_cap.calls, 0); +} + +/* End of tests for lp_write_i2c() / lp_read_i2c() / lp_write_dpcd() / lp_read_dpcd() */ + +/* + * Tests for lp_atomic_write_poll_read_i2c() / lp_atomic_write_poll_read_aux() + * + * These wrappers cast the opaque handle to a dc_link and forward to the + * dc_fused_io helpers. The success path submits a fused-IO command sequence to + * the DMCUB, which is out of reach for a unit test, so the coverage here is the + * hardware-free early returns: a NULL link and a payload that fails conversion + * (op size larger than the fused request buffer). + */ + +/** + * dm_test_lp_atomic_i2c_null_handle_returns_false - NULL link fails cleanly + * @test: KUnit test context + * + * With a NULL handle the forwarded dc_link is NULL, so the helper must return + * false without dereferencing anything. + */ +static void dm_test_lp_atomic_i2c_null_handle_returns_false(struct kunit *test) +{ + struct mod_hdcp_atomic_op_i2c op = { 0 }; + + KUNIT_EXPECT_FALSE(test, + lp_atomic_write_poll_read_i2c(NULL, &op, &op, &op, 0, 0)); +} + +/** + * dm_test_lp_atomic_i2c_oversized_op_returns_false - bad payload fails conversion + * @test: KUnit test context + * + * An op whose size exceeds the fused request buffer must fail conversion, so + * the helper returns false before any fused-IO submission. no_ddc_pin routes + * the DDC line through aux_hw_inst, avoiding the GPIO pin dereference. + */ +static void dm_test_lp_atomic_i2c_oversized_op_returns_false(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 0); + struct mod_hdcp_atomic_op_i2c write = { .size = 0x100 }; + struct mod_hdcp_atomic_op_i2c op = { 0 }; + struct dc_link *link; + + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + link = aconnector->dc_link; + link->no_ddc_pin = true; + + KUNIT_EXPECT_FALSE(test, + lp_atomic_write_poll_read_i2c(link, &write, &op, &op, 0, 0)); +} + +/** + * dm_test_lp_atomic_aux_null_handle_returns_false - NULL link fails cleanly + * @test: KUnit test context + * + * With a NULL handle the forwarded dc_link is NULL, so the helper must return + * false without dereferencing anything. + */ +static void dm_test_lp_atomic_aux_null_handle_returns_false(struct kunit *test) +{ + struct mod_hdcp_atomic_op_aux op = { 0 }; + + KUNIT_EXPECT_FALSE(test, + lp_atomic_write_poll_read_aux(NULL, &op, &op, &op, 0, 0)); +} + +/** + * dm_test_lp_atomic_aux_oversized_op_returns_false - bad payload fails conversion + * @test: KUnit test context + * + * The aux helper reads the DDC line from link->ddc->ddc_pin->pin_data before + * converting, so a minimal pin chain is wired up. An op larger than the fused + * request buffer then fails conversion and the helper returns false without + * any fused-IO submission. + */ +static void dm_test_lp_atomic_aux_oversized_op_returns_false(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 0); + struct mod_hdcp_atomic_op_aux write = { .size = 0x100 }; + struct mod_hdcp_atomic_op_aux op = { 0 }; + struct ddc_service *ddc; + struct ddc *ddc_pin; + struct dc_link *link; + void *pin_data; + + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ddc); + ddc_pin = kunit_kzalloc(test, sizeof(*ddc_pin), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ddc_pin); + /* Only ->en is read; over-allocate so struct gpio stays opaque here. */ + pin_data = kunit_kzalloc(test, 128, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, pin_data); + + ddc_pin->pin_data = pin_data; + ddc->ddc_pin = ddc_pin; + + link = aconnector->dc_link; + link->ddc = ddc; + + KUNIT_EXPECT_FALSE(test, + lp_atomic_write_poll_read_aux(link, &write, &op, &op, 0, 0)); +} + +/* End of tests for lp_atomic_write_poll_read_i2c() / lp_atomic_write_poll_read_aux() */ + +/* + * Tests for hdcp_update_display() / hdcp_remove_display() / + * hdcp_reset_display(). + */ + +/** + * alloc_test_workqueue_locked - workqueue with dworks and mutex initialised + * @test: KUnit test context for managed allocation + * + * Allocates a minimal hdcp_workqueue with its mutex and the three delayed + * works initialised, as required by the guard(mutex) and process_output() + * usage in the display update/remove/reset helpers. + */ +static struct hdcp_workqueue *alloc_test_workqueue_locked(struct kunit *test) +{ + struct hdcp_workqueue *work; + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + + mutex_init(&work->mutex); + INIT_DELAYED_WORK(&work->callback_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn); + + return work; +} + +/** + * alloc_test_connector - minimal amdgpu_dm_connector for display tests + * @test: KUnit test context for managed allocation + * @index: drm connector index to assign + * + * Allocates an amdgpu_dm_connector with an initialised connector refcount + * (so drm_connector_get() is valid) and a dc_link/dc pair. The connector's + * free_cb is left NULL so drm_connector_put() is a safe no-op that never + * releases the object. + */ +static struct amdgpu_dm_connector *alloc_test_connector(struct kunit *test, + unsigned int index) +{ + struct amdgpu_dm_connector *aconnector; + struct dc_link *dc_link; + struct dc *dc; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + dc_link = kunit_kzalloc(test, sizeof(*dc_link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc_link); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc); + + kref_init(&aconnector->base.base.refcount); + aconnector->base.index = index; + dc_link->dc = dc; + aconnector->dc_link = dc_link; + + return aconnector; +} + +/** + * dm_test_hdcp_update_display_enable_registers_connector - enable stores the connector + * @test: KUnit test context + * + * hdcp_update_display() with enable_encryption=true should register the + * connector in the per-link aconnector array at its connector index. The + * hdcp has no active display, so mod_hdcp_update_display() is effectively a + * no-op and the call must not touch encryption_status. + */ +static void dm_test_hdcp_update_display_enable_registers_connector(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 0); + + work->srm_size = 0; + + hdcp_update_display(work, 0, aconnector, DRM_MODE_HDCP_CONTENT_TYPE0, true); + + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[0], aconnector); + KUNIT_EXPECT_EQ(test, work->encryption_status[0], + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF); + + cancel_delayed_work_sync(&work->property_validate_dwork); + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); +} + +/** + * dm_test_hdcp_update_display_disable_sets_status_off - disable clears status + * @test: KUnit test context + * + * hdcp_update_display() with enable_encryption=false should register the + * connector and reset the connector's encryption_status entry to HDCP_OFF + * via hdcp_update_display_encryption_control(). + */ +static void dm_test_hdcp_update_display_disable_sets_status_off(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 2); + + work->encryption_status[2] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + + hdcp_update_display(work, 0, aconnector, DRM_MODE_HDCP_CONTENT_TYPE0, false); + + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[2], aconnector); + KUNIT_EXPECT_EQ(test, work->encryption_status[2], + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF); + + cancel_delayed_work_sync(&work->property_validate_dwork); + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); +} + +/** + * dm_test_hdcp_remove_display_enabled_resets_cp - ENABLED CP reverts to DESIRED + * @test: KUnit test context + * + * hdcp_remove_display() must revert a connector whose content_protection is + * ENABLED back to DESIRED and clear the per-link aconnector entry. + */ +static void dm_test_hdcp_remove_display_enabled_resets_cp(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 1); + struct drm_connector_state *conn_state; + + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; + aconnector->base.state = conn_state; + work->aconnector[1] = aconnector; + + hdcp_remove_display(work, 0, aconnector); + + KUNIT_EXPECT_EQ(test, conn_state->content_protection, + DRM_MODE_CONTENT_PROTECTION_DESIRED); + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[1], NULL); + + cancel_delayed_work_sync(&work->property_validate_dwork); + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); +} + +/** + * dm_test_hdcp_remove_display_null_state_clears_connector - NULL state skips CP change + * @test: KUnit test context + * + * When the connector has no drm_connector_state, hdcp_remove_display() must + * skip the content_protection update path and still clear the per-link + * aconnector entry. + */ +static void dm_test_hdcp_remove_display_null_state_clears_connector(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 0); + + aconnector->base.state = NULL; + work->aconnector[0] = aconnector; + + hdcp_remove_display(work, 0, aconnector); + + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[0], NULL); + + cancel_delayed_work_sync(&work->property_validate_dwork); + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); +} + +/** + * dm_test_hdcp_reset_display_clears_all_state - reset clears status and connectors + * @test: KUnit test context + * + * hdcp_reset_display() must reset every connector's encryption_status to + * HDCP_OFF and clear all per-link aconnector entries. + */ +static void dm_test_hdcp_reset_display_clears_all_state(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 4); + + work->encryption_status[4] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON; + work->aconnector[4] = aconnector; + + hdcp_reset_display(work, 0); + + KUNIT_EXPECT_EQ(test, work->encryption_status[4], + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF); + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[4], NULL); + + cancel_delayed_work_sync(&work->property_validate_dwork); + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); +} + +/* + * End of tests for hdcp_update_display() / hdcp_remove_display() / + * hdcp_reset_display(). + */ + +/* Tests for enable_assr() */ + +/** + * alloc_test_workqueue_for_assr - workqueue wired to a psp for enable_assr() + * @test: KUnit test context for managed allocation + * @adev: amdgpu device whose drm_device backs psp->adev (for drm_info()) + * + * Allocates a minimal hdcp_workqueue and a psp_context connected through + * hdcp.config.psp.handle, matching the dereference chain enable_assr() + * performs. The psp is left with dtm_context.context.initialized == false + * (from kzalloc) so enable_assr() takes the "DTM TA not initialized" path. + */ +static struct hdcp_workqueue *alloc_test_workqueue_for_assr(struct kunit *test, + struct amdgpu_device *adev) +{ + struct hdcp_workqueue *work; + struct psp_context *psp; + + work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, work); + + psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, psp); + + psp->adev = adev; + work->hdcp.config.psp.handle = psp; + + return work; +} + +/** + * dm_test_enable_assr_uninitialized_dtm_returns_false - DTM TA not initialized + * @test: KUnit test context + * + * When the DTM TA context is not initialized, enable_assr() must take the + * early-return path, emit the informational message and return false + * without invoking the (real) firmware path. + */ +static void dm_test_enable_assr_uninitialized_dtm_returns_false(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct hdcp_workqueue *work = alloc_test_workqueue_for_assr(test, adev); + struct dc_link *link; + bool ret; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + /* kzalloc leaves dtm_context.context.initialized == false */ + ret = enable_assr(work, link); + + KUNIT_EXPECT_FALSE(test, ret); +} + +/** + * dm_test_enable_assr_uninitialized_dtm_ignores_link - link untouched on failure + * @test: KUnit test context + * + * On the "DTM TA not initialized" path enable_assr() returns before reading + * any field of @link, so a NULL link must be tolerated and the call must + * still return false. + */ +static void dm_test_enable_assr_uninitialized_dtm_ignores_link(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct hdcp_workqueue *work = alloc_test_workqueue_for_assr(test, adev); + bool ret; + + /* link is not dereferenced before the initialized check. */ + ret = enable_assr(work, NULL); + + KUNIT_EXPECT_FALSE(test, ret); +} + +/** + * dm_test_enable_assr_initialized_builds_command_and_fails - full body, invoke bypassed + * @test: KUnit test context + * + * With the DTM TA marked initialized and a valid shared buffer, enable_assr() + * runs its full body: it acquires the DTM mutex, clears the shared command, + * fills in the ASSR-enable command from @link and pre-sets the status to + * GENERIC_FAILURE before invoking the TA. + * + * psp_dtm_invoke() is prevented from touching real firmware by marking the + * device as an SR-IOV virtual function, which makes it return early without + * modifying the shared status. The status therefore stays GENERIC_FAILURE, so + * enable_assr() must return false. Inspecting the shared command afterwards + * proves the body executed and consumed @link. + */ +static void dm_test_enable_assr_initialized_builds_command_and_fails(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct hdcp_workqueue *work = alloc_test_workqueue_for_assr(test, adev); + struct psp_context *psp = work->hdcp.config.psp.handle; + struct ta_dtm_shared_memory *dtm_cmd; + struct dc_link *link; + bool ret; + + dtm_cmd = kunit_kzalloc(test, sizeof(*dtm_cmd), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dtm_cmd); + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + link->link_enc_hw_inst = 3; + + /* Wire up an "initialized" DTM TA with a real shared buffer. */ + psp->dtm_context.context.initialized = true; + psp->dtm_context.context.mem_context.shared_buf = (uint8_t *)dtm_cmd; + mutex_init(&psp->dtm_context.mutex); + + /* + * Force the SR-IOV VF early-return in psp_dtm_invoke() so no GPU + * command is submitted; the shared status is left untouched. + */ + adev->virt.caps |= AMDGPU_SRIOV_CAPS_IS_VF; + + ret = enable_assr(work, link); + + /* Status was never advanced to SUCCESS, so the call must fail. */ + KUNIT_EXPECT_FALSE(test, ret); + /* The command body must have populated the shared buffer. */ + KUNIT_EXPECT_EQ(test, dtm_cmd->cmd_id, TA_DTM_COMMAND__TOPOLOGY_ASSR_ENABLE); + KUNIT_EXPECT_EQ(test, + dtm_cmd->dtm_in_message.topology_assr_enable.display_topology_dig_be_index, + link->link_enc_hw_inst); + KUNIT_EXPECT_EQ(test, dtm_cmd->dtm_status, TA_DTM_STATUS__GENERIC_FAILURE); + /* The DTM mutex must be released after the guard scope exits. */ + KUNIT_EXPECT_FALSE(test, mutex_is_locked(&psp->dtm_context.mutex)); +} + +/* End of tests for enable_assr() */ + +/* Tests for update_config() */ + +/** + * dm_test_update_config_null_connector_is_noop - NULL stream ctx returns early + * @test: KUnit test context + * + * When config->dm_stream_ctx is NULL, update_config() must return before + * touching the workqueue, leaving the per-link aconnector array untouched. + */ +static void dm_test_update_config_null_connector_is_noop(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct cp_psp_stream_config config = {0}; + + config.dm_stream_ctx = NULL; + + update_config(work, &config); + + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[0], NULL); +} + +/** + * dm_test_update_config_null_dc_link_is_noop - NULL dc_link returns early + * @test: KUnit test context + * + * A connector without a dc_link must cause update_config() to return before + * registering the connector, leaving the aconnector array untouched. + */ +static void dm_test_update_config_null_dc_link_is_noop(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct amdgpu_dm_connector *aconnector; + struct cp_psp_stream_config config = {0}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + aconnector->dc_link = NULL; + + config.dm_stream_ctx = aconnector; + + update_config(work, &config); + + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[0], NULL); +} + +/** + * dm_test_update_config_dpms_off_removes_display - dpms_off path removes display + * @test: KUnit test context + * + * With config->dpms_off set, update_config() must take the removal path: + * hdcp_remove_display() reverts an ENABLED connector to DESIRED and clears + * its per-link aconnector entry. + */ +static void dm_test_update_config_dpms_off_removes_display(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 0); + struct drm_connector_state *conn_state; + struct cp_psp_stream_config config = {0}; + + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; + aconnector->base.state = conn_state; + work->aconnector[0] = aconnector; + + config.dm_stream_ctx = aconnector; + config.dpms_off = true; + + update_config(work, &config); + + KUNIT_EXPECT_EQ(test, conn_state->content_protection, + DRM_MODE_CONTENT_PROTECTION_DESIRED); + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[0], NULL); + + cancel_delayed_work_sync(&work->property_validate_dwork); + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); +} + +/** + * dm_test_update_config_populates_display_and_link - active path fills state + * @test: KUnit test context + * + * With dpms_off clear, update_config() must build the display and link from + * @config, reset the connector's encryption_status to HDCP_OFF, register the + * connector and reach process_output() (which enqueues property_validate). + * + * mod_hdcp_add_display() reaches add_display_to_topology(), which returns + * early because the DTM TA is left uninitialized, so no firmware is touched. + */ +static void dm_test_update_config_populates_display_and_link(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue_locked(test); + struct amdgpu_dm_connector *aconnector = alloc_test_connector(test, 0); + struct psp_context *psp; + struct cp_psp_stream_config config = {0}; + + /* add_display_to_topology() dereferences the psp handle. */ + psp = kunit_kzalloc(test, sizeof(*psp), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, psp); + work->hdcp.config.psp.handle = psp; + + config.dm_stream_ctx = aconnector; + config.dpms_off = false; + config.otg_inst = 1; + config.dig_fe = 4; + config.dig_be = 5; + config.stream_enc_idx = 6; + config.link_enc_idx = 7; + config.dio_output_idx = 8; + config.phy_idx = 2; + + update_config(work, &config); + + KUNIT_EXPECT_EQ(test, work->encryption_status[0], + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF); + KUNIT_EXPECT_PTR_EQ(test, work->aconnector[0], aconnector); + + KUNIT_EXPECT_EQ(test, work->display.state, MOD_HDCP_DISPLAY_ACTIVE); + KUNIT_EXPECT_EQ(test, work->display.controller, + CONTROLLER_ID_D0 + config.otg_inst); + KUNIT_EXPECT_EQ(test, work->display.dig_fe, config.dig_fe); + KUNIT_EXPECT_EQ(test, work->display.stream_enc_idx, config.stream_enc_idx); + + KUNIT_EXPECT_EQ(test, work->link.dig_be, config.dig_be); + KUNIT_EXPECT_EQ(test, work->link.link_enc_idx, config.link_enc_idx); + KUNIT_EXPECT_EQ(test, work->link.dio_output_id, config.dio_output_idx); + KUNIT_EXPECT_EQ(test, work->link.phy_idx, config.phy_idx); + + KUNIT_EXPECT_TRUE(test, work_pending(&work->property_validate_dwork.work)); + + cancel_delayed_work_sync(&work->property_validate_dwork); + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); +} + +/* End of tests for update_config() */ + static struct kunit_case dm_hdcp_test_cases[] = { /* hdcp_get_content_protection_from_status() */ KUNIT_CASE(dm_test_hdcp_get_cp_disabled_returns_desired), @@ -655,6 +2534,26 @@ static struct kunit_case dm_hdcp_test_cases[] = { KUNIT_CASE(dm_test_process_output_watchdog_stop_and_needed_requeues), /* event_property_update() */ KUNIT_CASE(dm_test_event_property_update_skips_null_connector), + KUNIT_CASE(dm_test_event_property_update_skips_disconnected), + KUNIT_CASE(dm_test_event_property_update_skips_null_state), + KUNIT_CASE(dm_test_event_property_update_skips_null_dev), + KUNIT_CASE(dm_test_event_property_update_desired_when_off), + KUNIT_CASE(dm_test_event_property_update_enabled_when_encrypted), + /* event_callback() */ + KUNIT_CASE(dm_test_event_callback_cancels_callback_dwork), + KUNIT_CASE(dm_test_event_callback_schedules_property_validate), + /* event_property_validate() */ + KUNIT_CASE(dm_test_event_property_validate_skips_null_connector), + KUNIT_CASE(dm_test_event_property_validate_skips_disconnected), + KUNIT_CASE(dm_test_event_property_validate_skips_null_state), + KUNIT_CASE(dm_test_event_property_validate_updates_on_status_change), + KUNIT_CASE(dm_test_event_property_validate_no_update_when_unchanged), + /* event_watchdog_timer() */ + KUNIT_CASE(dm_test_event_watchdog_timer_cancels_watchdog_dwork), + KUNIT_CASE(dm_test_event_watchdog_timer_schedules_property_validate), + /* event_cpirq() */ + KUNIT_CASE(dm_test_event_cpirq_schedules_property_validate), + KUNIT_CASE(dm_test_event_cpirq_leaves_callback_and_watchdog_idle), /* hdcp_handle_cpirq() */ KUNIT_CASE(dm_test_hdcp_handle_cpirq_schedules_work), KUNIT_CASE(dm_test_hdcp_handle_cpirq_selects_link_index), @@ -663,6 +2562,50 @@ static struct kunit_case dm_hdcp_test_cases[] = { KUNIT_CASE(dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate), /* hdcp_create_workqueue() */ KUNIT_CASE(dm_test_hdcp_create_workqueue_zero_max_links_returns_null), + KUNIT_CASE(dm_test_hdcp_create_workqueue_initializes_work), + KUNIT_CASE(dm_test_hdcp_create_workqueue_sets_dtm_v3_for_dcn31), + KUNIT_CASE(dm_test_hdcp_create_workqueue_initializes_all_links), + /* hdcp_destroy() */ + KUNIT_CASE(dm_test_hdcp_destroy_frees_and_removes_sysfs), + KUNIT_CASE(dm_test_hdcp_destroy_zero_links_null_srm), + /* link_lock() */ + KUNIT_CASE(dm_test_link_lock_locks_and_unlocks_all_links), + KUNIT_CASE(dm_test_link_lock_zero_links_is_noop), + /* psp_get_srm() / psp_set_srm() */ + KUNIT_CASE(dm_test_psp_get_srm_uninitialized_returns_null), + KUNIT_CASE(dm_test_psp_set_srm_uninitialized_returns_einval), + KUNIT_CASE(dm_test_psp_set_srm_initialized_stages_command), + /* srm_data_write() / srm_data_read() */ + KUNIT_CASE(dm_test_srm_data_write_uninitialized_ta_keeps_srm), + KUNIT_CASE(dm_test_srm_data_read_uninitialized_ta_returns_einval), + KUNIT_CASE(dm_test_srm_data_read_empty_srm_returns_zero), + /* lp_write_i2c() / lp_read_i2c() / lp_write_dpcd() / lp_read_dpcd() */ + KUNIT_CASE(dm_test_lp_write_i2c_builds_single_write_payload), + KUNIT_CASE(dm_test_lp_read_i2c_builds_offset_then_read), + KUNIT_CASE(dm_test_lp_write_dpcd_forwards_native_write), + KUNIT_CASE(dm_test_lp_read_dpcd_forwards_native_read), + KUNIT_CASE(dm_test_lp_write_i2c_no_connector_returns_false), + KUNIT_CASE(dm_test_lp_read_dpcd_no_connector_returns_false), + /* lp_atomic_write_poll_read_i2c() / lp_atomic_write_poll_read_aux() */ + KUNIT_CASE(dm_test_lp_atomic_i2c_null_handle_returns_false), + KUNIT_CASE(dm_test_lp_atomic_i2c_oversized_op_returns_false), + KUNIT_CASE(dm_test_lp_atomic_aux_null_handle_returns_false), + KUNIT_CASE(dm_test_lp_atomic_aux_oversized_op_returns_false), + /* hdcp_update_display() / hdcp_remove_display() / hdcp_reset_display() */ + KUNIT_CASE(dm_test_hdcp_update_display_enable_registers_connector), + KUNIT_CASE(dm_test_hdcp_update_display_disable_sets_status_off), + KUNIT_CASE(dm_test_hdcp_remove_display_enabled_resets_cp), + KUNIT_CASE(dm_test_hdcp_remove_display_null_state_clears_connector), + KUNIT_CASE(dm_test_hdcp_reset_display_clears_all_state), + /* enable_assr() */ + KUNIT_CASE(dm_test_enable_assr_uninitialized_dtm_returns_false), + KUNIT_CASE(dm_test_enable_assr_uninitialized_dtm_ignores_link), + KUNIT_CASE(dm_test_enable_assr_initialized_builds_command_and_fails), + /* update_config() */ + KUNIT_CASE(dm_test_update_config_null_connector_is_noop), + KUNIT_CASE(dm_test_update_config_null_dc_link_is_noop), + KUNIT_CASE(dm_test_update_config_dpms_off_removes_display), + KUNIT_CASE(dm_test_update_config_populates_display_and_link), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c index ed20e278742d..28c36217f6a2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c @@ -202,22 +202,6 @@ static void dm_test_dmub_notify_callback(struct amdgpu_device *adev, dm_test_dmub_notify_count++; } -static struct dc *dm_test_alloc_dc_with_ctx(struct kunit *test) -{ - struct dc_context *ctx; - struct dc *dc; - - dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc); - ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); - - dc->ctx = ctx; - ctx->dc = dc; - - return dc; -} - static enum dc_irq_source dm_test_to_dal_irq_source_dce110( struct irq_service *irq_service, uint32_t src_id, @@ -299,7 +283,7 @@ static struct dc *dm_test_alloc_dc_with_irq_service(struct kunit *test, struct dc *dc; int i; - dc = dm_test_alloc_dc_with_ctx(test); + dc = dm_kunit_alloc_dc_with_ctx(test); res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res_pool); irqs = kunit_kzalloc(test, sizeof(*irqs), GFP_KERNEL); @@ -1842,12 +1826,15 @@ static void dm_test_irq_schedule_work_queues_handler(struct kunit *test) amdgpu_dm_irq_schedule_work(adev, DC_IRQ_SOURCE_HPD1); /* - * Low-context work runs asynchronously on system_highpri_wq. - * amdgpu_dm_irq_fini() flushes each pending work item before freeing - * the handlers, so the handler is guaranteed to have run afterwards. + * Low-context work runs asynchronously on the DM IRQ workqueue. + * Flush it so the handler completes before we check the count; + * amdgpu_dm_irq_fini() cancels (rather than runs) any work that is + * still pending, so the flush must happen first. */ - amdgpu_dm_irq_fini(adev); + flush_workqueue(adev->dm.irq_wq); KUNIT_EXPECT_EQ(test, count, 1); + + amdgpu_dm_irq_fini(adev); } /** @@ -1858,7 +1845,7 @@ static void dm_test_irq_schedule_work_queues_handler(struct kunit *test) * schedule before the work has run makes queue_work() fail for the * still-pending item, forcing amdgpu_dm_irq_schedule_work() into the fallback * that allocates and queues a fresh handler copy. Both work items run when - * amdgpu_dm_irq_fini() flushes the queue, so the handler fires twice. + * the DM IRQ workqueue is flushed, so the handler fires twice. */ static void dm_test_irq_schedule_work_requeue_fallback(struct kunit *test) { @@ -1880,8 +1867,15 @@ static void dm_test_irq_schedule_work_requeue_fallback(struct kunit *test) amdgpu_dm_irq_schedule_work(adev, DC_IRQ_SOURCE_HPD1); amdgpu_dm_irq_schedule_work(adev, DC_IRQ_SOURCE_HPD1); - amdgpu_dm_irq_fini(adev); + /* + * Flush the DM IRQ workqueue so both work items run before we check + * the count; amdgpu_dm_irq_fini() would cancel any still-pending work + * instead of running it. + */ + flush_workqueue(adev->dm.irq_wq); KUNIT_EXPECT_EQ(test, count, 2); + + amdgpu_dm_irq_fini(adev); } /* Tests for amdgpu_dm_set_hpd_irq_state() */ @@ -3273,7 +3267,7 @@ static void dm_test_register_hpd_handlers_dmub_outbox(struct kunit *test) adev = dm_kunit_alloc_adev(test); KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); - dc = dm_test_alloc_dc_with_ctx(test); + dc = dm_kunit_alloc_dc_with_ctx(test); adev->dm.dc = dc; /* Make dc_is_dmub_outbox_supported() return true. */ @@ -3485,7 +3479,7 @@ static void dm_test_dmub_outbox1_low_irq_empty(struct kunit *test) struct dc *dc; adev = dm_kunit_alloc_adev(test); - dc = dm_test_alloc_dc_with_ctx(test); + dc = dm_kunit_alloc_dc_with_ctx(test); dc_dmub_srv = kunit_kzalloc(test, sizeof(*dc_dmub_srv), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_dmub_srv); dmub = kunit_kzalloc(test, sizeof(*dmub), GFP_KERNEL); @@ -3519,7 +3513,7 @@ static struct amdgpu_device *dm_test_alloc_adev_outbox_notify(struct kunit *test struct dc *dc; adev = dm_kunit_alloc_adev(test); - dc = dm_test_alloc_dc_with_ctx(test); + dc = dm_kunit_alloc_dc_with_ctx(test); dc_dmub_srv = kunit_kzalloc(test, sizeof(*dc_dmub_srv), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_dmub_srv); dmub = kunit_kzalloc(test, sizeof(*dmub), GFP_KERNEL); @@ -3625,7 +3619,7 @@ static void dm_test_dce110_register_irq_handlers_rejects_uninitialized_sources(s struct dc *dc; adev = dm_kunit_alloc_adev(test); - dc = dm_test_alloc_dc_with_ctx(test); + dc = dm_kunit_alloc_dc_with_ctx(test); adev->dm.dc = dc; KUNIT_EXPECT_EQ(test, amdgpu_dm_dce110_register_irq_handlers(adev), -EINVAL); @@ -3686,7 +3680,7 @@ static void dm_test_dcn10_register_irq_handlers_zero_crtc(struct kunit *test) adev = dm_kunit_alloc_adev(test); KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_irq_sources, adev), 0); - dc = dm_test_alloc_dc_with_ctx(test); + dc = dm_kunit_alloc_dc_with_ctx(test); adev->dm.dc = dc; adev->mode_info.num_hpd = 1; amdgpu_dm_set_irq_funcs(adev); @@ -3749,7 +3743,7 @@ static void dm_test_register_outbox_irq_handlers_without_dmub(struct kunit *test adev = dm_kunit_alloc_adev(test); KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_irq_sources, adev), 0); - dc = dm_test_alloc_dc_with_ctx(test); + dc = dm_kunit_alloc_dc_with_ctx(test); adev->dm.dc = dc; amdgpu_dm_set_irq_funcs(adev); @@ -3855,11 +3849,14 @@ static void dm_test_irq_handler_dispatches_work(struct kunit *test) KUNIT_EXPECT_EQ(test, high_count, 1); /* - * Low-context work runs asynchronously; amdgpu_dm_irq_fini() flushes - * each pending work item before freeing, so it has run afterwards. + * Low-context work runs asynchronously; flush the DM IRQ workqueue so + * it completes before we check the count. amdgpu_dm_irq_fini() cancels + * any still-pending work rather than running it. */ - amdgpu_dm_irq_fini(adev); + flush_workqueue(adev->dm.irq_wq); KUNIT_EXPECT_EQ(test, low_count, 1); + + amdgpu_dm_irq_fini(adev); } /* Tests for dm_handle_vmin_vmax_update() */ @@ -3886,7 +3883,7 @@ static void dm_test_handle_vmin_vmax_update(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); mutex_init(&adev->dm.dc_lock); - dc = dm_test_alloc_dc_with_ctx(test); + dc = dm_kunit_alloc_dc_with_ctx(test); dc->current_state = kunit_kzalloc(test, sizeof(*dc->current_state), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc->current_state); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c index 58615cdbe854..862ada873fbb 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c @@ -8,6 +8,7 @@ #include <kunit/test.h> #include <linux/module.h> #include <drm/drm_kunit_helpers.h> +#include <drm/drm_managed.h> #include "dc.h" #include "core_types.h" @@ -45,23 +46,32 @@ struct dc_link *dm_kunit_alloc_link(struct kunit *test) } EXPORT_SYMBOL(dm_kunit_alloc_link); -struct dc_link *dm_kunit_alloc_link_with_ctx(struct kunit *test) +struct dc *dm_kunit_alloc_dc_with_ctx(struct kunit *test) { - struct dc_link *link; struct dc_context *ctx; struct dc *dc; - link = dm_kunit_alloc_link(test); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc); ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx); - dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, dc); - - link->ctx = ctx; - ctx->dc = dc; dc->ctx = ctx; + ctx->dc = dc; + + return dc; +} +EXPORT_SYMBOL(dm_kunit_alloc_dc_with_ctx); + +struct dc_link *dm_kunit_alloc_link_with_ctx(struct kunit *test) +{ + struct dc_link *link; + struct dc *dc; + + link = dm_kunit_alloc_link(test); + dc = dm_kunit_alloc_dc_with_ctx(test); + link->ctx = dc->ctx; return link; } @@ -127,7 +137,7 @@ struct amdgpu_dm_connector *dm_kunit_alloc_connector(struct kunit *test, { struct amdgpu_dm_connector *aconnector; - aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + aconnector = drmm_kzalloc(adev_to_drm(adev), sizeof(*aconnector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, aconnector); if (adev) @@ -138,5 +148,19 @@ struct amdgpu_dm_connector *dm_kunit_alloc_connector(struct kunit *test, } EXPORT_SYMBOL(dm_kunit_alloc_connector); +struct drm_device *dm_kunit_alloc_drm_with_connector_list(struct kunit *test) +{ + struct drm_device *dev; + + dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + INIT_LIST_HEAD(&dev->mode_config.connector_list); + spin_lock_init(&dev->mode_config.connector_list_lock); + + return dev; +} +EXPORT_SYMBOL(dm_kunit_alloc_drm_with_connector_list); + MODULE_LICENSE("Dual MIT/GPL"); MODULE_DESCRIPTION("KUnit test helpers for amdgpu_dm tests"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h index 0f1c48fa2128..49b358681d60 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h @@ -13,14 +13,18 @@ struct amdgpu_device; struct amdgpu_display_manager; struct amdgpu_dm_connector; +struct dc; struct dc_link; struct dc_state; struct dc_stream_state; +struct drm_device; struct amdgpu_device *dm_kunit_alloc_adev(struct kunit *test); +struct dc *dm_kunit_alloc_dc_with_ctx(struct kunit *test); struct dc_link *dm_kunit_alloc_link(struct kunit *test); struct dc_link *dm_kunit_alloc_link_with_ctx(struct kunit *test); struct amdgpu_display_manager *dm_kunit_alloc_dm(struct kunit *test); +struct drm_device *dm_kunit_alloc_drm_with_connector_list(struct kunit *test); struct dc_stream_state *dm_kunit_alloc_stream(struct kunit *test, struct dc_link *link); void dm_kunit_add_stream_to_state(struct kunit *test, struct dc_state *state, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c index a751e67cadf1..ba97092c7bb8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -328,12 +328,6 @@ static void dm_test_get_plane_modifiers(struct kunit *test) adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, adev); - adev->family = AMDGPU_FAMILY_SI; - KUNIT_EXPECT_EQ(test, - amdgpu_dm_plane_get_plane_modifiers(adev, DRM_PLANE_TYPE_PRIMARY, &mods), - 0); - KUNIT_EXPECT_PTR_EQ(test, mods, NULL); - adev->family = AMDGPU_FAMILY_NV; KUNIT_ASSERT_EQ(test, amdgpu_dm_plane_get_plane_modifiers(adev, DRM_PLANE_TYPE_CURSOR, &mods), @@ -413,62 +407,6 @@ static void dm_test_get_min_max_dc_plane_scaling(struct kunit *test) } /** - * dm_test_fill_plane_buffer_attributes_gfx8() - Verify graphics path and GFX8 tiling fill. - * @test: KUnit test context. - * - * Verify if GFX8 plane buffer attributes and tiling fields are filled correctly. - */ -static void dm_test_fill_plane_buffer_attributes_gfx8(struct kunit *test) -{ - struct amdgpu_device *adev; - struct amdgpu_framebuffer *afb; - struct dc_tiling_info *tiling_info; - struct plane_size *plane_size; - struct dc_plane_dcc_param *dcc; - struct dc_plane_address *address; - uint64_t tiling_flags = 0; - int ret; - - adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); - afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL); - tiling_info = kunit_kzalloc(test, sizeof(*tiling_info), GFP_KERNEL); - plane_size = kunit_kzalloc(test, sizeof(*plane_size), GFP_KERNEL); - dcc = kunit_kzalloc(test, sizeof(*dcc), GFP_KERNEL); - address = kunit_kzalloc(test, sizeof(*address), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, adev); - KUNIT_ASSERT_NOT_NULL(test, afb); - KUNIT_ASSERT_NOT_NULL(test, tiling_info); - KUNIT_ASSERT_NOT_NULL(test, plane_size); - KUNIT_ASSERT_NOT_NULL(test, dcc); - KUNIT_ASSERT_NOT_NULL(test, address); - - adev->family = AMDGPU_FAMILY_SI; - afb->address = 0x12345000ULL; - afb->base.width = 1920; - afb->base.height = 1080; - afb->base.offsets[0] = 0x1000; - afb->base.pitches[0] = 7680; - afb->base.format = drm_format_info(DRM_FORMAT_XRGB8888); - KUNIT_ASSERT_NOT_NULL(test, afb->base.format); - - tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_1D_TILED_THIN1); - tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 5); - - ret = amdgpu_dm_plane_fill_plane_buffer_attributes(adev, afb, - SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, ROTATION_ANGLE_0, - tiling_flags, tiling_info, plane_size, dcc, address, true); - - KUNIT_EXPECT_EQ(test, ret, 0); - KUNIT_EXPECT_EQ(test, plane_size->surface_size.width, 1920); - KUNIT_EXPECT_EQ(test, plane_size->surface_size.height, 1080); - KUNIT_EXPECT_EQ(test, plane_size->surface_pitch, 1920); - KUNIT_EXPECT_EQ(test, address->type, (int)PLN_ADDR_TYPE_GRAPHICS); - KUNIT_EXPECT_TRUE(test, address->tmz_surface); - KUNIT_EXPECT_EQ(test, (int)tiling_info->gfx8.array_mode, (int)DC_ARRAY_1D_TILED_THIN1); - KUNIT_EXPECT_EQ(test, tiling_info->gfx8.pipe_config, 5U); -} - -/** * dm_test_get_cursor_position() - Verify cursor clipping and off-screen handling. * @test: KUnit test context. * @@ -999,86 +937,6 @@ static void dm_test_add_modifier_noop_when_mods_null(struct kunit *test) } /** - * dm_test_fill_gfx8_tiling_info_2d_tiled() - Verify GFX8 2D tiled flag parsing. - * @test: KUnit test context. - * - * Verify if 2D tiled GFX8 flags populate expected tiling fields. - */ -static void dm_test_fill_gfx8_tiling_info_2d_tiled(struct kunit *test) -{ - struct dc_tiling_info tiling_info = {0}; - uint64_t tiling_flags = 0; - - tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_2D_TILED_THIN1); - tiling_flags |= AMDGPU_TILING_SET(BANK_WIDTH, 2); - tiling_flags |= AMDGPU_TILING_SET(BANK_HEIGHT, 1); - tiling_flags |= AMDGPU_TILING_SET(MACRO_TILE_ASPECT, 3); - tiling_flags |= AMDGPU_TILING_SET(TILE_SPLIT, 4); - tiling_flags |= AMDGPU_TILING_SET(NUM_BANKS, 2); - tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 7); - - amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags); - - KUNIT_EXPECT_EQ(test, (int)tiling_info.gfxversion, (int)DcGfxVersion8); - KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.array_mode, (int)DC_ARRAY_2D_TILED_THIN1); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.bank_width, 2U); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.bank_height, 1U); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_aspect, 3U); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_split, 4U); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.num_banks, 2U); - KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.tile_mode, - (int)DC_ADDR_SURF_MICRO_TILING_DISPLAY); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 7U); -} - -/** - * dm_test_fill_gfx8_tiling_info_1d_tiled() - Verify GFX8 1D tiled flag parsing. - * @test: KUnit test context. - * - * Verify if 1D tiled GFX8 flags populate array mode and pipe config. - */ -static void dm_test_fill_gfx8_tiling_info_1d_tiled(struct kunit *test) -{ - struct dc_tiling_info tiling_info = {0}; - uint64_t tiling_flags = 0; - - tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_1D_TILED_THIN1); - tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 5); - - amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags); - - KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.array_mode, (int)DC_ARRAY_1D_TILED_THIN1); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 5U); -} - -/** - * dm_test_fill_gfx8_tiling_info_other_mode() - Verify non-1D/non-2D mode handling. - * @test: KUnit test context. - * - * Verify if unsupported array mode keeps preset fields and updates pipe config. - */ -static void dm_test_fill_gfx8_tiling_info_other_mode(struct kunit *test) -{ - struct dc_tiling_info tiling_info = {0}; - uint64_t tiling_flags = 0; - - tiling_info.gfxversion = 0x7f; - tiling_info.gfx8.array_mode = 0x7f; - tiling_info.gfx8.tile_mode = 0x7f; - tiling_info.gfx8.num_banks = 0x7f; - - tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 6); - - amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags); - - KUNIT_EXPECT_EQ(test, tiling_info.gfxversion, 0x7f); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.array_mode, 0x7f); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_mode, 0x7f); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.num_banks, 0x7f); - KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 6U); -} - -/** * dm_test_fill_gfx9_tiling_info_from_device_pre_10_3() - Verify GFX9 field copy before 10.3. * @test: KUnit test context. * @@ -1405,7 +1263,7 @@ static int dm_test_plane_attrs(struct amdgpu_device *adev, struct dc_plane_address *address) { return amdgpu_dm_plane_fill_plane_buffer_attributes(adev, afb, format, - ROTATION_ANGLE_0, 0, tiling_info, plane_size, dcc, address, + ROTATION_ANGLE_0, tiling_info, plane_size, dcc, address, false); } @@ -3201,7 +3059,6 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = { KUNIT_CASE(dm_test_get_min_max_dc_plane_scaling), KUNIT_CASE(dm_test_get_min_max_dc_plane_scaling_fp16), /* amdgpu_dm_plane_fill_plane_buffer_attributes() */ - KUNIT_CASE(dm_test_fill_plane_buffer_attributes_gfx8), KUNIT_CASE(dm_test_fill_plane_buffer_attributes_video), KUNIT_CASE(dm_test_fill_plane_buffer_attributes_gfx12), /* amdgpu_dm_plane_get_cursor_position() */ @@ -3251,10 +3108,6 @@ static struct kunit_case amdgpu_dm_plane_test_cases[] = { KUNIT_CASE(dm_test_add_modifier_appends_value), KUNIT_CASE(dm_test_add_modifier_grows_capacity), KUNIT_CASE(dm_test_add_modifier_noop_when_mods_null), - /* amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags() */ - KUNIT_CASE(dm_test_fill_gfx8_tiling_info_2d_tiled), - KUNIT_CASE(dm_test_fill_gfx8_tiling_info_1d_tiled), - KUNIT_CASE(dm_test_fill_gfx8_tiling_info_other_mode), /* amdgpu_dm_plane_fill_gfx9_tiling_info_from_device() */ KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_device_pre_10_3), KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_device_10_3_plus), diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c index 8d1d26bfcc16..fbd07af60230 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c @@ -16,6 +16,7 @@ #include "amdgpu_mode.h" #include "amdgpu_dm.h" #include "amdgpu_dm_pp_smu.h" +#include "amdgpu_dm_kunit_test_helpers.h" /* ---- Stub DPM layer ---- */ @@ -2321,6 +2322,23 @@ static void dm_test_nv_get_uclk_dpm_states_fail(struct kunit *test) (int)PP_SMU_RESULT_FAIL); } +/* Tests for amdgpu_dm_smu_write_watermarks_table() */ + +/** + * dm_test_smu_write_watermarks_table_default - Test watermarks table skips non-Navi1x IP versions + * @test: The KUnit test context + */ +static void dm_test_smu_write_watermarks_table_default(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + + /* + * A zeroed adev reports DCE IP version 0, which is not one of the + * Navi1x versions handled by the switch, so the function returns early. + */ + KUNIT_EXPECT_EQ(test, amdgpu_dm_smu_write_watermarks_table(adev), 0); +} + static struct kunit_case dm_pp_smu_test_cases[] = { /* get_default_clock_levels */ KUNIT_CASE(dm_test_default_clock_levels_display), @@ -2440,6 +2458,8 @@ static struct kunit_case dm_pp_smu_test_cases[] = { KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_ok), KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_unsupported), KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_fail), + /* amdgpu_dm_smu_write_watermarks_table */ + KUNIT_CASE(dm_test_smu_write_watermarks_table_default), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c index 09bd98e93047..0a484ea7c0e8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c @@ -46,19 +46,10 @@ static struct core_power *create_test_power_module(struct kunit *test, static struct dc_link *alloc_test_psrsu_link(struct kunit *test) { - struct dc_link *link = dm_kunit_alloc_link(test); - struct dc_context *ctx; - struct dc *dc; - - ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, ctx); - - dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, dc); + struct dc_link *link = dm_kunit_alloc_link_with_ctx(test); + struct dc_context *ctx = link->ctx; + struct dc *dc = ctx->dc; - link->ctx = ctx; - ctx->dc = dc; - dc->ctx = ctx; dc->caps.dmcub_support = true; ctx->dce_version = DCN_VERSION_3_1; link->dpcd_caps.edp_rev = DP_EDP_14; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c index a09f31ee0a2a..150c53a68bed 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c @@ -6,6 +6,7 @@ */ #include <kunit/test.h> +#include <linux/pci.h> #include "dc.h" #include "amdgpu_mode.h" @@ -83,11 +84,75 @@ static void dm_test_quirks_no_dmi_match_both_false(struct kunit *test) KUNIT_EXPECT_FALSE(test, dm->edp0_on_dp1_quirk); } +/* Tests for dm_should_disable_stutter() */ + +/** + * dm_test_should_disable_stutter_match - Test the quirk device matches + * @test: The KUnit test context + */ +static void dm_test_should_disable_stutter_match(struct kunit *test) +{ + struct pci_dev *pdev; + + pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, pdev); + + pdev->vendor = 0x1002; + pdev->device = 0x15dd; + pdev->subsystem_vendor = 0x1002; + pdev->subsystem_device = 0x15dd; + pdev->revision = 0xc8; + + KUNIT_EXPECT_TRUE(test, dm_should_disable_stutter(pdev)); +} + +/** + * dm_test_should_disable_stutter_no_match - Test a non-quirk device does not match + * @test: The KUnit test context + */ +static void dm_test_should_disable_stutter_no_match(struct kunit *test) +{ + struct pci_dev *pdev; + + pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, pdev); + + pdev->vendor = 0x1002; + pdev->device = 0x1234; + + KUNIT_EXPECT_FALSE(test, dm_should_disable_stutter(pdev)); +} + +/** + * dm_test_should_disable_stutter_revision_differs - Test a partial match (revision) fails + * @test: The KUnit test context + */ +static void dm_test_should_disable_stutter_revision_differs(struct kunit *test) +{ + struct pci_dev *pdev; + + pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, pdev); + + /* Everything matches the quirk except the revision */ + pdev->vendor = 0x1002; + pdev->device = 0x15dd; + pdev->subsystem_vendor = 0x1002; + pdev->subsystem_device = 0x15dd; + pdev->revision = 0x00; + + KUNIT_EXPECT_FALSE(test, dm_should_disable_stutter(pdev)); +} + static struct kunit_case amdgpu_dm_quirks_tests[] = { /* retrieve_dmi_info */ KUNIT_CASE(dm_test_quirks_aux_hpd_discon_reset), KUNIT_CASE(dm_test_quirks_edp0_on_dp1_reset), KUNIT_CASE(dm_test_quirks_no_dmi_match_both_false), + /* dm_should_disable_stutter */ + KUNIT_CASE(dm_test_should_disable_stutter_match), + KUNIT_CASE(dm_test_should_disable_stutter_no_match), + KUNIT_CASE(dm_test_should_disable_stutter_revision_differs), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c index 0b29bf0a7d04..3c10eec9b1e0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c @@ -6,10 +6,245 @@ */ #include <kunit/test.h> +#include <linux/pci.h> +#include <drm/drm_atomic.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_modes.h> +#include <drm/drm_writeback.h> #include "dc.h" +#include "inc/core_types.h" +#include "amd_shared.h" +#include "amdgpu.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" +#include "amdgpu_dm_kunit_test_helpers.h" + +/* Tests for simple DM callbacks */ + +/** + * dm_test_is_idle - Test placeholder idle callback returns true + * @test: The KUnit test context + */ +static void dm_test_is_idle(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, dm_is_idle(NULL)); +} + +/** + * dm_test_wait_for_idle - Test placeholder wait-for-idle callback returns success + * @test: The KUnit test context + */ +static void dm_test_wait_for_idle(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_wait_for_idle(NULL), 0); +} + +/** + * dm_test_soft_reset - Test placeholder soft-reset callback returns success + * @test: The KUnit test context + */ +static void dm_test_soft_reset(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_soft_reset(NULL), 0); +} + +/** + * dm_test_set_clockgating_state - Test placeholder clockgating callback returns success + * @test: The KUnit test context + */ +static void dm_test_set_clockgating_state(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_set_clockgating_state(NULL, AMD_CG_STATE_GATE), 0); +} + +/** + * dm_test_set_powergating_state - Test placeholder powergating callback returns success + * @test: The KUnit test context + */ +static void dm_test_set_powergating_state(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_set_powergating_state(NULL, AMD_PG_STATE_GATE), 0); +} + +/** + * dm_test_bandwidth_update - Test placeholder bandwidth update is callable + * @test: The KUnit test context + */ +static void dm_test_bandwidth_update(struct kunit *test) +{ + dm_bandwidth_update(NULL); +} + +/** + * dm_test_crtc_complete_writeback_no_connector - Test no writeback connector returns false + * @test: The KUnit test context + */ +static void dm_test_crtc_complete_writeback_no_connector(struct kunit *test) +{ + struct amdgpu_crtc *acrtc; + + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, acrtc); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_complete_writeback(acrtc)); +} + +/** + * dm_test_crtc_complete_writeback_not_pending - Test non-pending writeback returns false + * @test: The KUnit test context + */ +static void dm_test_crtc_complete_writeback_not_pending(struct kunit *test) +{ + struct amdgpu_crtc *acrtc; + struct drm_writeback_connector *wb_conn; + + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, acrtc); + wb_conn = kunit_kzalloc(test, sizeof(*wb_conn), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, wb_conn); + + spin_lock_init(&wb_conn->job_lock); + acrtc->wb_conn = wb_conn; + acrtc->wb_pending = false; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_complete_writeback(acrtc)); +} + +/** + * dm_test_vblank_get_counter_out_of_range - Test out-of-range CRTC returns zero + * @test: The KUnit test context + */ +static void dm_test_vblank_get_counter_out_of_range(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + + adev->mode_info.num_crtc = 1; + + KUNIT_EXPECT_EQ(test, dm_vblank_get_counter(adev, 1), 0U); +} + +/** + * dm_test_vblank_get_counter_no_stream - Test missing stream returns zero + * @test: The KUnit test context + */ +static void dm_test_vblank_get_counter_no_stream(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc; + + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, acrtc); + + adev->mode_info.num_crtc = 1; + adev->mode_info.crtcs[0] = acrtc; + + KUNIT_EXPECT_EQ(test, dm_vblank_get_counter(adev, 0), 0U); +} + +/** + * dm_test_crtc_get_scanoutpos_invalid_crtc - Test invalid CRTC returns -EINVAL + * @test: The KUnit test context + */ +static void dm_test_crtc_get_scanoutpos_invalid_crtc(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + u32 vbl = 0; + u32 position = 0; + + adev->mode_info.num_crtc = 1; + + KUNIT_EXPECT_EQ(test, dm_crtc_get_scanoutpos(adev, -1, &vbl, &position), + -EINVAL); + KUNIT_EXPECT_EQ(test, dm_crtc_get_scanoutpos(adev, 1, &vbl, &position), + -EINVAL); +} + +/** + * dm_test_crtc_get_scanoutpos_no_stream - Test missing stream returns zero + * @test: The KUnit test context + */ +static void dm_test_crtc_get_scanoutpos_no_stream(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct amdgpu_crtc *acrtc; + u32 vbl = 0; + u32 position = 0; + + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, acrtc); + + adev->mode_info.num_crtc = 1; + adev->mode_info.crtcs[0] = acrtc; + + KUNIT_EXPECT_EQ(test, dm_crtc_get_scanoutpos(adev, 0, &vbl, &position), 0); + KUNIT_EXPECT_EQ(test, vbl, 0U); + KUNIT_EXPECT_EQ(test, position, 0U); +} + +/** + * dm_test_atomic_get_new_state_empty - Test empty atomic state has no DM state + * @test: The KUnit test context + */ +static void dm_test_atomic_get_new_state_empty(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct drm_atomic_commit *state; + + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state); + state->dev = &adev->ddev; + + KUNIT_EXPECT_NULL(test, dm_atomic_get_new_state(state)); +} + +/** + * dm_test_atomic_get_new_state_match - Test atomic state returns matching DM private state + * @test: The KUnit test context + */ +static void dm_test_atomic_get_new_state_match(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dm_atomic_state *dm_state; + struct drm_atomic_commit *state; + + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state); + + dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + + state->private_objs = kunit_kzalloc(test, sizeof(*state->private_objs), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state->private_objs); + + state->dev = &adev->ddev; + state->num_private_objs = 1; + state->private_objs[0].ptr = &adev->dm.atomic_obj; + state->private_objs[0].new_state = &dm_state->base; + + KUNIT_EXPECT_PTR_EQ(test, dm_atomic_get_new_state(state), dm_state); +} + +/** + * dm_test_atomic_destroy_state_no_context - Test destroying DM atomic state without a DC context + * @test: The KUnit test context + */ +static void dm_test_atomic_destroy_state_no_context(struct kunit *test) +{ + struct dm_atomic_state *dm_state; + + /* + * Use kzalloc(), not kunit_kzalloc(): dm_atomic_destroy_state() frees + * the state itself, so KUnit-managed memory would be double-freed. + */ + dm_state = kzalloc(sizeof(*dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_state); + + /* context == NULL: dc_state_release() is skipped and the state is freed. */ + dm_atomic_destroy_state(NULL, &dm_state->base); +} /* Tests for dm_plane_layer_index_cmp() */ @@ -294,156 +529,6 @@ static void dm_test_modereset_not_required_when_inactive_without_modeset(struct KUNIT_EXPECT_FALSE(test, modereset_required(&crtc_state)); } -/* Tests for dm_get_oriented_plane_size() */ - -/** - * dm_test_oriented_plane_size_rotate_0 - Test Oriented plane size rotate 0 - * @test: The KUnit test context - */ -static void dm_test_oriented_plane_size_rotate_0(struct kunit *test) -{ - struct drm_plane_state plane_state = { 0 }; - int src_w = 0; - int src_h = 0; - - plane_state.rotation = DRM_MODE_ROTATE_0; - plane_state.src_w = 1920 << 16; - plane_state.src_h = 1080 << 16; - - dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); - - KUNIT_EXPECT_EQ(test, src_w, 1920); - KUNIT_EXPECT_EQ(test, src_h, 1080); -} - -/** - * dm_test_oriented_plane_size_rotate_90 - Test Oriented plane size rotate 90 - * @test: The KUnit test context - */ -static void dm_test_oriented_plane_size_rotate_90(struct kunit *test) -{ - struct drm_plane_state plane_state = { 0 }; - int src_w = 0; - int src_h = 0; - - plane_state.rotation = DRM_MODE_ROTATE_90; - plane_state.src_w = 1920 << 16; - plane_state.src_h = 1080 << 16; - - dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); - - KUNIT_EXPECT_EQ(test, src_w, 1080); - KUNIT_EXPECT_EQ(test, src_h, 1920); -} - -/** - * dm_test_oriented_plane_size_rotate_180 - Test Oriented plane size rotate 180 - * @test: The KUnit test context - */ -static void dm_test_oriented_plane_size_rotate_180(struct kunit *test) -{ - struct drm_plane_state plane_state = { 0 }; - int src_w = 0; - int src_h = 0; - - plane_state.rotation = DRM_MODE_ROTATE_180; - plane_state.src_w = 1920 << 16; - plane_state.src_h = 1080 << 16; - - dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); - - KUNIT_EXPECT_EQ(test, src_w, 1920); - KUNIT_EXPECT_EQ(test, src_h, 1080); -} - -/** - * dm_test_oriented_plane_size_rotate_270 - Test Oriented plane size rotate 270 - * @test: The KUnit test context - */ -static void dm_test_oriented_plane_size_rotate_270(struct kunit *test) -{ - struct drm_plane_state plane_state = { 0 }; - int src_w = 0; - int src_h = 0; - - plane_state.rotation = DRM_MODE_ROTATE_270; - plane_state.src_w = 1920 << 16; - plane_state.src_h = 1080 << 16; - - dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); - - KUNIT_EXPECT_EQ(test, src_w, 1080); - KUNIT_EXPECT_EQ(test, src_h, 1920); -} - -/* Tests for dm_get_plane_scale() */ - -/** - * dm_test_get_plane_scale_identity - Test Get plane scale identity - * @test: The KUnit test context - */ -static void dm_test_get_plane_scale_identity(struct kunit *test) -{ - struct drm_plane_state plane_state = { 0 }; - int scale_w = 0; - int scale_h = 0; - - plane_state.rotation = DRM_MODE_ROTATE_0; - plane_state.src_w = 1920 << 16; - plane_state.src_h = 1080 << 16; - plane_state.crtc_w = 1920; - plane_state.crtc_h = 1080; - - dm_get_plane_scale(&plane_state, &scale_w, &scale_h); - - KUNIT_EXPECT_EQ(test, scale_w, 1000); - KUNIT_EXPECT_EQ(test, scale_h, 1000); -} - -/** - * dm_test_get_plane_scale_rotate_90_identity - Test Get plane scale rotate 90 identity - * @test: The KUnit test context - */ -static void dm_test_get_plane_scale_rotate_90_identity(struct kunit *test) -{ - struct drm_plane_state plane_state = { 0 }; - int scale_w = 0; - int scale_h = 0; - - plane_state.rotation = DRM_MODE_ROTATE_90; - plane_state.src_w = 1920 << 16; - plane_state.src_h = 1080 << 16; - plane_state.crtc_w = 1080; - plane_state.crtc_h = 1920; - - dm_get_plane_scale(&plane_state, &scale_w, &scale_h); - - KUNIT_EXPECT_EQ(test, scale_w, 1000); - KUNIT_EXPECT_EQ(test, scale_h, 1000); -} - -/** - * dm_test_get_plane_scale_zero_src_width - Test Get plane scale zero src width - * @test: The KUnit test context - */ -static void dm_test_get_plane_scale_zero_src_width(struct kunit *test) -{ - struct drm_plane_state plane_state = { 0 }; - int scale_w = 0; - int scale_h = 0; - - plane_state.rotation = DRM_MODE_ROTATE_0; - plane_state.src_w = 0; - plane_state.src_h = 1080 << 16; - plane_state.crtc_w = 100; - plane_state.crtc_h = 200; - - dm_get_plane_scale(&plane_state, &scale_w, &scale_h); - - KUNIT_EXPECT_EQ(test, scale_w, 0); - KUNIT_EXPECT_EQ(test, scale_h, 185); -} - /* Tests for is_scaling_state_different() */ /** @@ -511,6 +596,28 @@ static void dm_test_scaling_state_underscan_enabled(struct kunit *test) } /** + * dm_test_scaling_state_underscan_disabled - Test disabling underscan with borders differs + * @test: The KUnit test context + */ +static void dm_test_scaling_state_underscan_disabled(struct kunit *test) +{ + struct dm_connector_state *old_state; + struct dm_connector_state *new_state; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, old_state); + KUNIT_ASSERT_NOT_NULL(test, new_state); + + old_state->underscan_enable = true; + old_state->underscan_hborder = 16; + old_state->underscan_vborder = 16; + new_state->underscan_enable = false; + + KUNIT_EXPECT_TRUE(test, is_scaling_state_different(new_state, old_state)); +} + +/** * dm_test_scaling_state_underscan_border_changed - Test changed underscan borders differ * @test: The KUnit test context */ @@ -533,357 +640,533 @@ static void dm_test_scaling_state_underscan_border_changed(struct kunit *test) KUNIT_EXPECT_TRUE(test, is_scaling_state_different(a, b)); } -/* Tests for is_timing_unchanged_for_freesync() */ +/* Tests for set_multisync_trigger_params() */ /** - * dm_test_timing_unchanged_null_args - Test NULL crtc states return false + * dm_test_multisync_trigger_disabled - Test disabled reset leaves params untouched * @test: The KUnit test context */ -static void dm_test_timing_unchanged_null_args(struct kunit *test) +static void dm_test_multisync_trigger_disabled(struct kunit *test) { - struct drm_crtc_state crtc_state = { 0 }; + struct dc_stream_state *stream; - KUNIT_EXPECT_FALSE(test, - is_timing_unchanged_for_freesync(NULL, &crtc_state)); - KUNIT_EXPECT_FALSE(test, - is_timing_unchanged_for_freesync(&crtc_state, NULL)); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + stream->triggered_crtc_reset.enabled = false; + stream->triggered_crtc_reset.event = CRTC_EVENT_VSYNC_FALLING; + stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_LINE; + + set_multisync_trigger_params(stream); + + /* Nothing should change when the reset trigger is disabled */ + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, + (int)CRTC_EVENT_VSYNC_FALLING); + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, + (int)TRIGGER_DELAY_NEXT_LINE); } /** - * dm_test_timing_unchanged_identical_modes - Test identical modes are not "unchanged" + * dm_test_multisync_trigger_rising - Test positive vsync polarity selects rising edge * @test: The KUnit test context - * - * The helper only returns true when vtotal/vsync shift (vrr) while the rest - * of the timing stays fixed, so identical modes must return false. */ -static void dm_test_timing_unchanged_identical_modes(struct kunit *test) +static void dm_test_multisync_trigger_rising(struct kunit *test) { - struct drm_crtc_state old_state = { 0 }; - struct drm_crtc_state new_state = { 0 }; + struct dc_stream_state *stream; + struct dc_stream_state *master; + + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master); - old_state.mode.clock = 148500; - old_state.mode.hdisplay = 1920; - old_state.mode.vdisplay = 1080; - old_state.mode.htotal = 2200; - old_state.mode.vtotal = 1125; - new_state.mode = old_state.mode; + master->timing.flags.VSYNC_POSITIVE_POLARITY = 1; + stream->triggered_crtc_reset.enabled = true; + stream->triggered_crtc_reset.event_source = master; + + set_multisync_trigger_params(stream); - KUNIT_EXPECT_FALSE(test, - is_timing_unchanged_for_freesync(&old_state, &new_state)); + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, + (int)CRTC_EVENT_VSYNC_RISING); + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, + (int)TRIGGER_DELAY_NEXT_PIXEL); } /** - * dm_test_timing_unchanged_vrr_shift - Test vrr-style vtotal/vsync shift is detected + * dm_test_multisync_trigger_falling - Test negative vsync polarity selects falling edge * @test: The KUnit test context */ -static void dm_test_timing_unchanged_vrr_shift(struct kunit *test) +static void dm_test_multisync_trigger_falling(struct kunit *test) { - struct drm_crtc_state old_state = { 0 }; - struct drm_crtc_state new_state = { 0 }; + struct dc_stream_state *stream; + struct dc_stream_state *master; - old_state.mode.clock = 148500; - old_state.mode.hdisplay = 1920; - old_state.mode.vdisplay = 1080; - old_state.mode.htotal = 2200; - old_state.mode.vtotal = 1125; - old_state.mode.hsync_start = 2008; - old_state.mode.vsync_start = 1084; - old_state.mode.hsync_end = 2052; - old_state.mode.vsync_end = 1089; + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master); + + master->timing.flags.VSYNC_POSITIVE_POLARITY = 0; + stream->triggered_crtc_reset.enabled = true; + stream->triggered_crtc_reset.event_source = master; - /* Same horizontal timing, vertical totals/sync shifted by 125 lines */ - new_state.mode = old_state.mode; - new_state.mode.vtotal = 1250; - new_state.mode.vsync_start = 1209; - new_state.mode.vsync_end = 1214; + set_multisync_trigger_params(stream); - KUNIT_EXPECT_TRUE(test, - is_timing_unchanged_for_freesync(&old_state, &new_state)); + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, + (int)CRTC_EVENT_VSYNC_FALLING); + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, + (int)TRIGGER_DELAY_NEXT_PIXEL); } +/* Tests for set_master_stream() */ + /** - * dm_test_timing_unchanged_clock_changed - Test pixel clock change returns false + * dm_test_master_stream_highest_refresh - Test highest refresh-rate stream becomes master * @test: The KUnit test context */ -static void dm_test_timing_unchanged_clock_changed(struct kunit *test) +static void dm_test_master_stream_highest_refresh(struct kunit *test) { - struct drm_crtc_state old_state = { 0 }; - struct drm_crtc_state new_state = { 0 }; + struct dc_stream_state *stream0, *stream1; + struct dc_stream_state *stream_set[2]; - old_state.mode.clock = 148500; - old_state.mode.htotal = 2200; - old_state.mode.vtotal = 1125; - old_state.mode.vsync_start = 1084; - old_state.mode.vsync_end = 1089; + stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0); + stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1); + stream_set[0] = stream0; + stream_set[1] = stream1; - new_state.mode = old_state.mode; - new_state.mode.clock = 297000; - new_state.mode.vtotal = 1250; - new_state.mode.vsync_start = 1209; - new_state.mode.vsync_end = 1214; + /* stream0: 60Hz, stream1: 120Hz -> stream1 is master */ + stream0->triggered_crtc_reset.enabled = true; + stream0->timing.pix_clk_100hz = 1485000; + stream0->timing.h_total = 2200; + stream0->timing.v_total = 1125; - KUNIT_EXPECT_FALSE(test, - is_timing_unchanged_for_freesync(&old_state, &new_state)); -} + stream1->triggered_crtc_reset.enabled = true; + stream1->timing.pix_clk_100hz = 2970000; + stream1->timing.h_total = 2200; + stream1->timing.v_total = 1125; -/* Tests for set_freesync_fixed_config() */ + set_master_stream(stream_set, 2); + + KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source, + stream1); + KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source, + stream1); +} /** - * dm_test_set_freesync_fixed_config_60hz - Test fixed refresh computed for 1080p60 + * dm_test_master_stream_defaults_to_first - Test default master when none triggered * @test: The KUnit test context + * + * When no stream has the reset trigger enabled, master_stream stays 0 and all + * streams point at the first stream as their event source. */ -static void dm_test_set_freesync_fixed_config_60hz(struct kunit *test) +static void dm_test_master_stream_defaults_to_first(struct kunit *test) { - struct dm_crtc_state dm_crtc_state = { 0 }; + struct dc_stream_state *stream0, *stream1; + struct dc_stream_state *stream_set[2]; + + stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0); + stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1); + stream_set[0] = stream0; + stream_set[1] = stream1; - dm_crtc_state.base.mode.clock = 148500; - dm_crtc_state.base.mode.htotal = 2200; - dm_crtc_state.base.mode.vtotal = 1125; + set_master_stream(stream_set, 2); - set_freesync_fixed_config(&dm_crtc_state); + KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source, + stream0); + KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source, + stream0); +} + +/* Tests for is_content_protection_different() */ - KUNIT_EXPECT_EQ(test, (int)dm_crtc_state.freesync_config.state, - (int)VRR_STATE_ACTIVE_FIXED); - /* 148500 kHz / (2200 * 1125) = 60 Hz = 60000000 uHz */ - KUNIT_EXPECT_EQ(test, dm_crtc_state.freesync_config.fixed_refresh_in_uhz, - 60000000U); +struct dm_test_cp_ctx { + struct amdgpu_dm_connector *aconnector; + struct dm_connector_state *new_dm; /* also connector->state */ + struct dm_connector_state *old_dm; + struct drm_crtc_state *new_crtc; + struct drm_crtc_state *old_crtc; +}; + +static struct dm_test_cp_ctx *dm_test_cp_ctx_alloc(struct kunit *test) +{ + struct dm_test_cp_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + ctx->new_dm = kunit_kzalloc(test, sizeof(*ctx->new_dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->new_dm); + ctx->old_dm = kunit_kzalloc(test, sizeof(*ctx->old_dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->old_dm); + ctx->new_crtc = kunit_kzalloc(test, sizeof(*ctx->new_crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->new_crtc); + ctx->old_crtc = kunit_kzalloc(test, sizeof(*ctx->old_crtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->old_crtc); + + /* connector->state must be the new dm connector state */ + ctx->aconnector->base.state = &ctx->new_dm->base; + ctx->aconnector->base.dpms = DRM_MODE_DPMS_ON; + + return ctx; } -/* Tests for is_dc_timing_adjust_needed() */ +static bool dm_test_cp_diff(struct dm_test_cp_ctx *ctx) +{ + return is_content_protection_different(ctx->new_crtc, ctx->old_crtc, + &ctx->new_dm->base, + &ctx->old_dm->base, + &ctx->aconnector->base, NULL); +} /** - * dm_test_dc_timing_adjust_pending - Test a pending hw timing adjust forces true + * dm_test_cp_diff_hdcp_type_change - Test an HDCP content-type change forces true * @test: The KUnit test context */ -static void dm_test_dc_timing_adjust_pending(struct kunit *test) +static void dm_test_cp_diff_hdcp_type_change(struct kunit *test) { - struct dm_crtc_state *old_state, *new_state; - struct dc_stream_state *stream; + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); - old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); - new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); - stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + ctx->old_dm->base.hdcp_content_type = 0; + ctx->new_dm->base.hdcp_content_type = 1; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; - new_state->stream = stream; - stream->adjust.timing_adjust_pending = 1; + KUNIT_EXPECT_TRUE(test, dm_test_cp_diff(ctx)); + KUNIT_EXPECT_EQ(test, (int)ctx->new_dm->base.content_protection, + (int)DRM_MODE_CONTENT_PROTECTION_DESIRED); +} - KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state)); +/** + * dm_test_cp_diff_reenable_mode_changed - Test ENABLED->DESIRED with modeset forces true + * @test: The KUnit test context + */ +static void dm_test_cp_diff_reenable_mode_changed(struct kunit *test) +{ + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); + + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_crtc->mode_changed = true; + + KUNIT_EXPECT_TRUE(test, dm_test_cp_diff(ctx)); + KUNIT_EXPECT_EQ(test, (int)ctx->new_dm->base.content_protection, + (int)DRM_MODE_CONTENT_PROTECTION_DESIRED); } /** - * dm_test_dc_timing_adjust_active_fixed - Test VRR active-fixed forces true + * dm_test_cp_diff_reenable_no_change - Test ENABLED->DESIRED without modeset restores ENABLED * @test: The KUnit test context */ -static void dm_test_dc_timing_adjust_active_fixed(struct kunit *test) +static void dm_test_cp_diff_reenable_no_change(struct kunit *test) { - struct dm_crtc_state *old_state, *new_state; - struct dc_stream_state *stream; + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); - old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); - new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); - stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_crtc->mode_changed = false; - new_state->stream = stream; - new_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; + KUNIT_EXPECT_FALSE(test, dm_test_cp_diff(ctx)); + KUNIT_EXPECT_EQ(test, (int)ctx->new_dm->base.content_protection, + (int)DRM_MODE_CONTENT_PROTECTION_ENABLED); +} - KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state)); +/** + * dm_test_cp_diff_undesired - Test UNDESIRED->UNDESIRED needs no update + * @test: The KUnit test context + */ +static void dm_test_cp_diff_undesired(struct kunit *test) +{ + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); + + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + + KUNIT_EXPECT_FALSE(test, dm_test_cp_diff(ctx)); } /** - * dm_test_dc_timing_adjust_vrr_toggle - Test a change in vrr active state forces true + * dm_test_cp_diff_desired_mode_changed - Test DESIRED->DESIRED with modeset forces true * @test: The KUnit test context */ -static void dm_test_dc_timing_adjust_vrr_toggle(struct kunit *test) +static void dm_test_cp_diff_desired_mode_changed(struct kunit *test) { - struct dm_crtc_state *old_state, *new_state; - struct dc_stream_state *stream; + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); - old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); - new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); - stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_crtc->mode_changed = true; + + KUNIT_EXPECT_TRUE(test, dm_test_cp_diff(ctx)); +} + +/** + * dm_test_cp_diff_desired_no_change - Test steady DESIRED->DESIRED needs no update + * @test: The KUnit test context + */ +static void dm_test_cp_diff_desired_no_change(struct kunit *test) +{ + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); - new_state->stream = stream; - old_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE; - new_state->freesync_config.state = VRR_STATE_INACTIVE; + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_crtc->mode_changed = false; - KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state)); + KUNIT_EXPECT_FALSE(test, dm_test_cp_diff(ctx)); } /** - * dm_test_dc_timing_adjust_not_needed - Test steady-state timing needs no adjust + * dm_test_cp_diff_update_hdcp_hotplug - Test the update_hdcp hot-plug path forces true * @test: The KUnit test context */ -static void dm_test_dc_timing_adjust_not_needed(struct kunit *test) +static void dm_test_cp_diff_update_hdcp_hotplug(struct kunit *test) { - struct dm_crtc_state *old_state, *new_state; - struct dc_stream_state *stream; + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); + struct dc_sink *sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL); - old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); - new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); - stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + KUNIT_ASSERT_NOT_NULL(test, sink); - new_state->stream = stream; - old_state->freesync_config.state = VRR_STATE_INACTIVE; - new_state->freesync_config.state = VRR_STATE_INACTIVE; + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_dm->update_hdcp = true; + ctx->aconnector->base.dpms = DRM_MODE_DPMS_ON; + ctx->aconnector->dc_sink = sink; - KUNIT_EXPECT_FALSE(test, is_dc_timing_adjust_needed(old_state, new_state)); + KUNIT_EXPECT_TRUE(test, dm_test_cp_diff(ctx)); + KUNIT_EXPECT_FALSE(test, ctx->new_dm->update_hdcp); } -/* Tests for set_multisync_trigger_params() */ - /** - * dm_test_multisync_trigger_disabled - Test disabled reset leaves params untouched + * dm_test_cp_diff_stream_reenabled - Test the stream removed-and-re-enabled path forces true * @test: The KUnit test context */ -static void dm_test_multisync_trigger_disabled(struct kunit *test) +static void dm_test_cp_diff_stream_reenabled(struct kunit *test) { - struct dc_stream_state *stream; + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); + struct drm_crtc *crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); - stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + KUNIT_ASSERT_NOT_NULL(test, crtc); + crtc->enabled = true; - stream->triggered_crtc_reset.enabled = false; - stream->triggered_crtc_reset.event = CRTC_EVENT_VSYNC_FALLING; - stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_LINE; + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_dm->update_hdcp = true; + ctx->old_dm->base.crtc = NULL; + ctx->new_dm->base.crtc = crtc; - set_multisync_trigger_params(stream); + KUNIT_EXPECT_TRUE(test, dm_test_cp_diff(ctx)); + KUNIT_EXPECT_FALSE(test, ctx->new_dm->update_hdcp); +} - /* Nothing should change when the reset trigger is disabled */ - KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, - (int)CRTC_EVENT_VSYNC_FALLING); - KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, - (int)TRIGGER_DELAY_NEXT_LINE); +/** + * dm_test_cp_diff_s3_undesired_to_enabled - Test the S3 UNDESIRED->ENABLED path forces true + * @test: The KUnit test context + */ +static void dm_test_cp_diff_s3_undesired_to_enabled(struct kunit *test) +{ + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); + + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; + + KUNIT_EXPECT_TRUE(test, dm_test_cp_diff(ctx)); + KUNIT_EXPECT_EQ(test, (int)ctx->new_dm->base.content_protection, + (int)DRM_MODE_CONTENT_PROTECTION_DESIRED); } /** - * dm_test_multisync_trigger_rising - Test positive vsync polarity selects rising edge + * dm_test_cp_diff_desired_to_enabled - Test DESIRED->ENABLED needs no update * @test: The KUnit test context */ -static void dm_test_multisync_trigger_rising(struct kunit *test) +static void dm_test_cp_diff_desired_to_enabled(struct kunit *test) { - struct dc_stream_state *stream; - struct dc_stream_state *master; + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); - stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); - master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master); + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; - master->timing.flags.VSYNC_POSITIVE_POLARITY = 1; - stream->triggered_crtc_reset.enabled = true; - stream->triggered_crtc_reset.event_source = master; + KUNIT_EXPECT_FALSE(test, dm_test_cp_diff(ctx)); +} - set_multisync_trigger_params(stream); +/** + * dm_test_cp_diff_desired_to_undesired - Test DESIRED->UNDESIRED forces update + * @test: The KUnit test context + */ +static void dm_test_cp_diff_desired_to_undesired(struct kunit *test) +{ + struct dm_test_cp_ctx *ctx = dm_test_cp_ctx_alloc(test); - KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, - (int)CRTC_EVENT_VSYNC_RISING); - KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, - (int)TRIGGER_DELAY_NEXT_PIXEL); + ctx->old_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + ctx->new_dm->base.content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + + KUNIT_EXPECT_TRUE(test, dm_test_cp_diff(ctx)); } +/* Tests for dm_enable_per_frame_crtc_master_sync() */ + /** - * dm_test_multisync_trigger_falling - Test negative vsync polarity selects falling edge + * dm_test_per_frame_master_sync_single_stream - Test fewer than two streams is a no-op * @test: The KUnit test context */ -static void dm_test_multisync_trigger_falling(struct kunit *test) +static void dm_test_per_frame_master_sync_single_stream(struct kunit *test) { + struct dc_state *context; struct dc_stream_state *stream; - struct dc_stream_state *master; + context = kunit_kzalloc(test, sizeof(*context), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, context); stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); - master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master); + KUNIT_ASSERT_NOT_NULL(test, stream); - master->timing.flags.VSYNC_POSITIVE_POLARITY = 0; stream->triggered_crtc_reset.enabled = true; - stream->triggered_crtc_reset.event_source = master; + context->streams[0] = stream; + context->stream_count = 1; - set_multisync_trigger_params(stream); + dm_enable_per_frame_crtc_master_sync(context); - KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, - (int)CRTC_EVENT_VSYNC_FALLING); - KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, - (int)TRIGGER_DELAY_NEXT_PIXEL); + /* < 2 streams: early return, event_source stays NULL */ + KUNIT_EXPECT_NULL(test, stream->triggered_crtc_reset.event_source); } -/* Tests for set_master_stream() */ - /** - * dm_test_master_stream_highest_refresh - Test highest refresh-rate stream becomes master + * dm_test_per_frame_master_sync_two_streams - Test the master is picked and applied * @test: The KUnit test context */ -static void dm_test_master_stream_highest_refresh(struct kunit *test) +static void dm_test_per_frame_master_sync_two_streams(struct kunit *test) { + struct dc_state *context; struct dc_stream_state *stream0, *stream1; - struct dc_stream_state *stream_set[2]; + context = kunit_kzalloc(test, sizeof(*context), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, context); stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0); + KUNIT_ASSERT_NOT_NULL(test, stream0); stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1); - stream_set[0] = stream0; - stream_set[1] = stream1; + KUNIT_ASSERT_NOT_NULL(test, stream1); - /* stream0: 60Hz, stream1: 120Hz -> stream1 is master */ + /* stream0 60Hz, stream1 120Hz, both trigger-reset enabled */ stream0->triggered_crtc_reset.enabled = true; stream0->timing.pix_clk_100hz = 1485000; stream0->timing.h_total = 2200; stream0->timing.v_total = 1125; - stream1->triggered_crtc_reset.enabled = true; stream1->timing.pix_clk_100hz = 2970000; stream1->timing.h_total = 2200; stream1->timing.v_total = 1125; + stream1->timing.flags.VSYNC_POSITIVE_POLARITY = 1; - set_master_stream(stream_set, 2); + context->streams[0] = stream0; + context->streams[1] = stream1; + context->stream_count = 2; + + dm_enable_per_frame_crtc_master_sync(context); + /* set_master_stream picks the highest refresh (stream1) as event source */ KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source, stream1); KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source, stream1); + /* set_multisync_trigger_params applied to enabled streams */ + KUNIT_EXPECT_EQ(test, (int)stream0->triggered_crtc_reset.event, + (int)CRTC_EVENT_VSYNC_RISING); + KUNIT_EXPECT_EQ(test, (int)stream0->triggered_crtc_reset.delay, + (int)TRIGGER_DELAY_NEXT_PIXEL); } /** - * dm_test_master_stream_defaults_to_first - Test default master when none triggered + * dm_test_per_frame_master_sync_skips_null_stream - Test NULL stream entries are skipped * @test: The KUnit test context - * - * When no stream has the reset trigger enabled, master_stream stays 0 and all - * streams point at the first stream as their event source. */ -static void dm_test_master_stream_defaults_to_first(struct kunit *test) +static void dm_test_per_frame_master_sync_skips_null_stream(struct kunit *test) { - struct dc_stream_state *stream0, *stream1; - struct dc_stream_state *stream_set[2]; + struct dc_state *context; + struct dc_stream_state *stream; - stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0); - stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1); - stream_set[0] = stream0; - stream_set[1] = stream1; + context = kunit_kzalloc(test, sizeof(*context), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, context); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, stream); - set_master_stream(stream_set, 2); + stream->triggered_crtc_reset.enabled = true; + stream->timing.pix_clk_100hz = 1485000; + stream->timing.h_total = 2200; + stream->timing.v_total = 1125; + context->streams[0] = stream; + context->streams[1] = NULL; + context->stream_count = 2; - KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source, - stream0); - KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source, - stream0); + dm_enable_per_frame_crtc_master_sync(context); + + KUNIT_EXPECT_PTR_EQ(test, stream->triggered_crtc_reset.event_source, + stream); +} + +/* Tests for amdgpu_dm_apply_delay_after_dpcd_poweroff() */ + +/** + * dm_test_apply_delay_null_sink - Test a NULL sink returns without delay + * @test: The KUnit test context + */ +static void dm_test_apply_delay_null_sink(struct kunit *test) +{ + /* NULL sink: early return, no delay, no dereference */ + amdgpu_dm_apply_delay_after_dpcd_poweroff(NULL, NULL); +} + +/** + * dm_test_apply_delay_zero_wait - Test a zero wait interval skips the delay + * @test: The KUnit test context + */ +static void dm_test_apply_delay_zero_wait(struct kunit *test) +{ + struct dc_sink *sink; + + sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, sink); + + /* wait == 0: no msleep, adev is unused so NULL is safe */ + sink->edid_caps.panel_patch.wait_after_dpcd_poweroff_ms = 0; + amdgpu_dm_apply_delay_after_dpcd_poweroff(NULL, sink); +} + +/** + * dm_test_apply_delay_nonzero_wait - Test a non-zero wait interval executes delay path + * @test: The KUnit test context + */ +static void dm_test_apply_delay_nonzero_wait(struct kunit *test) +{ + struct amdgpu_device *adev = dm_kunit_alloc_adev(test); + struct dc_sink *sink; + + sink = kunit_kzalloc(test, sizeof(*sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, sink); + + sink->edid_caps.panel_patch.wait_after_dpcd_poweroff_ms = 1; + amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, sink); } static struct kunit_case amdgpu_dm_tests[] = { + /* Simple DM callbacks */ + KUNIT_CASE(dm_test_is_idle), + KUNIT_CASE(dm_test_wait_for_idle), + KUNIT_CASE(dm_test_soft_reset), + KUNIT_CASE(dm_test_set_clockgating_state), + KUNIT_CASE(dm_test_set_powergating_state), + KUNIT_CASE(dm_test_bandwidth_update), + KUNIT_CASE(dm_test_crtc_complete_writeback_no_connector), + KUNIT_CASE(dm_test_crtc_complete_writeback_not_pending), + KUNIT_CASE(dm_test_vblank_get_counter_out_of_range), + KUNIT_CASE(dm_test_vblank_get_counter_no_stream), + KUNIT_CASE(dm_test_crtc_get_scanoutpos_invalid_crtc), + KUNIT_CASE(dm_test_crtc_get_scanoutpos_no_stream), + KUNIT_CASE(dm_test_atomic_get_new_state_empty), + KUNIT_CASE(dm_test_atomic_get_new_state_match), + KUNIT_CASE(dm_test_atomic_destroy_state_no_context), /* dm_plane_layer_index_cmp */ KUNIT_CASE(dm_test_plane_layer_index_cmp_equal), KUNIT_CASE(dm_test_plane_layer_index_cmp_descending), @@ -901,32 +1184,12 @@ static struct kunit_case amdgpu_dm_tests[] = { KUNIT_CASE(dm_test_modereset_required_when_inactive_and_modeset), KUNIT_CASE(dm_test_modereset_not_required_when_active_and_modeset), KUNIT_CASE(dm_test_modereset_not_required_when_inactive_without_modeset), - /* dm_get_oriented_plane_size */ - KUNIT_CASE(dm_test_oriented_plane_size_rotate_0), - KUNIT_CASE(dm_test_oriented_plane_size_rotate_90), - KUNIT_CASE(dm_test_oriented_plane_size_rotate_180), - KUNIT_CASE(dm_test_oriented_plane_size_rotate_270), - /* dm_get_plane_scale */ - KUNIT_CASE(dm_test_get_plane_scale_identity), - KUNIT_CASE(dm_test_get_plane_scale_rotate_90_identity), - KUNIT_CASE(dm_test_get_plane_scale_zero_src_width), /* is_scaling_state_different */ KUNIT_CASE(dm_test_scaling_state_same), KUNIT_CASE(dm_test_scaling_state_scaling_changed), KUNIT_CASE(dm_test_scaling_state_underscan_enabled), + KUNIT_CASE(dm_test_scaling_state_underscan_disabled), KUNIT_CASE(dm_test_scaling_state_underscan_border_changed), - /* is_timing_unchanged_for_freesync */ - KUNIT_CASE(dm_test_timing_unchanged_null_args), - KUNIT_CASE(dm_test_timing_unchanged_identical_modes), - KUNIT_CASE(dm_test_timing_unchanged_vrr_shift), - KUNIT_CASE(dm_test_timing_unchanged_clock_changed), - /* set_freesync_fixed_config */ - KUNIT_CASE(dm_test_set_freesync_fixed_config_60hz), - /* is_dc_timing_adjust_needed */ - KUNIT_CASE(dm_test_dc_timing_adjust_pending), - KUNIT_CASE(dm_test_dc_timing_adjust_active_fixed), - KUNIT_CASE(dm_test_dc_timing_adjust_vrr_toggle), - KUNIT_CASE(dm_test_dc_timing_adjust_not_needed), /* set_multisync_trigger_params */ KUNIT_CASE(dm_test_multisync_trigger_disabled), KUNIT_CASE(dm_test_multisync_trigger_rising), @@ -934,6 +1197,26 @@ static struct kunit_case amdgpu_dm_tests[] = { /* set_master_stream */ KUNIT_CASE(dm_test_master_stream_highest_refresh), KUNIT_CASE(dm_test_master_stream_defaults_to_first), + /* is_content_protection_different */ + KUNIT_CASE(dm_test_cp_diff_hdcp_type_change), + KUNIT_CASE(dm_test_cp_diff_reenable_mode_changed), + KUNIT_CASE(dm_test_cp_diff_reenable_no_change), + KUNIT_CASE(dm_test_cp_diff_undesired), + KUNIT_CASE(dm_test_cp_diff_desired_mode_changed), + KUNIT_CASE(dm_test_cp_diff_desired_no_change), + KUNIT_CASE(dm_test_cp_diff_update_hdcp_hotplug), + KUNIT_CASE(dm_test_cp_diff_stream_reenabled), + KUNIT_CASE(dm_test_cp_diff_s3_undesired_to_enabled), + KUNIT_CASE(dm_test_cp_diff_desired_to_enabled), + KUNIT_CASE(dm_test_cp_diff_desired_to_undesired), + /* dm_enable_per_frame_crtc_master_sync */ + KUNIT_CASE(dm_test_per_frame_master_sync_single_stream), + KUNIT_CASE(dm_test_per_frame_master_sync_two_streams), + KUNIT_CASE(dm_test_per_frame_master_sync_skips_null_stream), + /* amdgpu_dm_apply_delay_after_dpcd_poweroff */ + KUNIT_CASE(dm_test_apply_delay_null_sink), + KUNIT_CASE(dm_test_apply_delay_zero_wait), + KUNIT_CASE(dm_test_apply_delay_nonzero_wait), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c index b43bc244487e..3454cf371414 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c @@ -12,6 +12,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_kunit_helpers.h> +#include <drm/drm_managed.h> #include <drm/drm_mode.h> #include <drm/drm_modes.h> #include <drm/drm_writeback.h> @@ -266,7 +267,7 @@ static void dm_test_wb_get_modes_returns_modes(struct kunit *test) DRIVER_MODESET | DRIVER_ATOMIC); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + connector = drmm_kzalloc(drm, sizeof(*connector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, connector); drmm_connector_init(drm, connector, &dm_wb_test_connector_funcs, @@ -301,7 +302,7 @@ static void dm_test_wb_get_modes_bounded_by_max(struct kunit *test) DRIVER_MODESET | DRIVER_ATOMIC); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); - connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + connector = drmm_kzalloc(drm, sizeof(*connector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, connector); drmm_connector_init(drm, connector, &dm_wb_test_connector_funcs, diff --git a/drivers/gpu/drm/amd/display/dc/basics/conversion.c b/drivers/gpu/drm/amd/display/dc/basics/conversion.c index a1f2350388e2..1c8d4907245a 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/conversion.c +++ b/drivers/gpu/drm/amd/display/dc/basics/conversion.c @@ -26,11 +26,7 @@ #include "dm_services.h" #include "basics/conversion.h" -#define DIVIDER 10000 -/* S2D13 value in [-3.00...0.9999] */ -#define S2D13_MIN (-3 * DIVIDER) -#define S2D13_MAX (3 * DIVIDER) uint16_t fixed_point_to_int_frac( struct fixed31_32 arg, @@ -74,28 +70,44 @@ uint16_t fixed_point_to_int_frac( return result; } /* - * convert_float_matrix - This converts a double into HW register spec defined format S2D13. + * convert_float_matrix - This converts a double into HW register spec defined format S2D13 / S3D12. */ void convert_float_matrix( uint16_t *matrix, - struct fixed31_32 *flt, + const struct fixed31_32 *flt, + enum cm_gamut_coef_format format, uint32_t buffer_size) { - const struct fixed31_32 min_2_13 = - dc_fixpt_from_fraction(S2D13_MIN, DIVIDER); - const struct fixed31_32 max_2_13 = - dc_fixpt_from_fraction(S2D13_MAX, DIVIDER); + struct fixed31_32 min; + struct fixed31_32 max; + uint8_t num_int_bits; + uint8_t num_dec_bits; uint32_t i; + if (format == CM_GAMUT_REMAP_COEF_FORMAT_S2_13) { + min = dc_fixpt_from_fraction(S2D13_MIN, DIVIDER); + max = dc_fixpt_from_fraction(S2D13_MAX, DIVIDER); + num_int_bits = 2; + num_dec_bits = 13; + } else if (format == CM_GAMUT_REMAP_COEF_FORMAT_S3_12) { + min = dc_fixpt_from_fraction(S3D12_MIN, DIVIDER); + max = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); + num_int_bits = 3; + num_dec_bits = 12; + } else { + ASSERT(false); + return; + } + for (i = 0; i < buffer_size; ++i) { uint32_t reg_value = fixed_point_to_int_frac( dc_fixpt_clamp( flt[i], - min_2_13, - max_2_13), - 2, - 13); + min, + max), + num_int_bits, + num_dec_bits); matrix[i] = (uint16_t)reg_value; } @@ -129,10 +141,27 @@ static struct fixed31_32 int_frac_to_fixed_point(uint16_t arg, */ void convert_hw_matrix(struct fixed31_32 *matrix, uint16_t *reg, + enum cm_gamut_coef_format format, uint32_t buffer_size) { - for (uint32_t i = 0; i < buffer_size; ++i) - matrix[i] = int_frac_to_fixed_point(reg[i], 2, 13); + uint8_t num_int_bits; + uint8_t num_dec_bits; + uint32_t i; + + if (format == CM_GAMUT_REMAP_COEF_FORMAT_S2_13) { + num_int_bits = 2; + num_dec_bits = 13; + } else if (format == CM_GAMUT_REMAP_COEF_FORMAT_S3_12) { + num_int_bits = 3; + num_dec_bits = 12; + } else { + ASSERT(false); + return; + } + + for (i = 0; i < buffer_size; ++i) + matrix[i] = int_frac_to_fixed_point(reg[i], + num_int_bits, num_dec_bits); } static uint32_t find_gcd(uint32_t a, uint32_t b) diff --git a/drivers/gpu/drm/amd/display/dc/basics/conversion.h b/drivers/gpu/drm/amd/display/dc/basics/conversion.h index a433cef78496..c7bed2539fa8 100644 --- a/drivers/gpu/drm/amd/display/dc/basics/conversion.h +++ b/drivers/gpu/drm/amd/display/dc/basics/conversion.h @@ -28,6 +28,14 @@ #include "include/fixed31_32.h" +#define DIVIDER 10000 + +#define S2D13_MIN (-4 * DIVIDER) +#define S2D13_MAX (39999) + +#define S3D12_MIN (-8 * DIVIDER) +#define S3D12_MAX (79998) + uint16_t fixed_point_to_int_frac( struct fixed31_32 arg, uint8_t integer_bits, @@ -35,7 +43,8 @@ uint16_t fixed_point_to_int_frac( void convert_float_matrix( uint16_t *matrix, - struct fixed31_32 *flt, + const struct fixed31_32 *flt, + enum cm_gamut_coef_format format, uint32_t buffer_size); void reduce_fraction(uint32_t num, uint32_t den, @@ -43,6 +52,7 @@ void reduce_fraction(uint32_t num, uint32_t den, void convert_hw_matrix(struct fixed31_32 *matrix, uint16_t *reg, + enum cm_gamut_coef_format format, uint32_t buffer_size); static inline unsigned int log_2(unsigned int num) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c index 3baac7fa313a..c7b9bad93a93 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c @@ -375,6 +375,24 @@ void dcn42_enable_pme_wa(struct clk_mgr *clk_mgr_base) dcn42_smu_enable_pme_wa(clk_mgr); } +void dcn42_notify_cstate_disable(struct clk_mgr *clk_mgr_base, bool disable) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + bool target_allow = !disable; + + DC_LOGGER_INIT(clk_mgr_base->ctx->logger); + + /* Idempotent: only send when the cached vote actually changes. */ + if (clk_mgr_base->clks.cstate_allow == target_allow) + return; + + if (dcn42_smu_set_df_cstate_disable(clk_mgr, disable)) + clk_mgr_base->clks.cstate_allow = target_allow; + else + DC_LOG_WARNING("%s: PMFW did not ack DfCstateDisable(%s); leaving cstate_allow=%d to retry\n", + __func__, disable ? "Disable" : "Allow", clk_mgr_base->clks.cstate_allow); +} + bool dcn42_are_clock_states_equal(struct dc_clocks *a, struct dc_clocks *b) @@ -594,6 +612,15 @@ void dcn42_init_clocks(struct clk_mgr *clk_mgr_base) init_clk_states(clk_mgr_base); + /* + * DF C-state policy + * D0 entry must NOT send a PMFW message, but must unconditionally clear + * the cached vote so the next allow-side transition (prepare_bandwidth + * or dc_power_down_on_boot) is guaranteed to issue a fresh + * DfCstateDisable(Allow) and resync DAL with PMFW. + */ + clk_mgr_base->clks.cstate_allow = false; + // to adjust dp_dto reference clock if ssc is enable otherwise to apply dprefclk if (dcn42_is_spll_ssc_enabled(clk_mgr_base)) clk_mgr_base->dp_dto_source_clock_in_khz = @@ -1038,6 +1065,7 @@ static struct clk_mgr_funcs dcn42_funcs = { .get_max_clock_khz = dcn42_get_max_clock_khz, .get_dispclk_from_dentist = dcn42_get_dispclk_from_dentist, .is_smu_present = dcn42_is_smu_present, + .notify_cstate_disable = dcn42_notify_cstate_disable, }; struct clk_mgr_funcs dcn42_fpga_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h index 9568ca06f00f..330242747fff 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h @@ -67,6 +67,7 @@ unsigned int dcn42_convert_wck_ratio(uint8_t wck_ratio); extern struct dcn42_ss_info_table dcn42_ss_info_table; void dcn42_build_watermark_ranges(struct clk_bw_params *bw_params, struct dcn42_watermarks *table); void dcn42_enable_pme_wa(struct clk_mgr *clk_mgr_base); +void dcn42_notify_cstate_disable(struct clk_mgr *clk_mgr_base, bool disable); void dcn42_notify_wm_ranges(struct clk_mgr *clk_mgr_base); void dcn42_set_low_power_state(struct clk_mgr *clk_mgr_base); void dcn42_exit_low_power_state(struct clk_mgr *clk_mgr_base); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c index 6d0012b7d6dc..5e8d979f25f0 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c @@ -85,7 +85,8 @@ #define DALSMC_MSG_DispIPS2Exit 0x11 ///< Display IPS2 exit #define DALSMC_MSG_QueryIPS2Support 0x12 ///< Return 1: support; else not supported -#define DALSMC_Message_Count 0x13 ///< Total number of VBIS and DAL messages +#define DALSMC_MSG_DfCstateDisable 0x13 ///< DCN DF C-state vote (PMFW FWDEV-193711): param 0 = Allow, 1 = Disable +#define DALSMC_Message_Count 0x14 ///< Total number of VBIS and DAL messages /** @}*/ @@ -428,3 +429,29 @@ void dcn42_smu_set_dtbclk(struct clk_mgr_internal *clk_mgr, bool enable) enable); smu_print("%s: smu_set_dtbclk = %d\n", __func__, enable ? 1 : 0); } + +/* + * Vote DCN's DF C-state policy to PMFW. param: 0 = Allow, 1 = Disable. + * Returns true only when PMFW acknowledged the vote (or there is no SMU to + * talk to, in which case there is no DF arbiter to satisfy). On a non-OK + * response the caller must NOT update its cached cstate_allow so the next + * transition retries + */ +bool dcn42_smu_set_df_cstate_disable(struct clk_mgr_internal *clk_mgr, bool disable) +{ + int retv; + + if (!clk_mgr->smu_present) + return true; + + retv = dcn42_smu_send_msg_with_param( + clk_mgr, + DALSMC_MSG_DfCstateDisable, + disable ? 1 : 0); + + smu_print("%s: DfCstateDisable param = %d, return = %d\n", + __func__, disable ? 1 : 0, retv); + + /* dcn42_smu_send_msg_with_param() returns -1 on a non-OK PMFW response. */ + return retv != -1; +} diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h index 8ba7ff04dc05..10c67326ff7e 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h @@ -187,4 +187,6 @@ void dcn42_vbios_smu_enable_48mhz_tmdp_refclk_pwrdwn(struct clk_mgr_internal *cl int dcn42_smu_get_dtbclk(struct clk_mgr_internal *clk_mgr); int dcn42_smu_get_dprefclk(struct clk_mgr_internal *clk_mgr); +bool dcn42_smu_set_df_cstate_disable(struct clk_mgr_internal *clk_mgr, bool disable); + #endif /* DAL_DC_42_SMU_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c index 4be5abdc60ea..2c2ba09221e4 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c @@ -221,10 +221,6 @@ static void init_clk_states(struct clk_mgr *clk_mgr) clk_mgr->clks.zstate_support = DCN_ZSTATE_SUPPORT_UNKNOWN; } -/* dcn42b_get_dpm_table_from_smu removed: reuse dcn42_get_dpm_table_from_smu. - * Function is identical - only uses SMU calls, no hardware register differences. - */ - void dcn42b_init_clocks(struct clk_mgr *clk_mgr_base) { struct clk_mgr_internal *clk_mgr_int = TO_CLK_MGR_INTERNAL(clk_mgr_base); @@ -412,6 +408,176 @@ static struct clk_mgr_funcs dcn42b_funcs = { .is_smu_present = dcn42_is_smu_present, }; +typedef struct { + uint32_t DcfClocks[NUM_DCFCLK_DPM_LEVELS]; + uint32_t DispClocks[NUM_DISPCLK_DPM_LEVELS]; + uint32_t DppClocks[NUM_DPPCLK_DPM_LEVELS]; + uint32_t SocClocks[NUM_SOCCLK_DPM_LEVELS]; + uint32_t VPEClocks[NUM_VPE_DPM_LEVELS]; + uint32_t FclkClocks_Freq[NUM_FCLK_DPM_LEVELS]; + uint32_t FclkClocks_Voltage[NUM_FCLK_DPM_LEVELS]; + uint32_t SocVoltage[NUM_SOC_VOLTAGE_LEVELS]; + MemPstateTable_t MemPstateTable[NUM_MEM_PSTATE_LEVELS]; + + uint8_t NumDcfClkLevelsEnabled; + uint8_t NumDispClkLevelsEnabled; //Applies to both Dispclk and Dppclk + uint8_t NumSocClkLevelsEnabled; + uint8_t VpeClkLevelsEnabled; + + uint8_t NumMemPstatesEnabled; + uint8_t NumFclkLevelsEnabled; + uint8_t Spare1; + uint8_t Spare2; + + uint8_t Spare3; + uint8_t Spare4; + uint8_t Spare5[2]; + + uint32_t MinGfxClk; + uint32_t MaxGfxClk; + + uint32_t Spare6[8]; + uint32_t Spare7[8]; + uint32_t Spare8[8]; + uint32_t Spare9[8]; +} DpmClocks_t_dcn42b; +struct dcn42b_smu_dpm_clks { + DpmClocks_t_dcn42b *dpm_clks; + union large_integer mc_address; +}; + +static void dcn42b_get_dpm_table_from_smu(struct clk_mgr_internal *clk_mgr, + struct dcn42b_smu_dpm_clks *smu_dpm_clks) +{ + DpmClocks_t_dcn42b *table = smu_dpm_clks->dpm_clks; + + if (!clk_mgr->smu_ver) + return; + + if (!table || smu_dpm_clks->mc_address.quad_part == 0) + return; + + memset(table, 0, sizeof(*table)); + + dcn42_smu_set_dram_addr_high(clk_mgr, + smu_dpm_clks->mc_address.high_part); + dcn42_smu_set_dram_addr_low(clk_mgr, + smu_dpm_clks->mc_address.low_part); + dcn42_smu_transfer_dpm_table_smu_2_dram(clk_mgr); +} + +static void dcn42b_get_smu_clocks(struct clk_mgr_internal *clk_mgr_int) +{ + struct clk_mgr *clk_mgr_base = &clk_mgr_int->base; + struct dcn42b_smu_dpm_clks smu_dpm_clks = { 0 }; + + DC_LOGGER_INIT(clk_mgr_base->ctx->logger); + (void)dc_logger; + + smu_dpm_clks.dpm_clks = (DpmClocks_t_dcn42b *)dm_helpers_allocate_gpu_mem( + clk_mgr_base->ctx, + DC_MEM_ALLOC_TYPE_GART, + sizeof(DpmClocks_t_dcn42b), + &smu_dpm_clks.mc_address.quad_part); + + ASSERT(smu_dpm_clks.dpm_clks); + if (clk_mgr_base->ctx->dc->debug.pstate_enabled && smu_dpm_clks.mc_address.quad_part != 0) { + int i; + DpmClocks_t_dcn42b *dpm_clks = smu_dpm_clks.dpm_clks; + + dcn42b_get_dpm_table_from_smu(clk_mgr_int, &smu_dpm_clks); + DC_LOG_SMU("NumDcfClkLevelsEnabled: %d\n" + "NumDispClkLevelsEnabled: %d\n" + "NumSocClkLevelsEnabled: %d\n" + "VpeClkLevelsEnabled: %d\n" + "FClkLevelsEnabled: %d\n" + "NumMemPstatesEnabled: %d\n" + "MinGfxClk: %d\n" + "MaxGfxClk: %d\n", + dpm_clks->NumDcfClkLevelsEnabled, + dpm_clks->NumDispClkLevelsEnabled, + dpm_clks->NumSocClkLevelsEnabled, + dpm_clks->VpeClkLevelsEnabled, + dpm_clks->NumFclkLevelsEnabled, + dpm_clks->NumMemPstatesEnabled, + dpm_clks->MinGfxClk, + dpm_clks->MaxGfxClk); + + for (i = 0; i < NUM_DCFCLK_DPM_LEVELS; i++) + DC_LOG_SMU("dpm_clks->DcfClocks[%d] = %d\n", i, dpm_clks->DcfClocks[i]); + for (i = 0; i < NUM_DISPCLK_DPM_LEVELS; i++) + DC_LOG_SMU("dpm_clks->DispClocks[%d] = %d\n", i, dpm_clks->DispClocks[i]); + for (i = 0; i < NUM_SOCCLK_DPM_LEVELS; i++) + DC_LOG_SMU("dpm_clks->SocClocks[%d] = %d\n", i, dpm_clks->SocClocks[i]); + for (i = 0; i < NUM_FCLK_DPM_LEVELS; i++) { + DC_LOG_SMU("dpm_clks->FclkClocks_Freq[%d] = %d\n", i, dpm_clks->FclkClocks_Freq[i]); + DC_LOG_SMU("dpm_clks->FclkClocks_Voltage[%d] = %d\n", i, dpm_clks->FclkClocks_Voltage[i]); + } + for (i = 0; i < NUM_SOCCLK_DPM_LEVELS; i++) + DC_LOG_SMU("dpm_clks->SocVoltage[%d] = %d\n", i, dpm_clks->SocVoltage[i]); + + for (i = 0; i < NUM_MEM_PSTATE_LEVELS; i++) + DC_LOG_SMU("dpm_clks.MemPstateTable[%d].UClk = %d MemClk = %d Voltage = %d\n", + i, dpm_clks->MemPstateTable[i].UClk, dpm_clks->MemPstateTable[i].MemClk, dpm_clks->MemPstateTable[i].Voltage); + + if (clk_mgr_base->ctx->dc_bios->integrated_info && clk_mgr_base->ctx->dc->config.use_default_clock_table == false) { + /* DCFCLK */ + dcn42_init_single_clock(&clk_mgr_base->bw_params->clk_table.entries[0].dcfclk_mhz, + dpm_clks->DcfClocks, + dpm_clks->NumDcfClkLevelsEnabled); + clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_dcfclk_levels = dpm_clks->NumDcfClkLevelsEnabled; + + /* SOCCLK */ + dcn42_init_single_clock(&clk_mgr_base->bw_params->clk_table.entries[0].socclk_mhz, + dpm_clks->SocClocks, + dpm_clks->NumSocClkLevelsEnabled); + clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_socclk_levels = dpm_clks->NumSocClkLevelsEnabled; + + /* DISPCLK */ + dcn42_init_single_clock(&clk_mgr_base->bw_params->clk_table.entries[0].dispclk_mhz, + dpm_clks->DispClocks, + dpm_clks->NumDispClkLevelsEnabled); + clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_dispclk_levels = dpm_clks->NumDispClkLevelsEnabled; + + /* DPPCLK */ + dcn42_init_single_clock(&clk_mgr_base->bw_params->clk_table.entries[0].dppclk_mhz, + dpm_clks->DppClocks, + dpm_clks->NumDispClkLevelsEnabled); + clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_dppclk_levels = dpm_clks->NumDispClkLevelsEnabled; + + /* FCLK */ + dcn42_init_single_clock(&clk_mgr_base->bw_params->clk_table.entries[0].fclk_mhz, + dpm_clks->FclkClocks_Freq, + NUM_FCLK_DPM_LEVELS); + clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_fclk_levels = dpm_clks->NumFclkLevelsEnabled; + clk_mgr_base->bw_params->clk_table.num_entries = dpm_clks->NumFclkLevelsEnabled; + + /* Memory Pstate table is in reverse order for dcn42. Proper way to map this is for pmfw to provide an fclk indexed uclk key since we consume fclk matched pairs.*/ + ASSERT(dpm_clks->NumMemPstatesEnabled <= NUM_MEM_PSTATE_LEVELS); + if (dpm_clks->NumMemPstatesEnabled > NUM_MEM_PSTATE_LEVELS) + dpm_clks->NumMemPstatesEnabled = NUM_MEM_PSTATE_LEVELS; + for (i = 0; i < dpm_clks->NumDcfClkLevelsEnabled; i++) { + if (i < dpm_clks->NumMemPstatesEnabled) { + clk_mgr_base->bw_params->clk_table.entries[dpm_clks->NumMemPstatesEnabled - 1 - i].memclk_mhz = dpm_clks->MemPstateTable[i].MemClk; + clk_mgr_base->bw_params->clk_table.entries[dpm_clks->NumMemPstatesEnabled - 1 - i].wck_ratio = dcn42_convert_wck_ratio(dpm_clks->MemPstateTable[i].WckRatio); + } else { + clk_mgr_base->bw_params->clk_table.entries[i].memclk_mhz = dpm_clks->MemPstateTable[0].MemClk; + clk_mgr_base->bw_params->clk_table.entries[i].wck_ratio = dcn42_convert_wck_ratio(dpm_clks->MemPstateTable[0].WckRatio); + } + } + + clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_memclk_levels = dpm_clks->NumDcfClkLevelsEnabled; + + /* DTBCLK tied off in DCN42B */ + clk_mgr_base->bw_params->clk_table.entries[0].dtbclk_mhz = 0; + clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels = 0; + } + } + if (smu_dpm_clks.dpm_clks && smu_dpm_clks.mc_address.quad_part != 0) + dm_helpers_free_gpu_mem(clk_mgr_base->ctx, DC_MEM_ALLOC_TYPE_GART, + smu_dpm_clks.dpm_clks); +} + void dcn42b_clk_mgr_construct( struct dc_context *ctx, struct clk_mgr_dcn42 *clk_mgr, @@ -473,11 +639,6 @@ void dcn42b_clk_mgr_construct( dcn42b_read_ss_info_from_lut(&clk_mgr->base); clk_mgr->base.base.bw_params = &dcn42b_bw_params; - if (clk_mgr->base.smu_present) { - dcn42_get_smu_clocks(&clk_mgr->base); - //overwrite values from dcn42_get_smu_clocks since dtbclk is tied off in DCN42B - clk_mgr->base.base.bw_params->clk_table.entries[0].dtbclk_mhz = 0; - clk_mgr->base.base.bw_params->clk_table.num_entries_per_clk.num_dtbclk_levels = 0; - clk_mgr->base.base.clks.ref_dtbclk_khz = 0; - } + if (clk_mgr->base.smu_present) + dcn42b_get_smu_clocks(&clk_mgr->base); } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 8d77158229f1..6c1a8ecc2cb5 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -151,8 +151,8 @@ static const char DC_BUILD_ID[] = "production-build"; /* Private functions */ static inline void elevate_update_type( - struct surface_update_descriptor *descriptor, - enum surface_update_type new_type, + struct dc_update_descriptor *descriptor, + enum dc_update_type new_type, enum dc_lock_descriptor new_locks ) { @@ -831,7 +831,15 @@ bool dc_stream_set_gamut_remap(struct dc *dc, const struct dc_stream_state *stre for (i = 0; i < MAX_PIPES; i++) { if (dc->current_state->res_ctx.pipe_ctx[i].stream == stream) { pipes = &dc->current_state->res_ctx.pipe_ctx[i]; - dc->hwss.program_gamut_remap(pipes); + dc->hwss.program_gamut_remap(&(struct program_gamut_remap_params) { + .xfm = pipes->plane_res.xfm, + .dpp = pipes->plane_res.dpp, + .mpc = dc->res_pool->mpc, + .mpcc_id = pipes->plane_res.mpcc_inst, + .stream = pipes->stream, + .plane = pipes->plane_state, + .is_top_pipe = pipes->top_pipe == NULL, + }); ret = true; } } @@ -890,6 +898,32 @@ void dc_stream_set_static_screen_params(struct dc *dc, dc->hwss.set_static_screen_control(pipes_affected, num_pipes_affected, params); } +static void dc_destruct_update_scratch_pool(struct dc *dc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) { + kfree(dc->update_scratch_pool[i]); + dc->update_scratch_pool[i] = NULL; + dc->update_scratch_in_use[i] = false; + } +} + +static bool dc_construct_update_scratch_pool(struct dc *dc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) { + dc->update_scratch_pool[i] = kzalloc( + sizeof(struct dc_update_scratch_space), GFP_KERNEL); + if (!dc->update_scratch_pool[i]) + return false; + dc->update_scratch_in_use[i] = false; + } + + return true; +} + static void dc_destruct(struct dc *dc) { // reset link encoder assignment table on destruct @@ -897,6 +931,8 @@ static void dc_destruct(struct dc *dc) !dc->config.unify_link_enc_assignment) link_enc_cfg_init(dc, dc->current_state); + dc_destruct_update_scratch_pool(dc); + if (dc->current_state) { dc_state_release(dc->current_state); dc->current_state = NULL; @@ -1140,6 +1176,11 @@ static bool dc_construct(struct dc *dc, goto fail; } + if (!dc_construct_update_scratch_pool(dc)) { + dm_error("%s: failed to create update scratch pool\n", __func__); + goto fail; + } + return true; fail: @@ -2471,7 +2512,7 @@ enum dc_status dc_commit_streams(struct dc *dc, struct dc_commit_streams_params unsigned int i, j; struct dc_state *context; enum dc_status res = DC_OK; - struct dc_validation_set set[MAX_STREAMS] = {0}; + struct dc_validation_set set = {0}; struct pipe_ctx *pipe; bool handle_exit_odm2to1 = false; @@ -2504,14 +2545,15 @@ enum dc_status dc_commit_streams(struct dc *dc, struct dc_commit_streams_params dc_stream_log(dc, stream); - set[i].stream = stream; + set.streams[i].stream = stream; if (status) { - set[i].plane_count = (uint8_t)status->plane_count; + set.streams[i].plane_count = (uint8_t)status->plane_count; for (j = 0; j < (unsigned int)status->plane_count; j++) - set[i].plane_states[j] = status->plane_states[j]; + set.streams[i].plane_states[j] = status->plane_states[j]; } } + set.stream_count = (uint8_t)params->stream_count; /* ODM Combine 2:1 power optimization is only applied for single stream * scenario, it uses extra pipes than needed to reduce power consumption @@ -2535,7 +2577,7 @@ enum dc_status dc_commit_streams(struct dc *dc, struct dc_commit_streams_params context->power_source = params->power_source; - res = dc_validate_with_context(dc, set, params->stream_count, context, DC_VALIDATE_MODE_AND_PROGRAMMING); + res = dc_validate_with_context(dc, &set, context, DC_VALIDATE_MODE_AND_PROGRAMMING); /* * Only update link encoder to stream assignment after bandwidth validation passed. @@ -2784,10 +2826,10 @@ static bool is_surface_in_context( return false; } -static struct surface_update_descriptor get_plane_info_update_type(const struct dc_surface_update *u) +static struct dc_update_descriptor get_plane_info_update_type(const struct dc_surface_update *u) { struct pipe_update_bits *update_bits = &u->surface->update_bits; - struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; + struct dc_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; if (!u->plane_info) return update_type; @@ -2874,12 +2916,12 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct return update_type; } -static struct surface_update_descriptor get_scaling_info_update_type( +static struct dc_update_descriptor get_scaling_info_update_type( const struct dc_check_config *check_config, const struct dc_surface_update *u) { struct pipe_update_bits *update_bits = &u->surface->update_bits; - struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; + struct dc_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; if (!u->scaling_info) return update_type; @@ -2930,11 +2972,11 @@ static struct surface_update_descriptor get_scaling_info_update_type( return update_type; } -static struct surface_update_descriptor det_surface_update( +static struct dc_update_descriptor det_surface_update( const struct dc_check_config *check_config, struct dc_surface_update *u) { - struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; + struct dc_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; struct pipe_update_bits *update_bits = &u->surface->update_bits; if (u->surface->force_full_update) { @@ -2945,8 +2987,7 @@ static struct surface_update_descriptor det_surface_update( dc_pipe_update_bits_clear(update_bits); - struct surface_update_descriptor inner_type = get_plane_info_update_type(u); - + struct dc_update_descriptor inner_type = get_plane_info_update_type(u); elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor); inner_type = get_scaling_info_update_type(check_config, u); @@ -3088,13 +3129,13 @@ static void force_immediate_gsl_plane_flip(struct dc *dc, struct dc_surface_upda } } -static struct surface_update_descriptor check_update_surfaces_for_stream( +static struct dc_update_descriptor check_update_surfaces_for_stream( const struct dc_check_config *check_config, struct dc_surface_update *updates, int surface_count, struct dc_stream_update *stream_update) { - struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; + struct dc_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; /* When countdown finishes, promote this flip to full to trigger deferred final transition */ if (check_config->deferred_transition_state && !check_config->transition_countdown_to_steady_state) { @@ -3217,7 +3258,7 @@ static struct surface_update_descriptor check_update_surfaces_for_stream( } for (int i = 0 ; i < surface_count; i++) { - struct surface_update_descriptor inner_type = + struct dc_update_descriptor inner_type = det_surface_update(check_config, &updates[i]); elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor); @@ -3226,23 +3267,65 @@ static struct surface_update_descriptor check_update_surfaces_for_stream( return overall_type; } -/* - * dc_check_update_surfaces_for_stream() - Determine update type (fast, med, or full) +/** + * dc_check_state_update - Classify a dc_state_update by locking / re-entrancy requirements. + * @check_config: ASIC capabilities and display configuration context + * @updates: root update object describing the full desired commit * - * See :c:type:`enum surface_update_type <surface_update_type>` for explanation of update types + * Determines whether the update requires a fast, medium, or full lock + * by inspecting the stream, stream_update, and surface_updates carried on + * the root object. A probe update elevates the result to at least MED with + * the PROBE lock, so a probe-carrying commit takes the probe mutex. + * + * Return: dc_update_descriptor with update_type and lock_descriptor. */ -struct surface_update_descriptor dc_check_update_surfaces_for_stream( +struct dc_update_descriptor dc_check_state_update( + const struct dc_check_config *check_config, + const struct dc_state_update *updates) +{ + struct dc_update_descriptor desc = {0}; + + if (updates->stream_update) + stream_update_flags_clear(&updates->stream_update->stream->update_flags); + for (int i = 0; i < updates->surface_count; i++) + dc_pipe_update_bits_clear(&updates->surface_updates[i].surface->update_bits); + + desc = check_update_surfaces_for_stream(check_config, updates->surface_updates, + updates->surface_count, updates->stream_update); + + if (updates->probe_updates && updates->probe_updates->probe_count > 0) + elevate_update_type(&desc, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_PROBE); + + return desc; +} + +/** + * dc_check_update_surfaces_for_stream - Shim for dc_check_state_update. + * @check_config: ASIC capabilities and display configuration context + * @updates: array of surface update descriptors + * @surface_count: number of entries in @updates + * @stream_update: optional stream update + * + * Packs the individual arguments into a dc_state_update and forwards to + * dc_check_state_update(). Preserved for out-of-tree and incremental callers. + * + * Return: dc_update_descriptor with update_type and lock_descriptor. + */ +struct dc_update_descriptor dc_check_update_surfaces_for_stream( const struct dc_check_config *check_config, struct dc_surface_update *updates, int surface_count, struct dc_stream_update *stream_update) { - if (stream_update) - stream_update_flags_clear(&stream_update->stream->update_flags); - for (int i = 0; i < surface_count; i++) - dc_pipe_update_bits_clear(&updates[i].surface->update_bits); + struct dc_state_update root = { + .stream = stream_update ? stream_update->stream : NULL, + .stream_update = stream_update, + .surface_updates = updates, + .surface_count = surface_count, + .probe_updates = NULL + }; - return check_update_surfaces_for_stream(check_config, updates, surface_count, stream_update); + return dc_check_state_update(check_config, &root); } static struct dc_stream_status *stream_get_status( @@ -3260,7 +3343,7 @@ static struct dc_stream_status *stream_get_status( return NULL; } -static const enum surface_update_type update_surface_trace_level = UPDATE_TYPE_FULL; +static const enum dc_update_type update_surface_trace_level = UPDATE_TYPE_FULL; static void copy_surface_update_to_plane( struct dc_plane_state *surface, @@ -3658,13 +3741,6 @@ static bool full_update_required_weak( const struct dc_stream_update *stream_update, const struct dc_stream_state *stream); -struct pipe_split_policy_backup { - bool dynamic_odm_policy; - bool subvp_policy; - enum pipe_split_policy mpc_policy; - char force_odm[MAX_PIPES]; -}; - static void backup_and_set_minimal_pipe_split_policy(struct dc *dc, struct dc_state *context, struct pipe_split_policy_backup *policy) @@ -3728,13 +3804,13 @@ static bool update_planes_and_stream_state(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update, - enum surface_update_type *new_update_type, + enum dc_update_type *new_update_type, struct dc_state **new_context) { struct dc_state *context; int i; unsigned int j; - enum surface_update_type update_type; + enum dc_update_type update_type; const struct dc_stream_status *stream_status; struct dc_context *dc_ctx = dc->ctx; @@ -4143,7 +4219,7 @@ static void add_link_update_dsc_config_sequence( static void commit_planes_do_stream_update_sequence(struct dc *dc, struct dc_stream_state *stream, struct dc_stream_update *stream_update, - enum surface_update_type update_type, + enum dc_update_type update_type, struct dc_state *context, struct block_sequence block_sequence[MAX_HWSS_BLOCK_SEQUENCE_SIZE], unsigned int *num_steps) @@ -4314,7 +4390,7 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc, static void commit_planes_do_stream_update(struct dc *dc, struct dc_stream_state *stream, struct dc_stream_update *stream_update, - enum surface_update_type update_type, + enum dc_update_type update_type, struct dc_state *context) { unsigned int j; @@ -4717,7 +4793,7 @@ static void commit_planes_for_stream_fast(struct dc *dc, int surface_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update, - enum surface_update_type update_type, + enum dc_update_type update_type, struct dc_state *context) { int i; @@ -4837,7 +4913,7 @@ static void commit_planes_for_stream(struct dc *dc, int surface_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update, - enum surface_update_type update_type, + enum dc_update_type update_type, struct dc_state *context) { int i; @@ -5920,7 +5996,7 @@ static bool update_planes_and_stream_v2(struct dc *dc, struct dc_stream_update *stream_update) { struct dc_state *context; - enum surface_update_type update_type; + enum dc_update_type update_type; struct dc_fast_update fast_update[MAX_SURFACES] = {0}; /* In cases where MPO and split or ODM are used transitions can @@ -6005,7 +6081,7 @@ static void commit_planes_and_stream_update_on_current_context(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update, - enum surface_update_type update_type) + enum dc_update_type update_type) { struct dc_fast_update fast_update[MAX_SURFACES] = {0}; @@ -6037,7 +6113,7 @@ static void commit_planes_and_stream_update_with_new_context(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream, struct dc_stream_update *stream_update, - enum surface_update_type update_type, + enum dc_update_type update_type, struct dc_state *new_context) { bool skip_new_context = false; @@ -6105,7 +6181,7 @@ static bool update_planes_and_stream_v3(struct dc *dc, struct dc_stream_update *stream_update) { struct dc_state *new_context; - enum surface_update_type update_type; + enum dc_update_type update_type; /* * When this function returns true and new_context is not equal to @@ -6151,30 +6227,93 @@ static void clear_update_bits(struct dc_surface_update *srf_updates, dc_pipe_update_bits_clear(&srf_updates[i].surface->update_bits); } -bool dc_update_planes_and_stream(struct dc *dc, - struct dc_surface_update *srf_updates, int surface_count, - struct dc_stream_state *stream, - struct dc_stream_update *stream_update) +static struct dc_update_scratch_space *dc_update_scratch_acquire(struct dc *dc) { - struct dc_update_scratch_space *scratch = dc_update_planes_and_stream_init( - dc, - srf_updates, - surface_count, - stream, - stream_update - ); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) { + if (dc->update_scratch_in_use[i]) + continue; + + dc->update_scratch_in_use[i] = true; + return dc->update_scratch_pool[i]; + } + + /* TODO: add recoverable scratch acquisition failure handling. */ + ASSERT(false); + return NULL; +} + +static void dc_update_scratch_release(struct dc *dc, + struct dc_update_scratch_space *scratch) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dc->update_scratch_pool); i++) { + if (dc->update_scratch_pool[i] == scratch) { + dc->update_scratch_in_use[i] = false; + return; + } + } +} + +/** + * dc_update_state - Commit an absolute dc_state_update. + * @dc: DC structure + * @updates: root update object carrying stream, plane, and probe updates + * Return: true on success, false on failure. + */ +bool dc_update_state(struct dc *dc, const struct dc_state_update *updates) +{ + struct dc_update_scratch_space *scratch; bool more = true; + if (!dc || !updates) + return false; + + scratch = dc_update_state_init(dc, updates); + if (!scratch) + return false; + while (more) { - if (!dc_update_planes_and_stream_prepare(scratch)) + if (!dc_update_state_prepare(scratch)) return false; - dc_update_planes_and_stream_execute(scratch); - more = dc_update_planes_and_stream_cleanup(scratch); + dc_update_state_execute(scratch); + more = dc_update_state_cleanup(scratch); } + return true; } +/** + * dc_update_planes_and_stream - Shim for dc_update_state. + * @dc: DC structure + * @srf_updates: array of surface update descriptors + * @surface_count: number of entries in @srf_updates + * @stream: target stream + * @stream_update: optional stream update + * + * Packs the individual arguments into a dc_state_update and forwards to + * dc_update_state(). Preserved for out-of-tree and incremental callers. + * + * Return: true on success; false on failure. + */ +bool dc_update_planes_and_stream(struct dc *dc, + struct dc_surface_update *srf_updates, int surface_count, + struct dc_stream_state *stream, + struct dc_stream_update *stream_update) +{ + struct dc_state_update updates = { + .stream = stream, + .stream_update = stream_update, + .surface_updates = srf_updates, + .surface_count = surface_count, + }; + + return dc_update_state(dc, &updates); +} + void dc_commit_updates_for_stream(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, @@ -6404,6 +6543,9 @@ void dc_power_down_on_boot(struct dc *dc) if (dc->caps.ips_support) dc_exit_ips_for_hw_access(dc); dc->hwss.power_down_on_boot(dc); + + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_cstate_disable) + dc->clk_mgr->funcs->notify_cstate_disable(dc->clk_mgr, false); } } @@ -8046,24 +8188,11 @@ void dc_log_preos_dmcub_info(const struct dc *dc) bool dc_get_qos_info(struct dc *dc, struct dc_qos_info *info) { const struct dc_clocks *clk = &dc->current_state->bw_ctx.bw.dcn.clk; - struct dc_measured_memory_qos measured = {}; struct dc_requested_memory_qos requested = {}; memset(info, 0, sizeof(*info)); - // Check if measurement function is available - if (!dc->hwss.measure_memory_qos) { - return false; - } - - dc->hwss.measure_memory_qos(dc, &measured); - - info->actual_peak_bw_in_mbps = measured.peak_bw_mbps; - info->actual_avg_bw_in_mbps = measured.avg_bw_mbps; - info->actual_min_latency_in_ns = measured.min_latency_ns; - info->actual_max_latency_in_ns = measured.max_latency_ns; - info->actual_avg_latency_in_ns = measured.avg_latency_ns; - info->dcn_bandwidth_ub_in_mbps = (uint32_t)(clk->fclk_khz / 1000 * 64); + info->dcn_bandwidth_ub_in_mbps = (uint32_t)(clk->fclk_khz / 1000 * 64); if (dc->clk_mgr && dc->clk_mgr->funcs->get_requested_memory_qos) { dc->clk_mgr->funcs->get_requested_memory_qos(dc->clk_mgr, &requested); @@ -8088,38 +8217,6 @@ unsigned int dc_override_memory_bandwidth_request( dc->clk_mgr, bw_mbps * 1000) / 1000; } -enum update_v3_flow { - UPDATE_V3_FLOW_INVALID, - UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST, - UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL, - UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS, - UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW, - UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT, -}; - -struct dc_update_scratch_space { - struct dc *dc; - struct dc_surface_update *surface_updates; - int surface_count; - struct dc_stream_state *stream; - struct dc_stream_update *stream_update; - bool update_v3; - bool do_clear_update_bits; - enum surface_update_type update_type; - struct dc_state *new_context; - enum update_v3_flow flow; - struct dc_state *backup_context; - struct dc_state *intermediate_context; - struct pipe_split_policy_backup intermediate_policy; - struct dc_surface_update intermediate_updates[MAX_SURFACES]; - int intermediate_count; -}; - -size_t dc_update_scratch_space_size(void) -{ - return sizeof(struct dc_update_scratch_space); -} - static bool update_planes_and_stream_prepare_v2( struct dc_update_scratch_space *scratch ) @@ -8286,6 +8383,19 @@ static bool update_planes_and_stream_prepare_v3( return false; } +/** + * should_commit_intermediate_context - Does this flow commit a transient + * minimal-transition intermediate context + * @flow: the commit flow selected for this iteration + * + * Return: true if this iteration commits the intermediate context. + */ +static bool should_commit_intermediate_context(enum update_v3_flow flow) +{ + return flow == UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW + || flow == UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT; +} + static void update_planes_and_stream_execute_v3_commit( const struct dc_update_scratch_space *scratch, bool intermediate_update, @@ -8309,6 +8419,8 @@ static void update_planes_and_stream_execute_v3( const struct dc_update_scratch_space *scratch ) { + bool intermediate_context = should_commit_intermediate_context(scratch->flow); + switch (scratch->flow) { case UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST: commit_planes_for_stream_fast( @@ -8324,16 +8436,16 @@ static void update_planes_and_stream_execute_v3( case UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL: case UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS: - update_planes_and_stream_execute_v3_commit(scratch, false, false, true); + update_planes_and_stream_execute_v3_commit(scratch, false, intermediate_context, true); break; case UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW: - update_planes_and_stream_execute_v3_commit(scratch, false, true, + update_planes_and_stream_execute_v3_commit(scratch, false, intermediate_context, scratch->dc->check_config.deferred_transition_state); break; case UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT: - update_planes_and_stream_execute_v3_commit(scratch, true, true, false); + update_planes_and_stream_execute_v3_commit(scratch, true, intermediate_context, false); break; case UPDATE_V3_FLOW_INVALID: @@ -8408,54 +8520,145 @@ static bool update_planes_and_stream_cleanup_v3( return false; } -struct dc_update_scratch_space *dc_update_planes_and_stream_init( +struct dc_update_scratch_space *dc_update_state_init( struct dc *dc, - struct dc_surface_update *surface_updates, - int surface_count, - struct dc_stream_state *stream, - struct dc_stream_update *stream_update + const struct dc_state_update *updates ) { const enum dce_version version = dc->ctx->dce_version; - struct dc_update_scratch_space *scratch = stream->update_scratch; + struct dc_update_scratch_space *scratch = dc_update_scratch_acquire(dc); + const bool has_stream_or_plane = updates->stream || updates->stream_update || updates->surface_updates; + const bool has_probe = updates->probe_updates; + const bool surface_without_stream = updates->surface_updates && !updates->stream; + const bool stream_update_without_stream = updates->stream_update && !updates->stream; + const bool bad_surface_count = updates->surface_count > 0 && !updates->surface_updates; + + if (!scratch) + return NULL; + + if (!has_stream_or_plane && !has_probe) { + dc_update_scratch_release(dc, scratch); + return NULL; + } + + if (surface_without_stream || stream_update_without_stream || bad_surface_count) { + dc_update_scratch_release(dc, scratch); + return NULL; + } *scratch = (struct dc_update_scratch_space){ .dc = dc, - .surface_updates = surface_updates, - .surface_count = surface_count, - .stream = stream, - .stream_update = stream_update, - .update_v3 = version >= DCN_VERSION_4_01 || version == DCN_VERSION_3_2 || version == DCN_VERSION_3_21, + .surface_updates = updates->surface_updates, + .surface_count = updates->surface_count, + .stream = updates->stream, + .stream_update = updates->stream_update, + .probe_updates = updates->probe_updates, + .update_v3 = version >= DCN_VERSION_4_01 + || version == DCN_VERSION_3_2 + || version == DCN_VERSION_3_21, .do_clear_update_bits = version >= DCN_VERSION_1_0, + .new_context = NULL, + .flow = UPDATE_V3_FLOW_INVALID, }; return scratch; } -bool dc_update_planes_and_stream_prepare( - struct dc_update_scratch_space *scratch -) +/** + * dc_update_probes_prepare - Commit the desired probe set into new_context. + * @scratch: commit scratch carrying the probe updates + * + * Return: true on success or when there is nothing to do; false when the + * desired set is unachievable. + */ +static bool dc_update_probes_prepare(struct dc_update_scratch_space *scratch) +{ + struct dc *dc = scratch->dc; + const struct dc_probe_updates *probe_updates = scratch->probe_updates; + uint8_t i; + + if (!probe_updates) + return true; + + if (resource_validate_probe_set(dc, probe_updates->probes, + (uint8_t)probe_updates->probe_count) != DC_OK) + return false; + + if (!scratch->new_context) + scratch->new_context = dc->current_state; + + for (i = 0; i < probe_updates->probe_count && i < MAX_PROBES; i++) + scratch->new_context->probes[i] = probe_updates->probes[i]; + scratch->new_context->probe_count = probe_updates->probe_count; + + return true; +} + +/** + * dc_update_probes_execute - Program the committed probes. + * @scratch: commit scratch carrying the probe updates + * + */ +static void dc_update_probes_execute(const struct dc_update_scratch_space *scratch) { - return scratch->update_v3 - ? update_planes_and_stream_prepare_v3(scratch) - : update_planes_and_stream_prepare_v2(scratch); + struct dc *dc = scratch->dc; + + if (should_commit_intermediate_context(scratch->flow)) + return; + + if (dc->hwss.program_perfmon) + dc->hwss.program_perfmon(dc, scratch->new_context); } -void dc_update_planes_and_stream_execute( +bool dc_update_state_prepare(struct dc_update_scratch_space *scratch) +{ + if (scratch->stream) { + bool ok = scratch->update_v3 + ? update_planes_and_stream_prepare_v3(scratch) + : update_planes_and_stream_prepare_v2(scratch); + + if (!ok) + goto release_scratch; + } + + if (!dc_update_probes_prepare(scratch)) + goto release_scratch; + + return true; + +release_scratch: + /* execute and cleanup never run on this path, so release here. */ + dc_update_scratch_release(scratch->dc, scratch); + return false; +} + +void dc_update_state_execute( const struct dc_update_scratch_space *scratch ) { - scratch->update_v3 - ? update_planes_and_stream_execute_v3(scratch) - : update_planes_and_stream_execute_v2(scratch); + if (scratch->stream) + scratch->update_v3 + ? update_planes_and_stream_execute_v3(scratch) + : update_planes_and_stream_execute_v2(scratch); + + if (scratch->probe_updates) + dc_update_probes_execute(scratch); } -bool dc_update_planes_and_stream_cleanup( +bool dc_update_state_cleanup( struct dc_update_scratch_space *scratch ) { - return scratch->update_v3 - ? update_planes_and_stream_cleanup_v3(scratch) - : update_planes_and_stream_cleanup_v2(scratch); + bool more = false; + + if (scratch->stream) + more = scratch->update_v3 + ? update_planes_and_stream_cleanup_v3(scratch) + : update_planes_and_stream_cleanup_v2(scratch); + + if (!more) + dc_update_scratch_release(scratch->dc, scratch); + + return more; } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index e47c8cf5d036..11411fa94665 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -1069,7 +1069,14 @@ void hwss_build_fast_sequence(struct dc *dc, if (dc->hwss.program_gamut_remap && (current_mpc_pipe->plane_state->update_bits.gamut_remap_change || current_mpc_pipe->stream->update_flags.bits.gamut_remap)) { - block_sequence[*num_steps].params.program_gamut_remap_params.pipe_ctx = current_mpc_pipe; + struct program_gamut_remap_params *params = &block_sequence[*num_steps].params.program_gamut_remap_params; + params->dpp = current_mpc_pipe->plane_res.dpp; + params->mpc = dc->res_pool->mpc; + params->xfm = current_mpc_pipe->plane_res.xfm; + params->mpcc_id = current_mpc_pipe->plane_res.hubp->inst; + params->plane = current_mpc_pipe->plane_state; + params->stream = current_mpc_pipe->stream; + params->is_top_pipe = current_mpc_pipe->top_pipe == NULL; block_sequence[*num_steps].func = DPP_PROGRAM_GAMUT_REMAP; (*num_steps)++; } @@ -1105,9 +1112,15 @@ void hwss_build_fast_sequence(struct dc *dc, } } if (hws->funcs.set_output_transfer_func && current_mpc_pipe->stream->update_flags.bits.out_tf) { - block_sequence[*num_steps].params.set_output_transfer_func_params.dc = dc; - block_sequence[*num_steps].params.set_output_transfer_func_params.pipe_ctx = current_mpc_pipe; - block_sequence[*num_steps].params.set_output_transfer_func_params.stream = current_mpc_pipe->stream; + struct set_output_transfer_func_params *otf_params = + &block_sequence[*num_steps].params.set_output_transfer_func_params; + + otf_params->dpp = current_mpc_pipe->plane_res.dpp; + otf_params->xfm = current_mpc_pipe->plane_res.xfm; + otf_params->mpc = dc->res_pool->mpc; + otf_params->mpcc_id = current_mpc_pipe->plane_res.hubp->inst; + otf_params->is_top_pipe = resource_is_pipe_type(current_mpc_pipe, OPP_HEAD); + otf_params->stream = current_mpc_pipe->stream; block_sequence[*num_steps].func = DPP_SET_OUTPUT_TRANSFER_FUNC; (*num_steps)++; } @@ -1236,7 +1249,8 @@ void hwss_execute_sequence(struct dc *dc, params->set_input_transfer_func_params.plane_state); break; case DPP_PROGRAM_GAMUT_REMAP: - hwss_program_gamut_remap(params); + if (dc->hwss.program_gamut_remap) + dc->hwss.program_gamut_remap(¶ms->program_gamut_remap_params); break; case HUBP_ENABLE_3DLUT_FL: hwss_hubp_enable_3dlut_fl(params); @@ -1263,9 +1277,7 @@ void hwss_execute_sequence(struct dc *dc, hwss_program_manual_trigger(params); break; case DPP_SET_OUTPUT_TRANSFER_FUNC: - hws->funcs.set_output_transfer_func(params->set_output_transfer_func_params.dc, - params->set_output_transfer_func_params.pipe_ctx, - params->set_output_transfer_func_params.stream); + hws->funcs.set_output_transfer_func(¶ms->set_output_transfer_func_params); break; case MPC_UPDATE_VISUAL_CONFIRM: dc->hwss.update_visual_confirm_color(params->update_visual_confirm_params.dc, @@ -1483,6 +1495,48 @@ void hwss_execute_sequence(struct dc *dc, case HUBBUB_SOFT_RESET: hwss_hubbub_soft_reset(params); break; + case HUBBUB_PERFMON_RESET: + hwss_hubbub_perfmon_reset(params); + break; + case HUBBUB_PERFMON_ARM_OUT_OF_ORDER_BW: + hwss_hubbub_perfmon_arm_out_of_order_bw(params); + break; + case HUBBUB_PERFMON_START_OUT_OF_ORDER_BW: + hwss_hubbub_perfmon_start_out_of_order_bw(params); + break; + case HUBBUB_PERFMON_START_IN_ORDER_BW: + hwss_hubbub_perfmon_start_in_order_bw(params); + break; + case HUBBUB_PERFMON_START_MEMORY_LATENCIES: + hwss_hubbub_perfmon_start_memory_latencies(params); + break; + case HUBBUB_PERFMON_START_URGENT_ASSERTION_COUNT: + hwss_hubbub_perfmon_start_urgent_assertion_count(params); + break; + case HUBBUB_PERFMON_START_URGENT_RAMP_LATENCY: + hwss_hubbub_perfmon_start_urgent_ramp_latency(params); + break; + case HUBBUB_PERFMON_START_PREFETCH_DATA_SIZE: + hwss_hubbub_perfmon_start_prefetch_data_size(params); + break; + case HUBBUB_PERFMON_GET_OUT_OF_ORDER_BW: + hwss_hubbub_perfmon_get_out_of_order_bw(params); + break; + case HUBBUB_PERFMON_GET_IN_ORDER_BW: + hwss_hubbub_perfmon_get_in_order_bw(params); + break; + case HUBBUB_PERFMON_GET_MEMORY_LATENCIES: + hwss_hubbub_perfmon_get_memory_latencies(params); + break; + case HUBBUB_PERFMON_GET_URGENT_ASSERTION_COUNT: + hwss_hubbub_perfmon_get_urgent_assertion_count(params); + break; + case HUBBUB_PERFMON_GET_PREFETCH_DATA_SIZE: + hwss_hubbub_perfmon_get_prefetch_data_size(params); + break; + case HUBBUB_PERFMON_GET_URGENT_RAMP_LATENCY: + hwss_hubbub_perfmon_get_urgent_ramp_latency(params); + break; case HUBP_CLK_CNTL: hwss_hubp_clk_cntl(params); break; @@ -1779,7 +1833,14 @@ void hwss_add_dpp_program_gamut_remap(struct block_sequence_state *seq_state, struct pipe_ctx *pipe_ctx) { if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { - seq_state->steps[*seq_state->num_steps].params.program_gamut_remap_params.pipe_ctx = pipe_ctx; + struct program_gamut_remap_params *params = &seq_state->steps[*seq_state->num_steps].params.program_gamut_remap_params; + params->xfm = pipe_ctx->plane_res.xfm; + params->dpp = pipe_ctx->plane_res.dpp; + params->mpc = pipe_ctx->stream->ctx->dc->res_pool->mpc; + params->mpcc_id = pipe_ctx->plane_res.hubp->inst; + params->plane = pipe_ctx->plane_state; + params->stream = pipe_ctx->stream; + params->is_top_pipe = pipe_ctx->top_pipe == NULL; seq_state->steps[*seq_state->num_steps].func = DPP_PROGRAM_GAMUT_REMAP; (*seq_state->num_steps)++; } @@ -1814,19 +1875,39 @@ void hwss_add_optc_program_manual_trigger(struct block_sequence_state *seq_state * Helper function to add DPP set output transfer function to block sequence */ void hwss_add_dpp_set_output_transfer_func(struct block_sequence_state *seq_state, - struct dc *dc, - struct pipe_ctx *pipe_ctx, - struct dc_stream_state *stream) + struct dc *dc, struct pipe_ctx *pipe_ctx) { if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { - seq_state->steps[*seq_state->num_steps].params.set_output_transfer_func_params.dc = dc; - seq_state->steps[*seq_state->num_steps].params.set_output_transfer_func_params.pipe_ctx = pipe_ctx; - seq_state->steps[*seq_state->num_steps].params.set_output_transfer_func_params.stream = stream; + seq_state->steps[*seq_state->num_steps].params.set_output_transfer_func_params = + (struct set_output_transfer_func_params) { + .xfm = pipe_ctx->plane_res.xfm, + .dpp = pipe_ctx->plane_res.dpp, + .mpc = dc->res_pool->mpc, + .mpcc_id = pipe_ctx->plane_res.hubp->inst, + .is_top_pipe = resource_is_pipe_type(pipe_ctx, OPP_HEAD), + .stream = pipe_ctx->stream, + }; seq_state->steps[*seq_state->num_steps].func = DPP_SET_OUTPUT_TRANSFER_FUNC; (*seq_state->num_steps)++; } } +void hwss_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx) +{ + if (dc->hwseq->funcs.set_output_transfer_func) { + dc->hwseq->funcs.set_output_transfer_func( + &(struct set_output_transfer_func_params) { + .xfm = pipe_ctx->plane_res.xfm, + .dpp = pipe_ctx->plane_res.dpp, + .mpc = dc->res_pool->mpc, + .mpcc_id = pipe_ctx->plane_res.hubp->inst, + .is_top_pipe = resource_is_pipe_type(pipe_ctx, OPP_HEAD), + .stream = pipe_ctx->stream, + } + ); + } +} + /* * Helper function to add MPC update visual confirm to block sequence */ @@ -3212,6 +3293,139 @@ void hwss_hubbub_soft_reset(union block_sequence_params *params) params->hubbub_soft_reset_params.hubbub_soft_reset(hubbub, reset); } +void hwss_hubbub_perfmon_reset(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_reset_params.hubbub; + + if (hubbub && hubbub->funcs->perfmon.reset) + hubbub->funcs->perfmon.reset(hubbub); +} + +void hwss_hubbub_perfmon_arm_out_of_order_bw(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_arm_out_of_order_bw_params.hubbub; + + if (hubbub && hubbub->funcs->perfmon.arm_measuring_out_of_order_bandwidth) + hubbub->funcs->perfmon.arm_measuring_out_of_order_bandwidth(hubbub); +} + +void hwss_hubbub_perfmon_start_out_of_order_bw(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_start_out_of_order_bw_params.hubbub; + + if (hubbub && hubbub->funcs->perfmon.start_measuring_out_of_order_bandwidth) + hubbub->funcs->perfmon.start_measuring_out_of_order_bandwidth(hubbub); +} + +void hwss_hubbub_perfmon_start_in_order_bw(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_start_in_order_bw_params.hubbub; + + if (hubbub && hubbub->funcs->perfmon.start_measuring_in_order_bandwidth) + hubbub->funcs->perfmon.start_measuring_in_order_bandwidth(hubbub); +} + +void hwss_hubbub_perfmon_start_memory_latencies(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_start_memory_latencies_params.hubbub; + + if (hubbub && hubbub->funcs->perfmon.start_measuring_memory_latencies) + hubbub->funcs->perfmon.start_measuring_memory_latencies(hubbub); +} + +void hwss_hubbub_perfmon_start_urgent_assertion_count(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_start_urgent_assertion_count_params.hubbub; + + if (hubbub && hubbub->funcs->perfmon.start_measuring_urgent_assertion_count) + hubbub->funcs->perfmon.start_measuring_urgent_assertion_count(hubbub); +} + +void hwss_hubbub_perfmon_start_urgent_ramp_latency(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_start_urgent_ramp_latency_params.hubbub; + + if (hubbub && hubbub->funcs->perfmon.start_measuring_urgent_ramp_latency) + hubbub->funcs->perfmon.start_measuring_urgent_ramp_latency( + hubbub, + ¶ms->hubbub_perfmon_start_urgent_ramp_latency_params.latency_params); +} + +void hwss_hubbub_perfmon_start_prefetch_data_size(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_start_prefetch_data_size_params.hubbub; + + if (hubbub && hubbub->funcs->perfmon.start_measuring_prefetch_data_size) + hubbub->funcs->perfmon.start_measuring_prefetch_data_size(hubbub); +} + +void hwss_hubbub_perfmon_get_out_of_order_bw(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_get_out_of_order_bw_params.hubbub; + uint32_t refclk_mhz = params->hubbub_perfmon_get_out_of_order_bw_params.refclk_mhz; + uint32_t *mbps = params->hubbub_perfmon_get_out_of_order_bw_params.bandwidth_mbps; + uint32_t *duration = params->hubbub_perfmon_get_out_of_order_bw_params.duration_ns; + + if (hubbub && hubbub->funcs->perfmon.get_out_of_order_bandwidth_mbps && mbps) + *mbps = hubbub->funcs->perfmon.get_out_of_order_bandwidth_mbps( + hubbub, refclk_mhz, duration); +} + +void hwss_hubbub_perfmon_get_in_order_bw(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_get_in_order_bw_params.hubbub; + uint32_t refclk_mhz = params->hubbub_perfmon_get_in_order_bw_params.refclk_mhz; + uint32_t min_duration_ns = params->hubbub_perfmon_get_in_order_bw_params.min_duration_ns; + uint32_t *mbps = params->hubbub_perfmon_get_in_order_bw_params.bandwidth_mbps; + uint32_t *duration = params->hubbub_perfmon_get_in_order_bw_params.duration_ns; + + if (hubbub && hubbub->funcs->perfmon.get_in_order_bandwidth_mbps && mbps) + *mbps = hubbub->funcs->perfmon.get_in_order_bandwidth_mbps( + hubbub, refclk_mhz, min_duration_ns, duration); +} + +void hwss_hubbub_perfmon_get_memory_latencies(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_get_memory_latencies_params.hubbub; + uint32_t refclk_mhz = params->hubbub_perfmon_get_memory_latencies_params.refclk_mhz; + struct dc_probe_latencies *result = params->hubbub_perfmon_get_memory_latencies_params.result; + + if (hubbub && hubbub->funcs->perfmon.get_memory_latencies_ns && result) + hubbub->funcs->perfmon.get_memory_latencies_ns( + hubbub, refclk_mhz, &result->min_latency_ns, &result->max_latency_ns, &result->avg_latency_ns); +} + +void hwss_hubbub_perfmon_get_urgent_assertion_count(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_get_urgent_assertion_count_params.hubbub; + uint32_t refclk_mhz = params->hubbub_perfmon_get_urgent_assertion_count_params.refclk_mhz; + uint32_t *count = params->hubbub_perfmon_get_urgent_assertion_count_params.assertion_count; + + if (hubbub && hubbub->funcs->perfmon.get_urgent_assertion_count) + hubbub->funcs->perfmon.get_urgent_assertion_count( + hubbub, refclk_mhz, count, NULL, NULL); +} + +void hwss_hubbub_perfmon_get_prefetch_data_size(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_get_prefetch_data_size_params.hubbub; + uint32_t *prefetch_size = params->hubbub_perfmon_get_prefetch_data_size_params.prefetch_data_size; + + if (hubbub && hubbub->funcs->perfmon.get_prefetch_data_size && prefetch_size) + *prefetch_size = hubbub->funcs->perfmon.get_prefetch_data_size(hubbub); +} + +void hwss_hubbub_perfmon_get_urgent_ramp_latency(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_perfmon_get_urgent_ramp_latency_params.hubbub; + uint32_t refclk_mhz = params->hubbub_perfmon_get_urgent_ramp_latency_params.refclk_mhz; + uint32_t *latency_ns = params->hubbub_perfmon_get_urgent_ramp_latency_params.latency_ns; + + if (hubbub && hubbub->funcs->perfmon.get_urgent_ramp_latency_ns && latency_ns) + *latency_ns = hubbub->funcs->perfmon.get_urgent_ramp_latency_ns( + hubbub, refclk_mhz); +} + void hwss_hubp_clk_cntl(union block_sequence_params *params) { struct hubp *hubp = params->hubp_clk_cntl_params.hubp; @@ -3617,12 +3831,20 @@ void hwss_set_cursor_sdr_white_level(union block_sequence_params *params) dc->hwss.set_cursor_sdr_white_level(pipe_ctx); } -void hwss_program_gamut_remap(union block_sequence_params *params) +void hwss_program_gamut_remap(struct pipe_ctx *pipe_ctx) { - struct dc *dc = params->program_gamut_remap_params.pipe_ctx->stream->ctx->dc; + struct dc *dc = pipe_ctx->stream->ctx->dc; - if (dc && dc->hwss.program_gamut_remap) - dc->hwss.program_gamut_remap(params->program_gamut_remap_params.pipe_ctx); + if (dc->hwss.program_gamut_remap) + dc->hwss.program_gamut_remap(&(struct program_gamut_remap_params) { + .xfm = pipe_ctx->plane_res.xfm, + .dpp = pipe_ctx->plane_res.dpp, + .mpc = dc->res_pool->mpc, + .mpcc_id = pipe_ctx->plane_res.hubp->inst, + .stream = pipe_ctx->stream, + .plane = pipe_ctx->plane_state, + .is_top_pipe = pipe_ctx->top_pipe == NULL, + }); } void hwss_program_output_csc(union block_sequence_params *params) @@ -4375,6 +4597,168 @@ void hwss_add_hubbub_soft_reset(struct block_sequence_state *seq_state, } } +void hwss_add_hubbub_perfmon_reset(struct block_sequence_state *seq_state, + struct hubbub *hubbub) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_RESET; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_reset_params.hubbub = hubbub; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_arm_out_of_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_ARM_OUT_OF_ORDER_BW; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_arm_out_of_order_bw_params.hubbub = hubbub; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_start_out_of_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_START_OUT_OF_ORDER_BW; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_start_out_of_order_bw_params.hubbub = hubbub; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_start_in_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_START_IN_ORDER_BW; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_start_in_order_bw_params.hubbub = hubbub; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_start_memory_latencies(struct block_sequence_state *seq_state, + struct hubbub *hubbub) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_START_MEMORY_LATENCIES; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_start_memory_latencies_params.hubbub = hubbub; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_start_urgent_assertion_count(struct block_sequence_state *seq_state, + struct hubbub *hubbub) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_START_URGENT_ASSERTION_COUNT; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_start_urgent_assertion_count_params.hubbub = hubbub; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_start_urgent_ramp_latency(struct block_sequence_state *seq_state, + struct hubbub *hubbub, + const struct hubbub_urgent_latency_params *latency_params) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_START_URGENT_RAMP_LATENCY; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_start_urgent_ramp_latency_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_start_urgent_ramp_latency_params.latency_params = + *latency_params; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_start_prefetch_data_size(struct block_sequence_state *seq_state, + struct hubbub *hubbub) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_START_PREFETCH_DATA_SIZE; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_start_prefetch_data_size_params.hubbub = hubbub; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_get_out_of_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t *bandwidth_mbps, uint32_t *duration_ns) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_GET_OUT_OF_ORDER_BW; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_out_of_order_bw_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_out_of_order_bw_params.refclk_mhz = refclk_mhz; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_out_of_order_bw_params.bandwidth_mbps = bandwidth_mbps; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_out_of_order_bw_params.duration_ns = duration_ns; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_get_in_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t min_duration_ns, + uint32_t *bandwidth_mbps, uint32_t *duration_ns) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_GET_IN_ORDER_BW; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_in_order_bw_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_in_order_bw_params.refclk_mhz = refclk_mhz; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_in_order_bw_params.min_duration_ns = min_duration_ns; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_in_order_bw_params.bandwidth_mbps = bandwidth_mbps; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_in_order_bw_params.duration_ns = duration_ns; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_get_memory_latencies(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, + struct dc_probe_latencies *result) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_GET_MEMORY_LATENCIES; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_memory_latencies_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_memory_latencies_params.refclk_mhz = refclk_mhz; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_memory_latencies_params.result = result; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_get_urgent_assertion_count(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t *assertion_count) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_GET_URGENT_ASSERTION_COUNT; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_urgent_assertion_count_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_urgent_assertion_count_params.refclk_mhz = refclk_mhz; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_urgent_assertion_count_params.assertion_count = assertion_count; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_get_prefetch_data_size(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t *prefetch_data_size) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_GET_PREFETCH_DATA_SIZE; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_prefetch_data_size_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_prefetch_data_size_params.prefetch_data_size = prefetch_data_size; + (*seq_state->num_steps)++; + } +} + +void hwss_add_hubbub_perfmon_get_urgent_ramp_latency(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t *latency_ns) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PERFMON_GET_URGENT_RAMP_LATENCY; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_urgent_ramp_latency_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_urgent_ramp_latency_params.refclk_mhz = refclk_mhz; + seq_state->steps[*seq_state->num_steps].params.hubbub_perfmon_get_urgent_ramp_latency_params.latency_ns = latency_ns; + (*seq_state->num_steps)++; + } +} + void hwss_add_hubp_clk_cntl(struct block_sequence_state *seq_state, struct hubp *hubp, bool enable) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c index f25d4b3e11ac..14fc4ef482f1 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c @@ -534,6 +534,13 @@ bool dc_link_set_pr_general_cmd(struct dc_link *link, return link->dc->link_srv->dp_pr_set_general_cmd(link, general_cmd_data); } +void dc_link_edp_replay_residency(const struct dc_link *link, + unsigned int *residency, bool is_start, enum pr_residency_mode mode) +{ + if (link && link->dc && link->dc->link_srv) + link->dc->link_srv->edp_replay_residency(link, residency, is_start, mode); +} + bool dc_link_get_pr_state(const struct dc_link *link, uint64_t *state) { return link->dc->link_srv->dp_pr_get_state(link, state); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index b21d41df0fab..b970f152d67f 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -4325,8 +4325,7 @@ bool dc_resource_is_dsc_encoding_supported(const struct dc *dc) static bool planes_changed_for_existing_stream(struct dc_state *context, struct dc_stream_state *stream, - const struct dc_validation_set set[], - unsigned int set_count) + const struct dc_validation_set *set) { unsigned int i, j; struct dc_stream_status *stream_status = NULL; @@ -4343,18 +4342,18 @@ static bool planes_changed_for_existing_stream(struct dc_state *context, return false; } - for (i = 0; i < set_count; i++) - if (set[i].stream == stream) + for (i = 0; i < set->stream_count; i++) + if (set->streams[i].stream == stream) break; - if (i == set_count) + if (i == set->stream_count) ASSERT(0); - if (set[i].plane_count != stream_status->plane_count) + if (set->streams[i].plane_count != stream_status->plane_count) return true; - for (j = 0; j < set[i].plane_count; j++) - if (set[i].plane_states[j] != stream_status->plane_states[j]) + for (j = 0; j < set->streams[i].plane_count; j++) + if (set->streams[i].plane_states[j] != stream_status->plane_states[j]) return true; return false; @@ -4363,34 +4362,66 @@ static bool planes_changed_for_existing_stream(struct dc_state *context, static bool add_all_planes_for_stream( const struct dc *dc, struct dc_stream_state *stream, - const struct dc_validation_set set[], - unsigned int set_count, + const struct dc_validation_set *set, struct dc_state *state) { unsigned int i, j; - for (i = 0; i < set_count; i++) - if (set[i].stream == stream) + for (i = 0; i < set->stream_count; i++) + if (set->streams[i].stream == stream) break; - if (i == set_count) { + if (i == set->stream_count) { dm_error("Stream %p not found in set!\n", stream); return false; } - for (j = 0; j < set[i].plane_count; j++) - if (!dc_state_add_plane(dc, stream, set[i].plane_states[j], state)) + for (j = 0; j < set->streams[i].plane_count; j++) + if (!dc_state_add_plane(dc, stream, set->streams[i].plane_states[j], state)) return false; return true; } /** + * resource_validate_probe_set - Validate a probe descriptor set against the ASIC. + * @dc: DC instance providing the HWSS capability hooks + * @probes: desired probe descriptors + * @probe_count: number of valid entries in @probes + * + * Return: DC_OK if achievable, otherwise a DC error. + */ +enum dc_status resource_validate_probe_set(struct dc *dc, + const struct dc_probe_state *probes, + uint8_t probe_count) +{ + uint8_t i; + + if (probe_count == 0) + return DC_OK; + + if (!dc->hwss.program_perfmon) + return DC_NOT_SUPPORTED; + + if (probe_count > MAX_PROBES) + return DC_NOT_SUPPORTED; + + for (i = 0; i < probe_count; i++) { + if (probes[i].target_state == DC_PROBE_MEASURING) + return DC_NOT_SUPPORTED; + + if (probes[i].scope.type != DC_PROBE_SCOPE_GLOBAL) + return DC_NOT_SUPPORTED; + } + + return DC_OK; +} + +/** * dc_validate_with_context - Validate and update the potential new stream in the context object * * @dc: Used to get the current state status - * @set: An array of dc_validation_set with all the current streams reference - * @set_count: Total of streams + * @set: Root validation object holding all streams and their planes * @context: New context * @validate_mode: identify the validation mode * @@ -4404,8 +4435,7 @@ static bool add_all_planes_for_stream( * In case of success, return DC_OK (1), otherwise, return a DC error. */ enum dc_status dc_validate_with_context(struct dc *dc, - const struct dc_validation_set set[], - unsigned int set_count, + const struct dc_validation_set *set, struct dc_state *context, enum dc_validate_mode validate_mode) { @@ -4426,8 +4456,8 @@ enum dc_status dc_validate_with_context(struct dc *dc, for (i = 0; i < old_stream_count; i++) { struct dc_stream_state *stream = context->streams[i]; - for (j = 0; j < set_count; j++) { - if (stream == set[j].stream) { + for (j = 0; j < set->stream_count; j++) { + if (stream == set->streams[j].stream) { found = true; break; } @@ -4440,8 +4470,8 @@ enum dc_status dc_validate_with_context(struct dc *dc, } /* Second, build a list of new streams */ - for (i = 0; i < set_count; i++) { - struct dc_stream_state *stream = set[i].stream; + for (i = 0; i < set->stream_count; i++) { + struct dc_stream_state *stream = set->streams[i].stream; for (j = 0; j < old_stream_count; j++) { if (stream == context->streams[j]) { @@ -4459,10 +4489,10 @@ enum dc_status dc_validate_with_context(struct dc *dc, /* Build a list of unchanged streams which is necessary for handling * planes change such as added, removed, and updated. */ - for (i = 0; i < set_count; i++) { + for (i = 0; i < set->stream_count; i++) { /* Check if stream is part of the delete list */ for (j = 0; j < del_streams_count; j++) { - if (set[i].stream == del_streams[j]) { + if (set->streams[i].stream == del_streams[j]) { found = true; break; } @@ -4471,7 +4501,7 @@ enum dc_status dc_validate_with_context(struct dc *dc, if (!found) { /* Check if stream is part of the add list */ for (j = 0; j < add_streams_count; j++) { - if (set[i].stream == add_streams[j]) { + if (set->streams[i].stream == add_streams[j]) { found = true; break; } @@ -4479,7 +4509,7 @@ enum dc_status dc_validate_with_context(struct dc *dc, } if (!found) - unchanged_streams[unchanged_streams_count++] = set[i].stream; + unchanged_streams[unchanged_streams_count++] = set->streams[i].stream; found = false; } @@ -4488,8 +4518,7 @@ enum dc_status dc_validate_with_context(struct dc *dc, for (i = 0; i < unchanged_streams_count; i++) { if (planes_changed_for_existing_stream(context, unchanged_streams[i], - set, - set_count)) { + set)) { if (!dc_state_rem_all_planes_for_stream(dc, unchanged_streams[i], @@ -4557,7 +4586,7 @@ enum dc_status dc_validate_with_context(struct dc *dc, if (res != DC_OK) goto fail; - if (!add_all_planes_for_stream(dc, add_streams[i], set, set_count, context)) { + if (!add_all_planes_for_stream(dc, add_streams[i], set, context)) { res = DC_FAIL_ATTACH_SURFACES; goto fail; } @@ -4567,9 +4596,8 @@ enum dc_status dc_validate_with_context(struct dc *dc, for (i = 0; i < unchanged_streams_count; i++) { if (planes_changed_for_existing_stream(context, unchanged_streams[i], - set, - set_count)) { - if (!add_all_planes_for_stream(dc, unchanged_streams[i], set, set_count, context)) { + set)) { + if (!add_all_planes_for_stream(dc, unchanged_streams[i], set, context)) { res = DC_FAIL_ATTACH_SURFACES; goto fail; } @@ -4760,6 +4788,10 @@ enum dc_status dc_validate_global_state( if (result == DC_OK) result = dc->res_pool->funcs->validate_bandwidth(dc, new_ctx, validate_mode); + if (result == DC_OK) + result = resource_validate_probe_set(dc, new_ctx->probes, + (uint8_t)new_ctx->probe_count); + return result; } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_sink.c b/drivers/gpu/drm/amd/display/dc/core/dc_sink.c index e5ac7056a187..38ef295e43d6 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_sink.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_sink.c @@ -61,6 +61,7 @@ void dc_sink_retain(struct dc_sink *sink) { kref_get(&sink->refcount); } +EXPORT_IF_KUNIT(dc_sink_retain); static void dc_sink_free(struct kref *kref) { @@ -73,6 +74,7 @@ void dc_sink_release(struct dc_sink *sink) { kref_put(&sink->refcount, dc_sink_free); } +EXPORT_IF_KUNIT(dc_sink_release); struct dc_sink *dc_sink_create(const struct dc_sink_init_data *init_params) { @@ -94,6 +96,7 @@ construct_fail: alloc_fail: return NULL; } +EXPORT_IF_KUNIT(dc_sink_create); /******************************************************************************* * Protected functions - visible only inside of DC (not visible in DM) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_state.c b/drivers/gpu/drm/amd/display/dc/core/dc_state.c index 7edeed52cc32..b534b1a641af 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c @@ -25,6 +25,7 @@ #include "dc_types.h" #include "core_types.h" #include "core_status.h" +#include "dc.h" #include "dc_state.h" #include "dc_state_priv.h" #include "dc_stream_priv.h" @@ -359,6 +360,10 @@ void dc_state_destruct(struct dc_state *state) } state->phantom_plane_count = 0; + memset(state->probes, 0, sizeof(state->probes)); + memset(state->probe_status, 0, sizeof(state->probe_status)); + state->probe_count = 0; + state->stream_mask = 0; memset(&state->res_ctx, 0, sizeof(state->res_ctx)); memset(&state->pp_display_cfg, 0, sizeof(state->pp_display_cfg)); @@ -678,28 +683,67 @@ bool dc_state_add_all_planes_for_stream( /* Private dc_state functions */ /** - * dc_state_get_stream_status - Get stream status from given dc state - * @state: DC state to find the stream status in - * @stream: The stream to get the stream status for + * dc_state_get_status - Unified status readback for dc_state. + * @status: output object populated per options->types + * @options: selects source state, status classes to fill, and optional filters * - * The given stream is expected to exist in the given dc state. Otherwise, NULL - * will be returned. + * Return: DC_OK on success, DC_ERROR_UNEXPECTED if options or state is NULL. */ -struct dc_stream_status *dc_state_get_stream_status( - struct dc_state *state, - const struct dc_stream_state *stream) +enum dc_status dc_state_get_status(struct dc_state_status *status, + const struct dc_get_status_options *options) { uint8_t i; - if (state == NULL) - return NULL; + if (!status || !options || !options->state) + return DC_ERROR_UNEXPECTED; - for (i = 0; i < state->stream_count; i++) { - if (stream == state->streams[i]) - return &state->stream_status[i]; + if (options->types & DC_GET_STATUS_STREAM) { + status->stream_count = 0; + for (i = 0; i < options->state->stream_count; i++) { + if (options->stream && + options->stream != options->state->streams[i]) + continue; + status->stream_status[status->stream_count++] = + &options->state->stream_status[i]; + } + } + + if (options->types & DC_GET_STATUS_PROBE) { + status->probe_count = 0; + for (i = 0; i < options->state->probe_count; i++) { + if (options->probe && + options->probe->type != options->state->probes[i].type) + continue; + status->probe_status[status->probe_count++] = + &options->state->probe_status[i]; + } } - return NULL; + return DC_OK; +} + +/** + * dc_state_get_stream_status - Shim for dc_state_get_status. + * @state: state to search + * @stream: stream to find status for + * + * Return: pointer to the matching dc_stream_status, or NULL if not found. + */ +struct dc_stream_status *dc_state_get_stream_status( + struct dc_state *state, + const struct dc_stream_state *stream) +{ + struct dc_state_status status = {}; + struct dc_get_status_options options = { + .state = state, + .types = DC_GET_STATUS_STREAM, + .stream = stream, + }; + + if (dc_state_get_status(&status, &options) != DC_OK) + return NULL; + + return status.stream_count > 0 ? status.stream_status[0] : NULL; } enum mall_stream_type dc_state_get_pipe_subvp_type(const struct dc_state *state, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c index dbc12640b01c..cdcf140bc1bb 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c @@ -24,6 +24,7 @@ */ #include "dm_services.h" +#include "dm_helpers.h" #include "basics/dc_common.h" #include "dc.h" #include "core_types.h" @@ -99,7 +100,9 @@ void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink) if (stream->link->frl_flags.force_frl_always || stream->link->frl_flags.force_frl_max - || stream->link->frl_flags.force_frl_dsc) + || stream->link->frl_flags.force_frl_dsc + || (stream->link->frl_flags.force_frl_rate != 0 && + stream->link->frl_flags.force_frl_rate != 0xF)) stream->signal = SIGNAL_TYPE_HDMI_FRL; } } @@ -193,7 +196,6 @@ static void dc_stream_free(struct kref *kref) struct dc_stream_state *stream = container_of(kref, struct dc_stream_state, refcount); dc_stream_destruct(stream); - kfree(stream->update_scratch); kfree(stream); } @@ -203,6 +205,7 @@ void dc_stream_release(struct dc_stream_state *stream) kref_put(&stream->refcount, dc_stream_free); } } +EXPORT_IF_KUNIT(dc_stream_release); struct dc_stream_state *dc_create_stream_for_sink( struct dc_sink *sink) @@ -217,13 +220,6 @@ struct dc_stream_state *dc_create_stream_for_sink( if (stream == NULL) goto fail; - DC_RUN_WITH_PREEMPTION_ENABLED(stream->update_scratch = - kzalloc((int32_t) dc_update_scratch_space_size(), - GFP_ATOMIC)); - - if (stream->update_scratch == NULL) - goto fail; - if (dc_stream_construct(stream, sink) == false) goto fail; @@ -232,10 +228,8 @@ struct dc_stream_state *dc_create_stream_for_sink( return stream; fail: - if (stream) { - kfree(stream->update_scratch); + if (stream) kfree(stream); - } return NULL; } @@ -248,16 +242,6 @@ struct dc_stream_state *dc_copy_stream(const struct dc_stream_state *stream) if (!new_stream) return NULL; - // Scratch is not meant to be reused across copies, as might have self-referential pointers - new_stream->update_scratch = kzalloc( - (int32_t) dc_update_scratch_space_size(), - GFP_KERNEL - ); - if (!new_stream->update_scratch) { - kfree(new_stream); - return NULL; - } - if (new_stream->sink) dc_sink_retain(new_stream->sink); diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index e1b7d359d917..63c0dec85697 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -29,6 +29,7 @@ #include "dc_types.h" #include "dc_state.h" #include "dc_plane.h" +#include "dc_probe.h" #include "grph_object_defs.h" #include "logger_types.h" #include "hdcp_msg_types.h" @@ -65,7 +66,7 @@ struct dcn_dsc_reg_state; struct dcn_optc_reg_state; struct dcn_dccg_reg_state; -#define DC_VER "3.2.389" +#define DC_VER "3.2.391" /** * MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC @@ -314,6 +315,7 @@ struct mpc_color_caps { struct lut3d_caps mcm_3d_lut_caps; struct lut3d_caps rmcm_3d_lut_caps; bool preblend; + struct fixed31_32 max_gamut_remap_coeff; }; /** @@ -518,7 +520,7 @@ struct dc_static_screen_params { * underscan we don't expect to see this call at all. */ -enum surface_update_type { +enum dc_update_type { UPDATE_TYPE_FAST, /* super fast, safe to execute in isr */ UPDATE_TYPE_MED, /* ISR safe, most of programming needed, no bw/clk change*/ UPDATE_TYPE_FULL, /* may need to shuffle resources */ @@ -529,10 +531,11 @@ enum dc_lock_descriptor { LOCK_DESCRIPTOR_STREAM = 0x1, LOCK_DESCRIPTOR_LINK = 0x2, LOCK_DESCRIPTOR_GLOBAL = 0x4, + LOCK_DESCRIPTOR_PROBE = 0x8, }; -struct surface_update_descriptor { - enum surface_update_type update_type; +struct dc_update_descriptor { + enum dc_update_type update_type; enum dc_lock_descriptor lock_descriptor; }; @@ -757,6 +760,15 @@ struct dc_clocks { * Elements below are not compared for the purposes of * optimization required */ + + /* + * @cstate_allow + * + * DCN's DF C-state vote as last successfully acknowledged by PMFW. + * false = DCN does NOT permit DF C-state; + * true = DCN permits DF C-state; + */ + bool cstate_allow; bool prev_p_state_change_support; bool fclk_prev_p_state_change_support; int num_ways; @@ -1017,14 +1029,9 @@ struct dc_bounding_box_overrides { }; struct dc_qos_info { - uint32_t actual_peak_bw_in_mbps; uint32_t qos_bandwidth_lb_in_mbps; - uint32_t actual_avg_bw_in_mbps; uint32_t calculated_avg_bw_in_mbps; - uint32_t actual_max_latency_in_ns; - uint32_t actual_min_latency_in_ns; uint32_t qos_max_latency_ub_in_ns; - uint32_t actual_avg_latency_in_ns; uint32_t qos_avg_latency_ub_in_ns; uint32_t dcn_bandwidth_ub_in_mbps; uint32_t qos_max_bw_budget_in_mbps; @@ -1508,13 +1515,9 @@ union dc_plane_cm_flags { unsigned int blend_enable : 1; /* whether legacy (lut3d_func) or DMA is valid */ unsigned int lut3d_dma_enable : 1; -#if defined(CONFIG_DRM_AMD_DC_DCN4_2) /* RMCM lut to be used instead of MCM */ unsigned int rmcm_enable : 1; unsigned int reserved: 27; -#else - unsigned int reserved: 28; -#endif } bits; }; @@ -1990,6 +1993,12 @@ struct dc { struct dc_state *current_state; struct resource_pool *res_pool; + /** + * @update_scratch_pool: Per-commit scratch buffers for dc_update_state. + */ + struct dc_update_scratch_space *update_scratch_pool[MAX_STREAMS + 1]; + bool update_scratch_in_use[MAX_STREAMS + 1]; + struct clk_mgr *clk_mgr; /* Display Engine Clock levels */ @@ -2109,6 +2118,157 @@ struct dc_surface_update { struct cm_hist_control *cm_hist_control; }; +struct dc_state_update { + struct dc_stream_state *stream; + struct dc_stream_update *stream_update; + struct dc_surface_update *surface_updates; + int surface_count; + const struct dc_probe_updates *probe_updates; +}; + +/** + * dc_check_state_update() - Classify an update without committing it. + * @check_config: DC check configuration + * @updates: root update object to classify + * + * Return: descriptor indicating update type and required lock scope. + */ +struct dc_update_descriptor dc_check_state_update( + const struct dc_check_config *check_config, + const struct dc_state_update *updates); + +/** + * dc_update_state - Commit an absolute dc_state_update. + * @dc: DC structure + * @updates: root update object carrying stream, plane, and probe updates + * + * Return: true on success, false on failure. + */ +bool dc_update_state(struct dc *dc, const struct dc_state_update *updates); + +struct dc_update_scratch_space; + +/** + * dc_update_state_init - Acquire and initialise a commit scratch buffer. + * @dc: DC structure + * @updates: update descriptor; validated before the slot is acquired + * + * Return: a scratch slot on success, NULL if validation fails or the pool + * is exhausted. The slot must be released via dc_update_state_cleanup() on + * success, or automatically by dc_update_state_prepare() on failure. + */ +struct dc_update_scratch_space *dc_update_state_init( + struct dc *dc, + const struct dc_state_update *updates +); + +/** + * dc_update_state_prepare - Prepare the commit under the global lock. + * @scratch: commit scratch from dc_update_state_init() + * + * On failure the scratch slot is released and false is returned; the caller + * must not call execute or cleanup. + */ +bool dc_update_state_prepare(struct dc_update_scratch_space *scratch); + +/** + * dc_update_state_execute - Program hardware; called without the global lock. + * @scratch: commit scratch from dc_update_state_init() + */ +void dc_update_state_execute(const struct dc_update_scratch_space *scratch); + +/** + * dc_update_state_cleanup - Finalise the commit and release the scratch slot. + * @scratch: commit scratch from dc_update_state_init() + * + * Must be called with the global lock held. Returns true if the caller must + * loop back to prepare (SEAMLESS continuation). + */ +bool dc_update_state_cleanup(struct dc_update_scratch_space *scratch); + +/** + * struct dc_probe_latencies - min/max/avg memory latency in ns. + * @max_latency_ns: maximum latency in nanoseconds + * @avg_latency_ns: average latency in nanoseconds + * @min_latency_ns: minimum latency in nanoseconds + */ +struct dc_probe_latencies { + uint32_t max_latency_ns; + uint32_t avg_latency_ns; + uint32_t min_latency_ns; +}; + +/** + * struct dc_probe_status - results for a probe. + * @valid: true if a measurement was latched. + * @type: type of the probe that produced this result. + * @u.bandwidth_mbps: peak BW in Mbps (DC_PROBE_PEAK_MEM_BW). + * @u.latency: min/max/avg memory latency in ns (DC_PROBE_MEM_LATENCY), + * stored as struct dc_probe_latencies. + * @u.urgent_assertion_count: number of urgent assertion events (DC_PROBE_URGENT_ASSERTION_COUNT). + * @u.prefetch_data_size: total prefetch data in bytes (DC_PROBE_PREFETCH_DATA_SIZE). + */ +struct dc_probe_status { + bool valid; + enum dc_probe_type type; + union { + uint32_t bandwidth_mbps; + struct dc_probe_latencies latency; + uint32_t urgent_assertion_count; + uint32_t prefetch_data_size; + } u; +}; + +/** + * enum dc_get_status_type - Bitmask selecting which status classes to populate. + * @DC_GET_STATUS_STREAM: populate stream_status fields in dc_state_status + * @DC_GET_STATUS_PROBE: populate probe_status fields in dc_state_status + */ +enum dc_get_status_type { + DC_GET_STATUS_STREAM = (1u << 0), + DC_GET_STATUS_PROBE = (1u << 1), +}; + +/** + * struct dc_get_status_options - Input selector for dc_state_get_status. + * @state: source state to read status from + * @types: OR of dc_get_status_type values selecting classes to populate + * @stream: optional stream filter for DC_GET_STATUS_STREAM. NULL means + * populate status for all streams in the state + * @probe: optional probe filter for DC_GET_STATUS_PROBE. NULL means + * populate status for all probes in the state + */ +struct dc_get_status_options { + struct dc_state *state; + uint32_t types; + const struct dc_stream_state *stream; + const struct dc_probe_state *probe; +}; + +/** + * struct dc_state_status - Output-only status object from dc_state_get_status. + * @stream_count: number of valid entries in stream_status (DC_GET_STATUS_STREAM) + * @stream_status: pointers to live per-stream status entries + * @probe_count: number of valid entries in probe_status (DC_GET_STATUS_PROBE) + * @probe_status: pointers to live per-probe status entries + */ +struct dc_state_status { + int stream_count; + struct dc_stream_status *stream_status[MAX_STREAMS]; + int probe_count; + struct dc_probe_status *probe_status[MAX_PROBES]; +}; + +/** + * dc_state_get_status - Unified status readback for dc_state. + * @status: output object populated according to options->types + * @options: selects the source state, status classes to fill, and filters + * + * Return: DC_OK on success, DC_ERROR_UNEXPECTED if state is NULL. + */ +enum dc_status dc_state_get_status(struct dc_state_status *status, + const struct dc_get_status_options *options); + struct dc_underflow_debug_data { struct dcn_hubbub_reg_state *hubbub_reg_state; struct dcn_hubp_reg_state *hubp_reg_state[MAX_PIPES]; @@ -2164,9 +2324,9 @@ void dc_post_update_surfaces_to_stream( void dc_get_default_tiling_info(const struct dc *dc, struct dc_tiling_info *tiling_info); /** - * struct dc_validation_set - Struct to store surface/stream associations for validation + * struct dc_validation_stream - Per-stream surface/stream association for validation */ -struct dc_validation_set { +struct dc_validation_stream { /** * @stream: Stream state properties */ @@ -2183,6 +2343,31 @@ struct dc_validation_set { uint8_t plane_count; }; +/** + * struct dc_validation_set - Root validation input grouping all streams for a commit + */ +struct dc_validation_set { + /** + * @streams: Per-stream entries (stream + its planes) + */ + struct dc_validation_stream streams[MAX_STREAMS]; + + /** + * @stream_count: Number of active entries in @streams + */ + uint8_t stream_count; + + /** + * @probes: Global probe descriptors to validate alongside the streams + */ + struct dc_probe_state probes[MAX_PROBES]; + + /** + * @probe_count: Number of active entries in @probes + */ + uint8_t probe_count; +}; + bool dc_validate_boot_timing(const struct dc *dc, const struct dc_sink *sink, struct dc_crtc_timing *crtc_timing); @@ -2190,8 +2375,7 @@ bool dc_validate_boot_timing(const struct dc *dc, enum dc_status dc_validate_plane(struct dc *dc, const struct dc_plane_state *plane_state); enum dc_status dc_validate_with_context(struct dc *dc, - const struct dc_validation_set set[], - unsigned int set_count, + const struct dc_validation_set *set, struct dc_state *context, enum dc_validate_mode validate_mode); @@ -2801,6 +2985,14 @@ bool dc_link_set_pr_general_cmd(struct dc_link *link, struct dmub_cmd_pr_general_cmd_data *general_cmd_data); /* + * Measure Panel Replay residency for the given link. + * mode: PR_RESIDENCY_MODE_PHY, PR_RESIDENCY_MODE_ALPM, or + * PR_RESIDENCY_MODE_ENABLEMENT_PERIOD + */ +void dc_link_edp_replay_residency(const struct dc_link *link, + unsigned int *residency, bool is_start, enum pr_residency_mode mode); + +/* * Get Panel Replay state: * * @link: pointer to the dc_link struct instance diff --git a/drivers/gpu/drm/amd/display/dc/dc_probe.h b/drivers/gpu/drm/amd/display/dc/dc_probe.h new file mode 100644 index 000000000000..ebf33b162b63 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/dc_probe.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2025 Advanced Micro Devices, Inc. + +#ifndef _DC_PROBE_H_ +#define _DC_PROBE_H_ + +#include "os_types.h" + +/** + * enum dc_probe_type - What DM wants to probe. + * + * Each value names a measurable quantity as an abstraction. DC resolves it to + * whatever HW measurement block fulfills it. DM never selects the HW block. + */ +enum dc_probe_type { + DC_PROBE_PEAK_MEM_BW = 0, + DC_PROBE_AVG_MEM_BW, + DC_PROBE_MEM_LATENCY, + DC_PROBE_URGENT_RAMP_LATENCY, + DC_PROBE_URGENT_ASSERTION_COUNT, + DC_PROBE_PREFETCH_DATA_SIZE, +}; + +/** + * enum dc_probe_target_state - Target lifecycle state DM wants DC to reach. + * + * DM sets this to describe the final state DC must reach by the end of the + * commit. DC performs whatever HW transition sequence is needed. + * + * @DC_PROBE_NOT_MEASURING: probe inactive, no valid data available. + * @DC_PROBE_MEASURING: probe runs continuously. The latest value can be + * read back at any time and may differ on each read. + * @DC_PROBE_MEASURED: probe performed one shot. The result is latched and + * stays valid until DM transitions back to DC_PROBE_NOT_MEASURING. + */ +enum dc_probe_target_state { + DC_PROBE_NOT_MEASURING = 0, + DC_PROBE_MEASURING, + DC_PROBE_MEASURED, +}; + +/** + * enum dc_probe_scope_type - What the probe is scoped to. + * @DC_PROBE_SCOPE_GLOBAL: whole memory subsystem, no stream/plane selector. + * + * Only GLOBAL is implemented. Per-stream/plane scoping must select targets by + * stable id, not object pointer — dc_state copy semantics would dangle a raw + * pointer when the absolute-set commit removes or replaces the target. + */ +enum dc_probe_scope_type { + DC_PROBE_SCOPE_GLOBAL = 0, +}; + +/** + * struct dc_probe_scope - Selects what a probe measures against. + * @type: scope kind, only DC_PROBE_SCOPE_GLOBAL is implemented. + */ +struct dc_probe_scope { + enum dc_probe_scope_type type; +}; + +/** + * struct dc_probe_state - DM-authored descriptor of a single probe. + * + * A plain inline value with copy semantics: no allocation, no refcount. DC + * resolves each descriptor to a HW measurement instance and diffs the desired + * set against the committed set to plan the transition. + * + * @type: what to measure. + * @target_state: desired lifecycle state for this probe. + * @scope: what the probe is scoped to (GLOBAL only for now). + */ +struct dc_probe_state { + enum dc_probe_type type; + enum dc_probe_target_state target_state; + struct dc_probe_scope scope; +}; + +#define MAX_PROBES 1 + +/** + * struct dc_probe_updates - Absolute set of probes DM wants active. + * + * Mirrors the plane/stream absolute-set model: the array is the complete + * desired set. DC compares it against the committed set to add, remove, or + * transition probes. + * + * @probes: desired probe descriptors. + * @probe_count: number of valid entries in @probes. + */ +struct dc_probe_updates { + struct dc_probe_state probes[MAX_PROBES]; + int probe_count; +}; + +#endif /* _DC_PROBE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_state.h b/drivers/gpu/drm/amd/display/dc/dc_state.h index db1e63a7d460..acf461225e9d 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_state.h +++ b/drivers/gpu/drm/amd/display/dc/dc_state.h @@ -74,4 +74,11 @@ bool dc_state_add_all_planes_for_stream( struct dc_stream_status *dc_state_get_stream_status( struct dc_state *state, const struct dc_stream_state *stream); + +struct dc_state_status; +struct dc_get_status_options; + +enum dc_status dc_state_get_status(struct dc_state_status *status, + const struct dc_get_status_options *options); + #endif /* _DC_STATE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index 8b164edc9c51..4530f294f1c2 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -329,7 +329,6 @@ struct dc_stream_state { enum dc_drr_trigger_mode drr_trigger_mode; - struct dc_update_scratch_space *update_scratch; bool firmware_controlled_hdr_info_packet; }; @@ -408,32 +407,8 @@ bool dc_update_planes_and_stream(struct dc *dc, struct dc_stream_state *dc_stream, struct dc_stream_update *stream_update); -struct dc_update_scratch_space; +struct dc_state_update; -size_t dc_update_scratch_space_size(void); - -struct dc_update_scratch_space *dc_update_planes_and_stream_init( - struct dc *dc, - struct dc_surface_update *surface_updates, - int surface_count, - struct dc_stream_state *dc_stream, - struct dc_stream_update *stream_update -); - -// Locked, false is failed -bool dc_update_planes_and_stream_prepare( - struct dc_update_scratch_space *scratch -); - -// Unlocked -void dc_update_planes_and_stream_execute( - const struct dc_update_scratch_space *scratch -); - -// Locked, true if call again -bool dc_update_planes_and_stream_cleanup( - struct dc_update_scratch_space *scratch -); /* * Set up surface attributes and associate to a stream @@ -518,7 +493,8 @@ void dc_enable_stereo( /* Triggers multi-stream synchronization. */ void dc_trigger_sync(struct dc *dc, struct dc_state *context); -struct surface_update_descriptor dc_check_update_surfaces_for_stream( +/* Shim: packs args into dc_state_update and calls dc_check_state_update(). */ +struct dc_update_descriptor dc_check_update_surfaces_for_stream( const struct dc_check_config *check_config, struct dc_surface_update *updates, int surface_count, diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index 5ba7a2fffcf0..3edeb94fba23 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -188,6 +188,7 @@ struct dc_panel_patch { unsigned int skip_audio_sab_check; unsigned int mst_start_top_delay; unsigned int remove_sink_ext_caps; + bool disable_second_tile; unsigned int disable_colorimetry; uint8_t blankstream_before_otg_off; bool oled_optimize_display_on; @@ -1414,28 +1415,20 @@ enum dc_cm_lut_swizzle { enum dc_cm_lut_pixel_format { CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB, -#if defined(CONFIG_DRM_AMD_DC_DCN4_2) CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12MSB, -#endif CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB, -#if defined(CONFIG_DRM_AMD_DC_DCN4_2) CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12LSB, -#endif CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10, -#if defined(CONFIG_DRM_AMD_DC_DCN4_2) CM_LUT_PIXEL_FORMAT_BGRA16161616_FLOAT_FP1_5_10 -#endif }; enum dc_cm_lut_size { CM_LUT_SIZE_NONE, CM_LUT_SIZE_999, CM_LUT_SIZE_171717, -#if defined(CONFIG_DRM_AMD_DC_DCN4_2) CM_LUT_SIZE_333333, CM_LUT_SIZE_454545, CM_LUT_SIZE_656565, -#endif }; #ifndef TRIM_CM2 diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index ecb8493ec523..8e3862f6dac2 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -45,9 +45,7 @@ clk_src->base.ctx #define DC_LOGGER \ - calc_pll_cs->ctx->logger -#define DC_LOGGER_INIT() \ - struct calc_pll_clock_source *calc_pll_cs = &clk_src->calc_pll + CTX->logger #undef FN #define FN(reg_name, field_name) \ @@ -287,6 +285,7 @@ static bool calc_pll_dividers_in_range( } static uint32_t calculate_pixel_clock_pll_dividers( + struct dce110_clk_src *clk_src, struct calc_pll_clock_source *calc_pll_cs, struct pll_settings *pll_settings) { @@ -474,7 +473,7 @@ static uint32_t dce110_get_pix_clk_dividers_helper ( { uint32_t field = 0; uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; - DC_LOGGER_INIT(); + /* Check if reference clock is external (not pcie/xtalin) * HW Dce80 spec: * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB @@ -517,12 +516,14 @@ static uint32_t dce110_get_pix_clk_dividers_helper ( /*Calculate Dividers by HDMI object, no SS case or SS case */ pll_calc_error = calculate_pixel_clock_pll_dividers( + clk_src, &clk_src->calc_pll_hdmi, pll_settings); else /*Calculate Dividers by default object, no SS case or SS case */ pll_calc_error = calculate_pixel_clock_pll_dividers( + clk_src, &clk_src->calc_pll, pll_settings); @@ -568,7 +569,6 @@ static uint32_t dce110_get_pix_clk_dividers( { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; - DC_LOGGER_INIT(); if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { @@ -600,7 +600,6 @@ static uint32_t dce112_get_pix_clk_dividers( struct pll_settings *pll_settings) { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - DC_LOGGER_INIT(); if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { @@ -1442,8 +1441,6 @@ static uint32_t dcn3_get_pix_clk_dividers( unsigned long long actual_pix_clk_100Hz = pix_clk_params ? pix_clk_params->requested_pix_clk_100hz : 0; struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - DC_LOGGER_INIT(); - if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { DC_LOG_ERROR( @@ -1513,7 +1510,6 @@ static const struct clock_source_funcs dce110_clk_src_funcs = { .get_dp_dto_frequency_100hz = get_dp_dto_frequency_100hz }; - static void get_ss_info_from_atombios( struct dce110_clk_src *clk_src, enum as_signal_type as_signal, @@ -1526,7 +1522,7 @@ static void get_ss_info_from_atombios( struct spread_spectrum_info *ss_info_cur; struct spread_spectrum_data *ss_data_cur; uint32_t i; - DC_LOGGER_INIT(); + if (ss_entries_num == NULL) { DC_LOG_SYNC( "Invalid entry !!!\n"); @@ -1657,6 +1653,7 @@ static void ss_info_from_atombios_create( } static bool calc_pll_max_vco_construct( + struct dce110_clk_src *clk_src, struct calc_pll_clock_source *calc_pll_cs, struct calc_pll_clock_source_init_data *init_data) { @@ -1808,6 +1805,7 @@ bool dce110_clk_src_construct( ss_info_from_atombios_create(clk_src); if (!calc_pll_max_vco_construct( + clk_src, &clk_src->calc_pll, &calc_pll_cs_init_data)) { ASSERT_CRITICAL(false); @@ -1822,7 +1820,7 @@ bool dce110_clk_src_construct( if (!calc_pll_max_vco_construct( - &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + clk_src, &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { ASSERT_CRITICAL(false); goto unexpected_failure; } diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c index 12c85c3afd6a..864491bfd7f7 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_transform.c @@ -1145,8 +1145,8 @@ static void dce_transform_set_gamut_remap( for (i = 0; i < GAMUT_MATRIX_SIZE; i++) arr_matrix[i] = adjust->temperature_matrix[i]; - convert_float_matrix( - arr_reg_val, arr_matrix, GAMUT_MATRIX_SIZE); + convert_float_matrix(arr_reg_val, arr_matrix, + CM_GAMUT_REMAP_COEF_FORMAT_S2_13, GAMUT_MATRIX_SIZE); program_gamut_remap(xfm_dce, arr_reg_val); } diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c index 9b50bf1d80fc..eda3a2f1402c 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c @@ -165,7 +165,7 @@ static bool dmub_replay_copy_settings(struct dmub_replay *dmub, // Misc copy_settings_data->line_time_in_ns = (uint16_t)replay_context->line_time_in_ns; copy_settings_data->panel_inst = (uint16_t)panel_inst; - copy_settings_data->debug.u32All = (uint16_t)link->replay_settings.config.debug_flags; + copy_settings_data->debug.u32All = link->replay_settings.config.debug_flags; copy_settings_data->pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line; copy_settings_data->max_deviation_line = (uint16_t)link->dpcd_caps.pr_info.max_deviation_line; copy_settings_data->smu_optimizations_en = link->replay_settings.replay_smu_opt_enable; diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h index 63704d21a0b5..f5252931ce35 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_helpers.h +++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h @@ -192,6 +192,17 @@ void dm_helpers_mccs_vcp_set( struct dc_link *link, struct dc_sink *sink); +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +#define STATIC_IFN_KUNIT +#define INLINE_IFN_KUNIT inline +#define EXPORT_IF_KUNIT(symbol) EXPORT_SYMBOL(symbol) + +#else +#define STATIC_IFN_KUNIT static +#define INLINE_IFN_KUNIT +#define EXPORT_IF_KUNIT(symbol) +#endif + bool dm_helpers_dp_handle_test_pattern_request( struct dc_context *ctx, const struct dc_link *link, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c index 260f58a97fc7..da2c92e5c02f 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c @@ -630,17 +630,17 @@ enum frl_cap_chk_result dml1_frl_cap_chk(struct frl_cap_chk_params *params) { struct frl_cap_chk_intermediates inter; -#if defined (CONFIG_DRM_AMD_DC_FP) - if (params->compressed) - return dml1_frl_cap_chk_compressed(params, &inter); -#endif - return dml1_frl_cap_chk_inter(params, &inter); } enum frl_cap_chk_result dml1_frl_cap_chk_inter(struct frl_cap_chk_params *params, struct frl_cap_chk_intermediates *inter) { +#if defined (CONFIG_DRM_AMD_DC_FP) + if (params->compressed) + return dml1_frl_cap_chk_compressed(params, inter); +#endif + return dml1_frl_cap_chk_uncompressed(params, inter); } diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile index 44e00c2b7ac7..e9ce7813b0f5 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile @@ -55,8 +55,11 @@ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/ # Add FPU flags to all dml2 files by default, remove NO_FPU flags. # FPU flags step 1: Find all .c files in dal/dc/dml2_0 and it's subfolders -DML2_ABS_PATH := $(FULL_AMD_DISPLAY_PATH)/dc/dml2_0 -DML2_C_FILES := $(shell find $(DML2_ABS_PATH) -name '*.c' -type f) + +THIS_MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) +DML2_ABS_PATH := $(patsubst %/,%,$(dir $(THIS_MAKEFILE_PATH))) + +DML2_C_FILES := $(shell find $(DML2_ABS_PATH) -name '*.c') # FPU flags step 2: Convert to .o and make paths relative to $(AMDDALPATH)/dc/dml2_0/ DML2_RELATIVE_O_FILES := $(patsubst $(DML2_ABS_PATH)/%,dc/dml2_0/%,$(patsubst %.c,%.o,$(DML2_C_FILES))) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c index c1a3e2496983..47164fa150d9 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c @@ -90,7 +90,7 @@ static unsigned int calc_max_hardware_v_total(const struct dc_stream_state *stre static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cfg *timing, struct dc_stream_state *stream, - struct pipe_ctx *pipe_ctx, + struct pipe_ctx *otg_master_pipe, struct dml2_context *dml_ctx) { const unsigned int min_v_front_porch = (stream->timing.flags.INTERLACE != 0) ? 2 : 1; @@ -99,24 +99,24 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf uint64_t min_hardware_refresh_in_uhz; uint32_t pix_clk_100hz; - timing->h_active = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right + pipe_ctx->dsc_padding_params.dsc_hactive_padding; + timing->h_active = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right + otg_master_pipe->dsc_padding_params.dsc_hactive_padding; timing->v_active = stream->timing.v_addressable + stream->timing.v_border_bottom + stream->timing.v_border_top; timing->h_front_porch = stream->timing.h_front_porch; timing->v_front_porch = stream->timing.v_front_porch > min_v_front_porch ? stream->timing.v_front_porch : min_v_front_porch; timing->pixel_clock_khz = stream->timing.pix_clk_100hz / 10; - if (pipe_ctx->dsc_padding_params.dsc_hactive_padding != 0) - timing->pixel_clock_khz = pipe_ctx->dsc_padding_params.dsc_pix_clk_100hz / 10; + if (otg_master_pipe->dsc_padding_params.dsc_hactive_padding != 0) + timing->pixel_clock_khz = otg_master_pipe->dsc_padding_params.dsc_pix_clk_100hz / 10; if (stream->timing.timing_3d_format == TIMING_3D_FORMAT_HW_FRAME_PACKING) timing->pixel_clock_khz *= 2; - timing->h_total = stream->timing.h_total + pipe_ctx->dsc_padding_params.dsc_htotal_padding; + timing->h_total = stream->timing.h_total + otg_master_pipe->dsc_padding_params.dsc_htotal_padding; timing->v_total = stream->timing.v_total; timing->h_sync_width = stream->timing.h_sync_width; timing->interlaced = (stream->timing.flags.INTERLACE != 0); hblank_start = stream->timing.h_total - stream->timing.h_front_porch; - timing->h_blank_end = hblank_start - stream->timing.h_addressable - pipe_ctx->dsc_padding_params.dsc_hactive_padding + timing->h_blank_end = hblank_start - stream->timing.h_addressable - otg_master_pipe->dsc_padding_params.dsc_hactive_padding - stream->timing.h_border_left - stream->timing.h_border_right; if (hblank_start < stream->timing.h_addressable) @@ -135,8 +135,8 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf /* limit min refresh rate to DC cap */ min_hardware_refresh_in_uhz = stream->timing.min_refresh_in_uhz; if (stream->ctx->dc->caps.max_v_total != 0) { - if (pipe_ctx->dsc_padding_params.dsc_hactive_padding != 0) { - pix_clk_100hz = pipe_ctx->dsc_padding_params.dsc_pix_clk_100hz; + if (otg_master_pipe->dsc_padding_params.dsc_hactive_padding != 0) { + pix_clk_100hz = otg_master_pipe->dsc_padding_params.dsc_pix_clk_100hz; } else { pix_clk_100hz = stream->timing.pix_clk_100hz; } @@ -197,7 +197,7 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf } static void populate_dml21_output_config_from_stream_state(struct dml2_link_output_cfg *output, - struct dc_stream_state *stream, const struct pipe_ctx *pipe) + struct dc_stream_state *stream, const struct pipe_ctx *otg_master_pipe) { output->output_dp_lane_count = 4; @@ -205,7 +205,7 @@ static void populate_dml21_output_config_from_stream_state(struct dml2_link_outp case SIGNAL_TYPE_DISPLAY_PORT_MST: case SIGNAL_TYPE_DISPLAY_PORT: output->output_encoder = dml2_dp; - if (check_dp2p0_output_encoder(pipe)) + if (check_dp2p0_output_encoder(otg_master_pipe)) output->output_encoder = dml2_dp2p0; break; case SIGNAL_TYPE_EDP: @@ -267,6 +267,61 @@ static void populate_dml21_output_config_from_stream_state(struct dml2_link_outp // output->validate_output } +static void populate_dml21_writeback_config_from_stream_state(struct dml2_writeback_cfg *writeback, + const struct dc_stream_state *stream) +{ + if (stream->num_wb_info > 0) { + writeback->active_writebacks_per_stream = stream->num_wb_info <= DML2_MAX_WRITEBACK ? + stream->num_wb_info : DML2_MAX_WRITEBACK; + + ASSERT(stream->num_wb_info <= DML2_MAX_WRITEBACK); + + for (unsigned int wb_index = 0; wb_index < stream->num_wb_info; wb_index++) { + const struct dc_writeback_info *dc_wb_info = &stream->writeback_info[wb_index]; + struct dml2_writeback_info *wb_info = &writeback->writeback_stream[wb_index]; + + switch (dc_wb_info->dwb_params.cnv_params.fc_out_format) { + case DWB_OUT_FORMAT_64BPP_ARGB: + case DWB_OUT_FORMAT_64BPP_RGBA: + wb_info->pixel_format = dml2_444_64; + break; + case DWB_OUT_FORMAT_32BPP_ARGB: + case DWB_OUT_FORMAT_32BPP_RGBA: + default: + wb_info->pixel_format = dml2_444_32; + break; + } + + wb_info->input_width = dc_wb_info->dwb_params.cnv_params.crop_en ? + dc_wb_info->dwb_params.cnv_params.crop_width : + dc_wb_info->dwb_params.cnv_params.src_width; + wb_info->input_height = dc_wb_info->dwb_params.cnv_params.crop_en ? + dc_wb_info->dwb_params.cnv_params.crop_height : + dc_wb_info->dwb_params.cnv_params.src_height; + wb_info->output_width = dc_wb_info->dwb_params.dest_width; + wb_info->output_height = dc_wb_info->dwb_params.dest_height; + wb_info->v_taps = dc_wb_info->dwb_params.scaler_taps.v_taps > 0 ? + dc_wb_info->dwb_params.scaler_taps.v_taps : 1; + wb_info->h_taps = dc_wb_info->dwb_params.scaler_taps.h_taps > 0 ? + dc_wb_info->dwb_params.scaler_taps.h_taps : 1; + wb_info->v_taps_chroma = dc_wb_info->dwb_params.scaler_taps.v_taps_c > 0 ? + dc_wb_info->dwb_params.scaler_taps.v_taps_c : 1; + wb_info->h_taps_chroma = dc_wb_info->dwb_params.scaler_taps.h_taps_c > 0 ? + dc_wb_info->dwb_params.scaler_taps.h_taps_c : 1; + wb_info->h_ratio = dc_wb_info->dwb_params.cnv_params.crop_en ? + (double)dc_wb_info->dwb_params.cnv_params.crop_width / + (double)dc_wb_info->dwb_params.dest_width : + (double)dc_wb_info->dwb_params.cnv_params.src_width / + (double)dc_wb_info->dwb_params.dest_width; + wb_info->v_ratio = dc_wb_info->dwb_params.cnv_params.crop_en ? + (double)dc_wb_info->dwb_params.cnv_params.crop_height / + (double)dc_wb_info->dwb_params.dest_height : + (double)dc_wb_info->dwb_params.cnv_params.src_height / + (double)dc_wb_info->dwb_params.dest_height; + } + } +} + static void populate_dml21_stream_overrides_from_stream_state( struct dml2_stream_parameters *stream_desc, struct dc_stream_state *stream, @@ -392,8 +447,8 @@ static void populate_dml21_dummy_surface_cfg(struct dml2_surface_cfg *surface, c surface->plane0.pitch = ((surface->plane0.width + 127) / 128) * 128; surface->plane1.pitch = 0; surface->dcc.enable = false; - surface->dcc.informative.dcc_rate_plane0 = 1.0; - surface->dcc.informative.dcc_rate_plane1 = 1.0; + surface->dcc.informative.dcc_rate_plane0 = 2.0; + surface->dcc.informative.dcc_rate_plane1 = 2.0; surface->dcc.informative.fraction_of_zero_size_request_plane0 = 0; surface->dcc.informative.fraction_of_zero_size_request_plane1 = 0; surface->tiling = dml2_sw_64kb_2d; @@ -462,8 +517,8 @@ static void populate_dml21_surface_config_from_plane_state( surface->plane1.height = plane_state->plane_size.chroma_size.height; surface->plane1.width = plane_state->plane_size.chroma_size.width; surface->dcc.enable = plane_state->dcc.enable; - surface->dcc.informative.dcc_rate_plane0 = 1.0; - surface->dcc.informative.dcc_rate_plane1 = 1.0; + surface->dcc.informative.dcc_rate_plane0 = 2.0; + surface->dcc.informative.dcc_rate_plane1 = 2.0; surface->dcc.informative.fraction_of_zero_size_request_plane0 = plane_state->dcc.independent_64b_blks; surface->dcc.informative.fraction_of_zero_size_request_plane1 = plane_state->dcc.independent_64b_blks_c; surface->dcc.plane0.pitch = plane_state->dcc.meta_pitch; @@ -795,6 +850,7 @@ bool dml21_map_dc_state_into_dml_display_cfg(const struct dc *in_dc, struct dc_s int disp_cfg_stream_location, disp_cfg_plane_location; struct dml2_display_cfg *dml_dispcfg = &dml_ctx->v21.display_config; unsigned int plane_count = 0; + struct pipe_ctx *otg_master_pipe; memset(&dml_ctx->v21.dml_to_dc_pipe_mapping, 0, sizeof(struct dml2_dml_to_dc_pipe_mapping)); @@ -819,9 +875,13 @@ bool dml21_map_dc_state_into_dml_display_cfg(const struct dc *in_dc, struct dc_s if (disp_cfg_stream_location < 0) disp_cfg_stream_location = dml_dispcfg->num_streams++; + otg_master_pipe = dml_ctx->config.callbacks.get_otg_master_for_stream(&context->res_ctx, context->streams[stream_index]); + ASSERT(otg_master_pipe); + ASSERT(disp_cfg_stream_location >= 0 && disp_cfg_stream_location < __DML2_WRAPPER_MAX_STREAMS_PLANES__); - populate_dml21_timing_config_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location].timing, context->streams[stream_index], &context->res_ctx.pipe_ctx[stream_index], dml_ctx); - populate_dml21_output_config_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location].output, context->streams[stream_index], &context->res_ctx.pipe_ctx[stream_index]); + populate_dml21_timing_config_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location].timing, context->streams[stream_index], otg_master_pipe, dml_ctx); + populate_dml21_output_config_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location].output, context->streams[stream_index], otg_master_pipe); + populate_dml21_writeback_config_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location].writeback, context->streams[stream_index]); populate_dml21_stream_overrides_from_stream_state(&dml_dispcfg->stream_descriptors[disp_cfg_stream_location], context->streams[stream_index], &context->stream_status[stream_index]); dml_dispcfg->stream_descriptors[disp_cfg_stream_location].overrides.hw.twait_budgeting.fclk_pstate = dml2_twait_budgeting_setting_if_needed; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c index 835fece1d46a..012dff4cf3fa 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c @@ -359,14 +359,13 @@ void dml21_handle_phantom_streams_planes(const struct dc *dc, struct dc_state *c main_plane = main_stream_status->plane_states[dc_plane_index]; /* create phantom planes for subvp enabled plane */ - dml21_add_phantom_plane(dml_ctx, - dc, - context, - phantom_stream, - main_plane, - &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index]); - - phantoms_added = true; + if (dml21_add_phantom_plane(dml_ctx, + dc, + context, + phantom_stream, + main_plane, + &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index])) + phantoms_added = true; } } } @@ -532,3 +531,20 @@ bool dml21_is_plane1_enabled(enum dml2_source_format_class source_format) { return source_format >= dml2_420_8 && source_format <= dml2_rgbe_alpha; } + +void dml21_program_dc_mcif_arb_params(struct dml2_context *dml_ctx, + struct dc_state *context, + struct dml2_per_stream_programming *stream_prog, + unsigned int wb_index, + unsigned int dwb_inst) +{ + /* DC struct contains global reg for every WB instance */ + memcpy(&context->bw_ctx.bw.dcn.bw_writeback.mcif_wb_arb[dwb_inst].dcn4x.global_regs, + &dml_ctx->v21.mode_programming.programming->mcif_global_regs, + sizeof(struct dml2_mcif_global_register_set)); + + /* copy per-DWB pipe registers */ + memcpy(&context->bw_ctx.bw.dcn.bw_writeback.mcif_wb_arb[dwb_inst].dcn4x.inst_regs, + stream_prog->mcif_regs[wb_index], + sizeof(struct dml2_mcif_per_pipe_register_set)); +} diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h index bff945a4ab3a..7312c33143c6 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h @@ -48,4 +48,9 @@ void dml21_build_fams2_programming(const struct dc *dc, struct dc_state *context, struct dml2_context *dml_ctx); bool dml21_is_plane1_enabled(enum dml2_source_format_class source_format); +void dml21_program_dc_mcif_arb_params(struct dml2_context *dml_ctx, + struct dc_state *context, + struct dml2_per_stream_programming *stream_prog, + unsigned int wb_index, + unsigned int dwb_inst); #endif diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c index 11fc0b1cd152..aba7dd65c539 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c @@ -45,7 +45,22 @@ void dml21_init(const struct dc *in_dc, struct dml2_context *dml_ctx, const stru void dml21_reinit(const struct dc *in_dc, struct dml2_context *dml_ctx, const struct dml2_configuration_options *config) { - dml21_init(in_dc, dml_ctx, config); + struct dml2_instance *dml2 = dml_ctx->v21.dml_init.dml2_instance; + + dml21_populate_configuration_options(in_dc, dml_ctx, config); + + dml21_populate_dml_init_params(&dml_ctx->v21.dml_init, &dml_ctx->config, in_dc); + + // Skip full re-initialization if soc_bb, ip_caps and pmo_options are unchanged + if (memcmp(&dml2->soc_bbox, &dml_ctx->v21.dml_init.soc_bb, + sizeof(struct dml2_soc_bb)) == 0 && + memcmp(&dml2->ip_caps, &dml_ctx->v21.dml_init.ip_caps, + sizeof(struct dml2_ip_capabilities)) == 0 && + memcmp(&dml2->pmo_options, &dml_ctx->v21.dml_init.options.pmo_options, + sizeof(struct dml2_pmo_options)) == 0) + return; + + dml2_initialize_instance(&dml_ctx->v21.dml_init); } static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_state *context, struct resource_context *out_new_hw_state, @@ -60,6 +75,7 @@ static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_sta struct pipe_ctx *dc_phantom_pipes[__DML2_WRAPPER_MAX_STREAMS_PLANES__] = {0}; int num_pipes; unsigned int dml_phantom_prog_idx; + unsigned int stream_wb_idx; context->bw_ctx.bw.dcn.clk.dppclk_khz = 0; @@ -102,6 +118,15 @@ static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_sta } } + /* program WB */ + for (stream_wb_idx = 0; stream_wb_idx < stream_prog->stream_descriptor->writeback.active_writebacks_per_stream; stream_wb_idx++) { + dml21_program_dc_mcif_arb_params(in_ctx, + context, + stream_prog, + stream_wb_idx, + dc_main_pipes[0]->stream->writeback_info[stream_wb_idx].dwb_pipe_inst); + } + /* copy per plane mcache allocation */ memcpy(&context->bw_ctx.bw.dcn.mcache_allocations[dml_prog_idx], &pln_prog->mcache_allocation, sizeof(struct dml2_mcache_surface_allocation)); if (pln_prog->phantom_plane.valid) { @@ -130,6 +155,11 @@ static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_sta context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[0]; } + if (dc->config.forced_clocks || dc->debug.max_disp_clk) { + context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz; + context->bw_ctx.bw.dcn.clk.bw_dppclk_khz = context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz; + } + /* get global mall allocation */ if (dc->res_pool->funcs->calculate_mall_ways_from_bytes) { context->bw_ctx.bw.dcn.clk.num_ways = dc->res_pool->funcs->calculate_mall_ways_from_bytes(dc, context->bw_ctx.bw.dcn.mall_subvp_size_bytes); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h index 60ef56419846..5d844ff4b671 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h @@ -210,7 +210,7 @@ static const struct dml2_soc_bb dml2_socbb_dcn42b = { .fabric_datapath_to_dcn_data_return_bytes = 32, .return_bus_width_bytes = 64, .hostvm_min_page_size_kbytes = 4, - .gpuvm_min_page_size_kbytes = 256, + .gpuvm_min_page_size_kbytes = 4, .gpuvm_max_page_table_levels = 1, .hostvm_max_non_cached_page_table_levels = 2, .phy_downspread_percent = 0.38, @@ -236,7 +236,7 @@ static const struct dml2_ip_capabilities dml2_dcn42b_max_ip_caps = { .config_return_buffer_segment_size_in_kbytes = 64, .meta_fifo_size_in_kentries = 32, .compressed_buffer_segment_size_in_kbytes = 64, - .cursor_buffer_size = 24, + .cursor_buffer_size = 42, .max_flip_time_us = 110, .max_flip_time_lines = 50, .hostvm_mode = 0, diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h index bf57df42d1d9..5a5f7b92c324 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h @@ -46,6 +46,9 @@ struct dml2_display_dlg_regs { uint32_t dst_y_delta_drq_limit; uint32_t refcyc_per_vm_dmdata; uint32_t dmdata_dl_delta; + uint32_t dst_y_svp_drq_limit; + uint32_t force_prefetch_to_vblank; + uint32_t force_cursor_to_disp_pref; // MRQ uint32_t refcyc_per_meta_chunk_vblank_l; @@ -153,6 +156,13 @@ struct dml2_dchub_per_pipe_register_set { uint32_t det_size; }; +struct dml2_mcif_per_pipe_register_set { + unsigned int time_per_pixel; // U6.6 format + unsigned int arbitration_slice; + unsigned int slice_lines; + unsigned int max_scaled_time_ns; +}; + struct dml2_dchub_watermark_regs { /* watermarks */ uint32_t urgent; @@ -164,8 +174,15 @@ struct dml2_dchub_watermark_regs { uint32_t sr_exit_low_power; uint32_t uclk_pstate; uint32_t fclk_pstate; + union { uint32_t temp_read_or_ppt; + uint32_t temp_read; + }; + uint32_t ppt; + union { uint32_t usr; + uint32_t buffer_fullness; + }; /* qos */ uint32_t refcyc_per_trip_to_mem; uint32_t refcyc_per_meta_trip_to_mem; @@ -188,4 +205,17 @@ struct dml2_dchub_global_register_set { unsigned int num_watermark_sets; }; +struct dml2_mcif_watermark_regs { + /* watermarks */ + uint32_t urgent; /* (CLI) */ + uint32_t uclk_pstate; + uint32_t fclk_pstate; + uint32_t temp_read_or_ppt; +}; + +struct dml2_mcif_global_register_set { + struct dml2_mcif_watermark_regs wm_regs[DML2_DCHUB_WATERMARK_SET_NUM]; + unsigned int num_watermark_sets; +}; + #endif diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h index 8d7960a340c2..f00ca2983ff5 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h @@ -279,6 +279,8 @@ struct dml2_per_stream_programming { enum dml2_pstate_method uclk_pstate_method; + struct dml2_mcif_per_pipe_register_set *mcif_regs[DML2_MAX_WRITEBACK]; + struct { bool enabled; struct dml2_stream_parameters descriptor; @@ -436,12 +438,14 @@ struct dml2_display_cfg_programming { } z8_stutter; struct dml2_dchub_global_register_set global_regs; + struct dml2_mcif_global_register_set mcif_global_regs; struct dml2_per_plane_programming plane_programming[DML2_MAX_PLANES]; struct dml2_per_stream_programming stream_programming[DML2_MAX_PLANES]; // Don't access this structure directly, access it through plane_programming.pipe_regs struct dml2_dchub_per_pipe_register_set pipe_regs[DML2_MAX_PLANES]; + struct dml2_mcif_per_pipe_register_set mcif_regs[DML2_MAX_WRITEBACK]; struct { struct { @@ -457,6 +461,7 @@ struct dml2_display_cfg_programming { double fclk_pstate_change_us; double usr_retraining_us; double temp_read_or_ppt_watermark_us; + double writeback_temp_read_or_ppt_watermark_us; } watermarks; struct { diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c index c983869e0fa3..6ea36aedcf20 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c @@ -368,9 +368,10 @@ static void expand_implict_subvp(const struct display_configuation_with_meta *di static void pack_mode_programming_params_with_implicit_subvp(struct dml2_core_instance *core, const struct display_configuation_with_meta *display_cfg, const struct dml2_display_cfg *svp_expanded_display_cfg, struct dml2_display_cfg_programming *programming, struct dml2_core_scratch *scratch) { - unsigned int stream_index, plane_index, pipe_offset, stream_already_populated_mask, main_plane_index, mcache_index; + unsigned int stream_index, plane_index, pipe_offset, stream_already_populated_mask, main_plane_index, mcache_index, dwb_index; unsigned int total_main_mcaches_required = 0; int total_pipe_regs_copied = 0; + int total_dwb_regs_copied = 0; int dml_internal_pipe_index = 0; const struct dml2_plane_parameters *main_plane; const struct dml2_plane_parameters *phantom_plane; @@ -386,6 +387,8 @@ static void pack_mode_programming_params_with_implicit_subvp(struct dml2_core_in // display config dml2_core_calcs_get_watermarks(&display_cfg->display_config, &core->clean_me_up.mode_lib, &programming->global_regs.wm_regs[0]); + dml2_core_calcs_get_mcif_arb_params(&core->clean_me_up.mode_lib, &programming->mcif_global_regs); + // Check if FAMS2 is required if (display_cfg->stage3.performed && display_cfg->stage3.success) { programming->fams2_required = display_cfg->stage3.fams2_required; @@ -470,6 +473,19 @@ static void pack_mode_programming_params_with_implicit_subvp(struct dml2_core_in programming->stream_programming[main_plane->stream_index].uclk_pstate_method, plane_index); + /* populate DWB */ + for (dwb_index = 0; dwb_index < svp_expanded_display_cfg->stream_descriptors[main_plane->stream_index].writeback.active_writebacks_per_stream; dwb_index++) { + programming->stream_programming[main_plane->stream_index].mcif_regs[dwb_index] = &programming->mcif_regs[total_dwb_regs_copied]; + memset(programming->stream_programming[main_plane->stream_index].mcif_regs[dwb_index], 0, sizeof(struct dml2_mcif_per_pipe_register_set)); + total_dwb_regs_copied++; + + dml2_core_calcs_get_per_dwb_params(svp_expanded_display_cfg, + &core->clean_me_up.mode_lib, + programming->stream_programming[main_plane->stream_index].mcif_regs[dwb_index], + main_plane->stream_index, + dwb_index); + } + stream_already_populated_mask |= (0x1 << main_plane->stream_index); } dml_internal_pipe_index++; @@ -538,6 +554,10 @@ bool core_dcn4_mode_support(struct dml2_core_mode_support_in_out *in_out) l->mode_support_ex_params.min_clk_index = in_out->min_clk_index; l->mode_support_ex_params.out_evaluation_info = &in_out->mode_support_result.cfg_support_info.clean_me_up.support_info; + l->mode_support_ex_params.uclk_pstate_switch_modes = + in_out->display_cfg->stage3.performed ? + in_out->display_cfg->stage3.pstate_switch_modes : NULL; + result = dml2_core_calcs_mode_support_ex(&l->mode_support_ex_params); in_out->mode_support_result.cfg_support_info.is_supported = result; @@ -661,10 +681,12 @@ bool core_dcn4_mode_programming(struct dml2_core_mode_programming_in_out *in_out unsigned int pipe_offset; int dml_internal_pipe_index; int total_pipe_regs_copied = 0; + int total_dwb_regs_copied = 0; int stream_already_populated_mask = 0; int main_stream_index; unsigned int plane_index; + unsigned int dwb_index; expand_implict_subvp(in_out->display_cfg, &l->svp_expanded_display_cfg, &core->scratch); @@ -688,6 +710,8 @@ bool core_dcn4_mode_programming(struct dml2_core_mode_programming_in_out *in_out dml2_core_calcs_get_arb_params(&l->svp_expanded_display_cfg, &core->clean_me_up.mode_lib, &in_out->programming->global_regs.arb_regs); dml2_core_calcs_get_watermarks(&l->svp_expanded_display_cfg, &core->clean_me_up.mode_lib, &in_out->programming->global_regs.wm_regs[0]); + dml2_core_calcs_get_mcif_arb_params(&core->clean_me_up.mode_lib, &in_out->programming->mcif_global_regs); + dml_internal_pipe_index = 0; for (plane_index = 0; plane_index < in_out->programming->display_config.num_planes; plane_index++) { @@ -733,6 +757,18 @@ bool core_dcn4_mode_programming(struct dml2_core_mode_programming_in_out *in_out in_out->programming->stream_programming[main_stream_index].num_odms_required = in_out->cfg_support_info->stream_support_info[main_stream_index].odms_used; dml2_core_calcs_get_stream_programming(&core->clean_me_up.mode_lib, &in_out->programming->stream_programming[main_stream_index], dml_internal_pipe_index); + for (dwb_index = 0; dwb_index < l->svp_expanded_display_cfg.stream_descriptors[main_stream_index].writeback.active_writebacks_per_stream; dwb_index++) { + in_out->programming->stream_programming[main_stream_index].mcif_regs[dwb_index] = &in_out->programming->mcif_regs[total_dwb_regs_copied]; + memset(in_out->programming->stream_programming[main_stream_index].mcif_regs[dwb_index], 0, sizeof(struct dml2_mcif_per_pipe_register_set)); + total_dwb_regs_copied++; + + dml2_core_calcs_get_per_dwb_params(&l->svp_expanded_display_cfg, + &core->clean_me_up.mode_lib, + in_out->programming->stream_programming[main_stream_index].mcif_regs[dwb_index], + main_stream_index, + dwb_index); + } + stream_already_populated_mask |= (0x1 << main_stream_index); } dml_internal_pipe_index++; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index 51a66e1be7a1..09452f3e49db 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -317,6 +317,7 @@ dml_get_var_func(meta_trip_memory_us, double, mode_lib->mp.MetaTripToMemory); dml_get_var_func(wm_fclk_change, double, mode_lib->mp.Watermark.FCLKChangeWatermark); dml_get_var_func(wm_usr_retraining, double, mode_lib->mp.Watermark.USRRetrainingWatermark); dml_get_var_func(wm_temp_read_or_ppt, double, mode_lib->mp.Watermark.temp_read_or_ppt_watermark_us); +dml_get_var_func(wm_writeback_temp_read_or_ppt, double, mode_lib->mp.Watermark.writeback_temp_read_or_ppt_watermark_us); dml_get_var_func(wm_dram_clock_change, double, mode_lib->mp.Watermark.DRAMClockChangeWatermark); dml_get_var_func(fraction_of_urgent_bandwidth, double, mode_lib->mp.FractionOfUrgentBandwidth); dml_get_var_func(fraction_of_urgent_bandwidth_imm_flip, double, mode_lib->mp.FractionOfUrgentBandwidthImmediateFlip); @@ -2452,7 +2453,7 @@ static void calculate_mcache_row_bytes( DML_ASSERT(*p->num_mcaches > 0); } -static void calculate_mcache_setting( +static bool calculate_mcache_setting( struct dml2_core_internal_scratch *scratch, struct dml2_core_calcs_calculate_mcache_setting_params *p) { @@ -2478,7 +2479,7 @@ static void calculate_mcache_setting( *p->lc_comb_mcache = 0; if (!p->dcc_enable) - return; + return true; l->is_dual_plane = dml_is_420(p->source_format) || p->source_format == dml2_rgbe_alpha; @@ -2515,7 +2516,14 @@ static void calculate_mcache_setting( l->l_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_l; calculate_mcache_row_bytes(scratch, &l->l_p); - DML_ASSERT(*p->num_mcaches_l > 0); + if (*p->num_mcaches_l == 0 || + (p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l) == 0) { + DML_LOG_VERBOSE("DML::%s: degenerate luma viewport (num_mcaches_l=%u mvmpg_%s_l=%u) — mode not supported\n", + __func__, *p->num_mcaches_l, + p->surf_vert ? "height" : "width", + p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l); + return false; + } if (l->is_dual_plane) { l->c_p.num_chans = p->num_chans; @@ -2551,7 +2559,14 @@ static void calculate_mcache_setting( l->c_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_c; calculate_mcache_row_bytes(scratch, &l->c_p); - DML_ASSERT(*p->num_mcaches_c > 0); + if (*p->num_mcaches_c == 0 || + (p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c) == 0) { + DML_LOG_VERBOSE("DML::%s: degenerate chroma viewport (num_mcaches_c=%u mvmpg_%s_c=%u) — mode not supported\n", + __func__, *p->num_mcaches_c, + p->surf_vert ? "height" : "width", + p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c); + return false; + } } // Sharing for iMALL access @@ -2661,6 +2676,7 @@ static void calculate_mcache_setting( *p->mcache_shift_granularity_l = l->mvmpg_access_width_l; *p->mcache_shift_granularity_c = l->mvmpg_access_width_c; + return true; } static void calculate_mall_bw_overhead_factor( @@ -5606,7 +5622,8 @@ static bool CalculatePrefetchSchedule(struct dml2_core_internal_scratch *scratch + 2 * (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) + *p->prefetch_sw_bytes) / (*p->Tpre_rounded - *p->Tno_bw); - s->Tsw_est1 = *p->prefetch_sw_bytes / s->prefetch_bw1; + /* due to rounding, VM can clamp to +1/4, Row can clamp to +2/4, giving SW less time */ + s->Tsw_est1 = *p->prefetch_sw_bytes / s->prefetch_bw1 - 3.0 * s->LineTime / 4.0; } else s->prefetch_bw1 = 0; @@ -5630,7 +5647,8 @@ static bool CalculatePrefetchSchedule(struct dml2_core_internal_scratch *scratch if (*p->Tpre_rounded - *p->Tno_bw - 2.0 * s->Tr0_trips_rounded > 0) { s->prefetch_bw2 = (vm_bytes * p->HostVMInefficiencyFactor + *p->prefetch_sw_bytes) / (*p->Tpre_rounded - *p->Tno_bw - 2.0 * s->Tr0_trips_rounded); - s->Tsw_est2 = *p->prefetch_sw_bytes / s->prefetch_bw2; + /* due to rounding, VM can clamp to +1/4 */ + s->Tsw_est2 = *p->prefetch_sw_bytes / s->prefetch_bw2 - 1.0 * s->LineTime / 4.0; } else s->prefetch_bw2 = 0; @@ -5644,7 +5662,8 @@ static bool CalculatePrefetchSchedule(struct dml2_core_internal_scratch *scratch if (*p->Tpre_rounded - s->Tvm_trips_rounded > 0) { s->prefetch_bw3 = (2 * (p->PixelPTEBytesPerRow * p->HostVMInefficiencyFactor + p->meta_row_bytes + tdlut_row_bytes) + *p->prefetch_sw_bytes) / (*p->Tpre_rounded - s->Tvm_trips_rounded); - s->Tsw_est3 = *p->prefetch_sw_bytes / s->prefetch_bw3; + /* due to rounding, Row can clamp to +2/4 */ + s->Tsw_est3 = *p->prefetch_sw_bytes / s->prefetch_bw3 - 2.0 * s->LineTime / 4.0; } else s->prefetch_bw3 = 0; @@ -6740,6 +6759,25 @@ static void CalculateFlipSchedule( #endif } +static double calculate_writeback_latency_hiding_us( + const struct dml2_display_cfg *display_cfg, + unsigned int writeback_buffer_size_bytes, + unsigned int stream_index, + unsigned int dwb_index) +{ + double line_time_us = (double)display_cfg->stream_descriptors[stream_index].timing.h_total / + (double)display_cfg->stream_descriptors[stream_index].timing.pixel_clock_khz / 1000.0; + + double writeback_latency_hiding_us = (double)writeback_buffer_size_bytes / + ((double)display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].output_height * + (double)display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].output_width / + ((double)display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].input_height * + line_time_us) * 4.0); + + return display_cfg->stream_descriptors[stream_index].writeback.writeback_stream[dwb_index].pixel_format == dml2_444_64 ? + writeback_latency_hiding_us / 2 : writeback_latency_hiding_us; +} + static void CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport( struct dml2_core_internal_scratch *scratch, struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_params *p) @@ -6904,13 +6942,10 @@ static void CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport( p->VActiveLatencyHidingUs[k] = s->ActiveClockChangeLatencyHiding; if (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream > 0) { - s->WritebackLatencyHiding = (double)p->WritebackInterfaceBufferSize * 1024.0 - / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[0].output_height - * (double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[0].output_width - / ((double)p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[0].input_height * (double)h_total / pixel_clock_mhz) * 4.0); - if (p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[k].stream_index].writeback.writeback_stream[0].pixel_format == dml2_444_64) { - s->WritebackLatencyHiding = s->WritebackLatencyHiding / 2; - } + s->WritebackLatencyHiding = calculate_writeback_latency_hiding_us(p->display_cfg, + p->WritebackInterfaceBufferSize * 1024, + p->display_cfg->plane_descriptors[k].stream_index, + 0); s->WritebackDRAMClockChangeLatencyMargin = s->WritebackLatencyHiding - p->Watermark->WritebackDRAMClockChangeWatermark; s->WritebackFCLKChangeLatencyMargin = s->WritebackLatencyHiding - p->Watermark->WritebackFCLKChangeWatermark; @@ -8022,6 +8057,16 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out memset(&mode_lib->ms, 0, sizeof(struct dml2_core_internal_mode_support)); mode_lib->ms.num_active_planes = display_cfg->num_planes; + + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + if (in_out_params->uclk_pstate_switch_modes && + !dml_is_phantom_pipe(&display_cfg->plane_descriptors[k])) + mode_lib->ms.uclk_pstate_switch_modes[k] = + in_out_params->uclk_pstate_switch_modes[k]; + else + mode_lib->ms.uclk_pstate_switch_modes[k] = dml2_pstate_method_na; + } + get_stream_output_bpp(s->OutputBpp, display_cfg); mode_lib->ms.state_idx = in_out_params->min_clk_index; @@ -8692,6 +8737,7 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out s->TotalNumberOfActiveWriteback = 0; memset(s->stream_visited, 0, DML2_MAX_PLANES * sizeof(bool)); + mode_lib->ms.support.EnoughWritebackUnits = true; for (k = 0; k < mode_lib->ms.num_active_planes; ++k) { if (!dml_is_phantom_pipe(&display_cfg->plane_descriptors[k])) { if (!s->stream_visited[display_cfg->plane_descriptors[k].stream_index]) { @@ -8700,6 +8746,10 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream > 0) s->TotalNumberOfActiveWriteback = s->TotalNumberOfActiveWriteback + 1; + /* >1 writeback per stream is currently not supported */ + if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].writeback.active_writebacks_per_stream > 1) + mode_lib->ms.support.EnoughWritebackUnits = false; + s->TotalNumberOfActiveOTG = s->TotalNumberOfActiveOTG + 1; if (display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].output.output_encoder == dml2_hdmifrl) s->TotalNumberOfActiveHDMIFRL = s->TotalNumberOfActiveHDMIFRL + 1; @@ -8715,10 +8765,10 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out } /* Writeback Mode Support Check */ - mode_lib->ms.support.EnoughWritebackUnits = 1; if (s->TotalNumberOfActiveWriteback > (unsigned int)mode_lib->ip.max_num_wb) { mode_lib->ms.support.EnoughWritebackUnits = false; } + mode_lib->ms.support.NumberOfOTGSupport = (s->TotalNumberOfActiveOTG <= (unsigned int)mode_lib->ip.max_num_otg); mode_lib->ms.support.NumberOfHDMIFRLSupport = (s->TotalNumberOfActiveHDMIFRL <= (unsigned int)mode_lib->ip.max_num_hdmi_frl_outputs); mode_lib->ms.support.NumberOfDP2p0Support = (s->TotalNumberOfActiveDP2p0 <= (unsigned int)mode_lib->ip.max_num_dp2p0_streams && s->TotalNumberOfActiveDP2p0Outputs <= (unsigned int)mode_lib->ip.max_num_dp2p0_outputs); @@ -9468,7 +9518,10 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->ms.mall_comb_mcache_c[k]; calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->ms.lc_comb_mcache[k]; - calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params); + if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params)) { + mode_lib->ms.support.ModeSupport = false; + return false; + } } calculate_mall_bw_overhead_factor( @@ -9597,6 +9650,21 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out DML_LOG_VERBOSE("DML::%s: ROBSupport = %u\n", __func__, mode_lib->ms.support.ROBSupport); #endif + mode_lib->ms.support.global_dram_clock_change_support_required = false; + + if (!display_cfg->overrides.all_streams_blanked) { + for (k = 0; k < mode_lib->ms.num_active_planes; k++) { + if (mode_lib->ms.uclk_pstate_switch_modes[k] != dml2_pstate_method_vactive && + mode_lib->ms.uclk_pstate_switch_modes[k] != dml2_pstate_method_fw_vactive_drr) + continue; + + mode_lib->ms.support.global_dram_clock_change_support_required = true; + + if (mode_lib->ms.VActiveLatencyHidingMargin[k] < 0) + mode_lib->ms.support.global_dram_clock_change_supported = false; + } + } + /*Mode Support, Voltage State and SOC Configuration*/ { if (mode_lib->ms.support.ScaleRatioAndTapsSupport @@ -9643,6 +9711,8 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out && mode_lib->ms.support.DCCMetaBufferSizeNotExceeded && !mode_lib->ms.support.ExceededMALLSize && mode_lib->ms.support.g6_temp_read_support + && (mode_lib->ms.support.global_dram_clock_change_supported + || !mode_lib->ms.support.global_dram_clock_change_support_required) && ((!display_cfg->hostvm_enable && !s->ImmediateFlipRequired) || mode_lib->ms.support.ImmediateFlipSupport)) { DML_LOG_VERBOSE("DML::%s: mode is supported\n", __func__); mode_lib->ms.support.ModeSupport = true; @@ -10947,7 +11017,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex calculate_mcache_setting_params->mall_comb_mcache_l = &mode_lib->mp.mall_comb_mcache_l[k]; calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->mp.mall_comb_mcache_c[k]; calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->mp.lc_comb_mcache[k]; - calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params); + if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params)) + return false; } calculate_mall_bw_overhead_factor( @@ -12830,6 +12901,14 @@ void dml2_core_calcs_get_arb_params(const struct dml2_display_cfg *display_cfg, rq_dlg_get_arb_params(display_cfg, mode_lib, out); } +void dml2_core_calcs_get_mcif_arb_params(const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_mcif_global_register_set *out) +{ + out->wm_regs[0].fclk_pstate = (unsigned int)(mode_lib->mp.Watermark.WritebackFCLKChangeWatermark * 1000.0); + out->wm_regs[0].uclk_pstate = (unsigned int)(mode_lib->mp.Watermark.WritebackDRAMClockChangeWatermark * 1000.0); + out->wm_regs[0].urgent = (unsigned int)(mode_lib->mp.Watermark.WritebackUrgentWatermark * 1000.0); + out->wm_regs[0].temp_read_or_ppt = (unsigned int)(mode_lib->mp.Watermark.writeback_temp_read_or_ppt_watermark_us * 1000.0); +} + void dml2_core_calcs_get_pipe_regs(const struct dml2_display_cfg *display_cfg, struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_dchub_per_pipe_register_set *out, int pipe_index) @@ -12853,6 +12932,29 @@ void dml2_core_calcs_get_stream_programming(const struct dml2_core_internal_disp dml2_core_calcs_get_global_sync_programming(mode_lib, &out->global_sync, pipe_index); } +void dml2_core_calcs_get_per_dwb_params(const struct dml2_display_cfg *display_cfg, + const struct dml2_core_internal_display_mode_lib *mode_lib, + struct dml2_mcif_per_pipe_register_set *out, + int stream_index, + int dwb_index) +{ + double writeback_latency_hiding_us = calculate_writeback_latency_hiding_us(display_cfg, + mode_lib->ip.writeback_interface_buffer_size_kbytes * 1024, + stream_index, + dwb_index); + + out->max_scaled_time_ns = (unsigned int)math_max2( + (writeback_latency_hiding_us - mode_lib->mp.Watermark.WritebackUrgentWatermark) * 1000.0, + 0.0); + + /* 1024ps units in U6.6 format */ + out->time_per_pixel = (unsigned int)((1000000.0 * math_pow(2, 6)) / + (double)display_cfg->stream_descriptors[stream_index].timing.pixel_clock_khz); + + out->slice_lines = 31; + out->arbitration_slice = 2; +} + void dml2_core_calcs_get_global_fams2_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, const struct display_configuation_with_meta *display_cfg, struct dmub_cmd_fams2_global_config *fams2_global_config) @@ -13230,6 +13332,8 @@ void dml2_core_calcs_get_informative(const struct dml2_core_internal_display_mod out->informative.watermarks.fclk_pstate_change_us = dml_get_wm_fclk_change(mode_lib); out->informative.watermarks.usr_retraining_us = dml_get_wm_usr_retraining(mode_lib); out->informative.watermarks.temp_read_or_ppt_watermark_us = dml_get_wm_temp_read_or_ppt(mode_lib); + out->informative.watermarks.writeback_temp_read_or_ppt_watermark_us = dml_get_wm_writeback_temp_read_or_ppt(mode_lib); + out->informative.mall.total_surface_size_in_mall_bytes = 0; out->informative.dpp.total_num_dpps_required = 0; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h index 27ef0e096b25..3249f6bcf7bc 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h @@ -19,6 +19,7 @@ struct display_configuation_with_meta; unsigned int dml2_core_calcs_mode_support_ex(struct dml2_core_calcs_mode_support_ex *in_out_params); bool dml2_core_calcs_mode_programming_ex(struct dml2_core_calcs_mode_programming_ex *in_out_params); void dml2_core_calcs_get_watermarks(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_dchub_watermark_regs *out); +void dml2_core_calcs_get_mcif_arb_params(const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_mcif_global_register_set *out); void dml2_core_calcs_get_arb_params(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_display_arb_regs *out); void dml2_core_calcs_get_pipe_regs(const struct dml2_display_cfg *dml2_display_cfg, struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_dchub_per_pipe_register_set *out, int pipe_index); void dml2_core_calcs_get_stream_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_per_stream_programming *out, int pipe_index); @@ -30,6 +31,7 @@ void dml2_core_calcs_get_stream_support_info(const struct dml2_display_cfg *disp void dml2_core_calcs_get_mall_allocation(struct dml2_core_internal_display_mode_lib *mode_lib, unsigned int *out, int pipe_index); void dml2_core_calcs_get_stream_fams2_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, const struct display_configuation_with_meta *display_cfg, union dmub_cmd_fams2_config *fams2_base_programming, union dmub_cmd_fams2_config *fams2_sub_programming, enum dml2_pstate_method pstate_method, int plane_index); void dml2_core_calcs_get_global_fams2_programming(const struct dml2_core_internal_display_mode_lib *mode_lib, const struct display_configuation_with_meta *display_cfg, struct dmub_cmd_fams2_global_config *fams2_global_config); +void dml2_core_calcs_get_per_dwb_params(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, struct dml2_mcif_per_pipe_register_set *out, int stream_index, int dwb_index); void dml2_core_calcs_get_dpte_row_height(unsigned int *dpte_row_height, struct dml2_core_internal_display_mode_lib *mode_lib, bool is_plane1, enum dml2_source_format_class SourcePixelFormat, enum dml2_swizzle_mode SurfaceTiling, enum dml2_rotation_angle ScanDirection, unsigned int pitch, unsigned int GPUVMMinPageSizeKBytes); void dml2_core_calcs_cursor_dlg_reg(struct dml2_cursor_dlg_regs *cursor_dlg_regs, const struct dml2_get_cursor_dlg_reg *p); diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h index e9f970794488..b8af72392176 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h @@ -212,6 +212,7 @@ struct dml2_core_internal_watermarks { double Z8StutterEnterPlusExitWatermark; double USRRetrainingWatermark; double temp_read_or_ppt_watermark_us; + double writeback_temp_read_or_ppt_watermark_us; }; struct dml2_core_internal_mode_support_info { @@ -1269,6 +1270,7 @@ struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_local double FullDETBufferingTimeC; double WritebackDRAMClockChangeLatencyMargin; double WritebackFCLKChangeLatencyMargin; + double WritebackTempReadOrPptLatencyMargin; double WritebackLatencyHiding; unsigned int TotalActiveWriteback; @@ -2331,6 +2333,7 @@ struct dml2_core_calcs_mode_support_ex { int min_clk_index; //unsigned int in_state_index; struct dml2_core_internal_mode_support_info *out_evaluation_info; + const enum dml2_pstate_method *uclk_pstate_switch_modes; }; struct core_display_cfg_support_info; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c index 5ffe211a6643..c60a1faf2d5d 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c @@ -779,6 +779,25 @@ bool dpmm_dcn4_map_mode_to_soc_dpm(struct dml2_dpmm_map_mode_to_soc_dpm_params_i return result; } +static void dpmm_dcn4_map_mcif_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_out) +{ + const struct dml2_core_internal_display_mode_lib *mode_lib = &in_out->core->clean_me_up.mode_lib; + struct dml2_mcif_global_register_set *mcif_regs = &in_out->programming->mcif_global_regs; + + /* MCIF */ + mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].urgent = (int unsigned)(mode_lib->mp.Watermark.WritebackUrgentWatermark * 1000.0); + mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].uclk_pstate = (int unsigned)(mode_lib->mp.Watermark.WritebackDRAMClockChangeWatermark * 1000.0); + mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].fclk_pstate = (int unsigned)(mode_lib->mp.Watermark.WritebackFCLKChangeWatermark * 1000.0); + mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A].temp_read_or_ppt = (int unsigned)(mode_lib->mp.Watermark.writeback_temp_read_or_ppt_watermark_us * 1000.0); + + /* replicate sets A through D */ + memcpy(&mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_B], &mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A], sizeof(mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A])); + memcpy(&mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_C], &mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A], sizeof(mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A])); + memcpy(&mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_D], &mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A], sizeof(mcif_regs->wm_regs[DML2_DCHUB_WATERMARK_SET_A])); + + mcif_regs->num_watermark_sets = 4; +} + bool dpmm_dcn4_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_out) { const struct dml2_display_cfg *display_cfg = &in_out->display_cfg->display_config; @@ -821,6 +840,8 @@ bool dpmm_dcn4_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_ dchubbub_regs->num_watermark_sets = 2; + dpmm_dcn4_map_mcif_watermarks(in_out); + return true; } bool dpmm_dcn42_map_watermarks(struct dml2_dpmm_map_watermarks_params_in_out *in_out) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c index 790aef95caa6..57a6a7d5b740 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c @@ -13,8 +13,6 @@ * configurations, ensuring p-state watermark support in the blank period only. */ -static const double MIN_VACTIVE_MARGIN_PCT = 0.25; // We need more than non-zero margin because DET buffer granularity can alter vactive latency hiding - static const struct dml2_pmo_pstate_strategy dcn42_strategy_list_1_display[] = { // VBlank only { @@ -179,7 +177,7 @@ bool pmo_dcn42_init_for_pstate_support(struct dml2_pmo_init_for_pstate_support_i // Figure out which streams can do vactive, and also build up implicit SVP and FAMS2 meta for (stream_index = 0; stream_index < display_config->display_config.num_streams; stream_index++) { - if (dcn4_get_vactive_pstate_margin(display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) >= (int)(MIN_VACTIVE_MARGIN_PCT * pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us)) + if (dcn4_get_vactive_pstate_margin(display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) >= 0) dcn42_set_bit_in_bitfield(&s->pmo_dcn4.stream_vactive_capability_mask, stream_index); } @@ -259,27 +257,17 @@ bool pmo_dcn42_fams2_optimize_for_pstate_support(struct dml2_pmo_optimize_for_ps bool pmo_dcn42_test_for_pstate_support(struct dml2_pmo_test_for_pstate_support_in_out *in_out) { - const struct dml2_pmo_scratch *s = &in_out->instance->scratch; - bool p_state_supported = true; - unsigned int stream_index; - - if (s->pmo_dcn4.cur_pstate_candidate < 0) + /* Return false on the initial candidate (cur_pstate_candidate == -1) so the + * optimization phase runs at least one optimize iteration; otherwise the + * FAMS2/stage-3 setup in the optimize callback is skipped. + */ + if (in_out->instance->scratch.pmo_dcn4.cur_pstate_candidate < 0) return false; - for (stream_index = 0; stream_index < in_out->base_display_config->display_config.num_streams; stream_index++) { - if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive) { - if (dcn4_get_minimum_reserved_time_us_for_planes(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (int)in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us || - dcn4_get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (int)(MIN_VACTIVE_MARGIN_PCT * in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us)) { - p_state_supported = false; - break; - } - } else { - p_state_supported = false; - break; - } - } - - return p_state_supported; + /* No-op: reserved time is guaranteed by the override and vactive p-state + * margin is now enforced in core mode support. + */ + return true; } bool pmo_dcn42_initialize(struct dml2_pmo_initialize_in_out *in_out) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c index d5f20c85006e..8f481ef9316f 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c @@ -1710,7 +1710,7 @@ static void build_pstate_meta_per_stream(struct dml2_pmo_instance *pmo, /* common */ stream_pstate_meta->valid = true; stream_pstate_meta->otg_vline_time_us = (double)timing->h_total / timing->pixel_clock_khz * 1000.0; - stream_pstate_meta->nom_vtotal = stream_descriptor->timing.vblank_nom + stream_descriptor->timing.v_active; + stream_pstate_meta->nom_vtotal = stream_descriptor->timing.v_total; stream_pstate_meta->nom_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 / (stream_pstate_meta->nom_vtotal * timing->h_total); stream_pstate_meta->nom_frame_time_us = diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c index 9788628cf0ad..ff17a9c9eed0 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c @@ -171,8 +171,8 @@ void dpp1_cm_set_gamut_remap( for (i = 0; i < 12; i++) arr_matrix[i] = adjust->temperature_matrix[i]; - convert_float_matrix( - arr_reg_val, arr_matrix, 12); + convert_float_matrix(arr_reg_val, arr_matrix, + CM_GAMUT_REMAP_COEF_FORMAT_S2_13, 12); program_gamut_remap(dpp, arr_reg_val, GAMUT_REMAP_COEFF); } @@ -242,8 +242,8 @@ void dpp1_cm_get_gamut_remap(struct dpp *dpp_base, } adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; - convert_hw_matrix(adjust->temperature_matrix, - arr_reg_val, ARRAY_SIZE(arr_reg_val)); + convert_hw_matrix(adjust->temperature_matrix, arr_reg_val, + CM_GAMUT_REMAP_COEF_FORMAT_S2_13, ARRAY_SIZE(arr_reg_val)); } static void dpp1_cm_program_color_matrix( diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c index 26f9485f165d..74cc176d9437 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c @@ -227,8 +227,8 @@ void dpp2_cm_set_gamut_remap( for (i = 0; i < 12; i++) arr_matrix[i] = adjust->temperature_matrix[i]; - convert_float_matrix( - arr_reg_val, arr_matrix, 12); + convert_float_matrix(arr_reg_val, arr_matrix, + CM_GAMUT_REMAP_COEF_FORMAT_S2_13, 12); program_gamut_remap(dpp, arr_reg_val, DCN2_GAMUT_REMAP_COEF_A); } @@ -285,8 +285,8 @@ void dpp2_cm_get_gamut_remap(struct dpp *dpp_base, } adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; - convert_hw_matrix(adjust->temperature_matrix, - arr_reg_val, ARRAY_SIZE(arr_reg_val)); + convert_hw_matrix(adjust->temperature_matrix, arr_reg_val, + CM_GAMUT_REMAP_COEF_FORMAT_S2_13, ARRAY_SIZE(arr_reg_val)); } void dpp2_program_input_csc( diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c index e01d8e9672fc..8fe3474411b7 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c @@ -392,8 +392,8 @@ void dpp3_cm_set_gamut_remap( for (i = 0; i < 12; i++) arr_matrix[i] = adjust->temperature_matrix[i]; - convert_float_matrix( - arr_reg_val, arr_matrix, 12); + convert_float_matrix(arr_reg_val, arr_matrix, + CM_GAMUT_REMAP_COEF_FORMAT_S2_13, 12); //current coefficient set in use REG_GET(CM_GAMUT_REMAP_CONTROL, CM_GAMUT_REMAP_MODE_CURRENT, &gamut_mode); @@ -460,6 +460,6 @@ void dpp3_cm_get_gamut_remap(struct dpp *dpp_base, } adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; - convert_hw_matrix(adjust->temperature_matrix, - arr_reg_val, ARRAY_SIZE(arr_reg_val)); + convert_hw_matrix(adjust->temperature_matrix, arr_reg_val, + CM_GAMUT_REMAP_COEF_FORMAT_S2_13, ARRAY_SIZE(arr_reg_val)); } diff --git a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb_cm.c b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb_cm.c index 37adf0e6a166..5a611dd14581 100644 --- a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb_cm.c @@ -371,7 +371,8 @@ void dwb3_set_gamut_remap( for (i = 0; i < 12; i++) arr_matrix[i] = adjust.temperature_matrix[i]; - convert_float_matrix(arr_reg_val, arr_matrix, 12); + convert_float_matrix(arr_reg_val, arr_matrix, + CM_GAMUT_REMAP_COEF_FORMAT_S2_13, 12); REG_GET(DWB_GAMUT_REMAP_MODE, DWB_GAMUT_REMAP_MODE_CURRENT, ¤t_mode); diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h index 5ae9fb76c675..ddc614c98234 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h @@ -340,7 +340,12 @@ HUBBUB_SF(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_Z8_D, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_Z8_D, mask_sh), \ HUBBUB_SF(DCHUBBUB_ARB_DRAM_STATE_CNTL, DCHUBBUB_ARB_ALLOW_CSTATE_DEEPSLEEP_LEGACY_MODE, mask_sh), \ HUBBUB_SF(DCHUBBUB_ARB_HOSTVM_CNTL, DCHUBBUB_ARB_MAX_QOS_COMMIT_THRESHOLD, mask_sh),\ - HUBBUB_SF(DCHUBBUB_ARB_DF_REQ_OUTSTAND, DCHUBBUB_ARB_MIN_REQ_OUTSTAND_COMMIT_THRESHOLD, mask_sh) + HUBBUB_SF(DCHUBBUB_ARB_DF_REQ_OUTSTAND, DCHUBBUB_ARB_MIN_REQ_OUTSTAND_COMMIT_THRESHOLD, mask_sh), \ + HUBBUB_SF(DCHUBBUB_CTRL_STATUS, DCHUBBUB_HW_DEBUG, mask_sh) + +#define DCHUBBUB_CTRL_STATUS__DCHUBBUB_HW_DEBUG__SHIFT 0x4 + +#define DCHUBBUB_CTRL_STATUS__DCHUBBUB_HW_DEBUG_MASK 0x3FFFFFF0L void hubbub35_construct(struct dcn20_hubbub *hubbub2, struct dc_context *ctx, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index cce4f3065575..f91ce2b4b1bf 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -607,11 +607,10 @@ dce110_translate_regamma_to_hw_format(const struct dc_transfer_func *output_tf, } static bool -dce110_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream) +dce110_set_output_transfer_func(struct set_output_transfer_func_params *params) { - (void)dc; - struct transform *xfm = pipe_ctx->plane_res.xfm; + struct transform *xfm = params->xfm; + const struct dc_stream_state *stream = params->stream; xfm->funcs->opp_power_on_regamma_lut(xfm, true); xfm->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM; @@ -1287,9 +1286,7 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx) return; if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) { - if (link->skip_implict_edp_power_control) - return; - if (hws) + if (!link->skip_implict_edp_power_control && hws) hws->funcs.edp_backlight_control(link, false); link->dc->hwss.set_abm_immediate_disable(pipe_ctx); } @@ -1339,8 +1336,27 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx) void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) { - if (pipe_ctx != NULL && pipe_ctx->stream_res.stream_enc != NULL) + if (pipe_ctx == NULL || pipe_ctx->stream_res.stream_enc == NULL) + return; + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable); + + /* Wait for three frames to make sure AV mute is sent out. + * Some HDMI sinks need additional GCP packets to properly + * process the mute state, especially after link re-establishment + * with HDMI 2.0 scrambling enabled. + */ + if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { + int i; + + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + for (i = 0; i < 3; i++) { + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + } + } + } } enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) @@ -1795,9 +1811,7 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw( dc->link_srv->set_dsc_enable(pipe_ctx, true); } - if (!stream->dpms_off && - !(link->connector_signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control)) + if (!stream->dpms_off) dc->link_srv->set_dpms_on(context, pipe_ctx); /* DCN3.1 FPGA Workaround @@ -1816,9 +1830,7 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw( * is constructed with the same sink). Make sure not to override * and link programming on the main. */ - if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM && - !(link->connector_signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control)) { + if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM) { pipe_ctx->stream->link->psr_settings.psr_feature_enabled = false; pipe_ctx->stream->link->replay_settings.replay_feature_enabled = false; } @@ -2810,23 +2822,26 @@ static void program_surface_visibility(const struct dc *dc, } -static void program_gamut_remap(struct pipe_ctx *pipe_ctx) +static void program_gamut_remap(struct program_gamut_remap_params *params) { + struct transform *xfm = params->xfm; + const struct dc_stream_state *stream = params->stream; int i = 0; struct xfm_grph_csc_adjustment adjust; + memset(&adjust, 0, sizeof(adjust)); adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; - if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + if (stream->gamut_remap_matrix.enable_remap == true) { adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) adjust.temperature_matrix[i] = - pipe_ctx->stream->gamut_remap_matrix.matrix[i]; + stream->gamut_remap_matrix.matrix[i]; } - pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust); + xfm->funcs->transform_set_gamut_remap(xfm, &adjust); } static void update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx) @@ -3178,7 +3193,7 @@ static void dce110_program_front_end_for_pipe( hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); if (pipe_ctx->plane_state->update_bits.full_update) - hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); + hwss_set_output_transfer_func(dc, pipe_ctx); DC_LOG_SURFACE( "Pipe:%d %p: addr hi:0x%x, " diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c index 26aa303b8237..221996b348ab 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c @@ -338,7 +338,7 @@ dce60_program_front_end_for_pipe( hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); if (pipe_ctx->plane_state->update_bits.full_update) - hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); + hwss_set_output_transfer_func(dc, pipe_ctx); DC_LOG_SURFACE( "Pipe:%d %p: addr hi:0x%x, " diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 541cd908b341..22c7535fd9c4 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -2154,10 +2154,11 @@ static void log_tf(struct dc_context *ctx, } } -bool dcn10_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream) +bool dcn10_set_output_transfer_func(struct set_output_transfer_func_params *params) { - struct dpp *dpp = pipe_ctx->plane_res.dpp; + struct dpp *dpp = params->dpp; + const struct dc_stream_state *stream = params->stream; + struct dc *dc; if (!stream) return false; @@ -2165,6 +2166,8 @@ bool dcn10_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, if (dpp == NULL) return false; + dc = dpp->ctx->dc; + dpp->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM; if (stream->out_transfer_func.type == TF_TYPE_PREDEFINED && @@ -2814,28 +2817,32 @@ static void dcn10_enable_plane( } -void dcn10_program_gamut_remap(struct pipe_ctx *pipe_ctx) +void dcn10_program_gamut_remap(struct program_gamut_remap_params *params) { + struct dpp *dpp = params->dpp; + const struct dc_stream_state *stream = params->stream; + const struct dc_plane_state *plane = params->plane; int i = 0; struct dpp_grph_csc_adjustment adjust; + memset(&adjust, 0, sizeof(adjust)); adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; - if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + if (stream->gamut_remap_matrix.enable_remap == true) { adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) adjust.temperature_matrix[i] = - pipe_ctx->stream->gamut_remap_matrix.matrix[i]; - } else if (pipe_ctx->plane_state && - pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) { + stream->gamut_remap_matrix.matrix[i]; + } else if (plane && + plane->gamut_remap_matrix.enable_remap == true) { adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) adjust.temperature_matrix[i] = - pipe_ctx->plane_state->gamut_remap_matrix.matrix[i]; + plane->gamut_remap_matrix.matrix[i]; } - pipe_ctx->plane_res.dpp->funcs->dpp_set_gamut_remap(pipe_ctx->plane_res.dpp, &adjust); + dpp->funcs->dpp_set_gamut_remap(dpp, &adjust); } @@ -3152,7 +3159,7 @@ static void dcn10_update_dchubp_dpp( if (plane_state->update_bits.full_update) { /*gamut remap*/ - dc->hwss.program_gamut_remap(pipe_ctx); + hwss_program_gamut_remap(pipe_ctx); dc->hwss.program_output_csc(dc, pipe_ctx, @@ -3297,7 +3304,7 @@ void dcn10_program_pipe( * doing heavy calculation and programming */ if (pipe_ctx->plane_state->update_bits.full_update) - hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); + hwss_set_output_transfer_func(dc, pipe_ctx); } void dcn10_wait_for_pending_cleared(struct dc *dc, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h index 476095c5dd0c..2cb674ba54e1 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h @@ -73,8 +73,7 @@ void dcn10_program_output_csc(struct dc *dc, enum dc_color_space colorspace, uint16_t *matrix, int opp_id); -bool dcn10_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream); +bool dcn10_set_output_transfer_func(struct set_output_transfer_func_params *params); bool dcn10_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state); void dcn10_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx); @@ -108,7 +107,7 @@ void dcn10_program_pipe( struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_state *context); -void dcn10_program_gamut_remap(struct pipe_ctx *pipe_ctx); +void dcn10_program_gamut_remap(struct program_gamut_remap_params *params); void dcn10_init_hw(struct dc *dc); void dcn10_init_pipes(struct dc *dc, struct dc_state *context); void dcn10_power_down_on_boot(struct dc *dc); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index 95e5b6a6ba0f..98778d5e114e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -1019,26 +1019,30 @@ void dcn20_program_output_csc(struct dc *dc, } } -bool dcn20_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream) +bool dcn20_set_output_transfer_func(struct set_output_transfer_func_params *otf_params) { - int mpcc_id = pipe_ctx->plane_res.hubp->inst; - struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; + struct dpp *dpp = otf_params->dpp; + struct mpc *mpc = otf_params->mpc; + int mpcc_id = otf_params->mpcc_id; + bool is_top_pipe = otf_params->is_top_pipe; + const struct dc_stream_state *stream = otf_params->stream; + struct dc *dc = dpp->ctx->dc; const struct pwl_params *params = NULL; + /* * program OGAM only for the top pipe * if there is a pipe split then fix diagnostic is required: * how to pass OGAM parameter for stream. * if programming for all pipes is required then remove condition - * pipe_ctx->top_pipe == NULL ,but then fix the diagnostic. + * is_top_pipe ,but then fix the diagnostic. */ if (mpc->funcs->power_on_mpc_mem_pwr) mpc->funcs->power_on_mpc_mem_pwr(mpc, mpcc_id, true); - if (pipe_ctx->top_pipe == NULL + if (is_top_pipe && mpc->funcs->set_output_gamma) { if (stream->out_transfer_func.type == TF_TYPE_HWPWL) params = &stream->out_transfer_func.pwl; - else if (pipe_ctx->stream->out_transfer_func.type == + else if (stream->out_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS && cm_helper_translate_curve_to_hw_format(dc->ctx, &stream->out_transfer_func, @@ -1815,7 +1819,7 @@ void dcn20_update_dchubp_dpp( || plane_state->update_bits.gamut_remap_change || pipe_ctx->stream->update_flags.bits.out_csc) { /* dpp/cm gamut remap*/ - dc->hwss.program_gamut_remap(pipe_ctx); + hwss_program_gamut_remap(pipe_ctx); /*call the dcn2 method which uses mpc csc*/ dc->hwss.program_output_csc(dc, @@ -1991,7 +1995,7 @@ static void dcn20_program_pipe( if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || pipe_ctx->stream->update_flags.bits.out_tf) - hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); + hwss_set_output_transfer_func(dc, pipe_ctx); /* If the pipe has been enabled or has a different opp, we * should reprogram the fmt. This deals with cases where diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h index 9d1ad3b29ca5..749348d3c793 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h @@ -44,8 +44,7 @@ void dcn20_update_plane_addr(const struct dc *dc, struct pipe_ctx *pipe_ctx); void dcn20_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx); bool dcn20_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state); -bool dcn20_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream); +bool dcn20_set_output_transfer_func(struct set_output_transfer_func_params *params); void dcn20_program_output_csc(struct dc *dc, struct pipe_ctx *pipe_ctx, enum dc_color_space colorspace, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index aed9d06ec538..59184e146fc9 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -256,13 +256,10 @@ bool dcn30_set_blend_lut( return result; } -static bool dcn30_set_mpc_shaper_3dlut(struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream) +static bool dcn30_set_mpc_shaper_3dlut(struct dpp *dpp, struct mpc *mpc, + int mpcc_id, const struct dc_stream_state *stream) { - struct dpp *dpp_base = pipe_ctx->plane_res.dpp; - int mpcc_id = pipe_ctx->plane_res.hubp->inst; - struct dc *dc = pipe_ctx->stream->ctx->dc; - struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; + struct dc *dc = dpp->ctx->dc; bool result = false; uint32_t acquired_rmu = 0; int mpcc_id_projected = 0; @@ -274,8 +271,8 @@ static bool dcn30_set_mpc_shaper_3dlut(struct pipe_ctx *pipe_ctx, shaper_lut = &stream->func_shaper->pwl; } else if (stream->func_shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { cm_helper_translate_curve_to_hw_format(stream->ctx, stream->func_shaper, - &dpp_base->shaper_params, true); - shaper_lut = &dpp_base->shaper_params; + &dpp->shaper_params, true); + shaper_lut = &dpp->shaper_params; } } @@ -357,60 +354,66 @@ bool dcn30_set_input_transfer_func(struct dc *dc, return result; } -void dcn30_program_gamut_remap(struct pipe_ctx *pipe_ctx) +void dcn30_program_gamut_remap(struct program_gamut_remap_params *params) { + struct dpp *dpp = params->dpp; + struct mpc *mpc = params->mpc; + int mpcc_id = params->mpcc_id; + const struct dc_stream_state *stream = params->stream; + const struct dc_plane_state *plane = params->plane; + bool is_top_pipe = params->is_top_pipe; int i = 0; struct dpp_grph_csc_adjustment dpp_adjust; struct mpc_grph_gamut_adjustment mpc_adjust; - int mpcc_id = pipe_ctx->plane_res.hubp->inst; - struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; memset(&dpp_adjust, 0, sizeof(dpp_adjust)); dpp_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; - if (pipe_ctx->plane_state && - pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) { + if (plane && + plane->gamut_remap_matrix.enable_remap == true) { dpp_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) dpp_adjust.temperature_matrix[i] = - pipe_ctx->plane_state->gamut_remap_matrix.matrix[i]; + plane->gamut_remap_matrix.matrix[i]; } - pipe_ctx->plane_res.dpp->funcs->dpp_set_gamut_remap(pipe_ctx->plane_res.dpp, + dpp->funcs->dpp_set_gamut_remap(dpp, &dpp_adjust); memset(&mpc_adjust, 0, sizeof(mpc_adjust)); mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; - if (pipe_ctx->top_pipe == NULL) { - if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + if (is_top_pipe) { + if (stream->gamut_remap_matrix.enable_remap == true) { mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) mpc_adjust.temperature_matrix[i] = - pipe_ctx->stream->gamut_remap_matrix.matrix[i]; + stream->gamut_remap_matrix.matrix[i]; } } mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust); } -bool dcn30_set_output_transfer_func(struct dc *dc, - struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream) +bool dcn30_set_output_transfer_func(struct set_output_transfer_func_params *otf_params) { - int mpcc_id = pipe_ctx->plane_res.hubp->inst; - struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; + struct dpp *dpp = otf_params->dpp; + struct mpc *mpc = otf_params->mpc; + int mpcc_id = otf_params->mpcc_id; + bool is_top_pipe = otf_params->is_top_pipe; + const struct dc_stream_state *stream = otf_params->stream; + struct dc *dc = dpp->ctx->dc; const struct pwl_params *params = NULL; bool ret = false; /* program OGAM or 3DLUT only for the top pipe*/ - if (pipe_ctx->top_pipe == NULL) { + if (is_top_pipe) { /*program rmu shaper and 3dlut in MPC*/ - ret = dcn30_set_mpc_shaper_3dlut(pipe_ctx, stream); + ret = dcn30_set_mpc_shaper_3dlut(dpp, mpc, mpcc_id, stream); if (ret == false && mpc->funcs->set_output_gamma) { if (stream->out_transfer_func.type == TF_TYPE_HWPWL) params = &stream->out_transfer_func.pwl; - else if (pipe_ctx->stream->out_transfer_func.type == + else if (stream->out_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS && cm3_helper_translate_curve_to_hw_format(stream->ctx, &stream->out_transfer_func, @@ -849,13 +852,19 @@ void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) pipe_ctx->stream_res.stream_enc, enable); - /* Wait for two frame to make sure AV mute is sent out */ + /* Wait for three frames to make sure AV mute is sent out. + * Some HDMI sinks need additional GCP packets to properly + * process the mute state, especially after link re-establishment + * with HDMI 2.0 scrambling enabled. + */ if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { + int i; + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + for (i = 0; i < 3; i++) { + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + } } } } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h index a963d360a368..0399642076eb 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h @@ -63,11 +63,9 @@ bool dcn30_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state); -void dcn30_program_gamut_remap(struct pipe_ctx *pipe_ctx); +void dcn30_program_gamut_remap(struct program_gamut_remap_params *params); -bool dcn30_set_output_transfer_func(struct dc *dc, - struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream); +bool dcn30_set_output_transfer_func(struct set_output_transfer_func_params *params); void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable); void dcn30_update_info_frame(struct pipe_ctx *pipe_ctx); void dcn30_program_dmdata_engine(struct pipe_ctx *pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c index 6f1ad651ed2c..b6f3c0480ab6 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c @@ -537,11 +537,9 @@ static void dcn31_reset_back_end_for_pipe( } ASSERT(!pipe_ctx->top_pipe); - link = pipe_ctx->stream->link; + dc->hwss.set_abm_immediate_disable(pipe_ctx); - if (!(link->connector_signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control)) - dc->hwss.set_abm_immediate_disable(pipe_ctx); + link = pipe_ctx->stream->link; if (dc->hwseq) dc->hwseq->wa_state.skip_blank_stream = false; @@ -557,11 +555,9 @@ static void dcn31_reset_back_end_for_pipe( pipe_ctx->stream_res.tg, OPTC_DSC_DISABLED, 0, 0); - if (!(link->connector_signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control)) { - pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg); - pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); - } + pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg); + + pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); if (pipe_ctx->stream_res.tg->funcs->set_odm_bypass) pipe_ctx->stream_res.tg->funcs->set_odm_bypass( pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); @@ -590,12 +586,7 @@ static void dcn31_reset_back_end_for_pipe( * screen only, the dpms_off would be true but * VBIOS lit up eDP, so check link status too. */ - if (link->connector_signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control) { - /* DMSS is holding the panel across the commit; skip dpms-off. */ - if (pipe_ctx->stream_res.audio) - dc->hwss.disable_audio_stream(pipe_ctx); - } else if (!pipe_ctx->stream->dpms_off || link->link_status.link_active) + if (!pipe_ctx->stream->dpms_off || link->link_status.link_active) dc->link_srv->set_dpms_off(pipe_ctx); else if (pipe_ctx->stream_res.audio) dc->hwss.disable_audio_stream(pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c index c2ea25927765..c5f6eb482f07 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c @@ -443,13 +443,10 @@ void dcn32_subvp_pipe_control_lock_fast(union block_sequence_params *params) } } -bool dcn32_set_mpc_shaper_3dlut( - struct pipe_ctx *pipe_ctx, const struct dc_stream_state *stream) +bool dcn32_set_mpc_shaper_3dlut(struct dpp *dpp, struct mpc *mpc, + int mpcc_id, const struct dc_stream_state *stream) { - struct dpp *dpp_base = pipe_ctx->plane_res.dpp; - int mpcc_id = pipe_ctx->plane_res.hubp->inst; - struct dc *dc = pipe_ctx->stream->ctx->dc; - struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; + struct dc *dc = dpp->ctx->dc; bool result = false; const struct pwl_params *shaper_lut = NULL; @@ -460,8 +457,8 @@ bool dcn32_set_mpc_shaper_3dlut( else if (stream->func_shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { cm_helper_translate_curve_to_hw_format(stream->ctx, stream->func_shaper, - &dpp_base->shaper_params, true); - shaper_lut = &dpp_base->shaper_params; + &dpp->shaper_params, true); + shaper_lut = &dpp->shaper_params; } } @@ -566,24 +563,24 @@ bool dcn32_set_input_transfer_func(struct dc *dc, return result; } -bool dcn32_set_output_transfer_func(struct dc *dc, - struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream) +bool dcn32_set_output_transfer_func(struct set_output_transfer_func_params *otf_params) { - (void)dc; - int mpcc_id = pipe_ctx->plane_res.hubp->inst; - struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; + struct dpp *dpp = otf_params->dpp; + struct mpc *mpc = otf_params->mpc; + int mpcc_id = otf_params->mpcc_id; + bool is_top_pipe = otf_params->is_top_pipe; + const struct dc_stream_state *stream = otf_params->stream; const struct pwl_params *params = NULL; bool ret = false; /* program OGAM or 3DLUT only for the top pipe*/ - if (resource_is_pipe_type(pipe_ctx, OPP_HEAD)) { + if (is_top_pipe) { /*program shaper and 3dlut in MPC*/ - ret = dcn32_set_mpc_shaper_3dlut(pipe_ctx, stream); + ret = dcn32_set_mpc_shaper_3dlut(dpp, mpc, mpcc_id, stream); if (ret == false && mpc->funcs->set_output_gamma) { if (stream->out_transfer_func.type == TF_TYPE_HWPWL) params = &stream->out_transfer_func.pwl; - else if (pipe_ctx->stream->out_transfer_func.type == + else if (stream->out_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS && cm3_helper_translate_curve_to_hw_format(stream->ctx, &stream->out_transfer_func, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h index 0303a5953673..090d94d38343 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h @@ -54,12 +54,10 @@ bool dcn32_set_input_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state); -bool dcn32_set_mpc_shaper_3dlut( - struct pipe_ctx *pipe_ctx, const struct dc_stream_state *stream); +bool dcn32_set_mpc_shaper_3dlut(struct dpp *dpp_base, struct mpc *mpc, + int mpcc_id, const struct dc_stream_state *stream); -bool dcn32_set_output_transfer_func(struct dc *dc, - struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream); +bool dcn32_set_output_transfer_func(struct set_output_transfer_func_params *params); void dcn32_init_hw(struct dc *dc); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index 4ed7480d1efa..f14c39a643da 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -1818,11 +1818,8 @@ void dcn35_disable_link_output(struct dc_link *link, disable_link_output_symclk_on_tx_off(link, DP_UNKNOWN_ENCODING); link->phy_state.symclk_state = SYMCLK_ON_TX_OFF; } else { - if (!(signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control)) { - link_hwss->disable_link_output(link, link_res, signal); - link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF; - } + link_hwss->disable_link_output(link, link_res, signal); + link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF; } /* * Add the logic to extract BOTH power up and power down sequences diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index d8d3208fc1af..fa9a5d8065d3 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -89,28 +89,31 @@ void dcn401_initialize_min_clocks(struct dc *dc) true); } -void dcn401_program_gamut_remap(struct pipe_ctx *pipe_ctx) +void dcn401_program_gamut_remap(struct program_gamut_remap_params *params) { + struct mpc *mpc = params->mpc; + int mpcc_id = params->mpcc_id; + const struct dc_stream_state *stream = params->stream; + const struct dc_plane_state *plane = params->plane; + bool is_top_pipe = params->is_top_pipe; unsigned int i = 0; struct mpc_grph_gamut_adjustment mpc_adjust; - unsigned int mpcc_id = pipe_ctx->plane_res.mpcc_inst; - struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; //For now assert if location is not pre-blend - if (pipe_ctx->plane_state) - ASSERT(pipe_ctx->plane_state->mcm_location == MPCC_MOVABLE_CM_LOCATION_BEFORE); + if (plane) + ASSERT(plane->mcm_location == MPCC_MOVABLE_CM_LOCATION_BEFORE); // program MPCC_MCM_FIRST_GAMUT_REMAP memset(&mpc_adjust, 0, sizeof(mpc_adjust)); mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; mpc_adjust.mpcc_gamut_remap_block_id = MPCC_MCM_FIRST_GAMUT_REMAP; - if (pipe_ctx->plane_state && - pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) { + if (plane && + plane->gamut_remap_matrix.enable_remap == true) { mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) mpc_adjust.temperature_matrix[i] = - pipe_ctx->plane_state->gamut_remap_matrix.matrix[i]; + plane->gamut_remap_matrix.matrix[i]; } mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust); @@ -126,12 +129,12 @@ void dcn401_program_gamut_remap(struct pipe_ctx *pipe_ctx) mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; mpc_adjust.mpcc_gamut_remap_block_id = MPCC_OGAM_GAMUT_REMAP; - if (pipe_ctx->top_pipe == NULL) { - if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) { + if (is_top_pipe) { + if (stream->gamut_remap_matrix.enable_remap == true) { mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++) mpc_adjust.temperature_matrix[i] = - pipe_ctx->stream->gamut_remap_matrix.matrix[i]; + stream->gamut_remap_matrix.matrix[i]; } } @@ -515,12 +518,10 @@ void dcn401_populate_mcm_luts(struct dc *dc, /* Select width based on the requested LUT size */ switch (cm->lut3d_dma.size) { -#if defined(CONFIG_DRM_AMD_DC_DCN4_2) case CM_LUT_SIZE_333333: if (dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33) width = hubp_3dlut_fl_width_33; break; -#endif // CONFIG_DRM_AMD_DC_DCN4_2 case CM_LUT_SIZE_171717: width = hubp_3dlut_fl_width_17; break; @@ -695,24 +696,24 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, return result; } -bool dcn401_set_output_transfer_func(struct dc *dc, - struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream) +bool dcn401_set_output_transfer_func(struct set_output_transfer_func_params *otf_params) { - (void)dc; - int mpcc_id = pipe_ctx->plane_res.hubp->inst; - struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc; + struct dpp *dpp = otf_params->dpp; + struct mpc *mpc = otf_params->mpc; + int mpcc_id = otf_params->mpcc_id; + bool is_top_pipe = otf_params->is_top_pipe; + const struct dc_stream_state *stream = otf_params->stream; const struct pwl_params *params = NULL; bool ret = false; /* program OGAM or 3DLUT only for the top pipe*/ - if (resource_is_pipe_type(pipe_ctx, OPP_HEAD)) { + if (is_top_pipe) { /*program shaper and 3dlut in MPC*/ - ret = dcn32_set_mpc_shaper_3dlut(pipe_ctx, stream); + ret = dcn32_set_mpc_shaper_3dlut(dpp, mpc, mpcc_id, stream); if (ret == false && mpc->funcs->set_output_gamma) { if (stream->out_transfer_func.type == TF_TYPE_HWPWL) params = &stream->out_transfer_func.pwl; - else if (pipe_ctx->stream->out_transfer_func.type == + else if (stream->out_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS && cm3_helper_translate_curve_to_hw_format(stream->ctx, &stream->out_transfer_func, @@ -1101,11 +1102,8 @@ void dcn401_disable_link_output(struct dc_link *link, disable_link_output_symclk_on_tx_off(link, DP_UNKNOWN_ENCODING); link->phy_state.symclk_state = SYMCLK_ON_TX_OFF; } else { - if (!(signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control)) { - link_hwss->disable_link_output(link, link_res, signal); - link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF; - } + link_hwss->disable_link_output(link, link_res, signal); + link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF; } if (signal == SIGNAL_TYPE_EDP && @@ -2153,12 +2151,7 @@ void dcn401_reset_back_end_for_pipe( * screen only, the dpms_off would be true but * VBIOS lit up eDP, so check link status too. */ - if (link->connector_signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control) { - /* DMSS is holding the panel across the commit; skip dpms-off. */ - if (pipe_ctx->stream_res.audio) - dc->hwss.disable_audio_stream(pipe_ctx); - } else if (!pipe_ctx->stream->dpms_off || link->link_status.link_active) + if (!pipe_ctx->stream->dpms_off || link->link_status.link_active) dc->link_srv->set_dpms_off(pipe_ctx); else if (pipe_ctx->stream_res.audio) dc->hwss.disable_audio_stream(pipe_ctx); @@ -2183,15 +2176,12 @@ void dcn401_reset_back_end_for_pipe( * parent pipe. */ if (pipe_ctx->top_pipe == NULL) { - if (!(link->connector_signal == SIGNAL_TYPE_EDP && - link->skip_implict_edp_power_control)) { - dc->hwss.set_abm_immediate_disable(pipe_ctx); + dc->hwss.set_abm_immediate_disable(pipe_ctx); - pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg); + pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg); - pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); - } + pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false); if (pipe_ctx->stream_res.tg->funcs->set_odm_bypass) pipe_ctx->stream_res.tg->funcs->set_odm_bypass( pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing); @@ -2396,7 +2386,7 @@ void dcn401_program_pipe( if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || pipe_ctx->stream->update_flags.bits.out_tf) - hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); + hwss_set_output_transfer_func(dc, pipe_ctx); /* If the pipe has been enabled or has a different opp, we * should reprogram the fmt. This deals with cases where @@ -2554,7 +2544,7 @@ void dcn401_program_pipe_sequence( if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || pipe_ctx->stream->update_flags.bits.out_tf) { - hwss_add_dpp_set_output_transfer_func(seq_state, dc, pipe_ctx, pipe_ctx->stream); + hwss_add_dpp_set_output_transfer_func(seq_state, dc, pipe_ctx); } /* If the pipe has been enabled or has a different opp, we diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h index a760050eea8c..f90e25243ead 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h @@ -32,15 +32,13 @@ struct ips_ono_region_state { uint32_t current_pwr_state; }; -void dcn401_program_gamut_remap(struct pipe_ctx *pipe_ctx); +void dcn401_program_gamut_remap(struct program_gamut_remap_params *params); void dcn401_init_hw(struct dc *dc); bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state); -bool dcn401_set_output_transfer_func(struct dc *dc, - struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream); +bool dcn401_set_output_transfer_func(struct set_output_transfer_func_params *params); void dcn401_trigger_3dlut_dma_load(struct dc *dc, struct pipe_ctx *pipe_ctx); void dcn401_calculate_dccg_tmds_div_value(struct pipe_ctx *pipe_ctx, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index cc8e96ffe7d1..151a29bf0e9d 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -470,7 +470,6 @@ static bool dc_is_rmcm_3dlut_supported(struct hubp *hubp, struct mpc *mpc) return false; } -#if defined(CONFIG_DRM_AMD_DC_DCN4_2) static bool is_rmcm_3dlut_fl_supported(struct dc *dc) { /* size was previously hard-coded to TRANSFORMED in local_mcm, @@ -480,7 +479,6 @@ static bool is_rmcm_3dlut_fl_supported(struct dc *dc) return false; return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_17 != 0u; } -#endif static void dcn42_set_mcm_location_post_blend(struct dc *dc, struct pipe_ctx *pipe_ctx, bool bPostBlend) { @@ -724,9 +722,8 @@ void dcn42_populate_mcm_luts(struct dc *dc, false); //RMCM - 3dLUT+Shaper -#if defined(CONFIG_DRM_AMD_DC_DCN4_2) if (cm->flags.bits.rmcm_enable && - is_rmcm_3dlut_fl_supported(dc)) { + is_rmcm_3dlut_fl_supported(dc)) dcn42_program_rmcm_luts( hubp, pipe_ctx, @@ -734,8 +731,6 @@ void dcn42_populate_mcm_luts(struct dc *dc, mpc, lut_bank_a, mpcc_id); - } -#endif /* CONFIG_DRM_AMD_DC_DCN4_2 */ /* 1D LUT */ { @@ -951,7 +946,11 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, } void dcn42_hardware_release(struct dc *dc) { + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_cstate_disable) + dc->clk_mgr->funcs->notify_cstate_disable(dc->clk_mgr, true); + dcn35_hardware_release(dc); + dc_dmub_srv_release_hw(dc); } @@ -1087,6 +1086,12 @@ void dcn42_prepare_bandwidth( } dcn401_prepare_bandwidth(dc, context); + + /* valid C-state watermarks have now been committed to HW, so it + * is safe to vote "allow" to PMFW. + */ + if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->notify_cstate_disable) + dc->clk_mgr->funcs->notify_cstate_disable(dc->clk_mgr, false); } void dcn42_optimize_bandwidth(struct dc *dc, struct dc_state *context) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index 65df8002d3d7..4549c435501a 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -32,6 +32,7 @@ #include "inc/hw/link_encoder.h" #include "inc/core_status.h" #include "inc/hw/hw_shared.h" +#include "inc/hw/dchubbub.h" #include "dsc/dsc.h" #include "link_service_types.h" @@ -53,7 +54,6 @@ struct drr_params; struct dc_underflow_debug_data; struct dsc_optc_config; struct vm_system_aperture_param; -struct dc_measured_memory_qos; struct stream_encoder; struct hpo_dp_stream_encoder; struct hpo_frl_stream_encoder; @@ -95,7 +95,13 @@ struct set_input_transfer_func_params { }; struct program_gamut_remap_params { - struct pipe_ctx *pipe_ctx; + struct transform *xfm; + struct dpp *dpp; + struct mpc *mpc; + int mpcc_id; + const struct dc_stream_state *stream; + const struct dc_plane_state *plane; + bool is_top_pipe; }; struct hubp_enable_3dlut_fl_params { @@ -132,8 +138,11 @@ struct program_bias_and_scale_params { }; struct set_output_transfer_func_params { - struct dc *dc; - struct pipe_ctx *pipe_ctx; + struct transform *xfm; + struct dpp *dpp; + struct mpc *mpc; + int mpcc_id; + bool is_top_pipe; const struct dc_stream_state *stream; }; @@ -571,6 +580,77 @@ struct hubbub_soft_reset_params { bool reset; }; +struct hubbub_perfmon_reset_params { + struct hubbub *hubbub; +}; + +struct hubbub_perfmon_arm_out_of_order_bw_params { + struct hubbub *hubbub; +}; + +struct hubbub_perfmon_start_out_of_order_bw_params { + struct hubbub *hubbub; +}; + +struct hubbub_perfmon_start_in_order_bw_params { + struct hubbub *hubbub; +}; + +struct hubbub_perfmon_start_memory_latencies_params { + struct hubbub *hubbub; +}; + +struct hubbub_perfmon_start_urgent_assertion_count_params { + struct hubbub *hubbub; +}; + +struct hubbub_perfmon_start_urgent_ramp_latency_params { + struct hubbub *hubbub; + struct hubbub_urgent_latency_params latency_params; +}; + +struct hubbub_perfmon_start_prefetch_data_size_params { + struct hubbub *hubbub; +}; + +struct hubbub_perfmon_get_out_of_order_bw_params { + struct hubbub *hubbub; + uint32_t refclk_mhz; + uint32_t *bandwidth_mbps; + uint32_t *duration_ns; +}; + +struct hubbub_perfmon_get_in_order_bw_params { + struct hubbub *hubbub; + uint32_t refclk_mhz; + uint32_t min_duration_ns; + uint32_t *bandwidth_mbps; + uint32_t *duration_ns; +}; + +struct hubbub_perfmon_get_memory_latencies_params { + struct hubbub *hubbub; + uint32_t refclk_mhz; + struct dc_probe_latencies *result; +}; + +struct hubbub_perfmon_get_urgent_assertion_count_params { + struct hubbub *hubbub; + uint32_t refclk_mhz; + uint32_t *assertion_count; +}; + +struct hubbub_perfmon_get_prefetch_data_size_params { + struct hubbub *hubbub; + uint32_t *prefetch_data_size; +}; + +struct hubbub_perfmon_get_urgent_ramp_latency_params { + struct hubbub *hubbub; + uint32_t refclk_mhz; + uint32_t *latency_ns; +}; + struct hubp_clk_cntl_params { struct hubp *hubp; bool enable; @@ -1022,6 +1102,20 @@ union block_sequence_params { struct hubp_set_blank_en_params hubp_set_blank_en_params; struct hubp_disable_control_params hubp_disable_control_params; struct hubbub_soft_reset_params hubbub_soft_reset_params; + struct hubbub_perfmon_reset_params hubbub_perfmon_reset_params; + struct hubbub_perfmon_arm_out_of_order_bw_params hubbub_perfmon_arm_out_of_order_bw_params; + struct hubbub_perfmon_start_out_of_order_bw_params hubbub_perfmon_start_out_of_order_bw_params; + struct hubbub_perfmon_start_in_order_bw_params hubbub_perfmon_start_in_order_bw_params; + struct hubbub_perfmon_start_memory_latencies_params hubbub_perfmon_start_memory_latencies_params; + struct hubbub_perfmon_start_urgent_assertion_count_params hubbub_perfmon_start_urgent_assertion_count_params; + struct hubbub_perfmon_start_urgent_ramp_latency_params hubbub_perfmon_start_urgent_ramp_latency_params; + struct hubbub_perfmon_start_prefetch_data_size_params hubbub_perfmon_start_prefetch_data_size_params; + struct hubbub_perfmon_get_out_of_order_bw_params hubbub_perfmon_get_out_of_order_bw_params; + struct hubbub_perfmon_get_in_order_bw_params hubbub_perfmon_get_in_order_bw_params; + struct hubbub_perfmon_get_memory_latencies_params hubbub_perfmon_get_memory_latencies_params; + struct hubbub_perfmon_get_urgent_assertion_count_params hubbub_perfmon_get_urgent_assertion_count_params; + struct hubbub_perfmon_get_prefetch_data_size_params hubbub_perfmon_get_prefetch_data_size_params; + struct hubbub_perfmon_get_urgent_ramp_latency_params hubbub_perfmon_get_urgent_ramp_latency_params; struct hubp_clk_cntl_params hubp_clk_cntl_params; struct hubp_init_params hubp_init_params; struct hubp_set_vm_system_aperture_settings_params hubp_set_vm_system_aperture_settings_params; @@ -1249,6 +1343,20 @@ enum block_sequence_func { HUBBUB_PROGRAM_WATERMARKS, HUBBUB_PROGRAM_ARBITER, HUBBUB_PROGRAM_COMPBUF_SEGMENTS, + HUBBUB_PERFMON_RESET, + HUBBUB_PERFMON_ARM_OUT_OF_ORDER_BW, + HUBBUB_PERFMON_START_OUT_OF_ORDER_BW, + HUBBUB_PERFMON_START_IN_ORDER_BW, + HUBBUB_PERFMON_START_MEMORY_LATENCIES, + HUBBUB_PERFMON_START_URGENT_ASSERTION_COUNT, + HUBBUB_PERFMON_START_URGENT_RAMP_LATENCY, + HUBBUB_PERFMON_START_PREFETCH_DATA_SIZE, + HUBBUB_PERFMON_GET_OUT_OF_ORDER_BW, + HUBBUB_PERFMON_GET_IN_ORDER_BW, + HUBBUB_PERFMON_GET_MEMORY_LATENCIES, + HUBBUB_PERFMON_GET_URGENT_ASSERTION_COUNT, + HUBBUB_PERFMON_GET_PREFETCH_DATA_SIZE, + HUBBUB_PERFMON_GET_URGENT_RAMP_LATENCY, /* This must be the last value in this enum, add new ones above */ HWSS_BLOCK_SEQUENCE_FUNC_COUNT }; @@ -1389,7 +1497,7 @@ struct hw_sequencer_funcs { void (*program_cursor_offload_now)(struct dc *dc, const struct pipe_ctx *pipe); /* Colour Related */ - void (*program_gamut_remap)(struct pipe_ctx *pipe_ctx); + void (*program_gamut_remap)(struct program_gamut_remap_params *params); void (*program_output_csc)(struct dc *dc, struct pipe_ctx *pipe_ctx, enum dc_color_space colorspace, uint16_t *matrix, int opp_id); @@ -1582,14 +1690,16 @@ struct hw_sequencer_funcs { struct dc_underflow_debug_data *out_data); /** - * measure_memory_qos - Measure memory QoS metrics - * @dc: DC structure - * @qos: Pointer to dc_measured_memory_qos struct to populate with measured values + * program_perfmon - Program/transition perfmon probes for a commit. + * @dc: DC structure + * @context: target state; probes, probe_count, and probe_status are + * read from and written to this object * - * Populates the provided dc_measured_memory_qos struct with peak bandwidth, average bandwidth, - * max latency, min latency, and average latency from hardware performance counters. + * Invoked during the execute phase of dc_update_state. The hook resolves + * each probe's transition by diffing @context against dc->current_state + * and latches MEASURED results into @context->probe_status. */ - void (*measure_memory_qos)(struct dc *dc, struct dc_measured_memory_qos *qos); + void (*program_perfmon)(struct dc *dc, struct dc_state *context); }; @@ -1866,6 +1976,34 @@ void hwss_hubp_disable_control(union block_sequence_params *params); void hwss_hubbub_soft_reset(union block_sequence_params *params); +void hwss_hubbub_perfmon_reset(union block_sequence_params *params); + +void hwss_hubbub_perfmon_arm_out_of_order_bw(union block_sequence_params *params); + +void hwss_hubbub_perfmon_start_out_of_order_bw(union block_sequence_params *params); + +void hwss_hubbub_perfmon_start_in_order_bw(union block_sequence_params *params); + +void hwss_hubbub_perfmon_start_memory_latencies(union block_sequence_params *params); + +void hwss_hubbub_perfmon_start_urgent_assertion_count(union block_sequence_params *params); + +void hwss_hubbub_perfmon_start_urgent_ramp_latency(union block_sequence_params *params); + +void hwss_hubbub_perfmon_start_prefetch_data_size(union block_sequence_params *params); + +void hwss_hubbub_perfmon_get_out_of_order_bw(union block_sequence_params *params); + +void hwss_hubbub_perfmon_get_in_order_bw(union block_sequence_params *params); + +void hwss_hubbub_perfmon_get_memory_latencies(union block_sequence_params *params); + +void hwss_hubbub_perfmon_get_urgent_assertion_count(union block_sequence_params *params); + +void hwss_hubbub_perfmon_get_prefetch_data_size(union block_sequence_params *params); + +void hwss_hubbub_perfmon_get_urgent_ramp_latency(union block_sequence_params *params); + void hwss_hubp_clk_cntl(union block_sequence_params *params); void hwss_hubp_init(union block_sequence_params *params); @@ -1940,7 +2078,7 @@ void hwss_set_cursor_position(union block_sequence_params *params); void hwss_set_cursor_sdr_white_level(union block_sequence_params *params); -void hwss_program_gamut_remap(union block_sequence_params *params); +void hwss_program_gamut_remap(struct pipe_ctx *pipe_ctx); void hwss_program_output_csc(union block_sequence_params *params); @@ -1987,7 +2125,9 @@ void hwss_add_optc_program_manual_trigger(struct block_sequence_state *seq_state struct pipe_ctx *pipe_ctx); void hwss_add_dpp_set_output_transfer_func(struct block_sequence_state *seq_state, - struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_stream_state *stream); + struct dc *dc, struct pipe_ctx *pipe_ctx); + +void hwss_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx); void hwss_add_mpc_update_visual_confirm(struct block_sequence_state *seq_state, struct dc *dc, struct pipe_ctx *pipe_ctx, int mpcc_id); @@ -2202,6 +2342,54 @@ void hwss_add_hubbub_soft_reset(struct block_sequence_state *seq_state, void (*hubbub_soft_reset)(struct hubbub *hubbub, bool reset), bool reset); +void hwss_add_hubbub_perfmon_reset(struct block_sequence_state *seq_state, + struct hubbub *hubbub); + +void hwss_add_hubbub_perfmon_arm_out_of_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub); + +void hwss_add_hubbub_perfmon_start_out_of_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub); + +void hwss_add_hubbub_perfmon_start_in_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub); + +void hwss_add_hubbub_perfmon_start_memory_latencies(struct block_sequence_state *seq_state, + struct hubbub *hubbub); + +void hwss_add_hubbub_perfmon_start_urgent_assertion_count(struct block_sequence_state *seq_state, + struct hubbub *hubbub); + +void hwss_add_hubbub_perfmon_start_urgent_ramp_latency(struct block_sequence_state *seq_state, + struct hubbub *hubbub, + const struct hubbub_urgent_latency_params *latency_params); + +void hwss_add_hubbub_perfmon_start_prefetch_data_size(struct block_sequence_state *seq_state, + struct hubbub *hubbub); + +void hwss_add_hubbub_perfmon_get_out_of_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t *bandwidth_mbps, uint32_t *duration_ns); + +void hwss_add_hubbub_perfmon_get_in_order_bw(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t min_duration_ns, + uint32_t *bandwidth_mbps, uint32_t *duration_ns); + +void hwss_add_hubbub_perfmon_get_memory_latencies(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, + struct dc_probe_latencies *result); + +void hwss_add_hubbub_perfmon_get_urgent_assertion_count(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t *assertion_count); + +void hwss_add_hubbub_perfmon_get_prefetch_data_size(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t *prefetch_data_size); + +void hwss_add_hubbub_perfmon_get_urgent_ramp_latency(struct block_sequence_state *seq_state, + struct hubbub *hubbub, uint32_t refclk_mhz, + uint32_t *latency_ns); + void hwss_add_hubp_clk_cntl(struct block_sequence_state *seq_state, struct hubp *hubp, bool enable); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h index b4956893ae9a..31ace62a37d9 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h @@ -67,6 +67,8 @@ struct dc_phy_addr_space_config; struct dc_virtual_addr_space_config; struct hubp; struct dpp; +struct transform; +struct mpc; struct dce_hwseq; struct timing_generator; struct tg_color; @@ -92,9 +94,7 @@ struct hwseq_private_funcs { bool (*set_input_transfer_func)(struct dc *dc, struct pipe_ctx *pipe_ctx, const struct dc_plane_state *plane_state); - bool (*set_output_transfer_func)(struct dc *dc, - struct pipe_ctx *pipe_ctx, - const struct dc_stream_state *stream); + bool (*set_output_transfer_func)(struct set_output_transfer_func_params *params); void (*power_down)(struct dc *dc); void (*enable_display_pipe_clock_gating)(struct dc_context *ctx, bool clock_gating); diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_status.h b/drivers/gpu/drm/amd/display/dc/inc/core_status.h index 388f801f4582..1a17e727ed04 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_status.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_status.h @@ -62,6 +62,16 @@ enum dc_status { DC_FAIL_DP_LINK_BANDWIDTH = 28, DC_FAIL_HW_CURSOR_SUPPORT = 29, DC_FAIL_DP_TUNNEL_BW_VALIDATE = 30, + + /// Link protocol handshake and DPMS hardware programming successful. + DC_DPMS_SUCCESS = DC_OK, + /// Handshake skipped by optimized path, programming successfully completed. + DC_DPMS_SKIPPED_HANDSHAKE = 31, + /// Handshake failed, programming successful, DCN in consistent state. + DC_DPMS_FAILED_HANDSHAKE = 32, + /// Handshake failed, programming aborted, DCN may be in inconsistent state. + DC_DPMS_FAILED_INCOMPLETE = 33, + DC_ERROR_UNEXPECTED = -1 }; diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index cbbc1fb4b3dd..e87f9fd60bcb 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -33,6 +33,7 @@ #include "dc_bios_types.h" #include "mem_input.h" #include "hubp.h" +#include "hw/dchubbub.h" #include "mpc.h" #include "dwb.h" #include "hw/dio.h" @@ -622,6 +623,7 @@ struct dc_state { * @stream_status: Planes status on a given stream */ struct dc_stream_status stream_status[MAX_PIPES]; + /** * @phantom_streams: Stream state properties for phantoms */ @@ -645,6 +647,22 @@ struct dc_state { * @stream_count: Total phantom planes in use */ uint8_t phantom_plane_count; + + /** + * @probes: Committed absolute set of probe descriptors. + */ + struct dc_probe_state probes[MAX_PROBES]; + + /** + * @probe_status: Committed absolute set of probe results. + */ + struct dc_probe_status probe_status[MAX_PROBES]; + + /** + * @probe_count: Number of valid entries in @probes. + */ + int probe_count; + /** * @res_ctx: Persistent state of resources */ @@ -735,4 +753,39 @@ struct dc_requested_memory_qos { uint32_t max_bw_budget_in_mbps; }; +enum update_v3_flow { + UPDATE_V3_FLOW_INVALID, + UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FAST, + UPDATE_V3_FLOW_NO_NEW_CONTEXT_CONTEXT_FULL, + UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS, + UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_NEW, + UPDATE_V3_FLOW_NEW_CONTEXT_MINIMAL_CURRENT, +}; + +struct pipe_split_policy_backup { + bool dynamic_odm_policy; + bool subvp_policy; + enum pipe_split_policy mpc_policy; + char force_odm[MAX_PIPES]; +}; + +struct dc_update_scratch_space { + struct dc *dc; + struct dc_surface_update *surface_updates; + int surface_count; + struct dc_stream_state *stream; + struct dc_stream_update *stream_update; + const struct dc_probe_updates *probe_updates; + bool update_v3; + bool do_clear_update_bits; + enum dc_update_type update_type; + struct dc_state *new_context; + enum update_v3_flow flow; + struct dc_state *backup_context; + struct dc_state *intermediate_context; + struct pipe_split_policy_backup intermediate_policy; + struct dc_surface_update intermediate_updates[MAX_SURFACES]; + int intermediate_count; +}; + #endif /* _CORE_TYPES_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h index 68dc2d4ba7ca..ec678bd249ef 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h @@ -412,6 +412,16 @@ struct clk_mgr_funcs { struct clk_mgr *clk_mgr, struct dc_requested_memory_qos *qos); + /** + * notify_cstate_disable - Vote DCN's DF C-state policy to PMFW. + * @disable: true -> vote "disable" + * false -> vote "allow" + * Sends the message only when the cached dc_clocks.cstate_allow would + * change, then updates the cache on an OK response (idempotent no-op + * otherwise). + */ + void (*notify_cstate_disable)(struct clk_mgr *clk_mgr, bool disable); + void (*build_clock_update_for_bls)(struct clk_mgr *clk_mgr, struct dc_state *context, bool safe_to_lower, struct block_sequence_state *seq_state); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h index 4307362749f0..02bc0010e565 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h @@ -145,12 +145,6 @@ struct dcn_hubbub_reg_state { uint32_t compbuf_ctrl; }; -struct hubbub_system_latencies { - uint32_t max_latency_ns; - uint32_t avg_latency_ns; - uint32_t min_latency_ns; -}; - struct hubbub_urgent_latency_params { uint32_t refclk_mhz; uint32_t t_win_ns; @@ -158,6 +152,12 @@ struct hubbub_urgent_latency_params { uint32_t bw_factor_x1000; }; +struct hubbub; +struct dchub_init_data; +struct dc_dcc_surface_param; +struct dc_surface_dcc_cap; +union dcn_watermark_set; +struct dml2_display_arb_regs; struct hubbub_funcs { void (*update_dchub)( struct hubbub *hubbub, @@ -274,9 +274,11 @@ struct hubbub_funcs { const struct hubbub_urgent_latency_params *params); uint32_t (*get_urgent_ramp_latency_ns)(struct hubbub *hubbub, uint32_t refclk_mhz); - void (*start_measuring_unbounded_bandwidth)( + void (*arm_measuring_out_of_order_bandwidth)( + struct hubbub *hubbub); + void (*start_measuring_out_of_order_bandwidth)( struct hubbub *hubbub); - uint32_t (*get_unbounded_bandwidth_mbps)(struct hubbub *hubbub, + uint32_t (*get_out_of_order_bandwidth_mbps)(struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t *duration_ns); void (*start_measuring_in_order_bandwidth)( struct hubbub *hubbub); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mcif_wb.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mcif_wb.h index 15cb782f129b..aae0ad7ae835 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/mcif_wb.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mcif_wb.h @@ -27,6 +27,7 @@ #include "dc_hw_types.h" +#include "dml2_0/dml21/inc/dml_top_dchub_registers.h" enum mmhubbub_wbif_mode { PACKED_444 = 0, @@ -36,14 +37,21 @@ enum mmhubbub_wbif_mode { }; struct mcif_arb_params { - - unsigned int time_per_pixel; - unsigned int cli_watermark[4]; - unsigned int pstate_watermark[4]; - unsigned int arbitration_slice; - unsigned int slice_lines; - unsigned int max_scaled_time; - unsigned int dram_speed_change_duration; + union { + struct { + unsigned int time_per_pixel; + unsigned int cli_watermark[4]; + unsigned int pstate_watermark[4]; + unsigned int arbitration_slice; + unsigned int slice_lines; + unsigned int max_scaled_time; + unsigned int dram_speed_change_duration; + }; + struct { + struct dml2_mcif_global_register_set global_regs; + struct dml2_mcif_per_pipe_register_set inst_regs; + } dcn4x; //dcn4+ + }; }; struct mcif_irq_params { diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_service.h b/drivers/gpu/drm/amd/display/dc/inc/link_service.h index addeb3e3b25a..026d28046eea 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/link_service.h +++ b/drivers/gpu/drm/amd/display/dc/inc/link_service.h @@ -155,8 +155,8 @@ struct link_service { /*************************** DPMS *************************************/ - void (*set_dpms_on)(struct dc_state *state, struct pipe_ctx *pipe_ctx); - void (*set_dpms_off)(struct pipe_ctx *pipe_ctx); + enum dc_status (*set_dpms_on)(struct dc_state *state, struct pipe_ctx *pipe_ctx); + enum dc_status (*set_dpms_off)(struct pipe_ctx *pipe_ctx); void (*resume)(struct dc_link *link); void (*blank_all_dp_displays)(struct dc *dc); void (*blank_all_edp_displays)(struct dc *dc); diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h index 945cdcefb7d4..d3171e70e07c 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/resource.h +++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h @@ -577,12 +577,15 @@ struct pipe_ctx *resource_find_free_secondary_pipe_legacy( const struct pipe_ctx *primary_pipe); bool resource_validate_attach_surfaces( - const struct dc_validation_set set[], - int set_count, + const struct dc_validation_set *set, const struct dc_state *old_context, struct dc_state *context, const struct resource_pool *pool); +enum dc_status resource_validate_probe_set(struct dc *dc, + const struct dc_probe_state *probes, + uint8_t probe_count); + enum dc_status resource_map_clock_resources( const struct dc *dc, struct dc_state *context, diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index 335ae952ef60..48b086d15ab0 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -384,7 +384,7 @@ static bool write_i2c_retimer_vga( for (size_t i = 0; i < ARRAY_SIZE(vga_data); i++) { if (!write_i2c_retimer_offset_value(link, address, vga_data[i][0], vga_data[i][1])) { - DC_LOG_ERROR("Set retimer failed, vga index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, vga index: %zu\n", i); return false; } } @@ -405,7 +405,7 @@ static bool write_i2c_retimer_byte( return true; if (!write_i2c_retimer_offset_value(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value); + DC_LOG_DEBUG("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value); return false; } @@ -421,14 +421,14 @@ static bool write_i2c_retimer_byte( if (!link_query_ddc_data( link->ddc, address, &offset, 1, &value, 1 )) { - DC_LOG_ERROR("Set retimer failed, link_query_ddc_data\n"); + DC_LOG_DEBUG("Set retimer failed, link_query_ddc_data\n"); return false; } } value |= apply_rx_tx_change; if (!write_i2c_retimer_offset_value(link, address, offset, value)) { - DC_LOG_ERROR("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value); + DC_LOG_DEBUG("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value); return false; } } @@ -449,7 +449,7 @@ static bool write_i2c_retimer_setting( uint8_t value = settings->reg_settings[i].i2c_reg_val; if (!write_i2c_retimer_byte(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, index: %zu\n", i); return false; } } @@ -460,7 +460,7 @@ static bool write_i2c_retimer_setting( uint8_t value = settings->reg_settings_6g[i].i2c_reg_val; if (!write_i2c_retimer_byte(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, 6g index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, 6g index: %zu\n", i); return false; } } @@ -492,7 +492,7 @@ static bool write_i2c_default_retimer_setting( for (size_t i = 0; i < ARRAY_SIZE(data); i++) { if (!write_i2c_retimer_offset_value(link, address, data[i][0], data[i][1])) { - DC_LOG_ERROR("Set default retimer failed, index: %zu\n", i); + DC_LOG_DEBUG("Set default retimer failed, index: %zu\n", i); return false; } } @@ -524,23 +524,31 @@ static bool write_i2c_redriver_setting( ); if (!success) - DC_LOG_ERROR("Set redriver failed"); + DC_LOG_DEBUG("Set redriver failed"); return success; } +static struct link_encoder *get_link_encoder(struct pipe_ctx *pipe_ctx) +{ + struct link_encoder *link_enc + = pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment + ? pipe_ctx->link_res.dio_link_enc + : link_enc_cfg_get_link_enc(pipe_ctx->stream->link); + + ASSERT(link_enc); + return link_enc; +} + static void update_psp_stream_config(struct pipe_ctx *pipe_ctx, bool dpms_off) { struct cp_psp *cp_psp = &pipe_ctx->stream->ctx->cp_psp; - struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc; + struct link_encoder *link_enc = get_link_encoder(pipe_ctx); struct cp_psp_stream_config config = {0}; enum dp_panel_mode panel_mode = dp_get_panel_mode(pipe_ctx->stream->link); if (cp_psp == NULL || cp_psp->funcs.update_stream_config == NULL) return; - if (!pipe_ctx->stream->ctx->dc->config.unify_link_enc_assignment) - link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); - ASSERT(link_enc); if (link_enc == NULL) return; @@ -2375,7 +2383,7 @@ static struct vpg *get_vpg(struct pipe_ctx *pipe_ctx) return pipe_ctx->stream_res.stream_enc->vpg; } -void link_set_dpms_off(struct pipe_ctx *pipe_ctx) +enum dc_status link_set_dpms_off(struct pipe_ctx *pipe_ctx) { DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); struct dc_stream_state *stream = pipe_ctx->stream; @@ -2387,7 +2395,8 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) ASSERT(is_master_pipe_for_link(link, pipe_ctx)); if (dc_is_virtual_signal(stream->signal)) - return; + /* No real hardware to program for virtual signals. */ + return DC_DPMS_SKIPPED_HANDSHAKE; if (stream->sink) { if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL && @@ -2481,18 +2490,23 @@ void link_set_dpms_off(struct pipe_ctx *pipe_ctx) /* since current psp not loaded, we need to reset it to default */ link->panel_mode = panel_mode; } + + return DC_DPMS_SUCCESS; } -void link_set_dpms_on( +static enum dc_status link_set_dpms_on_pre_enable_link( struct dc_state *state, - struct pipe_ctx *pipe_ctx) + struct pipe_ctx *pipe_ctx +) { + // Used conditionally in ifdef'ed diagnostic builds + (void) state; + DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); struct dc_stream_state *stream = pipe_ctx->stream; struct dc *dc = stream->ctx->dc; struct dc_link *link = stream->link; - enum dc_status status; - struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc; + enum otg_out_mux_dest otg_out_dest = OUT_MUX_DIO; struct vpg *vpg = get_vpg(pipe_ctx); const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res); @@ -2502,7 +2516,8 @@ void link_set_dpms_on( ASSERT(is_master_pipe_for_link(link, pipe_ctx)); if (dc_is_virtual_signal(stream->signal)) - return; + /* No real hardware to program for virtual signals. */ + return DC_DPMS_SKIPPED_HANDSHAKE; if (stream->sink) { if (stream->sink->sink_signal != SIGNAL_TYPE_VIRTUAL && @@ -2516,13 +2531,12 @@ void link_set_dpms_on( } link_wait_for_unlocked(link); - if (!dc->config.unify_link_enc_assignment) - link_enc = link_enc_cfg_get_link_enc(link); - ASSERT(link_enc); if (!dc_is_virtual_signal(stream->signal) && !dc_is_hdmi_frl_signal(stream->signal) && !dp_is_128b_132b_signal(pipe_ctx)) { + struct link_encoder *link_enc = get_link_encoder(pipe_ctx); + if (link_enc) link_enc->funcs->setup( link_enc, @@ -2569,7 +2583,9 @@ void link_set_dpms_on( } update_psp_stream_config(pipe_ctx, false); - return; + + /* Seamless boot: hardware already enabled by BIOS; skip link training. */ + return DC_DPMS_SKIPPED_HANDSHAKE; } /* eDP lit up by bios already, no need to enable again. */ @@ -2587,11 +2603,16 @@ void link_set_dpms_on( msleep(post_oui_delay); } - return; + /* eDP already lit by BIOS; skip standard enable steps. */ + return DC_DPMS_SKIPPED_HANDSHAKE; } if (stream->dpms_off) - return; + /* + * Stream is configured as DPMS-off; skip link enable. + * Hardware will NOT be in a fully enabled state after this early exit. + */ + return DC_DPMS_FAILED_INCOMPLETE; /* For Dp tunneling link, a pending HPD means that we have a race condition between processing * current link and processing the pending HPD. If we enable the link now, we may end up with a @@ -2599,7 +2620,11 @@ void link_set_dpms_on( */ if (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && link->is_hpd_pending) { DC_LOG_DEBUG("%s, Link%d HPD is pending, not enable it.\n", __func__, link->link_index); - return; + /* + * Pending HPD on USB4 DPIA link: skip enable to avoid race condition. + * Hardware will NOT be in a fully enabled state after this early exit. + */ + return DC_DPMS_FAILED_INCOMPLETE; } /* Have to setup DSC before DIG FE and BE are connected (which happens before the @@ -2617,30 +2642,61 @@ void link_set_dpms_on( if (link->replay_settings.config.replay_supported && !dc_is_embedded_signal(link->connector_signal)) dp_setup_replay(link, stream); - // TODO: Split DPMS-on into 3 functions at this point - status = enable_link(state, pipe_ctx); + return DC_DPMS_SUCCESS; +} + +static enum dc_status link_set_dpms_on_enable_link( + struct dc_state *state, + struct pipe_ctx *pipe_ctx +) +{ + DC_LOGGER_INIT(pipe_ctx->stream->ctx->logger); + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + const enum dc_status status = enable_link(state, pipe_ctx); + + if (status == DC_OK) + return DC_DPMS_SUCCESS; - if (status != DC_OK) { - DC_LOG_WARNING("enabling link %u failed: %d\n", - link->link_index, - status); + DC_LOG_WARNING("enabling link %u failed: %d\n", link->link_index, status); - /* Abort stream enable *unless* the failure was due to - * DP link training - some DP monitors will recover and - * show the stream anyway. But MST displays can't proceed - * without link training. - */ - if ((status != DC_FAIL_DP_LINK_TRAINING && - status != DC_FAIL_HDMI_FRL_LINK_TRAINING) || - stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) { - if (false == link->link_status.link_active) - disable_link(link, &pipe_ctx->link_res, - stream->signal); - BREAK_TO_DEBUGGER(); - return; - } + /* Abort stream enable *unless* the failure was due to + * DP link training - some DP monitors will recover and + * show the stream anyway. But MST displays can't proceed + * without link training. + */ + switch (status) { + case DC_FAIL_DP_LINK_TRAINING: + case DC_FAIL_HDMI_FRL_LINK_TRAINING: + if (stream->signal != SIGNAL_TYPE_DISPLAY_PORT_MST) + return DC_DPMS_SUCCESS; + break; + + default: + break; } - // TODO: Split DPMS-on into 3 functions at this point + + if (!link->link_status.link_active) + disable_link(link, &pipe_ctx->link_res, stream->signal); + + /* + * Link enable failed; do NOT set skip_remaining so that post_enable_link + * still runs and leaves hardware in a consistent state. + */ + return DC_DPMS_FAILED_HANDSHAKE; +} + +static enum dc_status link_set_dpms_on_post_enable_link( + struct dc_state *state, + struct pipe_ctx *pipe_ctx +) +{ + // Used conditionally in ifdef'ed diagnostic builds + (void) state; + + struct dc_stream_state *stream = pipe_ctx->stream; + struct dc_link *link = stream->link; + struct hw_sequencer_funcs *hwss = &stream->ctx->dc->hwss; if (stream->timing.flags.DSC && dc_is_hdmi_frl_signal(stream->signal)) //TODO: bring HDMI FRL in line with DP @@ -2659,13 +2715,15 @@ void link_set_dpms_on( if (!(dc_is_virtual_signal(stream->signal) || dc_is_hdmi_frl_signal(stream->signal) || dp_is_128b_132b_signal(pipe_ctx))) { + struct link_encoder *link_enc = get_link_encoder(pipe_ctx); + if (link_enc) link_enc->funcs->setup( link_enc, stream->signal); } - dc->hwss.enable_stream(pipe_ctx); + hwss->enable_stream(pipe_ctx); /* Set DPS PPS SDP (AKA "info frames") */ if (stream->timing.flags.DSC) { @@ -2697,7 +2755,7 @@ void link_set_dpms_on( link->is_display_mux_present) msleep(20); - dc->hwss.unblank_stream(pipe_ctx, + hwss->unblank_stream(pipe_ctx, &link->cur_link_settings); if (stream->sink_patches.delay_ignore_msa > 0) @@ -2707,8 +2765,50 @@ void link_set_dpms_on( enable_stream_features(pipe_ctx); update_psp_stream_config(pipe_ctx, false); - dc->hwss.enable_audio_stream(pipe_ctx); + hwss->enable_audio_stream(pipe_ctx); if (dc_is_hdmi_signal(stream->signal)) set_avmute(pipe_ctx, false); + + return DC_DPMS_SUCCESS; +} + +enum dc_status link_set_dpms_on( + struct dc_state *state, + struct pipe_ctx *pipe_ctx +) +{ + enum dc_status result = DC_DPMS_SUCCESS; + + typedef enum dc_status (*step)(struct dc_state *, struct pipe_ctx *); + const step steps[] = { + link_set_dpms_on_pre_enable_link, + link_set_dpms_on_enable_link, + link_set_dpms_on_post_enable_link, + }; + + for (size_t i = 0; i < ARRAY_SIZE(steps); i++) { + const enum dc_status step_result = steps[i](state, pipe_ctx); + + switch (step_result) { + case DC_DPMS_SUCCESS: + case DC_DPMS_FAILED_HANDSHAKE: + // Enum is ordered from "best" to "worst" results + result = max(result, step_result); + break; + + case DC_DPMS_SKIPPED_HANDSHAKE: + return step_result; + + case DC_DPMS_FAILED_INCOMPLETE: + ASSERT(false); + return step_result; + + default: + ASSERT(false); + return step_result; + } + } + + return result; } diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.h b/drivers/gpu/drm/amd/display/dc/link/link_dpms.h index e8662147dd8e..6559bc04d90e 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.h +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.h @@ -27,10 +27,10 @@ #define __DC_LINK_DPMS_H__ #include "link_service.h" -void link_set_dpms_on( +enum dc_status link_set_dpms_on( struct dc_state *state, struct pipe_ctx *pipe_ctx); -void link_set_dpms_off(struct pipe_ctx *pipe_ctx); +enum dc_status link_set_dpms_off(struct pipe_ctx *pipe_ctx); void link_resume(struct dc_link *link); void link_blank_all_dp_displays(struct dc *dc); void link_blank_all_edp_displays(struct dc *dc); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c index 605bf19dc4f2..04eedec8a230 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c @@ -1721,6 +1721,20 @@ bool perform_link_training_with_retries( } } + if (j == (attempts - 1)) { + DC_LOG_WARNING( + "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n", + __func__, link->link_index, (unsigned int)j + 1, attempts, + cur_link_settings.link_rate, cur_link_settings.lane_count, + cur_link_settings.link_spread, status); + } else { + DC_LOG_HW_LINK_TRAINING( + "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n", + __func__, link->link_index, (unsigned int)j + 1, attempts, + cur_link_settings.link_rate, cur_link_settings.lane_count, + cur_link_settings.link_spread, status); + } + fail_count++; dp_trace_lt_fail_count_update(link, fail_count, false); if (link->ep_type == DISPLAY_ENDPOINT_PHY) { @@ -1740,20 +1754,6 @@ bool perform_link_training_with_retries( do_fallback = false; } - if (j == (attempts - 1)) { - DC_LOG_WARNING( - "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n", - __func__, link->link_index, (unsigned int)j + 1, attempts, - cur_link_settings.link_rate, cur_link_settings.lane_count, - cur_link_settings.link_spread, status); - } else { - DC_LOG_HW_LINK_TRAINING( - "%s: Link(%d) training attempt %u of %d failed @ rate(%d) x lane(%d) @ spread = %x : fail reason:(%d)\n", - __func__, link->link_index, (unsigned int)j + 1, attempts, - cur_link_settings.link_rate, cur_link_settings.lane_count, - cur_link_settings.link_spread, status); - } - dp_disable_link_phy(link, &pipe_ctx->link_res, signal); /* Abort link training if failure due to sink being unplugged. */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c index c35074177a36..2afdfc113675 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c @@ -1090,7 +1090,7 @@ void hdmi_frl_decide_link_settings(struct dc_stream_state *stream, &stream->link->frl_verified_link_cap); if (stream->link->frl_flags.force_frl_rate != 0 && - stream->link->frl_flags.force_frl_rate < stream->link->frl_verified_link_cap.frl_link_rate) { + stream->link->frl_flags.force_frl_rate <= stream->link->frl_verified_link_cap.frl_link_rate) { switch (stream->link->frl_flags.force_frl_rate) { case HDMI_FRL_LINK_RATE_3GBPS: temp_settings.frl_link_rate = HDMI_FRL_LINK_RATE_3GBPS; diff --git a/drivers/gpu/drm/amd/display/dc/mmhubbub/Makefile b/drivers/gpu/drm/amd/display/dc/mmhubbub/Makefile index afc376defa0b..dddf5a9eb0b8 100644 --- a/drivers/gpu/drm/amd/display/dc/mmhubbub/Makefile +++ b/drivers/gpu/drm/amd/display/dc/mmhubbub/Makefile @@ -50,6 +50,16 @@ MMHUBBUB_DCN35 = dcn35_mmhubbub.o AMD_DAL_MMHUBBUB_DCN35 = $(addprefix $(AMDDALPATH)/dc/mmhubbub/dcn35/,$(MMHUBBUB_DCN35)) AMD_DISPLAY_FILES += $(AMD_DAL_MMHUBBUB_DCN35) + + +############################################################################### +# DCN401 +############################################################################### +MMHUBBUB_DCN401 = dcn401_mmhubbub.o + +AMD_DAL_MMHUBBUB_DCN401 = $(addprefix $(AMDDALPATH)/dc/mmhubbub/dcn401/,$(MMHUBBUB_DCN401)) + +AMD_DISPLAY_FILES += $(AMD_DAL_MMHUBBUB_DCN401) endif ############################################################################### diff --git a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.c b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.c index 6b6f80a3bfd5..5ce787632fe9 100644 --- a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.c +++ b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.c @@ -73,7 +73,7 @@ * the bufmgr status can show the progress of write back, can be used for debug purpose */ -static void mmhubbub32_warmup_mcif(struct mcif_wb *mcif_wb, +void mmhubbub32_warmup_mcif(struct mcif_wb *mcif_wb, struct mcif_warmup_params *params) { struct dcn30_mmhubbub *mcif_wb30 = TO_DCN30_MMHUBBUB(mcif_wb); @@ -100,7 +100,7 @@ static void mmhubbub32_warmup_mcif(struct mcif_wb *mcif_wb, REG_UPDATE(MMHUBBUB_WARMUP_CONTROL_STATUS, MMHUBBUB_WARMUP_EN, false); } -static void mmhubbub32_config_mcif_buf(struct mcif_wb *mcif_wb, +void mmhubbub32_config_mcif_buf(struct mcif_wb *mcif_wb, struct mcif_buf_params *params, unsigned int dest_height) { diff --git a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.h b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.h index ef15b4f1f6b9..682f3bba6d50 100644 --- a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.h +++ b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.h @@ -200,6 +200,11 @@ SF(MMHUBBUB_WARMUP_CONTROL_STATUS, MMHUBBUB_WARMUP_SW_INT_ACK, mask_sh),\ SF(MMHUBBUB_WARMUP_CONTROL_STATUS, MMHUBBUB_WARMUP_INC_ADDR, mask_sh) +void mmhubbub32_warmup_mcif(struct mcif_wb *mcif_wb, + struct mcif_warmup_params *params); +void mmhubbub32_config_mcif_buf(struct mcif_wb *mcif_wb, + struct mcif_buf_params *params, + unsigned int dest_height); void dcn32_mmhubbub_construct(struct dcn30_mmhubbub *mcif_wb30, struct dc_context *ctx, diff --git a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn401/dcn401_mmhubbub.c b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn401/dcn401_mmhubbub.c new file mode 100644 index 000000000000..d2845acb74c8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn401/dcn401_mmhubbub.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#include "dcn401_mmhubbub.h" +#include "reg_helper.h" + +#define REG(reg) \ + ((const struct dcn35_mmhubbub_registers *)(mcif_wb30->mcif_wb_regs)) \ + ->reg + +#define CTX mcif_wb30->base.ctx + +#undef FN +#define FN(reg_name, field_name) \ + ((const struct dcn401_mmhubbub_shift *)(mcif_wb30->mcif_wb_shift)) \ + ->field_name, \ + ((const struct dcn401_mmhubbub_mask *)(mcif_wb30->mcif_wb_mask)) \ + ->field_name + +static void mmhubbub401_config_mcif_arb(struct mcif_wb *mcif_wb, + struct mcif_arb_params *params) +{ + struct dcn30_mmhubbub *mcif_wb30 = TO_DCN30_MMHUBBUB(mcif_wb); + + /* Programmed by the video driver based on the CRTC timing (for DWB) */ + REG_UPDATE(MCIF_WB_ARBITRATION_CONTROL, + MCIF_WB_TIME_PER_PIXEL, params->dcn4x.inst_regs.time_per_pixel); + + /* + * Programming DWB watermark. + * Watermark to generate urgent in MCIF_WB_CLI, value is determined by MCIF_WB_CLI_WATERMARK_MASK. + * Program in ns. A formula will be provided in the pseudo code to calculate the value. + */ + /* Program urgent_watermarkA */ + REG_UPDATE_2(MCIF_WB_WATERMARK, + MCIF_WB_CLI_WATERMARK_MASK, 0x0, + MCIF_WB_CLI_WATERMARK, params->dcn4x.global_regs.wm_regs[0].urgent); + /* Program urgent_watermarkB */ + REG_UPDATE_2(MCIF_WB_WATERMARK, + MCIF_WB_CLI_WATERMARK_MASK, 0x1, + MCIF_WB_CLI_WATERMARK, params->dcn4x.global_regs.wm_regs[0].urgent); + /* Program urgent_watermarkC */ + REG_UPDATE_2(MCIF_WB_WATERMARK, + MCIF_WB_CLI_WATERMARK_MASK, 0x2, + MCIF_WB_CLI_WATERMARK, params->dcn4x.global_regs.wm_regs[0].urgent); + /* Program urgent_watermarkD */ + REG_UPDATE_2(MCIF_WB_WATERMARK, + MCIF_WB_CLI_WATERMARK_MASK, 0x3, + MCIF_WB_CLI_WATERMARK, params->dcn4x.global_regs.wm_regs[0].urgent); + + /* Programming UCLK P-State watermark */ + /* Program nbp_state_change_watermarkA */ + REG_UPDATE_3(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, + NB_PSTATE_CHANGE_WATERMARK_MASK, 0x0, + NB_PSTATE_CHANGE_WATERMARK_TYPE, 0x0, + NB_PSTATE_CHANGE_REFRESH_WATERMARK, params->dcn4x.global_regs.wm_regs[0].uclk_pstate); + /* Program nbp_state_change_watermarkB */ + REG_UPDATE_3(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, + NB_PSTATE_CHANGE_WATERMARK_MASK, 0x1, + NB_PSTATE_CHANGE_WATERMARK_TYPE, 0x0, + NB_PSTATE_CHANGE_REFRESH_WATERMARK, params->dcn4x.global_regs.wm_regs[0].uclk_pstate); + /* Program nbp_state_change_watermarkC */ + REG_UPDATE_3(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, + NB_PSTATE_CHANGE_WATERMARK_MASK, 0x2, + NB_PSTATE_CHANGE_WATERMARK_TYPE, 0x0, + NB_PSTATE_CHANGE_REFRESH_WATERMARK, params->dcn4x.global_regs.wm_regs[0].uclk_pstate); + /* Program nbp_state_change_watermarkD */ + REG_UPDATE_3(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, + NB_PSTATE_CHANGE_WATERMARK_MASK, 0x3, + NB_PSTATE_CHANGE_WATERMARK_TYPE, 0x0, + NB_PSTATE_CHANGE_REFRESH_WATERMARK, params->dcn4x.global_regs.wm_regs[0].uclk_pstate); + + /* Programming FCLK P-State watermark */ + /* Program nbp_state_change_watermarkA */ + REG_UPDATE_3(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, + NB_PSTATE_CHANGE_WATERMARK_MASK, 0x0, + NB_PSTATE_CHANGE_WATERMARK_TYPE, 0x1, + NB_PSTATE_CHANGE_REFRESH_WATERMARK, params->dcn4x.global_regs.wm_regs[0].temp_read_or_ppt); + /* Program nbp_state_change_watermarkB */ + REG_UPDATE_3(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, + NB_PSTATE_CHANGE_WATERMARK_MASK, 0x1, + NB_PSTATE_CHANGE_WATERMARK_TYPE, 0x1, + NB_PSTATE_CHANGE_REFRESH_WATERMARK, params->dcn4x.global_regs.wm_regs[0].temp_read_or_ppt); + /* Program nbp_state_change_watermarkC */ + REG_UPDATE_3(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, + NB_PSTATE_CHANGE_WATERMARK_MASK, 0x2, + NB_PSTATE_CHANGE_WATERMARK_TYPE, 0x1, + NB_PSTATE_CHANGE_REFRESH_WATERMARK, params->dcn4x.global_regs.wm_regs[0].fclk_pstate); + /* Program nbp_state_change_watermarkD */ + REG_UPDATE_3(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, + NB_PSTATE_CHANGE_WATERMARK_MASK, 0x3, + NB_PSTATE_CHANGE_WATERMARK_TYPE, 0x1, + NB_PSTATE_CHANGE_REFRESH_WATERMARK, params->dcn4x.global_regs.wm_regs[0].fclk_pstate); + + /* Program max_scaled_time */ + REG_UPDATE(MULTI_LEVEL_QOS_CTRL, + MAX_SCALED_TIME_TO_URGENT, params->dcn4x.inst_regs.max_scaled_time_ns); + + /* Program slice_lines */ + REG_UPDATE(MCIF_WB_BUFMGR_VCE_CONTROL, + MCIF_WB_BUFMGR_SLICE_SIZE, params->dcn4x.inst_regs.slice_lines); + + /* Set arbitration unit for Luma/Chroma */ + /* arb_unit=2 should be chosen for more efficiency */ + /* Arbitration size, 0: 2048 bytes 1: 4096 bytes 2: 8192 Bytes */ + REG_UPDATE(MCIF_WB_ARBITRATION_CONTROL, + MCIF_WB_CLIENT_ARBITRATION_SLICE, params->dcn4x.inst_regs.arbitration_slice); +} + +static const struct mcif_wb_funcs dcn401_mmhubbub_funcs = { + .warmup_mcif = mmhubbub32_warmup_mcif, + .enable_mcif = mmhubbub2_enable_mcif, + .disable_mcif = mmhubbub2_disable_mcif, + .config_mcif_buf = mmhubbub32_config_mcif_buf, + .config_mcif_arb = mmhubbub401_config_mcif_arb, + .config_mcif_irq = mmhubbub2_config_mcif_irq, + .dump_frame = mcifwb2_dump_frame, +}; + +void dcn401_mmhubbub_construct(struct dcn30_mmhubbub *mcif_wb30, + struct dc_context *ctx, + const struct dcn35_mmhubbub_registers *mcif_wb_regs, + const struct dcn401_mmhubbub_shift *mcif_wb_shift, + const struct dcn401_mmhubbub_mask *mcif_wb_mask, + int inst) +{ + mcif_wb30->base.ctx = ctx; + + mcif_wb30->base.inst = inst; + mcif_wb30->base.funcs = &dcn401_mmhubbub_funcs; + mcif_wb30->mcif_wb_regs = (const struct dcn30_mmhubbub_registers *)mcif_wb_regs; + mcif_wb30->mcif_wb_shift = (const struct dcn30_mmhubbub_shift *)mcif_wb_shift; + mcif_wb30->mcif_wb_mask = (const struct dcn30_mmhubbub_mask *)mcif_wb_mask; +} diff --git a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn401/dcn401_mmhubbub.h b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn401/dcn401_mmhubbub.h new file mode 100644 index 000000000000..fbc69a210fa7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn401/dcn401_mmhubbub.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright 2026 Advanced Micro Devices, Inc. */ + + +#ifndef __DCN401_MMHUBBUB_H +#define __DCN401_MMHUBBUB_H + +#include "mcif_wb.h" +#include "dcn32/dcn32_mmhubbub.h" +#include "dcn35/dcn35_mmhubbub.h" + +#define MCIF_WB_REG_VARIABLE_LIST_DCN4_01 \ + MCIF_WB_REG_VARIABLE_LIST_DCN3_5; + +#define MCIF_WB_COMMON_MASK_SH_LIST_DCN4_01(mask_sh) \ + MCIF_WB_COMMON_MASK_SH_LIST_DCN3_5(mask_sh), \ + SF(MCIF_WB_NB_PSTATE_LATENCY_WATERMARK, NB_PSTATE_CHANGE_WATERMARK_TYPE, mask_sh) + +#define MCIF_WB_REG_FIELD_LIST_DCN4_01(type) \ + struct { \ + MCIF_WB_REG_FIELD_LIST_DCN3_5(type); \ + type NB_PSTATE_CHANGE_WATERMARK_TYPE; \ + } + +struct dcn401_mmhubbub_mask { + MCIF_WB_REG_FIELD_LIST_DCN4_01(uint32_t); +}; + +struct dcn401_mmhubbub_shift { + MCIF_WB_REG_FIELD_LIST_DCN4_01(uint8_t); +}; + +void dcn401_mmhubbub_construct(struct dcn30_mmhubbub *mcif_wb30, + struct dc_context *ctx, + const struct dcn35_mmhubbub_registers *mcif_wb_regs, + const struct dcn401_mmhubbub_shift *mcif_wb_shift, + const struct dcn401_mmhubbub_mask *mcif_wb_mask, + int inst); + +#endif // __DCN401_MMHUBBUB_H diff --git a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn42/dcn42_mmhubbub.h b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn42/dcn42_mmhubbub.h index 0dd1f0d2eb88..0001903a04ec 100644 --- a/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn42/dcn42_mmhubbub.h +++ b/drivers/gpu/drm/amd/display/dc/mmhubbub/dcn42/dcn42_mmhubbub.h @@ -8,6 +8,7 @@ #include "mcif_wb.h" #include "dcn32/dcn32_mmhubbub.h" #include "dcn35/dcn35_mmhubbub.h" +#include "dcn401/dcn401_mmhubbub.h" void dcn42_mmhubbub_set_fgcg(struct dcn30_mmhubbub *mcif_wb30, bool enabled); #endif // __DCN42_MMHUBBUB_H diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c index d7a07e29d23a..9cc6fd38da19 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c @@ -1067,7 +1067,8 @@ static void program_gamut_remap( struct dcn30_mpc *mpc30, int mpcc_id, const uint16_t *regval, - uint32_t select) + uint32_t select, + enum cm_gamut_coef_format coef_format) { uint16_t selection = 0; struct color_matrices_reg gam_regs; @@ -1117,9 +1118,11 @@ static void program_gamut_remap( &gam_regs); } - //select coefficient set to use + // select coefficient set + format to use REG_SET(MPCC_GAMUT_REMAP_MODE[mpcc_id], 0, MPCC_GAMUT_REMAP_MODE, selection); + REG_SET(MPCC_GAMUT_REMAP_COEF_FORMAT[mpcc_id], 0, + MPCC_GAMUT_REMAP_COEF_FORMAT, coef_format); } void mpc3_set_gamut_remap( @@ -1130,18 +1133,26 @@ void mpc3_set_gamut_remap( struct dcn30_mpc *mpc30 = TO_DCN30_MPC(mpc); int i = 0; uint32_t gamut_mode; + uint16_t hw_matrix[12]; + enum cm_gamut_coef_format coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S2_13; + struct fixed31_32 abs_max_coef = {0}; if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW) - program_gamut_remap(mpc30, mpcc_id, NULL, GAMUT_REMAP_BYPASS); + program_gamut_remap(mpc30, mpcc_id, NULL, GAMUT_REMAP_BYPASS, coef_format); else { - struct fixed31_32 arr_matrix[12]; - uint16_t arr_reg_val[12]; + // take largest absolute value of coefficient in temperature matrix + // if S2D13 cannot fit value, use S3D12 + for (i = 0; i < 12; i++) { + if (dc_fixpt_le(abs_max_coef, dc_fixpt_abs(adjust->temperature_matrix[i]))) + abs_max_coef = dc_fixpt_abs(adjust->temperature_matrix[i]); + } - for (i = 0; i < 12; i++) - arr_matrix[i] = adjust->temperature_matrix[i]; + if (dc_fixpt_le(abs_max_coef, dc_fixpt_from_fraction(S2D13_MAX, DIVIDER))) + coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S2_13; + else + coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S3_12; - convert_float_matrix( - arr_reg_val, arr_matrix, 12); + convert_float_matrix(hw_matrix, adjust->temperature_matrix, coef_format, 12); //current coefficient set in use REG_GET(MPCC_GAMUT_REMAP_MODE[mpcc_id], MPCC_GAMUT_REMAP_MODE_CURRENT, &gamut_mode); @@ -1153,19 +1164,21 @@ void mpc3_set_gamut_remap( else gamut_mode = 1; - program_gamut_remap(mpc30, mpcc_id, arr_reg_val, gamut_mode); + program_gamut_remap(mpc30, mpcc_id, hw_matrix, gamut_mode, coef_format); } } static void read_gamut_remap(struct dcn30_mpc *mpc30, int mpcc_id, uint16_t *regval, - uint32_t *select) + uint32_t *select, + enum cm_gamut_coef_format *coef_format) { struct color_matrices_reg gam_regs; - //current coefficient set in use + //current coefficient set in use + format REG_GET(MPCC_GAMUT_REMAP_MODE[mpcc_id], MPCC_GAMUT_REMAP_MODE_CURRENT, select); + REG_GET(MPCC_GAMUT_REMAP_COEF_FORMAT[mpcc_id], MPCC_GAMUT_REMAP_COEF_FORMAT, coef_format); gam_regs.shifts.csc_c11 = mpc30->mpc_shift->MPCC_GAMUT_REMAP_C11_A; gam_regs.masks.csc_c11 = mpc30->mpc_mask->MPCC_GAMUT_REMAP_C11_A; @@ -1202,8 +1215,9 @@ void mpc3_get_gamut_remap(struct mpc *mpc, struct dcn30_mpc *mpc30 = TO_DCN30_MPC(mpc); uint16_t arr_reg_val[12] = {0}; uint32_t select; + enum cm_gamut_coef_format coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S2_13; - read_gamut_remap(mpc30, mpcc_id, arr_reg_val, &select); + read_gamut_remap(mpc30, mpcc_id, arr_reg_val, &select, &coef_format); if (select == GAMUT_REMAP_BYPASS) { adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; @@ -1211,8 +1225,8 @@ void mpc3_get_gamut_remap(struct mpc *mpc, } adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; - convert_hw_matrix(adjust->temperature_matrix, - arr_reg_val, ARRAY_SIZE(arr_reg_val)); + convert_hw_matrix(adjust->temperature_matrix, arr_reg_val, + coef_format, ARRAY_SIZE(arr_reg_val)); } bool mpc3_program_3dlut( diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c index ce1ee2062e41..d8a4e604a34e 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c @@ -302,7 +302,8 @@ void mpc_program_gamut_remap( unsigned int mpcc_id, const uint16_t *regval, enum mpcc_gamut_remap_id gamut_remap_block_id, - enum mpcc_gamut_remap_mode_select mode_select) + enum mpcc_gamut_remap_mode_select mode_select, + enum cm_gamut_coef_format coef_format) { struct color_matrices_reg gamut_regs; struct dcn401_mpc *mpc401 = TO_DCN401_MPC(mpc); @@ -339,8 +340,11 @@ void mpc_program_gamut_remap( regval, &gamut_regs); - //select coefficient set to use, set A (MODE_1) or set B (MODE_2) - REG_SET(MPCC_GAMUT_REMAP_MODE[mpcc_id], 0, MPCC_GAMUT_REMAP_MODE, mode_select); + //select coefficient set to use, set A (MODE_1) or set B (MODE_2) + format + REG_SET(MPCC_GAMUT_REMAP_MODE[mpcc_id], 0, + MPCC_GAMUT_REMAP_MODE, mode_select); + REG_SET(MPCC_GAMUT_REMAP_COEF_FORMAT[mpcc_id], 0, + MPCC_GAMUT_REMAP_COEF_FORMAT, coef_format); break; case MPCC_MCM_FIRST_GAMUT_REMAP: @@ -373,9 +377,11 @@ void mpc_program_gamut_remap( regval, &gamut_regs); - //select coefficient set to use, set A (MODE_1) or set B (MODE_2) + //select coefficient set to use, set A (MODE_1) or set B (MODE_2) + format REG_SET(MPCC_MCM_FIRST_GAMUT_REMAP_MODE[mpcc_id], 0, MPCC_MCM_FIRST_GAMUT_REMAP_MODE, mode_select); + REG_SET(MPCC_MCM_FIRST_GAMUT_REMAP_COEF_FORMAT[mpcc_id], 0, + MPCC_MCM_FIRST_GAMUT_REMAP_COEF_FORMAT, coef_format); break; case MPCC_MCM_SECOND_GAMUT_REMAP: @@ -408,9 +414,11 @@ void mpc_program_gamut_remap( regval, &gamut_regs); - //select coefficient set to use, set A (MODE_1) or set B (MODE_2) + //select coefficient set to use, set A (MODE_1) or set B (MODE_2) + format REG_SET(MPCC_MCM_SECOND_GAMUT_REMAP_MODE[mpcc_id], 0, MPCC_MCM_SECOND_GAMUT_REMAP_MODE, mode_select); + REG_SET(MPCC_MCM_SECOND_GAMUT_REMAP_COEF_FORMAT[mpcc_id], 0, + MPCC_MCM_SECOND_GAMUT_REMAP_COEF_FORMAT, coef_format); break; default: @@ -426,19 +434,28 @@ void mpc401_set_gamut_remap( struct dcn401_mpc *mpc401 = TO_DCN401_MPC(mpc); unsigned int i = 0; uint32_t mode_select = 0; + uint16_t hw_matrix[12]; + enum cm_gamut_coef_format coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S2_13; + struct fixed31_32 abs_max_coef = {0}; if (adjust->gamut_adjust_type != GRAPHICS_GAMUT_ADJUST_TYPE_SW) { /* Bypass / Disable if type is bypass or hw */ - mpc_program_gamut_remap(mpc, mpcc_id, NULL, - adjust->mpcc_gamut_remap_block_id, MPCC_GAMUT_REMAP_MODE_SELECT_0); + mpc_program_gamut_remap(mpc, mpcc_id, NULL, adjust->mpcc_gamut_remap_block_id, + MPCC_GAMUT_REMAP_MODE_SELECT_0, coef_format); } else { - struct fixed31_32 arr_matrix[12]; - uint16_t arr_reg_val[12]; + // take largest absolute value of coefficient in temperature matrix + // if S2D13 cannot fit value, use S3D12 + for (i = 0; i < 12; i++) { + if (dc_fixpt_le(abs_max_coef, dc_fixpt_abs(adjust->temperature_matrix[i]))) + abs_max_coef = dc_fixpt_abs(adjust->temperature_matrix[i]); + } - for (i = 0; i < 12; i++) - arr_matrix[i] = adjust->temperature_matrix[i]; + if (dc_fixpt_le(abs_max_coef, dc_fixpt_from_fraction(S2D13_MAX, DIVIDER))) + coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S2_13; + else + coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S3_12; - convert_float_matrix(arr_reg_val, arr_matrix, 12); + convert_float_matrix(hw_matrix, adjust->temperature_matrix, coef_format, 12); switch (adjust->mpcc_gamut_remap_block_id) { case MPCC_OGAM_GAMUT_REMAP: @@ -463,8 +480,8 @@ void mpc401_set_gamut_remap( else mode_select = MPCC_GAMUT_REMAP_MODE_SELECT_2; - mpc_program_gamut_remap(mpc, mpcc_id, arr_reg_val, - adjust->mpcc_gamut_remap_block_id, mode_select); + mpc_program_gamut_remap(mpc, mpcc_id, hw_matrix, + adjust->mpcc_gamut_remap_block_id, mode_select, coef_format); } } @@ -472,7 +489,8 @@ void mpc_read_gamut_remap(struct mpc *mpc, int mpcc_id, uint16_t *regval, enum mpcc_gamut_remap_id gamut_remap_block_id, - uint32_t *mode_select) + uint32_t *mode_select, + enum cm_gamut_coef_format *coef_format) { struct color_matrices_reg gamut_regs = {0}; struct dcn401_mpc *mpc401 = TO_DCN401_MPC(mpc); @@ -480,7 +498,10 @@ void mpc_read_gamut_remap(struct mpc *mpc, switch (gamut_remap_block_id) { case MPCC_OGAM_GAMUT_REMAP: //current coefficient set in use - REG_GET(MPCC_GAMUT_REMAP_MODE[mpcc_id], MPCC_GAMUT_REMAP_MODE_CURRENT, mode_select); + REG_GET(MPCC_GAMUT_REMAP_MODE[mpcc_id], + MPCC_GAMUT_REMAP_MODE_CURRENT, mode_select); + REG_GET(MPCC_GAMUT_REMAP_COEF_FORMAT[mpcc_id], + MPCC_GAMUT_REMAP_COEF_FORMAT, coef_format); gamut_regs.shifts.csc_c11 = mpc401->mpc_shift->MPCC_GAMUT_REMAP_C11_A; gamut_regs.masks.csc_c11 = mpc401->mpc_mask->MPCC_GAMUT_REMAP_C11_A; @@ -503,7 +524,9 @@ void mpc_read_gamut_remap(struct mpc *mpc, case MPCC_MCM_FIRST_GAMUT_REMAP: REG_GET(MPCC_MCM_FIRST_GAMUT_REMAP_MODE[mpcc_id], - MPCC_MCM_FIRST_GAMUT_REMAP_MODE_CURRENT, mode_select); + MPCC_MCM_FIRST_GAMUT_REMAP_MODE_CURRENT, mode_select); + REG_GET(MPCC_MCM_FIRST_GAMUT_REMAP_COEF_FORMAT[mpcc_id], + MPCC_MCM_FIRST_GAMUT_REMAP_COEF_FORMAT, coef_format); gamut_regs.shifts.csc_c11 = mpc401->mpc_shift->MPCC_MCM_FIRST_GAMUT_REMAP_C11_A; gamut_regs.masks.csc_c11 = mpc401->mpc_mask->MPCC_MCM_FIRST_GAMUT_REMAP_C11_A; @@ -526,7 +549,9 @@ void mpc_read_gamut_remap(struct mpc *mpc, case MPCC_MCM_SECOND_GAMUT_REMAP: REG_GET(MPCC_MCM_SECOND_GAMUT_REMAP_MODE[mpcc_id], - MPCC_MCM_SECOND_GAMUT_REMAP_MODE_CURRENT, mode_select); + MPCC_MCM_SECOND_GAMUT_REMAP_MODE_CURRENT, mode_select); + REG_GET(MPCC_MCM_SECOND_GAMUT_REMAP_COEF_FORMAT[mpcc_id], + MPCC_MCM_SECOND_GAMUT_REMAP_COEF_FORMAT, coef_format); gamut_regs.shifts.csc_c11 = mpc401->mpc_shift->MPCC_MCM_SECOND_GAMUT_REMAP_C11_A; gamut_regs.masks.csc_c11 = mpc401->mpc_mask->MPCC_MCM_SECOND_GAMUT_REMAP_C11_A; @@ -565,8 +590,10 @@ void mpc401_get_gamut_remap(struct mpc *mpc, { uint16_t arr_reg_val[12] = {0}; uint32_t mode_select = MPCC_GAMUT_REMAP_MODE_SELECT_0; + enum cm_gamut_coef_format coef_format = CM_GAMUT_REMAP_COEF_FORMAT_S2_13; - mpc_read_gamut_remap(mpc, mpcc_id, arr_reg_val, adjust->mpcc_gamut_remap_block_id, &mode_select); + mpc_read_gamut_remap(mpc, mpcc_id, arr_reg_val, + adjust->mpcc_gamut_remap_block_id, &mode_select, &coef_format); if (mode_select == MPCC_GAMUT_REMAP_MODE_SELECT_0) { adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS; @@ -574,8 +601,8 @@ void mpc401_get_gamut_remap(struct mpc *mpc, } adjust->gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW; - convert_hw_matrix(adjust->temperature_matrix, - arr_reg_val, ARRAY_SIZE(arr_reg_val)); + convert_hw_matrix(adjust->temperature_matrix, arr_reg_val, + coef_format, ARRAY_SIZE(arr_reg_val)); } static const struct mpc_funcs dcn401_mpc_funcs = { diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h index 6d842d7b95c7..3d00d9497ed2 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h @@ -242,13 +242,15 @@ void mpc_program_gamut_remap( unsigned int mpcc_id, const uint16_t *regval, enum mpcc_gamut_remap_id gamut_remap_block_id, - enum mpcc_gamut_remap_mode_select mode_select); + enum mpcc_gamut_remap_mode_select mode_select, + enum cm_gamut_coef_format coef_format); void mpc_read_gamut_remap(struct mpc *mpc, int mpcc_id, uint16_t *regval, enum mpcc_gamut_remap_id gamut_remap_block_id, - uint32_t *mode_select); + uint32_t *mode_select, + enum cm_gamut_coef_format *coef_format); void mpc401_get_3dlut_fast_load_status( struct mpc *mpc, diff --git a/drivers/gpu/drm/amd/display/dc/os_types.h b/drivers/gpu/drm/amd/display/dc/os_types.h index 6af831710489..538d00105738 100644 --- a/drivers/gpu/drm/amd/display/dc/os_types.h +++ b/drivers/gpu/drm/amd/display/dc/os_types.h @@ -57,6 +57,16 @@ #include "amdgpu_dm/dc_fpu.h" #endif /* CONFIG_DRM_AMD_DC_FP */ + +/* + * On Linux this is provided by <linux/kconfig.h> and evaluates Kconfig + * options for both built-in (=y) and module (=m) cases. Windows has no + * Kconfig, so config options are never set here and this always yields 0. + */ +#ifndef IS_ENABLED +#define IS_ENABLED(option) 0 +#endif + /* * * general debug capabilities diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.h index dd150a4b4610..bc130793348a 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.h @@ -33,7 +33,6 @@ struct dc; struct resource_pool; -struct dc_validation_set; struct resource_pool *dce100_create_resource_pool( uint8_t num_virtual_links, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.h index 3efc4c55d2d2..f2493945b8f4 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.h @@ -35,13 +35,6 @@ struct resource_pool *dce112_create_resource_pool( uint8_t num_virtual_links, struct dc *dc); -enum dc_status dce112_validate_with_context( - struct dc *dc, - const struct dc_validation_set set[], - int set_count, - struct dc_state *context, - struct dc_state *old_context); - enum dc_status dce112_validate_bandwidth( struct dc *dc, struct dc_state *context, diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index d11ab57afcdd..40fc66e64c1b 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -31,6 +31,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn20/dcn20_resource.h" #include "dcn30_resource.h" @@ -2515,6 +2516,7 @@ static bool dcn30_resource_construct( dc->caps.color.mpc.ogam_rom_caps.pq = 0; dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.dp_hdmi21_pcon_support = true; dc->caps.max_v_total = (1 << 15) - 1; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c index 4ecf448d15b6..551565ae9ef2 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c @@ -31,6 +31,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn30/dcn30_resource.h" #include "dcn301_resource.h" @@ -1516,6 +1517,7 @@ static bool dcn301_resource_construct( dc->caps.color.mpc.ogam_rom_caps.pq = 0; dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c index ae8918a4ad3e..2574e706631d 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c @@ -27,6 +27,7 @@ #include "dcn302_resource.h" #include "dcn302/dcn302_dccg.h" #include "irq/dcn302/irq_service_dcn302.h" +#include "basics/conversion.h" #include "dcn30/dcn30_dio_link_encoder.h" #include "dcn30/dcn30_dio_stream_encoder.h" @@ -1416,6 +1417,7 @@ static bool dcn302_resource_construct( dc->caps.color.mpc.ogam_rom_caps.pq = 0; dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c index 75e6f4e46f60..8b24628e3e14 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c @@ -27,6 +27,7 @@ #include "dcn303_resource.h" #include "dcn303/dcn303_dccg.h" #include "irq/dcn303/irq_service_dcn303.h" +#include "basics/conversion.h" #include "dcn30/dcn30_dio_link_encoder.h" #include "dcn30/dcn30_dio_stream_encoder.h" @@ -1360,6 +1361,7 @@ static bool dcn303_resource_construct( dc->caps.color.mpc.ogam_rom_caps.pq = 0; dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index e29efa452c87..8bb2b8b29cbf 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -31,6 +31,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn31_resource.h" #include "dcn20/dcn20_resource.h" @@ -2123,6 +2124,7 @@ static bool dcn31_resource_construct( dc->caps.color.mpc.ogam_rom_caps.pq = 0; dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.num_of_host_routers = 2; dc->caps.num_of_dpias_per_host_router = 2; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index f50b3250dcba..372e05487847 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -33,6 +33,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn314_resource.h" #include "dcn20/dcn20_resource.h" @@ -2051,6 +2052,7 @@ static bool dcn314_resource_construct( dc->caps.color.mpc.ogam_rom_caps.pq = 0; dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.max_disp_clock_khz_at_vmin = 650000; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index 8297f2f04c16..6e112ef0a49a 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -31,6 +31,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn315_resource.h" #include "dcn20/dcn20_resource.h" @@ -2095,6 +2096,7 @@ static bool dcn315_resource_construct( dc->caps.color.mpc.ogam_rom_caps.pq = 0; dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->config.no_native422_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index 046566ad1afe..b9d2567a4180 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -31,6 +31,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn316_resource.h" #include "dcn20/dcn20_resource.h" @@ -1969,6 +1970,7 @@ static bool dcn316_resource_construct( dc->caps.color.mpc.ogam_rom_caps.pq = 0; dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); /* read VBIOS LTTPR caps */ { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index 004c5690f876..cd9d2087d14e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -32,6 +32,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn32_resource.h" #include "dcn20/dcn20_resource.h" @@ -2448,6 +2449,7 @@ static bool dcn32_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; dc->caps.color.mpc.preblend = true; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); /* Use pipe context based otg sync logic */ dc->config.use_pipe_ctx_sync_logic = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c index 53fd32249310..ff9c1379896e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c @@ -31,6 +31,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn32/dcn32_resource.h" #include "dcn321_resource.h" @@ -1939,6 +1940,7 @@ static bool dcn321_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; dc->caps.color.mpc.preblend = true; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); /* HACK: Force FRL support until BIOS is ready. */ dc->config.force_hdmi21_frl_enc_enable = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 5541b89b1350..1b9b6eb8b322 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -32,6 +32,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn35_resource.h" #include "dml2_0/dml2_wrapper.h" @@ -2078,6 +2079,7 @@ static bool dcn35_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; dc->caps.color.mpc.preblend = true; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.num_of_host_routers = 2; dc->caps.num_of_dpias_per_host_router = 2; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index 053b4380f57e..2268ced16969 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -10,6 +10,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn351_resource.h" #include "dcn20/dcn20_resource.h" @@ -2051,6 +2052,7 @@ static bool dcn351_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; dc->caps.color.mpc.preblend = true; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.num_of_host_routers = 2; dc->caps.num_of_dpias_per_host_router = 2; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index 592000cf9250..b1323e76801e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -10,6 +10,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn36_resource.h" #include "dml2_0/dml2_wrapper.h" @@ -2048,6 +2049,7 @@ static bool dcn36_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; dc->caps.color.mpc.preblend = true; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.num_of_host_routers = 2; dc->caps.num_of_dpias_per_host_router = 2; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c index e3ee792fb2e8..181cb7135dfb 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c @@ -10,12 +10,14 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn401_resource.h" #include "dcn20/dcn20_resource.h" #include "dcn30/dcn30_resource.h" #include "dcn32/dcn32_resource.h" #include "dcn321/dcn321_resource.h" +#include "dcn35/dcn35_resource.h" #include "dcn10/dcn10_ipp.h" #include "dcn401/dcn401_hubbub.h" @@ -58,7 +60,7 @@ #include "dcn31/dcn31_panel_cntl.h" #include "dcn30/dcn30_dwb.h" -#include "dcn32/dcn32_mmhubbub.h" +#include "dcn401/dcn401_mmhubbub.h" #include "dcn/dcn_4_1_0_offset.h" #include "dcn/dcn_4_1_0_sh_mask.h" @@ -435,17 +437,17 @@ static const struct dcn30_dwbc_mask dwbc401_mask = { }; -#define mcif_wb_regs_dcn3_init(id)\ - MCIF_WB_COMMON_REG_LIST_DCN32_RI(id) +#define mcif_wb_regs_dcn401_init(id) \ + MCIF_WB_COMMON_REG_LIST_DCN4_01_RI(id) -static struct dcn30_mmhubbub_registers mcif_wb30_regs[1]; +static struct dcn35_mmhubbub_registers mcif_wb401_regs[1]; -static const struct dcn30_mmhubbub_shift mcif_wb30_shift = { - MCIF_WB_COMMON_MASK_SH_LIST_DCN32(__SHIFT) +static const struct dcn401_mmhubbub_shift mcif_wb401_shift = { + MCIF_WB_COMMON_MASK_SH_LIST_DCN4_01(__SHIFT) }; -static const struct dcn30_mmhubbub_mask mcif_wb30_mask = { - MCIF_WB_COMMON_MASK_SH_LIST_DCN32(_MASK) +static const struct dcn401_mmhubbub_mask mcif_wb401_mask = { + MCIF_WB_COMMON_MASK_SH_LIST_DCN4_01(_MASK) }; #define dsc_regs_init(id)\ @@ -1704,13 +1706,13 @@ static bool dcn401_mmhubbub_create(struct dc_context *ctx, struct resource_pool } #undef REG_STRUCT -#define REG_STRUCT mcif_wb30_regs - mcif_wb_regs_dcn3_init(0); +#define REG_STRUCT mcif_wb401_regs + mcif_wb_regs_dcn401_init(0); - dcn32_mmhubbub_construct(mcif_wb30, ctx, - &mcif_wb30_regs[i], - &mcif_wb30_shift, - &mcif_wb30_mask, + dcn401_mmhubbub_construct(mcif_wb30, ctx, + &mcif_wb401_regs[i], + &mcif_wb401_shift, + &mcif_wb401_mask, i); pool->mcif_wb[i] = &mcif_wb30->base; @@ -2130,6 +2132,7 @@ static bool dcn401_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; dc->caps.color.mpc.preblend = true; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); /* HACK: Force FRL support until BIOS is ready. */ dc->config.force_hdmi21_frl_enc_enable = true; dc->config.use_spl = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h index ce96fe1ddb7c..42a0986d6fe2 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h @@ -7,6 +7,7 @@ #include "core_types.h" #include "dcn32/dcn32_resource.h" +#include "dcn35/dcn35_resource.h" #include "dcn401/dcn401_hubp.h" #define TO_DCN401_RES_POOL(pool)\ @@ -660,4 +661,7 @@ int dcn401_get_power_profile(const struct dc_state *context); SR(SYMCLKC_CLOCK_ENABLE),\ SR(SYMCLKD_CLOCK_ENABLE) +#define MCIF_WB_COMMON_REG_LIST_DCN4_01_RI(inst) \ + MCIF_WB_COMMON_REG_LIST_DCN3_5_RI(inst) + #endif /* _DCN401_RESOURCE_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index 547a0b816539..dba1b69137f9 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -9,6 +9,7 @@ #include "dcn42/dcn42_init.h" #include "resource.h" +#include "basics/conversion.h" #include "include/irq_service_interface.h" #include "dcn42_resource.h" @@ -400,16 +401,18 @@ static const struct dcn30_dwbc_shift dwbc401_shift = { static const struct dcn30_dwbc_mask dwbc401_mask = { DWBC_COMMON_MASK_SH_LIST_DCN30(_MASK)}; -#define mcif_wb_regs_dcn3_init(id) \ - MCIF_WB_COMMON_REG_LIST_DCN3_5_RI(id) +#define mcif_wb_regs_dcn401_init(id) \ + MCIF_WB_COMMON_REG_LIST_DCN4_01_RI(id) -static struct dcn35_mmhubbub_registers mcif_wb35_regs[1]; +static struct dcn35_mmhubbub_registers mcif_wb401_regs[1]; -static const struct dcn35_mmhubbub_shift mcif_wb35_shift = { - MCIF_WB_COMMON_MASK_SH_LIST_DCN3_5(__SHIFT)}; +static const struct dcn401_mmhubbub_shift mcif_wb401_shift = { + MCIF_WB_COMMON_MASK_SH_LIST_DCN4_01(__SHIFT) +}; -static const struct dcn35_mmhubbub_mask mcif_wb35_mask = { - MCIF_WB_COMMON_MASK_SH_LIST_DCN3_5(_MASK)}; +static const struct dcn401_mmhubbub_mask mcif_wb401_mask = { + MCIF_WB_COMMON_MASK_SH_LIST_DCN4_01(_MASK) +}; #define dsc_regs_init(id) \ DSC_REG_LIST_DCN401_RI(id) @@ -1768,13 +1771,13 @@ static bool dcn42_mmhubbub_create(struct dc_context *ctx, struct resource_pool * } #undef REG_STRUCT -#define REG_STRUCT mcif_wb35_regs - mcif_wb_regs_dcn3_init(0); +#define REG_STRUCT mcif_wb401_regs + mcif_wb_regs_dcn401_init(0); - dcn35_mmhubbub_construct(mcif_wb30, ctx, - &mcif_wb35_regs[i], - &mcif_wb35_shift, - &mcif_wb35_mask, + dcn401_mmhubbub_construct(mcif_wb30, ctx, + &mcif_wb401_regs[i], + &mcif_wb401_shift, + &mcif_wb401_mask, i); dcn42_mmhubbub_init(mcif_wb30, ctx); @@ -2108,6 +2111,7 @@ static bool dcn42_resource_construct( dc->caps.color.mpc.rmcm_3d_lut_caps.mem_format_support.float_fp1_5_10 = 1; dc->caps.color.mpc.rmcm_3d_lut_caps.mem_pixel_order_support.order_rgba = 1; dc->caps.color.mpc.rmcm_3d_lut_caps.mem_pixel_order_support.order_bgra = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.num_of_host_routers = 3; dc->caps.num_of_dpias_per_host_router = 2; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index 2334bc5b75b8..dbd6267340ac 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -13,6 +13,7 @@ #include "resource.h" #include "include/irq_service_interface.h" +#include "basics/conversion.h" #include "dcn42b_resource.h" #include "dcn20/dcn20_resource.h" @@ -2080,6 +2081,7 @@ static bool dcn42b_resource_construct( dc->caps.color.mpc.rmcm_3d_lut_caps.mem_format_support.float_fp1_5_10 = 1; dc->caps.color.mpc.rmcm_3d_lut_caps.mem_pixel_order_support.order_rgba = 1; dc->caps.color.mpc.rmcm_3d_lut_caps.mem_pixel_order_support.order_bgra = 1; + dc->caps.color.mpc.max_gamut_remap_coeff = dc_fixpt_from_fraction(S3D12_MAX, DIVIDER); dc->caps.num_of_host_routers = 0; dc->caps.num_of_dpias_per_host_router = 0; diff --git a/drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c b/drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c index d8aebaff7c3f..1f1318769d5b 100644 --- a/drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c +++ b/drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c @@ -9,7 +9,6 @@ #define IDENTITY_RATIO(ratio) (SPL_NAMESPACE(spl_fixpt_u3d19(ratio)) == (1 << 19)) #define MIN_VIEWPORT_SIZE 12 - static bool spl_is_yuv420(enum spl_pixel_format format) { if ((format >= SPL_PIXEL_FORMAT_420BPP8) && @@ -19,6 +18,15 @@ static bool spl_is_yuv420(enum spl_pixel_format format) return false; } +static bool spl_is_yuv422(enum spl_pixel_format format) +{ + if ((format >= SPL_PIXEL_FORMAT_422BPP8) && + (format <= SPL_PIXEL_FORMAT_422BPP12)) + return true; + + return false; +} + static bool spl_is_rgb8(enum spl_pixel_format format) { if (format == SPL_PIXEL_FORMAT_ARGB8888) @@ -468,6 +476,12 @@ static void spl_calculate_scaling_ratios(struct spl_in *spl_in, if (spl_is_yuv420(spl_in->basic_in.format)) { spl_scratch->scl_data.ratios.horz_c.value /= 2; spl_scratch->scl_data.ratios.vert_c.value /= 2; + } else if (spl_is_yuv422(spl_in->basic_in.format)) { + if (spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_90 || + spl_in->basic_in.rotation == SPL_ROTATION_ANGLE_270) + spl_scratch->scl_data.ratios.vert_c.value /= 2; + else + spl_scratch->scl_data.ratios.horz_c.value /= 2; } spl_scratch->scl_data.ratios.horz = spl_fixpt_truncate( spl_scratch->scl_data.ratios.horz, 19); @@ -612,7 +626,8 @@ static void spl_calculate_inits_and_viewports(struct spl_in *spl_in, struct spl_rect recout_clip_in_recout_dst; struct spl_rect overlap_in_active_timing; struct spl_rect odm_slice = calculate_odm_slice_in_timing_active(spl_in); - int vpc_div = spl_is_subsampled_format(spl_in->basic_in.format) ? 2 : 1; + int vp_hc_div = spl_is_subsampled_format(spl_in->basic_in.format) ? 2 : 1; + int vp_vc_div = spl_is_yuv420(spl_in->basic_in.format) ? 2 : 1; bool orthogonal_rotation, flip_vert_scan_dir, flip_horz_scan_dir; struct spl_fixed31_32 init_adj_h = spl_fixpt_zero; struct spl_fixed31_32 init_adj_v = spl_fixpt_zero; @@ -667,6 +682,7 @@ static void spl_calculate_inits_and_viewports(struct spl_in *spl_in, if (orthogonal_rotation) { spl_swap(src.width, src.height); spl_swap(flip_vert_scan_dir, flip_horz_scan_dir); + spl_swap(vp_hc_div, vp_vc_div); spl_swap(init_adj_h, init_adj_v); } @@ -685,7 +701,7 @@ static void spl_calculate_inits_and_viewports(struct spl_in *spl_in, flip_horz_scan_dir, recout_clip_in_recout_dst.x, spl_scratch->scl_data.recout.width, - src.width / vpc_div, + src.width / vp_hc_div, spl_scratch->scl_data.taps.h_taps_c, spl_scratch->scl_data.ratios.horz_c, init_adj_h, @@ -707,7 +723,7 @@ static void spl_calculate_inits_and_viewports(struct spl_in *spl_in, flip_vert_scan_dir, recout_clip_in_recout_dst.y, spl_scratch->scl_data.recout.height, - src.height / vpc_div, + src.height / vp_vc_div, spl_scratch->scl_data.taps.v_taps_c, spl_scratch->scl_data.ratios.vert_c, init_adj_v, @@ -719,12 +735,13 @@ static void spl_calculate_inits_and_viewports(struct spl_in *spl_in, spl_swap(spl_scratch->scl_data.viewport.width, spl_scratch->scl_data.viewport.height); spl_swap(spl_scratch->scl_data.viewport_c.x, spl_scratch->scl_data.viewport_c.y); spl_swap(spl_scratch->scl_data.viewport_c.width, spl_scratch->scl_data.viewport_c.height); + spl_swap(vp_hc_div, vp_vc_div); } spl_scratch->scl_data.viewport.x += src.x; spl_scratch->scl_data.viewport.y += src.y; - SPL_ASSERT(src.x % vpc_div == 0 && src.y % vpc_div == 0); - spl_scratch->scl_data.viewport_c.x += src.x / vpc_div; - spl_scratch->scl_data.viewport_c.y += src.y / vpc_div; + SPL_ASSERT(src.x % vp_hc_div == 0 && src.y % vp_vc_div == 0); + spl_scratch->scl_data.viewport_c.x += src.x / vp_hc_div; + spl_scratch->scl_data.viewport_c.y += src.y / vp_vc_div; } static void spl_handle_3d_recout(struct spl_in *spl_in, struct spl_rect *recout) @@ -760,6 +777,7 @@ static enum scl_mode spl_get_dscl_mode(const struct spl_in *spl_in, const struct spl_scaler_data *data, bool enable_isharp, bool enable_easf) { + (void)enable_easf; const long long one = spl_fixpt_one.value; enum spl_pixel_format pixel_format = spl_in->basic_in.format; @@ -894,7 +912,8 @@ static bool spl_get_isharp_en(struct spl_in *spl_in, static void spl_get_taps_non_adaptive_scaler( struct spl_scratch *spl_scratch, const struct spl_taps *in_taps, - bool is_subsampled) + bool is_horz_subsampled, + bool is_vert_subsampled) { bool check_max_downscale = false; @@ -959,11 +978,10 @@ static void spl_get_taps_non_adaptive_scaler( spl_scratch->scl_data.taps.h_taps = 1; if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert)) spl_scratch->scl_data.taps.v_taps = 1; - if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c) && !is_subsampled) + if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c) && !is_horz_subsampled) spl_scratch->scl_data.taps.h_taps_c = 1; - if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c) && !is_subsampled) + if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c) && !is_vert_subsampled) spl_scratch->scl_data.taps.v_taps_c = 1; - } /* Calculate optimal number of taps */ @@ -977,12 +995,13 @@ static bool spl_get_optimal_number_of_taps( unsigned int min_taps_y, min_taps_c; enum lb_memory_config lb_config; bool skip_easf = false; - bool is_subsampled = spl_is_subsampled_format(spl_in->basic_in.format); + bool is_horz_subsampled = spl_is_subsampled_format(spl_in->basic_in.format); + bool is_vert_subsampled = spl_is_yuv420(spl_in->basic_in.format); if (spl_scratch->scl_data.viewport.width > spl_scratch->scl_data.h_active && max_downscale_src_width != 0 && spl_scratch->scl_data.viewport.width > max_downscale_src_width) { - spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_subsampled); + spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_horz_subsampled, is_vert_subsampled); *enable_easf_v = false; *enable_easf_h = false; *enable_isharp = false; @@ -991,7 +1010,7 @@ static bool spl_get_optimal_number_of_taps( /* Disable adaptive scaler and sharpener when integer scaling is enabled */ if (spl_in->scaling_quality.integer_scaling) { - spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_subsampled); + spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_horz_subsampled, is_vert_subsampled); *enable_easf_v = false; *enable_easf_h = false; *enable_isharp = false; @@ -1007,15 +1026,15 @@ static bool spl_get_optimal_number_of_taps( * taps = 4 for upscaling */ if (skip_easf) { - spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_subsampled); + spl_get_taps_non_adaptive_scaler(spl_scratch, in_taps, is_horz_subsampled, is_vert_subsampled); } else { - if (spl_is_video_format(spl_in->basic_in.format)) { + if (spl_is_subsampled_format(spl_in->basic_in.format)) { spl_scratch->scl_data.taps.h_taps = 6; spl_scratch->scl_data.taps.v_taps = 6; spl_scratch->scl_data.taps.h_taps_c = 4; spl_scratch->scl_data.taps.v_taps_c = 4; - } else { /* RGB */ + } else { /* RGB / YUV444 */ spl_scratch->scl_data.taps.h_taps = 6; spl_scratch->scl_data.taps.v_taps = 6; spl_scratch->scl_data.taps.h_taps_c = 6; @@ -1149,10 +1168,10 @@ static bool spl_get_optimal_number_of_taps( (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert))) { spl_scratch->scl_data.taps.h_taps = 1; spl_scratch->scl_data.taps.v_taps = 1; - if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c) && !is_subsampled) + if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c) && !is_horz_subsampled) spl_scratch->scl_data.taps.h_taps_c = 1; - if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c) && !is_subsampled) + if (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c) && !is_vert_subsampled) spl_scratch->scl_data.taps.v_taps_c = 1; *enable_easf_v = false; @@ -1166,11 +1185,11 @@ static bool spl_get_optimal_number_of_taps( (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert))) spl_scratch->scl_data.taps.v_taps = 1; - if ((!*enable_easf_h) && !is_subsampled && + if ((!*enable_easf_h) && !is_horz_subsampled && (IDENTITY_RATIO(spl_scratch->scl_data.ratios.horz_c))) spl_scratch->scl_data.taps.h_taps_c = 1; - if ((!*enable_easf_v) && !is_subsampled && + if ((!*enable_easf_v) && !is_vert_subsampled && (IDENTITY_RATIO(spl_scratch->scl_data.ratios.vert_c))) spl_scratch->scl_data.taps.v_taps_c = 1; @@ -1693,6 +1712,7 @@ static void spl_set_isharp_data(struct dscl_prog_data *dscl_prog_data, const struct spl_scaler_data *data, struct spl_fixed31_32 ratio, enum system_setup setup, enum scale_to_sharpness_policy scale_to_sharpness_policy) { + (void)format; /* Turn off sharpener if not required */ if (!enable_isharp) { dscl_prog_data->isharp_en = 0; @@ -1823,6 +1843,144 @@ static void spl_set_isharp_data(struct dscl_prog_data *dscl_prog_data, SPL_NAMESPACE(spl_set_blur_scale_data(dscl_prog_data, data)); } +static void determine_upsp_values(struct spl_in *spl_in, struct dscl_prog_data *dscl_prog_data) +{ + dscl_prog_data->upsp_mode = spl_in->upsp_mode; + + if (dscl_prog_data->upsp_mode == UPSP_BYPASS) { //Set all UPSP register fields to 0 if bypass + dscl_prog_data->upsp_v_num_taps = UPSP_2_TAPS; + dscl_prog_data->upsp_h_num_taps = UPSP_2_TAPS; + dscl_prog_data->upsp_boundary_mode = UPSP_BOUNDARY_EDGE; + dscl_prog_data->upsp_v_init_int = 0x0; + dscl_prog_data->upsp_v_init_frac = 0x0; + dscl_prog_data->upsp_v_coef_tap0_p0 = 0x0; + dscl_prog_data->upsp_v_coef_tap1_p0 = 0x0; + dscl_prog_data->upsp_v_coef_tap2_p0 = 0x0; + dscl_prog_data->upsp_v_coef_tap3_p0 = 0x0; + dscl_prog_data->upsp_v_coef_tap0_p1 = 0x0; + dscl_prog_data->upsp_v_coef_tap1_p1 = 0x0; + dscl_prog_data->upsp_v_coef_tap2_p1 = 0x0; + dscl_prog_data->upsp_v_coef_tap3_p1 = 0x0; + dscl_prog_data->upsp_h_init_int = 0x0; + dscl_prog_data->upsp_h_init_frac = 0x0; + dscl_prog_data->upsp_h_coef_tap0_p0 = 0x0; + dscl_prog_data->upsp_h_coef_tap1_p0 = 0x0; + dscl_prog_data->upsp_h_coef_tap2_p0 = 0x0; + dscl_prog_data->upsp_h_coef_tap3_p0 = 0x0; + dscl_prog_data->upsp_h_coef_tap0_p1 = 0x0; + dscl_prog_data->upsp_h_coef_tap1_p1 = 0x0; + dscl_prog_data->upsp_h_coef_tap2_p1 = 0x0; + dscl_prog_data->upsp_h_coef_tap3_p1 = 0x0; + dscl_prog_data->upsp_clamp_max = 0x0; + dscl_prog_data->upsp_clamp_min = 0x0; + } else { + dscl_prog_data->upsp_v_num_taps = UPSP_4_TAPS; + dscl_prog_data->upsp_h_num_taps = UPSP_4_TAPS; + dscl_prog_data->upsp_boundary_mode = UPSP_BOUNDARY_EDGE; + dscl_prog_data->upsp_clamp_max = 0xFFF;//4095 + dscl_prog_data->upsp_clamp_min = 0x0; + + if (spl_in->basic_in.cositing == CHROMA_COSITING_TOPLEFT) { //Vertical Subsampling: Co-sited + if (dscl_prog_data->upsp_v_num_taps == UPSP_4_TAPS) { + dscl_prog_data->upsp_v_init_int = 0x3; + dscl_prog_data->upsp_v_init_frac = 0x0; + dscl_prog_data->upsp_v_coef_tap0_p0 = 0x00; + dscl_prog_data->upsp_v_coef_tap1_p0 = 0x40; + dscl_prog_data->upsp_v_coef_tap2_p0 = 0x00; + dscl_prog_data->upsp_v_coef_tap3_p0 = 0x00; + dscl_prog_data->upsp_v_coef_tap0_p1 = 0xFC; + dscl_prog_data->upsp_v_coef_tap1_p1 = 0x24; + dscl_prog_data->upsp_v_coef_tap2_p1 = 0x24; + dscl_prog_data->upsp_v_coef_tap3_p1 = 0xFC; + } else { //2 taps + dscl_prog_data->upsp_v_init_int = 0x2; + dscl_prog_data->upsp_v_init_frac = 0x0; + dscl_prog_data->upsp_v_coef_tap0_p0 = 0x40; + dscl_prog_data->upsp_v_coef_tap1_p0 = 0x00; + dscl_prog_data->upsp_v_coef_tap2_p0 = 0x00; + dscl_prog_data->upsp_v_coef_tap3_p0 = 0x00; + dscl_prog_data->upsp_v_coef_tap0_p1 = 0x20; + dscl_prog_data->upsp_v_coef_tap1_p1 = 0x20; + dscl_prog_data->upsp_v_coef_tap2_p1 = 0x00; + dscl_prog_data->upsp_v_coef_tap3_p1 = 0x00; + } + } else { //Vertical Subsampling: Interstitial + if (dscl_prog_data->upsp_v_num_taps == UPSP_4_TAPS) { + dscl_prog_data->upsp_v_init_int = 0x2; + dscl_prog_data->upsp_v_init_frac = 0x1; + dscl_prog_data->upsp_v_coef_tap0_p0 = 0xFB; + dscl_prog_data->upsp_v_coef_tap1_p0 = 0x2F; + dscl_prog_data->upsp_v_coef_tap2_p0 = 0x19; + dscl_prog_data->upsp_v_coef_tap3_p0 = 0xFD; + dscl_prog_data->upsp_v_coef_tap0_p1 = 0xFD; + dscl_prog_data->upsp_v_coef_tap1_p1 = 0x19; + dscl_prog_data->upsp_v_coef_tap2_p1 = 0x2F; + dscl_prog_data->upsp_v_coef_tap3_p1 = 0xFB; + } else { //2 taps + dscl_prog_data->upsp_v_init_int = 0x1; + dscl_prog_data->upsp_v_init_frac = 0x1; + dscl_prog_data->upsp_v_coef_tap0_p0 = 0x28; + dscl_prog_data->upsp_v_coef_tap1_p0 = 0x18; + dscl_prog_data->upsp_v_coef_tap2_p0 = 0x00; + dscl_prog_data->upsp_v_coef_tap3_p0 = 0x00; + dscl_prog_data->upsp_v_coef_tap0_p1 = 0x18; + dscl_prog_data->upsp_v_coef_tap1_p1 = 0x28; + dscl_prog_data->upsp_v_coef_tap2_p1 = 0x00; + dscl_prog_data->upsp_v_coef_tap3_p1 = 0x00; + } + } + if (spl_in->basic_in.cositing == CHROMA_COSITING_LEFT || spl_in->basic_in.cositing == CHROMA_COSITING_TOPLEFT) { //Horizontal Subsampling: Co-sited + if (dscl_prog_data->upsp_h_num_taps == UPSP_4_TAPS) { + dscl_prog_data->upsp_h_init_int = 0x3; + dscl_prog_data->upsp_h_init_frac = 0x0; + dscl_prog_data->upsp_h_coef_tap0_p0 = 0x00; + dscl_prog_data->upsp_h_coef_tap1_p0 = 0x40; + dscl_prog_data->upsp_h_coef_tap2_p0 = 0x00; + dscl_prog_data->upsp_h_coef_tap3_p0 = 0x00; + dscl_prog_data->upsp_h_coef_tap0_p1 = 0xFC; + dscl_prog_data->upsp_h_coef_tap1_p1 = 0x24; + dscl_prog_data->upsp_h_coef_tap2_p1 = 0x24; + dscl_prog_data->upsp_h_coef_tap3_p1 = 0xFC; + } else { //2 taps + dscl_prog_data->upsp_h_init_int = 0x2; + dscl_prog_data->upsp_h_init_frac = 0x0; + dscl_prog_data->upsp_h_coef_tap0_p0 = 0x40; + dscl_prog_data->upsp_h_coef_tap1_p0 = 0x00; + dscl_prog_data->upsp_h_coef_tap2_p0 = 0x00; + dscl_prog_data->upsp_h_coef_tap3_p0 = 0x00; + dscl_prog_data->upsp_h_coef_tap0_p1 = 0x20; + dscl_prog_data->upsp_h_coef_tap1_p1 = 0x20; + dscl_prog_data->upsp_h_coef_tap2_p1 = 0x00; + dscl_prog_data->upsp_h_coef_tap3_p1 = 0x00; + } + } else { //Horizontal Subsampling: Interstitial + if (dscl_prog_data->upsp_h_num_taps == UPSP_4_TAPS) { + dscl_prog_data->upsp_h_init_int = 0x2; + dscl_prog_data->upsp_h_init_frac = 0x1; + dscl_prog_data->upsp_h_coef_tap0_p0 = 0xFB; + dscl_prog_data->upsp_h_coef_tap1_p0 = 0x2F; + dscl_prog_data->upsp_h_coef_tap2_p0 = 0x19; + dscl_prog_data->upsp_h_coef_tap3_p0 = 0xFD; + dscl_prog_data->upsp_h_coef_tap0_p1 = 0xFD; + dscl_prog_data->upsp_h_coef_tap1_p1 = 0x19; + dscl_prog_data->upsp_h_coef_tap2_p1 = 0x2F; + dscl_prog_data->upsp_h_coef_tap3_p1 = 0xFB; + } else { //2 taps + dscl_prog_data->upsp_h_init_int = 0x1; + dscl_prog_data->upsp_h_init_frac = 0x1; + dscl_prog_data->upsp_h_coef_tap0_p0 = 0x28; + dscl_prog_data->upsp_h_coef_tap1_p0 = 0x18; + dscl_prog_data->upsp_h_coef_tap2_p0 = 0x00; + dscl_prog_data->upsp_h_coef_tap3_p0 = 0x00; + dscl_prog_data->upsp_h_coef_tap0_p1 = 0x18; + dscl_prog_data->upsp_h_coef_tap1_p1 = 0x28; + dscl_prog_data->upsp_h_coef_tap2_p1 = 0x00; + dscl_prog_data->upsp_h_coef_tap3_p1 = 0x00; + } + } + } +} + /* Calculate recout, scaling ratio, and viewport, then get optimal number of taps */ static bool spl_calculate_number_of_taps(struct spl_in *spl_in, struct spl_scratch *spl_scratch, struct spl_out *spl_out, bool *enable_easf_v, bool *enable_easf_h, bool *enable_isharp) @@ -1865,6 +2023,8 @@ bool SPL_NAMESPACE(spl_calculate_scaler_params(struct spl_in *spl_in, struct spl bool enable_isharp = false; const struct spl_scaler_data *data = &spl_scratch.scl_data; + determine_upsp_values(spl_in, spl_out->dscl_prog_data); + res = spl_calculate_number_of_taps(spl_in, &spl_scratch, spl_out, &enable_easf_v, &enable_easf_h, &enable_isharp); diff --git a/drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h b/drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h index 20e4e52a77ac..b24f1ef3fe17 100644 --- a/drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h +++ b/drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h @@ -62,17 +62,21 @@ enum spl_pixel_format { /*video*/ SPL_PIXEL_FORMAT_420BPP8, SPL_PIXEL_FORMAT_420BPP10, + SPL_PIXEL_FORMAT_422BPP8, + SPL_PIXEL_FORMAT_422BPP10, + SPL_PIXEL_FORMAT_422BPP12, + SPL_PIXEL_FORMAT_444BPP8, + SPL_PIXEL_FORMAT_444BPP10, /*end of pixel format definition*/ SPL_PIXEL_FORMAT_GRPH_BEGIN = SPL_PIXEL_FORMAT_INDEX8, SPL_PIXEL_FORMAT_GRPH_END = SPL_PIXEL_FORMAT_FP16, SPL_PIXEL_FORMAT_SUBSAMPLED_BEGIN = SPL_PIXEL_FORMAT_420BPP8, - SPL_PIXEL_FORMAT_SUBSAMPLED_END = SPL_PIXEL_FORMAT_420BPP10, + SPL_PIXEL_FORMAT_SUBSAMPLED_END = SPL_PIXEL_FORMAT_422BPP12, SPL_PIXEL_FORMAT_VIDEO_BEGIN = SPL_PIXEL_FORMAT_420BPP8, - SPL_PIXEL_FORMAT_VIDEO_END = SPL_PIXEL_FORMAT_420BPP10, + SPL_PIXEL_FORMAT_VIDEO_END = SPL_PIXEL_FORMAT_444BPP10, SPL_PIXEL_FORMAT_INVALID, SPL_PIXEL_FORMAT_UNKNOWN }; - enum lb_memory_config { /* Enable all 3 pieces of memory */ LB_MEMORY_CONFIG_0 = 0, @@ -88,7 +92,6 @@ enum lb_memory_config { */ LB_MEMORY_CONFIG_3 = 3 }; - /* Rotation angle */ enum spl_rotation_angle { SPL_ROTATION_ANGLE_0 = 0, @@ -128,6 +131,23 @@ enum chroma_cositing { CHROMA_COSITING_COUNT }; +enum upsp_mode { + UPSP_BYPASS = 0, + UPSP_HORIZONTAL_UPSAMPLING_ONLY, + UPSP_VERTICAL_UPSAMPLING_ONLY, + UPSP_HORIZONTAL_VERTICAL_UPSAMPLING +}; + +enum upsp_num_taps { + UPSP_2_TAPS, + UPSP_4_TAPS +}; + +enum upsp_boundary_mode { + UPSP_BOUNDARY_EDGE, //Replace out of bound samples with the edge samples + UPSP_BOUNDARY_BLACK //Replace out of bound samples with black as 12bpc(0x800) +}; + // Scratch space for calculating scaler params struct spl_scaler_data { int h_active; @@ -253,7 +273,7 @@ enum isharp_en { #define ISHARP_LUT_TABLE_SIZE 32 // Below struct holds values that can be directly used to program // hardware registers. No conversion/clamping is required -struct dscl_prog_data { +struct dscl_prog_data { struct spl_rect recout; // RECOUT - set based on scl_data.recout struct mpc_size mpc_size; uint32_t dscl_mode; @@ -395,6 +415,33 @@ struct dscl_prog_data { uint32_t easf_matrix_c1; uint32_t easf_matrix_c2; uint32_t easf_matrix_c3; + // UPSP registers + uint32_t upsp_mode;//UPSP_MODE + uint32_t upsp_v_num_taps; + uint32_t upsp_v_init_int; + uint32_t upsp_v_init_frac; + uint32_t upsp_h_num_taps; + uint32_t upsp_h_init_int; + uint32_t upsp_h_init_frac; + uint32_t upsp_boundary_mode; + uint32_t upsp_v_coef_tap0_p0;//UPSP_V_COEF_P0 + uint32_t upsp_v_coef_tap1_p0; + uint32_t upsp_v_coef_tap2_p0; + uint32_t upsp_v_coef_tap3_p0; + uint32_t upsp_v_coef_tap0_p1;//UPSP_V_COEF_P1 + uint32_t upsp_v_coef_tap1_p1; + uint32_t upsp_v_coef_tap2_p1; + uint32_t upsp_v_coef_tap3_p1; + uint32_t upsp_h_coef_tap0_p0;//UPSP_H_COEF_P0 + uint32_t upsp_h_coef_tap1_p0; + uint32_t upsp_h_coef_tap2_p0; + uint32_t upsp_h_coef_tap3_p0; + uint32_t upsp_h_coef_tap0_p1;//UPSP_H_COEF_P1 + uint32_t upsp_h_coef_tap1_p1; + uint32_t upsp_h_coef_tap2_p1; + uint32_t upsp_h_coef_tap3_p1; + uint32_t upsp_clamp_max;//UPSP_CLAMP + uint32_t upsp_clamp_min; // iSharp uint32_t isharp_en; // ISHARP_EN struct isharp_noise_det isharp_noise_det; // ISHARP_NOISEDET @@ -554,6 +601,7 @@ struct spl_in { int min_viewport_size; int sdr_white_level_nits; enum sharpen_policy sharpen_policy; + enum upsp_mode upsp_mode; }; // end of SPL inputs diff --git a/drivers/gpu/drm/amd/display/modules/power/power_psr.c b/drivers/gpu/drm/amd/display/modules/power/power_psr.c index 0ad4c4924696..5ecb570c204e 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_psr.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_psr.c @@ -58,13 +58,6 @@ bool mod_power_psr_notify_mode_change(struct mod_power *mod_power, // stream_index is passed as validated parameter active_psr_events = core_power->map[stream_index].psr_events; - /* DMSS holds the panel in a forced PSR freeze (e.g. during HDR/SDR toggle). - * Re-running edp_setup_psr would reprogram DPCD 0x170 and disturb the freeze, - * so skip the PSR re-setup until DMSS releases the override. - */ - if (active_psr_events & psr_event_os_override_hold) - return false; - /* Calculate PSR configurations */ mod_power_calc_psr_configs(&psr_config, link, stream); diff --git a/drivers/gpu/drm/amd/display/modules/power/power_replay.c b/drivers/gpu/drm/amd/display/modules/power/power_replay.c index 1ad2ee01d560..e782501442c4 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_replay.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_replay.c @@ -805,13 +805,6 @@ void mod_power_replay_notify_mode_change(struct mod_power *mod_power, core_power = MOD_POWER_TO_CORE(mod_power); active_replay_events = core_power->map[stream_index].replay_events; - /* DMSS holds the panel in a forced freeze (e.g. during HDR/SDR toggle). - * Re-running dp_setup_replay would reprogram DPCD 0x37B and disturb the - * freeze, so skip the replay re-setup until DMSS releases the override. - */ - if (active_replay_events & replay_event_os_override_hold) - return; - link->replay_settings.replay_smu_opt_enable = (link->replay_settings.config.replay_smu_opt_supported && mod_power_only_edp(dc->current_state, stream)); diff --git a/drivers/gpu/drm/amd/include/amdgpu_ptl.h b/drivers/gpu/drm/amd/include/amdgpu_ptl.h index 154b8da3bfa9..eead62b4b327 100644 --- a/drivers/gpu/drm/amd/include/amdgpu_ptl.h +++ b/drivers/gpu/drm/amd/include/amdgpu_ptl.h @@ -39,11 +39,18 @@ enum amdgpu_ptl_disable_source { AMDGPU_PTL_DISABLE_PROFILER, AMDGPU_PTL_DISABLE_MAX, }; + +enum amdgpu_ptl_hw_supported_state { + AMDGPU_PTL_HW_UNINIT = 0, /* Not yet initialized */ + AMDGPU_PTL_HW_SUPPORTED, /* Initialized and supported */ + AMDGPU_PTL_HW_NOT_SUPPORTED, /* Initialized and not supported */ +}; + struct amdgpu_ptl { enum amdgpu_ptl_fmt fmt1; enum amdgpu_ptl_fmt fmt2; bool enabled; - bool hw_supported; + enum amdgpu_ptl_hw_supported_state hw_supported_state; bool permanently_disabled; /* PTL disable reference counting */ atomic_t disable_ref; diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h index 6371f292f2d8..606d48115301 100644 --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h @@ -24,6 +24,8 @@ #ifndef __KGD_PP_INTERFACE_H__ #define __KGD_PP_INTERFACE_H__ +#include <linux/units.h> + extern const struct amdgpu_ip_block_version pp_smu_ip_block; extern const struct amdgpu_ip_block_version smu_v11_0_ip_block; extern const struct amdgpu_ip_block_version smu_v12_0_ip_block; @@ -150,8 +152,8 @@ enum amd_pp_sensors { AMDGPU_PP_SENSOR_MEM_TEMP, AMDGPU_PP_SENSOR_VCE_POWER, AMDGPU_PP_SENSOR_UVD_POWER, - AMDGPU_PP_SENSOR_GPU_AVG_POWER, - AMDGPU_PP_SENSOR_GPU_INPUT_POWER, + AMDGPU_PP_SENSOR_GPU_AVG_POWER, /* milliwatts */ + AMDGPU_PP_SENSOR_GPU_INPUT_POWER, /* milliwatts */ AMDGPU_PP_SENSOR_SS_APU_SHARE, AMDGPU_PP_SENSOR_SS_DGPU_SHARE, AMDGPU_PP_SENSOR_STABLE_PSTATE_SCLK, diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index c09c60ad3f38..8f59ea12fe0e 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1183,12 +1183,14 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table) +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + char *pptable = NULL; int ret = 0; - if (!table) + if ((!table && size) || (table && !size)) return -EINVAL; if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) @@ -1196,7 +1198,13 @@ int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table) mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle, - table); + &pptable); + if (ret > 0 && !pptable) { + ret = -EINVAL; + } else if (ret > 0 && table) { + ret = min_t(size_t, ret, size); + memcpy(table, pptable, ret); + } mutex_unlock(&adev->pm.mutex); return ret; @@ -1426,17 +1434,23 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table) +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - int ret = 0; + void *table; + ssize_t ret; if (!pp_funcs->get_gpu_metrics) return 0; mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle, - table); + &table); + if (ret > 0) { + ret = min_t(ssize_t, ret, size); + memcpy(buf, table, ret); + } mutex_unlock(&adev->pm.mutex); return ret; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index f5a5d72b4108..4c6cdaeac5a9 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -41,8 +41,6 @@ #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) -#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff)) - struct od_attribute { struct kobj_attribute attribute; struct list_head entry; @@ -595,25 +593,19 @@ static ssize_t amdgpu_get_pp_table(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - char *table = NULL; int size, ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; - size = amdgpu_dpm_get_pp_table(adev, &table); + size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1); amdgpu_pm_put_access(adev); if (size <= 0) return size; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, table, size); - return size; } @@ -1787,7 +1779,6 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - void *gpu_metrics; ssize_t size = 0; int ret; @@ -1795,15 +1786,10 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, if (ret) return ret; - size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics); + size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1); if (size <= 0) goto out; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, gpu_metrics, size); - out: amdgpu_pm_put_access(adev); @@ -2733,10 +2719,9 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ *states = ATTR_STATE_UNSUPPORTED; } else if (DEVICE_ATTR_IS(pp_table)) { int ret; - char *tmp = NULL; - ret = amdgpu_dpm_get_pp_table(adev, &tmp); - if (ret == -EOPNOTSUPP || !tmp) + ret = amdgpu_dpm_get_pp_table(adev, NULL, 0); + if (ret <= 0) *states = ATTR_STATE_UNSUPPORTED; else *states = ATTR_STATE_SUPPORTED; @@ -3371,7 +3356,7 @@ static int amdgpu_hwmon_get_power(struct device *dev, return r; /* convert to microwatts */ - return power_2_mwatt(query) * 1000; + return query * 1000; } static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev, @@ -4912,7 +4897,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a seq_printf(m, "\t%u mV (VDDNB)\n", value); size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) { - mwatt = power_2_mwatt(query); + mwatt = query; centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100); @@ -4921,7 +4906,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a } size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) { - mwatt = power_2_mwatt(query); + mwatt = query; centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100); diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index c7ea29385682..8e8a052e1d53 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -487,7 +487,8 @@ int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev, int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, enum amd_pp_task task_id, enum amd_pm_state_type *user_state); -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table); +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size); int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev, uint32_t type, long *input, @@ -517,7 +518,8 @@ int amdgpu_dpm_get_power_profile_mode(struct amdgpu_device *adev, char *buf); int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, long *input, uint32_t size); -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table); +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size); ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id, void *table); ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index fbb59e5a4453..2393fa8d4e4b 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -4086,7 +4086,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) (adev->asic_type != CHIP_FIJI) && (adev->asic_type != CHIP_TONGA)) { smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0, &tmp); - *query = tmp; + *query = PP_PWR_Q24_8_TO_MW(tmp); if (tmp != 0) return 0; @@ -4105,7 +4105,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) if (tmp != 0) break; } - *query = tmp; + *query = PP_PWR_Q24_8_TO_MW(tmp); return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c index 0e237feb1629..05b1e69116fd 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c @@ -3967,8 +3967,8 @@ static int vega10_get_gpu_power(struct pp_hwmgr *hwmgr, if (ret) return ret; - /* SMC returning actual watts, keep consistent with legacy asics, low 8 bit as 8 fractional bits */ - *query = value << 8; + /* SMC returns whole Watts, while power sensors use milliwatts. */ + *query = value * MILLIWATT_PER_WATT; return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c index 69a9074058ce..b466b08e4a67 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c @@ -1419,7 +1419,7 @@ static int vega12_get_gpu_power(struct pp_hwmgr *hwmgr, uint32_t *query) if (ret) return ret; - *query = metrics_table.CurrSocketPower << 8; + *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT; return ret; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c index 7b8f4c1b80eb..e9c194056e32 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c @@ -2154,12 +2154,13 @@ static int vega20_get_gpu_power(struct pp_hwmgr *hwmgr, int idx, switch (idx) { case AMDGPU_PP_SENSOR_GPU_AVG_POWER: if (hwmgr->smu_version == 0x282e00) - *query = metrics_table.AverageSocketPower << 8; + *query = metrics_table.AverageSocketPower * + MILLIWATT_PER_WATT; else ret = -EOPNOTSUPP; break; case AMDGPU_PP_SENSOR_GPU_INPUT_POWER: - *query = metrics_table.CurrSocketPower << 8; + *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT; break; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index e01afbc39d67..448246cc52cc 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -35,6 +35,11 @@ struct pp_hwmgr; struct phm_fan_speed_info; struct pp_atomctrl_voltage_table; +/* Decode legacy unsigned Q24.8 watts to internal milliwatts. */ +#define PP_PWR_Q24_8_TO_MW(power) \ + DIV_ROUND_CLOSEST_ULL((u64)(power) * MILLIWATT_PER_WATT, \ + BIT(8)) + #define VOLTAGE_SCALE 4 #define VOLTAGE_VID_OFFSET_SCALE1 625 #define VOLTAGE_VID_OFFSET_SCALE2 100 diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index 7ea7c4a5279b..eb945f1805ed 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -1336,7 +1336,7 @@ struct pptable_funcs { u32 (*set_gfx_off_residency)(struct smu_context *smu, bool start); /** - * @get_gfx_off_residency: Average GFXOFF residency % during the logging interval + * @get_gfx_off_residency: Live GFXOFF residency percentage */ u32 (*get_gfx_off_residency)(struct smu_context *smu, uint32_t *residency); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h index 47ddf533b08f..a5d3e8d88860 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h @@ -144,6 +144,44 @@ typedef struct { uint32_t MaxGfxClk; } DpmClocks_t; +//Freq in MHz +//Voltage in milli volts with 2 fractional bits +typedef struct { + uint32_t DcfClocks[NUM_DCFCLK_DPM_LEVELS]; + uint32_t DispClocks[NUM_DISPCLK_DPM_LEVELS]; + uint32_t DppClocks[NUM_DPPCLK_DPM_LEVELS]; + uint32_t SocClocks[NUM_SOCCLK_DPM_LEVELS]; + + uint32_t VPEClocks[NUM_VPE_DPM_LEVELS]; + uint32_t FclkClocks_Freq[NUM_FCLK_DPM_LEVELS]; + uint32_t FclkClocks_Voltage[NUM_FCLK_DPM_LEVELS]; + uint32_t SocVoltage[NUM_SOC_VOLTAGE_LEVELS]; + MemPstateTable_t MemPstateTable[NUM_MEM_PSTATE_LEVELS]; + + uint8_t NumDcfClkLevelsEnabled; + uint8_t NumDispClkLevelsEnabled; //Applies to both Dispclk and Dppclk + uint8_t NumSocClkLevelsEnabled; + + uint8_t VpeClkLevelsEnabled; + + uint8_t NumMemPstatesEnabled; + uint8_t NumFclkLevelsEnabled; + uint8_t Spare1; + uint8_t Spare2; + + uint8_t Spare3; + uint8_t Spare4; + uint8_t Spare5[2]; + + uint32_t MinGfxClk; + uint32_t MaxGfxClk; + + uint32_t Spare6[8]; + uint32_t Spare7[8]; + uint32_t Spare8[8]; + uint32_t Spare9[8]; +} DpmClocks_t_v15_0_5; + typedef struct { uint16_t CoreFrequency[16]; //Target core frequency [MHz] uint16_t CorePower[16]; //CAC calculated core power [mW] diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h index 7471e2df2828..4206514765cd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h @@ -110,8 +110,10 @@ #define PPSMC_MSG_GetSlowPPTLimit 0x4C #define PPSMC_MSG_GetGfxOffStatus 0x50 #define PPSMC_MSG_GetGfxOffEntryCount 0x51 -#define PPSMC_MSG_LogGfxOffResidency 0x52 -#define PPSMC_Message_Count 0x53 +#define PPSMC_MSG_GfxOffResidencyLogReadSample 0x52 +#define PPSMC_MSG_StopGfxOffResidencyLogging 0x53 +#define PPSMC_MSG_StartGfxOffResidencyLogging 0x56 +#define PPSMC_Message_Count 0x57 //Argument for PPSMC_MSG_GfxDeviceDriverReset enum { diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h index 636ff90923d9..acf03838d49d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h @@ -252,7 +252,9 @@ __SMU_DUMMY_MAP(DriverMode2Reset), \ __SMU_DUMMY_MAP(GetGfxOffStatus), \ __SMU_DUMMY_MAP(GetGfxOffEntryCount), \ - __SMU_DUMMY_MAP(LogGfxOffResidency), \ + __SMU_DUMMY_MAP(StartGfxOffResidencyLogging), \ + __SMU_DUMMY_MAP(GfxOffResidencyLogReadSample), \ + __SMU_DUMMY_MAP(StopGfxOffResidencyLogging), \ __SMU_DUMMY_MAP(SetNumBadMemoryPagesRetired), \ __SMU_DUMMY_MAP(SetBadMemoryPagesRetiredFlagsPerChannel), \ __SMU_DUMMY_MAP(AllowGpo), \ diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h index 13723d45a7de..38e625cda97d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h @@ -26,7 +26,7 @@ #include "amdgpu_smu.h" #define SMU15_DRIVER_IF_VERSION_INV 0xFFFFFFFF -#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x7 +#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x9 #define FEATURE_MASK(feature) (1ULL << feature) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c index db5db2c9c8e8..e7628f4f1de4 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c @@ -658,7 +658,7 @@ static int arcturus_get_smu_metrics_data(struct smu_context *smu, *value = metrics->VcnActivityPercentage; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c index e6e009df9840..bb9620666dae 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c @@ -158,12 +158,10 @@ cyan_skillfish_get_smu_metrics_data(struct smu_context *smu, *value = metrics->Current.MemclkFrequency; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->Current.CurrentSocketPower << 8) / - 1000; + *value = metrics->Current.CurrentSocketPower; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->Average.CurrentSocketPower << 8) / - 1000; + *value = metrics->Average.CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->Current.GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index 8feea44f3ca0..9f8ab4e097dd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -602,7 +602,7 @@ static int navi10_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -691,7 +691,7 @@ static int navi10_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -777,7 +777,7 @@ static int navi12_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -866,7 +866,7 @@ static int navi12_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index c0de73b85353..cef6287524d4 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -855,9 +855,10 @@ static int sienna_cichlid_get_smu_metrics_data(struct smu_context *smu, metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = use_metrics_v3 ? metrics_v3->AverageSocketPower << 8 : - use_metrics_v2 ? metrics_v2->AverageSocketPower << 8 : - metrics->AverageSocketPower << 8; + *value = use_metrics_v3 ? metrics_v3->AverageSocketPower : + use_metrics_v2 ? metrics_v2->AverageSocketPower : + metrics->AverageSocketPower; + *value *= MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = (use_metrics_v3 ? metrics_v3->TemperatureEdge : diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index 016a5c893fee..3e3c68448ba9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -72,6 +72,12 @@ static const struct smu_feature_bits vangogh_dpm_features = { } }; +/* + * SMU support new GFXOFF residency log interface since version 4.63.62.00, + * use this to get live readings of GFXOFF residency + */ +#define SUPPORT_LIVE_RESIDENCY_MSG_VERSION 0x043f3e00 + static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 0), MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 0), @@ -142,7 +148,9 @@ static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(GetSlowPPTLimit, PPSMC_MSG_GetSlowPPTLimit, 0), MSG_MAP(GetGfxOffStatus, PPSMC_MSG_GetGfxOffStatus, 0), MSG_MAP(GetGfxOffEntryCount, PPSMC_MSG_GetGfxOffEntryCount, 0), - MSG_MAP(LogGfxOffResidency, PPSMC_MSG_LogGfxOffResidency, 0), + MSG_MAP(StartGfxOffResidencyLogging, PPSMC_MSG_StartGfxOffResidencyLogging, 0), + MSG_MAP(GfxOffResidencyLogReadSample, PPSMC_MSG_GfxOffResidencyLogReadSample, 0), + MSG_MAP(StopGfxOffResidencyLogging, PPSMC_MSG_StopGfxOffResidencyLogging, 0), }; static struct cmn2asic_mapping vangogh_feature_mask_map[SMU_FEATURE_COUNT] = { @@ -310,8 +318,7 @@ static int vangogh_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / - 1000 ; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * @@ -379,12 +386,10 @@ static int vangogh_get_smu_metrics_data(struct smu_context *smu, *value = metrics->Current.UvdActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->Average.CurrentSocketPower << 8) / - 1000; + *value = metrics->Average.CurrentSocketPower; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->Current.CurrentSocketPower << 8) / - 1000; + *value = metrics->Current.CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->Current.GfxTemperature / 100 * @@ -2449,19 +2454,32 @@ static int vangogh_set_power_limit(struct smu_context *smu, static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start) { int ret = 0; - u32 residency; struct amdgpu_device *adev = smu->adev; if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) return 0; - ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_LogGfxOffResidency, - start, &residency); - if (ret) - return ret; + if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) { + u32 residency; + + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxOffResidencyLogReadSample, + start, &residency); + if (ret) + return ret; - if (!start) - adev->gfx.gfx_off_residency = residency; + if (!start) + adev->gfx.gfx_off_residency = residency; + } else { + if (start) { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StartGfxOffResidencyLogging, NULL); + if (ret) + return ret; + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StopGfxOffResidencyLogging, NULL); + if (ret) + return ret; + } + } return ret; } @@ -2478,11 +2496,20 @@ static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start) */ static u32 vangogh_get_gfxoff_residency(struct smu_context *smu, uint32_t *residency) { + int ret = 0; struct amdgpu_device *adev = smu->adev; - *residency = adev->gfx.gfx_off_residency; + if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) + return 0; + + if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) { + *residency = adev->gfx.gfx_off_residency; + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GfxOffResidencyLogReadSample, + residency); + } - return 0; + return ret; } /** diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c index 2b011610e3c7..0ec17c1054c0 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c @@ -1215,9 +1215,10 @@ static int renoir_get_smu_metrics_data(struct smu_context *smu, ((amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(12, 0, 0)) && (adev->pm.fw_version >= 0x373200))) - *value = metrics->CurrentSocketPower << 8; + *value = metrics->CurrentSocketPower * + MILLIWATT_PER_WATT; else - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = (metrics->GfxTemperature / 100) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c index cd7bf36673cb..cbdfde79cb9e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c @@ -677,7 +677,8 @@ static int aldebaran_get_smu_metrics_data(struct smu_context *smu, case METRICS_AVERAGE_SOCKETPOWER: /* Valid power data is available only from primary die */ if (aldebaran_is_primary(smu)) - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * + MILLIWATT_PER_WATT; else ret = -EOPNOTSUPP; break; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 4ce1429cf57b..48e54881a5d0 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -807,7 +807,7 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu, metrics->Vcn1ActivityPercentage); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c index ecdbf9bb8eb7..8c719fff754a 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c @@ -434,7 +434,8 @@ int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(metrics->DramBandwidthUtilization); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(metrics->SocketPower) << 8; + *value = SMUQ10_ROUND(metrics->SocketPower) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(metrics->MaxSocketTemperature) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c index ba91bf590eed..a9d558f5b739 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c @@ -338,10 +338,10 @@ static int smu_v13_0_4_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->AverageSocketPower << 8) / 1000; + *value = metrics->AverageSocketPower; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c index 27372a60e83d..f95b15af436d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c @@ -295,7 +295,7 @@ static int smu_v13_0_5_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 64db62a8d642..db94de88add2 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -1300,7 +1300,8 @@ static int smu_v13_0_6_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, version)); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) << 8; + *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, version)) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 5f23f2e7f401..4deb987e43e0 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -817,7 +817,7 @@ static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu, *value = smu_safe_u16_nn(metrics->AverageUclkActivity); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c index 7bf88ffd311b..edc3fedca9f8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c @@ -372,7 +372,7 @@ static int yellow_carp_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index 3d73f2050bbe..568665265a13 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -312,8 +312,7 @@ static int smu_v14_0_0_get_smu_metrics_data(struct smu_context *smu, break; case METRICS_AVERAGE_SOCKETPOWER: case METRICS_CURR_SOCKETPOWER: - *value = (metrics->SocketPower / 1000 << 8) + - (metrics->SocketPower % 1000 / 10); + *value = metrics->SocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index 0fbcaf884419..2fa4ce26e950 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -695,7 +695,7 @@ static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu, metrics->Vcn1ActivityPercentage); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c index bb8d09e73c7d..0da1ffb3a567 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c @@ -168,7 +168,7 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu) SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); - SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t), + SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t_v15_0_5), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); @@ -178,7 +178,7 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu) goto err0_out; smu_table->metrics_time = 0; - smu_table->clocks_table = kzalloc_obj(DpmClocks_t); + smu_table->clocks_table = kzalloc_obj(DpmClocks_t_v15_0_5); if (!smu_table->clocks_table) goto err1_out; @@ -378,8 +378,7 @@ static int smu_v15_0_0_get_smu_metrics_data(struct smu_context *smu, break; case METRICS_AVERAGE_SOCKETPOWER: case METRICS_CURR_SOCKETPOWER: - *value = (metrics->SocketPower / 1000 << 8) + - (metrics->SocketPower % 1000 / 10); + *value = metrics->SocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * @@ -752,11 +751,48 @@ static int smu_v15_0_0_get_dpm_freq_by_index(struct smu_context *smu, return 0; } +static int smu_v15_0_5_get_dpm_freq_by_index(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t dpm_level, + uint32_t *freq) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + if (!clk_table || clk_type >= SMU_CLK_COUNT) + return -EINVAL; + + switch (clk_type) { + case SMU_SOCCLK: + if (dpm_level >= clk_table->NumSocClkLevelsEnabled) + return -EINVAL; + *freq = clk_table->SocClocks[dpm_level]; + break; + case SMU_UCLK: + case SMU_MCLK: + if (dpm_level >= clk_table->NumMemPstatesEnabled) + return -EINVAL; + *freq = clk_table->MemPstateTable[dpm_level].MemClk; + break; + case SMU_FCLK: + if (dpm_level >= clk_table->NumFclkLevelsEnabled) + return -EINVAL; + *freq = clk_table->FclkClocks_Freq[dpm_level]; + break; + default: + return -EINVAL; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_freq_by_index(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t dpm_level, uint32_t *freq) { + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq); + else smu_v15_0_0_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq); return 0; @@ -907,12 +943,116 @@ failed: return ret; } +static int smu_v15_0_5_get_dpm_ultimate_freq(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *min, + uint32_t *max) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + uint32_t clock_limit; + uint32_t max_dpm_level, min_dpm_level; + int ret = 0; + + if (!smu_v15_0_0_clk_dpm_is_enabled(smu, clk_type)) { + switch (clk_type) { + case SMU_MCLK: + case SMU_UCLK: + clock_limit = smu->smu_table.boot_values.uclk; + break; + case SMU_FCLK: + clock_limit = smu->smu_table.boot_values.fclk; + break; + case SMU_GFXCLK: + case SMU_SCLK: + clock_limit = smu->smu_table.boot_values.gfxclk; + break; + case SMU_SOCCLK: + clock_limit = smu->smu_table.boot_values.socclk; + break; + default: + clock_limit = 0; + break; + } + + /* clock in Mhz unit */ + if (min) + *min = clock_limit / 100; + if (max) + *max = clock_limit / 100; + + return 0; + } + + if (max) { + switch (clk_type) { + case SMU_GFXCLK: + case SMU_SCLK: + *max = clk_table->MaxGfxClk; + break; + case SMU_MCLK: + case SMU_UCLK: + max_dpm_level = 0; + break; + case SMU_FCLK: + max_dpm_level = clk_table->NumFclkLevelsEnabled - 1; + break; + case SMU_SOCCLK: + max_dpm_level = clk_table->NumSocClkLevelsEnabled - 1; + break; + default: + ret = -EINVAL; + goto failed; + } + + if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) { + ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type, + max_dpm_level, max); + if (ret) + goto failed; + } + } + + if (min) { + switch (clk_type) { + case SMU_GFXCLK: + case SMU_SCLK: + *min = clk_table->MinGfxClk; + break; + case SMU_MCLK: + case SMU_UCLK: + min_dpm_level = clk_table->NumMemPstatesEnabled - 1; + break; + case SMU_FCLK: + min_dpm_level = 0; + break; + case SMU_SOCCLK: + min_dpm_level = 0; + break; + default: + ret = -EINVAL; + goto failed; + } + + if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) { + ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type, + min_dpm_level, min); + if (ret) + goto failed; + } + } + +failed: + return ret; +} + static int smu_v15_0_common_get_dpm_ultimate_freq(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *min, uint32_t *max) { - if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_ultimate_freq(smu, clk_type, min, max); + else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) smu_v15_0_0_get_dpm_ultimate_freq(smu, clk_type, min, max); return 0; @@ -986,11 +1126,36 @@ static int smu_v15_0_0_get_dpm_level_count(struct smu_context *smu, return 0; } +static int smu_v15_0_5_get_dpm_level_count(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *count) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + switch (clk_type) { + case SMU_SOCCLK: + *count = clk_table->NumSocClkLevelsEnabled; + break; + case SMU_MCLK: + *count = clk_table->NumMemPstatesEnabled; + break; + case SMU_FCLK: + *count = clk_table->NumFclkLevelsEnabled; + break; + default: + break; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_level_count(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *count) { - if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_level_count(smu, clk_type, count); + else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) smu_v15_0_0_get_dpm_level_count(smu, clk_type, count); return 0; @@ -1363,9 +1528,24 @@ static int smu_v15_0_0_set_fine_grain_gfx_freq_parameters(struct smu_context *sm return 0; } +static int smu_v15_0_5_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + smu->gfx_default_hard_min_freq = clk_table->MinGfxClk; + smu->gfx_default_soft_max_freq = clk_table->MaxGfxClk; + smu->gfx_actual_hard_min_freq = 0; + smu->gfx_actual_soft_max_freq = 0; + + return 0; +} + static int smu_v15_0_common_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) { - smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu); + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_set_fine_grain_gfx_freq_parameters(smu); + else + smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu); return 0; } @@ -1405,9 +1585,36 @@ static int smu_v15_0_0_get_dpm_table(struct smu_context *smu, struct dpm_clocks return 0; } +static int smu_v15_0_5_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + uint8_t idx; + + /* + * Only the Clock information of SOC and + * VPE is copied to provide VPE DPM settings for use. + */ + for (idx = 0; idx < NUM_SOCCLK_DPM_LEVELS; idx++) { + clock_table->SocClocks[idx].Freq = + (idx < clk_table->NumSocClkLevelsEnabled) ? clk_table->SocClocks[idx]:0; + clock_table->SocClocks[idx].Vol = 0; + } + + for (idx = 0; idx < NUM_VPE_DPM_LEVELS; idx++) { + clock_table->VPEClocks[idx].Freq = + (idx < clk_table->VpeClkLevelsEnabled) ? clk_table->VPEClocks[idx]:0; + clock_table->VPEClocks[idx].Vol = 0; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table) { - smu_v15_0_0_get_dpm_table(smu, clock_table); + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_table(smu, clock_table); + else + smu_v15_0_0_get_dpm_table(smu, clock_table); return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c index 227cd86e13e8..59fdd2319b66 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c @@ -411,7 +411,8 @@ static int smu_v15_0_8_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(metrics->DramBandwidthUtilization); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(metrics->SocketPower) << 8; + *value = SMUQ10_ROUND(metrics->SocketPower) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(metrics->MaxSocketTemperature) * diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c index c2285fde8b3c..f9ee297a6745 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c @@ -73,15 +73,6 @@ static int amdgpu_ras_trigger_error_end(struct ras_core_context *ras_core, return 0; } -static uint64_t local_addr_to_xgmi_global_addr(struct ras_core_context *ras_core, - uint64_t addr) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev; - struct amdgpu_xgmi *xgmi = &adev->gmc.xgmi; - - return (addr + xgmi->physical_node_id * xgmi->node_segment_size); -} - /* * UMC error injection is dispatched by the RAS TA using the injection method * carried in struct ras_cmd_inject_error_req. Only the "coherent" methods @@ -154,9 +145,11 @@ static int amdgpu_ras_inject_error(struct ras_core_context *ras_core, return RAS_CMD__ERROR_INVALID_INPUT_DATA; } - /* Calculate XGMI relative offset */ - if (adev->gmc.xgmi.num_physical_nodes > 1) - req->address = local_addr_to_xgmi_global_addr(ras_core, req->address); + /* + * Calculate XGMI relative offset if in XGMI hive, + * adapt to A+A platform with mc fb offset calculated. + */ + req->address += adev->vm_manager.vram_base_offset; } } diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c index 3b5da8515a20..c6c2e2cdaf18 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c @@ -284,6 +284,7 @@ static int amdgpu_ras_mgr_init_umc_config(struct amdgpu_device *adev, struct ras_umc_config *umc_cfg = &config->umc_cfg; umc_cfg->umc_vram_type = adev->gmc.vram_type; + umc_cfg->num_umc = adev->gmc.num_umc; return 0; } @@ -309,6 +310,7 @@ static struct ras_core_context *amdgpu_ras_mgr_create_ras_core(struct amdgpu_dev amdgpu_ras_mgr_eeprom_is_supported(adev); init_config.poison_supported = amdgpu_ras_is_poison_mode_supported(adev); + init_config.ras_debug_mask = amdgpu_debug_mask; amdgpu_ras_mgr_init_aca_config(adev, &init_config); amdgpu_ras_mgr_init_eeprom_config(adev, &init_config); diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c index e4444798bc73..0b36b689fcfc 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c @@ -171,6 +171,9 @@ static int amdgpu_ras_sys_check_gpu_status(struct ras_core_context *ras_core, if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) gpu_status |= RAS_GPU_STATUS__IN_RESET; + if (amdgpu_device_bus_status_check(adev)) + gpu_status |= RAS_GPU_STATUS__DEVICE_LOST; + if (amdgpu_sriov_vf(adev)) gpu_status |= RAS_GPU_STATUS__IS_VF; diff --git a/drivers/gpu/drm/amd/ras/rascore/ras.h b/drivers/gpu/drm/amd/ras/rascore/ras.h index 878dfdfcb18a..8c3d603ba801 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras.h +++ b/drivers/gpu/drm/amd/ras/rascore/ras.h @@ -137,6 +137,7 @@ enum ras_gpu_status { RAS_GPU_STATUS__IN_RESET = 0x2, RAS_GPU_STATUS__IS_RMA = 0x4, RAS_GPU_STATUS__IS_VF = 0x8, + RAS_GPU_STATUS__DEVICE_LOST = 0x10, }; enum ras_fw_eeprom_cmd { @@ -278,6 +279,7 @@ struct ras_psp_config { struct ras_umc_config { uint32_t umc_vram_type; + uint32_t num_umc; }; struct ras_eeprom_config { @@ -301,6 +303,7 @@ struct ras_core_config { bool poison_supported; bool ras_eeprom_supported; + uint ras_debug_mask; const struct ras_sys_func *sys_fn; struct ras_aca_config aca_cfg; @@ -372,6 +375,7 @@ int ras_core_query_block_ecc_data(struct ras_core_context *ras_core, bool ras_core_gpu_in_reset(struct ras_core_context *ras_core); bool ras_core_gpu_is_rma(struct ras_core_context *ras_core); bool ras_core_gpu_is_vf(struct ras_core_context *ras_core); +bool ras_core_gpu_device_lost(struct ras_core_context *ras_core); bool ras_core_handle_nbio_irq(struct ras_core_context *ras_core, void *data); int ras_core_handle_fatal_error(struct ras_core_context *ras_core); @@ -403,4 +407,5 @@ int ras_core_convert_soc_pa_to_cur_nps_pages(struct ras_core_context *ras_core, int ras_core_check_address_sanity(struct ras_core_context *ras_core, uint64_t addr); int ras_core_set_debug_mode(struct ras_core_context *ras_core, bool enable); +bool ras_core_is_ce_log_disabled(struct ras_core_context *ras_core); #endif diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_aca.c b/drivers/gpu/drm/amd/ras/rascore/ras_aca.c index 67a35409ff0e..6f57b5d09cd3 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_aca.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_aca.c @@ -83,22 +83,20 @@ static void aca_report_ecc_info(struct ras_core_context *ras_core, seq_no, skt, aid, ecc_count.total_ue_count, blk_name(blk)); } - if (ecc_count.new_de_count) { + if (ecc_count.new_de_count && blk == RAS_BLOCK_ID__UMC) { RAS_DEV_INFO(ras_core->dev, - "{%llu} socket: %d, die: %d, %u new %s detected in %s block\n", + "{%llu} socket: %d, die: %d, %u new deferred hardware errors detected in %s block\n", seq_no, skt, aid, ecc_count.new_de_count, - (blk == RAS_BLOCK_ID__UMC) ? - "deferred hardware errors" : "poison consumption", blk_name(blk)); RAS_DEV_INFO(ras_core->dev, - "{%llu} socket: %d, die: %d, %u %s detected in total in %s block\n", + "{%llu} socket: %d, die: %d, %u deferred hardware errors detected in total in %s block\n", seq_no, skt, aid, ecc_count.total_de_count, - (blk == RAS_BLOCK_ID__UMC) ? - "deferred hardware errors" : "poison consumption", blk_name(blk)); } if (ecc_count.new_ce_count) { + if (ras_core_is_ce_log_disabled(ras_core)) + return; RAS_DEV_INFO(ras_core->dev, "{%llu} socket: %d, die: %d, %u new correctable hardware errors detected in %s block\n", seq_no, skt, aid, ecc_count.new_ce_count, blk_name(blk)); @@ -114,6 +112,11 @@ static void aca_bank_log(struct ras_core_context *ras_core, { int i; + if (ras_core_is_ce_log_disabled(ras_core) && + bank->ecc_type == RAS_ERR_TYPE__CE && + !bank_ecc->de_count) + return; + RAS_DEV_INFO(ras_core->dev, "{%llu}" RAS_HW_ERR "Accelerator Check Architecture events logged\n", bank->seq_no); diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c index 840610538c1f..288fad0862a2 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c @@ -265,7 +265,7 @@ static int aca_parse_bank_default(struct ras_core_context *ras_core, ecc->bank_info.addr = bank->regs[ACA_REG_IDX__ADDR]; if (aca_check_bank_is_de(ras_core, status)) { - ecc->de_count = 0; + ecc->de_count = 1; } else { if (bank->ecc_type == RAS_ERR_TYPE__UE) ecc->ue_count = 1; diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_core.c b/drivers/gpu/drm/amd/ras/rascore/ras_core.c index c63a358b7e57..75e8216fae8a 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_core.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_core.c @@ -28,6 +28,10 @@ #define IS_LEAP_YEAR(x) ((x % 4 == 0 && x % 100 != 0) || x % 400 == 0) +enum RAS_DEBUG_MASK { + RAS_DEBUG_DISABLE_RAS_CE_LOG = 9, +}; + static const char * const ras_block_name[] = { "umc", "sdma", @@ -143,6 +147,20 @@ bool ras_core_gpu_is_vf(struct ras_core_context *ras_core) return (status & RAS_GPU_STATUS__IS_VF) ? true : false; } +bool ras_core_gpu_device_lost(struct ras_core_context *ras_core) +{ + uint32_t status = 0; + + if (!ras_core) + return false; + + if (ras_core->sys_fn && + ras_core->sys_fn->check_gpu_status) + ras_core->sys_fn->check_gpu_status(ras_core, &status); + + return (status & RAS_GPU_STATUS__DEVICE_LOST) ? true : false; +} + bool ras_core_gpu_is_rma(struct ras_core_context *ras_core) { if (!ras_core) @@ -545,6 +563,11 @@ bool ras_core_is_enabled(struct ras_core_context *ras_core) return ras_core->ras_core_enabled; } +bool ras_core_is_ce_log_disabled(struct ras_core_context *ras_core) +{ + return !!(ras_core->config->ras_debug_mask & BIT(RAS_DEBUG_DISABLE_RAS_CE_LOG)); +} + uint64_t ras_core_get_utc_second_timestamp(struct ras_core_context *ras_core) { if (!ras_core) diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c index 62d1a319c08c..3b1883a04179 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c @@ -1261,6 +1261,11 @@ int ras_eeprom_check_storage_status(struct ras_core_context *ras_core) } else { RAS_DEV_ERR(ras_core->dev, "RAS records:%d exceed threshold:%d", bad_page_count, control->record_threshold_count); + /* send the event when threshold is exceeded, and ignore the + * return value here + */ + ras_core_event_notify(ras_core, RAS_EVENT_ID__DEVICE_RMA, NULL); + if ((control->record_threshold_config == WARN_NONSTOP_OVER_THRESHOLD) || (control->record_threshold_config == NONSTOP_OVER_THRESHOLD)) { RAS_DEV_WARN(ras_core->dev, diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h index f2c001ef64e1..5c3e92f710b4 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h +++ b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h @@ -36,10 +36,10 @@ /* * Bad address pfn : eeprom_umc_record.retired_row_pfn[39:0], - * nps mode: eeprom_umc_record.retired_row_pfn[47:40] + * nps mode: eeprom_umc_record.retired_row_pfn[46:40] */ #define EEPROM_RECORD_UMC_ADDR_MASK 0xFFFFFFFFFFULL -#define EEPROM_RECORD_UMC_NPS_MASK 0xFF0000000000ULL +#define EEPROM_RECORD_UMC_NPS_MASK 0x7F0000000000ULL #define EEPROM_RECORD_UMC_NPS_SHIFT 40 #define EEPROM_RECORD_UMC_NPS_MODE(RECORD) \ diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_psp.c b/drivers/gpu/drm/amd/ras/rascore/ras_psp.c index 358f602b167d..35264c119bbb 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_psp.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_psp.c @@ -294,6 +294,9 @@ static int send_psp_cmd(struct ras_core_context *ras_core, psp_ctx->in_fence_value) { if (--timeout == 0) break; + + if (ras_core_gpu_device_lost(ras_core)) + break; /* * Shouldn't wait for timeout when err_event_athub occurs, * because gpu reset thread triggered and lock resource should diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c index e366fb97293e..f59f0b6cad06 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c @@ -346,7 +346,7 @@ static bool ras_umc_check_retired_record(struct ras_core_context *ras_core, if (ras_umc->ip_func && ras_umc->ip_func->eeprom_record_to_nps_record) { ret = ras_umc->ip_func->eeprom_record_to_nps_record(ras_core, record, nps); if (ret) - RAS_DEV_WARN(ras_core->dev, + RAS_DEV_WARN_RATELIMITED(ras_core->dev, "Failed to adjust eeprom record, ret:%d", ret); } return false; @@ -691,6 +691,7 @@ int ras_umc_hw_init(struct ras_core_context *ras_core) ras_umc->umc_err_data.umc_nps_mode = nps; ras_umc->umc_vram_type = ras_core->config->umc_cfg.umc_vram_type; + ras_umc->num_umc = ras_core->config->umc_cfg.num_umc; if (!ras_umc->umc_vram_type) { RAS_DEV_ERR(ras_core->dev, "Invalid UMC VRAM Type: %u!\n", ras_umc->umc_vram_type); diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h index ee7100f25f51..3d902b0d663a 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h +++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h @@ -46,6 +46,26 @@ #define UMC_ECC_NEW_DETECTED_TAG 0x1 #define UMC_INV_MEM_PFN (0xFFFFFFFFFFFFFFFF) +/* + * a flag to indicate v2 format channel index stored in eeprom + * + * v1: store channel index within a umc instance in eeprom + * range in UMC v12: 0 ~ 7 + * v2: store global channel index in eeprom + * range in UMC v12: 0 ~ 127 + * + * NOTE: it's better to store it in eeprom_table_record.mem_channel, + * but there is only 8 bits in mem_channel, and the channel number may + * increase in the future, we decide to save it in + * eeprom_table_record.retired_page. retired_page is useless in v2, + * we depend on eeprom_table_record.address instead of retired_page in v2. + * Only 48 bits are saved on eeprom, use bit 47 here. + * + * UMC_CHANNEL_IDX_V2 is replaced by nps value in cur_nps_retired_row_pfn + * in eeprom v3 format, so they have no conflict. + */ +#define UMC_CHANNEL_IDX_V2 BIT_ULL(47) + /* three column bits and one row bit in MCA address flip * in bad page retirement */ @@ -137,6 +157,7 @@ struct ras_umc_err_data { struct ras_umc { u32 umc_ip_version; u32 umc_vram_type; + u32 num_umc; const struct ras_umc_ip_func *ip_func; struct radix_tree_root root; struct mutex tree_lock; diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c index b809a2f21d73..6bf30dee8d17 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c @@ -64,6 +64,7 @@ static void __get_nps_pa_flip_bits(struct ras_core_context *ras_core, struct umc_flip_bits *flip_bits) { uint32_t vram_type = ras_core->ras_umc.umc_vram_type; + u32 num_umc = ras_core->ras_umc.num_umc; /* default setting */ flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_C2_BIT; @@ -74,41 +75,87 @@ static void __get_nps_pa_flip_bits(struct ras_core_context *ras_core, flip_bits->bit_num = 4; flip_bits->r13_in_pa = UMC_V12_0_PA_R13_BIT; - if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) { + if (num_umc == 16) { + if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) { + flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_CH5_BIT; + flip_bits->flip_bits_in_pa[1] = UMC_V12_0_PA_C2_BIT; + flip_bits->flip_bits_in_pa[2] = UMC_V12_0_PA_B1_BIT; + flip_bits->r13_in_pa = UMC_V12_0_PA_R12_BIT; + } else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4) { + flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_CH4_BIT; + flip_bits->flip_bits_in_pa[1] = UMC_V12_0_PA_CH5_BIT; + flip_bits->flip_bits_in_pa[2] = UMC_V12_0_PA_B0_BIT; + flip_bits->r13_in_pa = UMC_V12_0_PA_R11_BIT; + } + + switch (vram_type) { + case UMC_VRAM_TYPE_HBM: + /* other nps modes are taken as nps1 */ + if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R12_BIT; + else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4) + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R11_BIT; + + break; + case UMC_VRAM_TYPE_HBM3E: + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R12_BIT; + flip_bits->flip_row_bit = 12; + + if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R11_BIT; + else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4) + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R10_BIT; + + break; + default: + RAS_DEV_WARN(ras_core->dev, + "Unknown HBM type, set RAS retire flip bits to the value in NPS1 mode.\n"); + break; + } + } else if (num_umc == 8) { + /* num_umc == 8 base setting */ flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_CH5_BIT; flip_bits->flip_bits_in_pa[1] = UMC_V12_0_PA_C2_BIT; flip_bits->flip_bits_in_pa[2] = UMC_V12_0_PA_B1_BIT; + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R11_BIT; + flip_bits->flip_row_bit = 12; + flip_bits->bit_num = 4; flip_bits->r13_in_pa = UMC_V12_0_PA_R12_BIT; - } else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4) { - flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_CH4_BIT; - flip_bits->flip_bits_in_pa[1] = UMC_V12_0_PA_CH5_BIT; - flip_bits->flip_bits_in_pa[2] = UMC_V12_0_PA_B0_BIT; - flip_bits->r13_in_pa = UMC_V12_0_PA_R11_BIT; - } - - switch (vram_type) { - case UMC_VRAM_TYPE_HBM: - /* other nps modes are taken as nps1 */ - if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) - flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R12_BIT; - else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4) - flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R11_BIT; - break; - case UMC_VRAM_TYPE_HBM3E: - flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R12_BIT; - flip_bits->flip_row_bit = 12; + /* only NPS1 and NPS2 are supported in this mode */ + if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) { + flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_CH4_BIT; + flip_bits->flip_bits_in_pa[1] = UMC_V12_0_PA_CH5_BIT; + flip_bits->flip_bits_in_pa[2] = UMC_V12_0_PA_B0_BIT; + flip_bits->r13_in_pa = UMC_V12_0_PA_R11_BIT; + } - if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) - flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R11_BIT; - else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4) - flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R10_BIT; + switch (vram_type) { + case UMC_VRAM_TYPE_HBM: + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R12_BIT; - break; - default: + /* other nps modes are taken as nps1 */ + if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R11_BIT; + + break; + case UMC_VRAM_TYPE_HBM3E: + if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) + flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R10_BIT; + + break; + default: + RAS_DEV_WARN(ras_core->dev, + "Unknown HBM type, set RAS retire flip bits to the value in NPS1 mode.\n"); + break; + } + } else { + /* num_umc is always 8 or 16 on umc v12, this path should not be + * entered. + */ RAS_DEV_WARN(ras_core->dev, - "Unknown HBM type, set RAS retire flip bits to the value in NPS1 mode.\n"); - break; + "Unsupported UMC number(%u), use default RAS flip bits setting.\n", + num_umc); } } @@ -413,16 +460,30 @@ static int convert_eeprom_record_to_nps_addr(struct ras_core_context *ras_core, static int umc_v12_0_eeprom_record_to_nps_record(struct ras_core_context *ras_core, struct eeprom_umc_record *record, uint32_t nps) { - uint64_t pa = 0; + uint64_t ch_idx_v2, pa = 0; + uint32_t save_nps; int ret = 0; - if (nps == EEPROM_RECORD_UMC_NPS_MODE(record) && !ras_fw_eeprom_supported(ras_core)) { - record->cur_nps_retired_row_pfn = EEPROM_RECORD_UMC_ADDR_PFN(record); - } else { - ret = convert_eeprom_record_to_nps_addr(ras_core, + save_nps = EEPROM_RECORD_UMC_NPS_MODE(record); + /* eeprom v2 has no stored nps, always convert if the flag is set */ + ch_idx_v2 = record->retired_row_pfn & UMC_CHANNEL_IDX_V2; + + if (save_nps || ch_idx_v2) { + if ((nps == save_nps) && !ras_fw_eeprom_supported(ras_core)) { + record->cur_nps_retired_row_pfn = + EEPROM_RECORD_UMC_ADDR_PFN(record); + } else { + ret = convert_eeprom_record_to_nps_addr(ras_core, record, &pa, nps); - if (!ret) - record->cur_nps_retired_row_pfn = RAS_ADDR_TO_PFN(pa); + if (!ret) + record->cur_nps_retired_row_pfn = RAS_ADDR_TO_PFN(pa); + } + } else { + /* old eeprom data format, the scope of channel index is + * limited to umc instance + */ + /* TODO */ + ret = -EOPNOTSUPP; } record->cur_nps = nps; diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 215e47c94d29..8595f4c6e2e5 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c @@ -596,6 +596,7 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev) return legacy_read_disabled_bios(rdev); } +#ifdef CONFIG_ACPI /** * radeon_acpi_vfct_match() - Check if a VFCT entry matches the device * @rdev: Radeon device @@ -635,7 +636,6 @@ static int radeon_acpi_vfct_match(struct radeon_device *rdev, return 0; } -#ifdef CONFIG_ACPI static bool radeon_acpi_vfct_bios(struct radeon_device *rdev) { struct acpi_table_header *hdr; diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 3a4d4dc635bf..a103fdc0b3a3 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -1646,30 +1646,46 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier) * For multi-plane formats the above surfaces get merged into one plane for * each format plane, based on the required alignment only. * - * Bits Parameter Notes - * ----- ------------------------ --------------------------------------------- + * Bits Parameter Notes + * ------- ------------------------ --------------------------------------------- + * + * DRM format modifier fields on AMD GPUs: + * 7:0 TILE_VERSION Values are AMD_FMT_MOD_TILE_VER_* + * 12:8 TILE Values are AMD_FMT_MOD_TILE_<version>_* + * 13 DCC Delta Color Compression, supported on GFX8 and newer + * 55:14 (chip specific) See below for details, depends on GFX block version + * 63:56 Vendor Value is DRM_FORMAT_MOD_VENDOR_AMD + * + * Chip specific fields on Gfx9 and newer: + * 14 DCC_RETILE + * 15 DCC_PIPE_ALIGN + * 16 DCC_INDEPENDENT_64B + * 17 DCC_INDEPENDENT_128B + * 19:18 DCC_MAX_COMPRESSED_BLOCK Values are AMD_FMT_MOD_DCC_BLOCK_* + * 20 DCC_CONSTANT_ENCODE + * 23:21 PIPE_XOR_BITS Only for some chips + * 26:24 BANK_XOR_BITS Only for some chips + * 29:27 PACKERS Only for some chips + * 32:30 RB Only for some chips + * 35:33 PIPE Only for some chips + * 55:36 - Reserved for future use, must be zero + * + * Chip specific fields on Gfx6-8: + * 16:14 MICROTILE Micro tile format + * 21:17 PIPE_CONFIG Number of pipes and how pipes are interleaved + * 24:22 TILE_SPLIT Tile split size + * 26:25 BANK_WIDTH Number of tiles in the X direction in the same bank + * 28:27 BANK_HEIGHT Number of tiles in the Y direction in the same bank + * 30:29 MACRO_TILE_ASPECT Macro tile aspect ratio + * 32:31 NUM_BANKS Number of banks + * 55:33 - Reserved for future use, must be zero * - * 7:0 TILE_VERSION Values are AMD_FMT_MOD_TILE_VER_* - * 12:8 TILE Values are AMD_FMT_MOD_TILE_<version>_* - * 13 DCC - * 14 DCC_RETILE - * 15 DCC_PIPE_ALIGN - * 16 DCC_INDEPENDENT_64B - * 17 DCC_INDEPENDENT_128B - * 19:18 DCC_MAX_COMPRESSED_BLOCK Values are AMD_FMT_MOD_DCC_BLOCK_* - * 20 DCC_CONSTANT_ENCODE - * 23:21 PIPE_XOR_BITS Only for some chips - * 26:24 BANK_XOR_BITS Only for some chips - * 29:27 PACKERS Only for some chips - * 32:30 RB Only for some chips - * 35:33 PIPE Only for some chips - * 55:36 - Reserved for future use, must be zero */ #define AMD_FMT_MOD fourcc_mod_code(AMD, 0) #define IS_AMD_FMT_MOD(val) (((val) >> 56) == DRM_FORMAT_MOD_VENDOR_AMD) -/* Reserve 0 for GFX8 and older */ +#define AMD_FMT_MOD_TILE_VER_GFX6 0 #define AMD_FMT_MOD_TILE_VER_GFX9 1 #define AMD_FMT_MOD_TILE_VER_GFX10 2 #define AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS 3 @@ -1677,6 +1693,23 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier) #define AMD_FMT_MOD_TILE_VER_GFX12 5 /* + * Gfx6-8 tiling modes. + * A complete reference implementation is found in addrlib in the Mesa code base. + * + * - Microtiled modes (1D): + * Pixel data is organized into micro tiles of 8x8 pixels. + * + * - Macrotiled modes (2D): + * Micro tiles are further organized into macro tiles. + * These are optimized for even load distribution among memory channels. + * + * Note that only THIN1 modes are exposed here. + * THICK and XTHICK are for 3D images and not relevant to DRM format modifiers. + */ +#define AMD_FMT_MOD_TILE_GFX6_1D_TILED_THIN1 0x2 +#define AMD_FMT_MOD_TILE_GFX6_2D_TILED_THIN1 0x4 + +/* * 64K_S is the same for GFX9/GFX10/GFX10_RBPLUS and hence has GFX9 as canonical * version. */ @@ -1774,6 +1807,146 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier) #define AMD_FMT_MOD_PIPE_SHIFT 33 #define AMD_FMT_MOD_PIPE_MASK 0x7 +/* + * MICRO_TILE_MODE, 3 bits. Determines the micro tile format. + * Only relevant to Gfx6-8. + * + * DISPLAY - Displayable tiling + * THIN - Non-displayable tiling, a.k.a thin micro tiling + * DEPTH, THICK - not exposed, not relevant to DRM format modifier use cases + * ROTATED - not exposed, not implemented in Linux or Mesa + */ +#define AMD_FMT_MOD_MICROTILE_SHIFT 14ULL +#define AMD_FMT_MOD_MICROTILE_MASK 0x7 + +#define AMD_FMT_MOD_MICROTILE_DISPLAY 0x0 +#define AMD_FMT_MOD_MICROTILE_THIN 0x1 + +/* + * PIPE_CONFIG, 5 bits. Number of pipes and how pipes are interleaved on the surface, + * which means the shader engine tile size and packer tile size. + * Typically matches the number of memory channels, or number of RBs. + * Only relevant to Gfx6-8 macro tiled modes. + * + * P<n>_<a>x<b>_<c>x<d> + * where: + * <n> - number of pipes + * <a>x<b> - shader engine tile size + * <c>x<d> - packer tile size + */ +#define AMD_FMT_MOD_PIPE_CONFIG_SHIFT 17ULL +#define AMD_FMT_MOD_PIPE_CONFIG_MASK 0x1f + +#define AMD_FMT_MOD_PIPE_CONFIG_P2 0x0 +#define AMD_FMT_MOD_PIPE_CONFIG_P4_8x16 0x4 +#define AMD_FMT_MOD_PIPE_CONFIG_P4_16x16 0x5 +#define AMD_FMT_MOD_PIPE_CONFIG_P4_16x32 0x6 +#define AMD_FMT_MOD_PIPE_CONFIG_P4_32x32 0x7 +#define AMD_FMT_MOD_PIPE_CONFIG_P8_16x16_8x16 0x8 +#define AMD_FMT_MOD_PIPE_CONFIG_P8_16x32_8x16 0x9 +#define AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_8x16 0xa +#define AMD_FMT_MOD_PIPE_CONFIG_P8_16x32_16x16 0xb +#define AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_16x16 0xc +#define AMD_FMT_MOD_PIPE_CONFIG_P8_32x32_16x32 0xd +#define AMD_FMT_MOD_PIPE_CONFIG_P8_32x64_32x32 0xe +#define AMD_FMT_MOD_PIPE_CONFIG_P16_32x32_8x16 0x10 +#define AMD_FMT_MOD_PIPE_CONFIG_P16_32x32_16x16 0x11 + +/* + * TILE_SPLIT, 3 bits. + * Only relevant to Gfx6-8 macro tiled modes. + * + * On GFX6 (or with depth tiling modes on GFX7 and newer), + * the GFX block uses the GB_TILE_MODE.TILE_SPLIT field directly. + * + * On GFX7 and newer with non-depth tiling modes, the GFX block uses a + * split factor which is stored in the GB_TILE_MODE.SAMPLE_SPLIT field. + * SAMPLE_SPLIT may be: 0 - 1 byte; 1 - 2 bytes; 2 - 4 bytes; 3 - 8 bytes. + * The actual tile size and tile split bytes are calculated as follows: + * + * bpp = ... <- bits per pixel in the current image + * thickness = ... <- depends on array mode; may be: 1, 4, 8 + * num_samples = ... <- number of samples in the current image + * tile_size_pixels = 8 * 8 + * tile_bytes_1x = thickness * tile_size_pixels * bpp / 8 + * sample_split_factor = 1 << SAMPLE_SPLIT + * tile_split_bytes = clamp(tile_bytes_1x * sample_split_factor, 256, dram_row_size_bytes) + * tile_bytes = clamp(tile_bytes_1x * num_samples, 64, tile_split_bytes) + * + * In both cases, the display block (DCE) has no SAMPLE_SPLIT + * and just needs the tile split bytes in the GRPH_CONTROL.GRPH_TILE_SPLIT field. + * To maximize compatibility between GFX6-7, we don't include the SAMPLE_SPLIT + * in the format modifiers. + * + * The actual tile split in bytes is: 64 << field value + * Possible values of this field: + * + * 0 - Tile split is 64 bytes + * 1 - Tile split is 128 bytes + * 2 - Tile split is 256 bytes + * 3 - Tile split is 512 bytes + * 4 - Tile split is 1 KiB + * 5 - Tile split is 2 KiB + * 6 - Tile split is 4 KiB + */ +#define AMD_FMT_MOD_TILE_SPLIT_SHIFT 22ULL +#define AMD_FMT_MOD_TILE_SPLIT_MASK 0x7 + +/* + * BANK_WIDTH, 2 bits. Number of tiles in the X direction in the same bank. + * Only relevant to Gfx6-8 macro tiled modes. + * The actual bank width is: 1 << field value + * Possible values: + * + * 0 - bank width is 1 + * 1 - bank width is 2 + * 2 - bank width is 4 + * 3 - bank width is 8 + */ +#define AMD_FMT_MOD_BANK_WIDTH_SHIFT 25ULL +#define AMD_FMT_MOD_BANK_WIDTH_MASK 0x3 + +/* + * BANK_HEIGHT, 2 bits. Number of tiles in the Y direction in the same bank. + * Only relevant to Gfx6-8 macro tiled modes. + * The actual bank height is: 1 << field value + * Possible values: + * + * 0 - bank height is 1 + * 1 - bank height is 2 + * 2 - bank height is 4 + * 3 - bank height is 8 + */ +#define AMD_FMT_MOD_BANK_HEIGHT_SHIFT 27ULL +#define AMD_FMT_MOD_BANK_HEIGHT_MASK 0x3 + +/* + * MACRO_TILE_ASPECT, 2 bits. Macro tile aspect ratio. + * Only relevant to Gfx6-8 macro tiled modes. + * Possible values: + * + * 0 - aspect ratio is 1:1 + * 1 - aspect ratio is 4:1 + * 2 - aspect ratio is 16:1 + * 3 - aspect ratio is 64:1 + */ +#define AMD_FMT_MOD_MACRO_TILE_ASPECT_SHIFT 29ULL +#define AMD_FMT_MOD_MACRO_TILE_ASPECT_MASK 0x3 + +/* + * NUM_BANKS, 2 bits. Number of banks. + * Only relevant to Gfx6-8 macro tiled modes. + * The actual number of banks is: 2 << field value + * Possible values: + * + * 0 - number of banks is 2 + * 1 - number of banks is 4 + * 2 - number of banks is 8 + * 3 - number of banks is 16 + */ +#define AMD_FMT_MOD_NUM_BANKS_SHIFT 31ULL +#define AMD_FMT_MOD_NUM_BANKS_MASK 0x3 + #define AMD_FMT_MOD_SET(field, value) \ ((__u64)(value) << AMD_FMT_MOD_##field##_SHIFT) #define AMD_FMT_MOD_GET(field, value) \ |
