summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-07-31 15:44:43 +1000
committerDave Airlie <airlied@redhat.com>2026-07-31 16:23:53 +1000
commit22afd697fbbc000460d09282282b342feb7331c2 (patch)
tree3188cdf59cf2eb1afbbd20eb437ef9948690f056
parentea97ab2759506d9a818ffed1009bde01062b4091 (diff)
parent6f16fcbb0c46a87e3d9685407e906573d60104b0 (diff)
downloadlinux-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
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c25
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c34
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c88
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_display.c177
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c641
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c38
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c85
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c36
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c43
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c102
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c66
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c37
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c36
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c69
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c22
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c19
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c34
-rw-r--r--drivers/gpu/drm/amd/amdgpu/imu_v11_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/imu_v12_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_userqueue.c19
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v11_0.c27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v12_0.c15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v12_1.c16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_ai.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_nv.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c146
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v11_0.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v13_0.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v13_0_4.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v14_0.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v15_0.c93
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc21.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/uvd_v7_0.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c26
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vi.c20
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c29
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_crat.c9
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c19
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_events.c40
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_migrate.c4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_packet_manager.c16
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c70
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_queue.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c88
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.h10
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/Makefile4
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c1045
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h50
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c3
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c52
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h26
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c217
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h45
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c15
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_cursor.c443
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_cursor.h52
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c11
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h9
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.c362
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_freesync.h65
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c100
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h37
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c12
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c35
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h20
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c1
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c345
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c54
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h3
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c34
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c55
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c651
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c61
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c1401
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c1787
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c728
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c30
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_cursor_test.c268
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c837
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_freesync_test.c453
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c1945
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c63
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c42
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h4
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c149
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c20
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c15
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c65
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c1031
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/conversion.c61
-rw-r--r--drivers/gpu/drm/amd/display/dc/basics/conversion.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c28
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.c29
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_smu.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c183
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc.c471
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c422
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_resource.c98
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_sink.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_state.c72
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_stream.c28
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h226
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_probe.h97
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_state.h7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_stream.h30
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_types.h9
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_transform.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_helpers.h11
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dml1_frl_cap_chk.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/Makefile7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c92
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c32
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c32
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h30
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c38
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c138
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c21
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn42.c32
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn20/dcn20_dpp_cm.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp_cm.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_dwb_cm.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h7
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c53
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c31
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c22
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c71
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c21
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c31
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h8
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c72
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c17
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h214
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/core_status.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/core_types.h53
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h18
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/mcif_wb.h24
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/link_service.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/resource.h7
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.c200
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_training.c28
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/Makefile10
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn32/dcn32_mmhubbub.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn401/dcn401_mmhubbub.c135
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn401/dcn401_mmhubbub.h40
-rw-r--r--drivers/gpu/drm/amd/display/dc/mmhubbub/dcn42/dcn42_mmhubbub.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c44
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c69
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/os_types.h10
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.h7
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c31
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c30
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c202
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h58
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_psr.c7
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_replay.c7
-rw-r--r--drivers/gpu/drm/amd/include/amdgpu_ptl.h9
-rw-r--r--drivers/gpu/drm/amd/include/kgd_pp_interface.h6
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_dpm.c26
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_pm.c29
-rw-r--r--drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c5
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h38
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h6
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c6
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c59
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c223
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c17
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c2
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c3
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras.h5
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca.c17
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c2
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_core.c23
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c5
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h4
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp.c3
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc.c3
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc.h21
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c129
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c2
-rw-r--r--include/uapi/drm/drm_fourcc.h209
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, &reg_offset, &reg_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, &notify);
+}
+
/* 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(&params->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(&params->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,
+ &params->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, &current_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) \