summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-03 15:44:49 +0100
committerMark Brown <broonie@kernel.org>2026-07-03 15:44:49 +0100
commit4dac529c9a2c0ed1286801ec62d23538035a3a1d (patch)
treec99335269438629bc089c9895e6156cf9a374a22
parent5a42c9f18870162ae14f0d8e193671017421a982 (diff)
parent50be7c9b5d5ea55fd40bb411cf324cec99ec7417 (diff)
downloadlinux-next-4dac529c9a2c0ed1286801ec62d23538035a3a1d.tar.gz
linux-next-4dac529c9a2c0ed1286801ec62d23538035a3a1d.zip
Merge branch 'drm-next' of https://gitlab.freedesktop.org/agd5f/linux.git
# Conflicts: # drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c # drivers/gpu/drm/amd/amdgpu/amdgpu_device.c # drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c # drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
-rw-r--r--drivers/gpu/drm/amd/amdgpu/Makefile2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h52
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c985
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_aca.h232
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c68
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c63
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c20
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c33
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c170
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h22
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c116
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c67
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c211
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c188
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c310
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c53
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c284
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c143
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c38
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c486
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h107
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c70
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h23
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.h40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c862
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c368
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c188
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c39
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h77
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c19
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h150
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c269
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h26
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c165
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c76
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c42
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c299
-rw-r--r--drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c63
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atom.c66
-rw-r--r--drivers/gpu/drm/amd/amdgpu/atom.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cik.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v10_0.c66
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v6_0.c57
-rw-r--r--drivers/gpu/drm/amd/amdgpu/dce_v8_0.c57
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c334
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c251
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c224
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c1097
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c125
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c19
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c88
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c92
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_userqueue.c147
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_userqueue.h9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v11_0.c240
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v12_0.c267
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v12_1.c25
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c231
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nv.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c62
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c196
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si.c7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc15.c9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc15_common.h65
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc21.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc24.c11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc_v1_0.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/tonga_ih.c40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v12_0.c585
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v12_0.h28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c45
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v3_0.c69
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c45
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c87
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c90
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vi.c22
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c12
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device.c24
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c223
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_events.c224
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_events.h4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c16
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c14
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_priv.h25
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process.c76
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c8
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c8
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_topology.c11
-rw-r--r--drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c18
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/Makefile6
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c7222
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h79
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c323
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h (renamed from drivers/gpu/drm/amd/display/modules/power/power_helpers.c)47
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c726
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h75
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c179
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h8
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c1
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c3601
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h162
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c102
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h6
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c22
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h6
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c78
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c943
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h68
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c115
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h12
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c64
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h20
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c1548
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h27
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h1
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c189
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h25
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c137
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h51
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c303
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h61
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c51
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h5
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c10
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c16
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h13
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig6
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile18
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c490
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c1242
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c64
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c140
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c2158
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c122
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c523
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c582
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c297
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c634
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c909
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c39
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c142
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h32
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c1092
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c1228
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c2454
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c518
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c103
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c432
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_services_test.c313
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c949
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c392
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c57
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h9
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc.c291
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c174
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_resource.c11
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_surface.c44
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h238
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c41
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h43
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dp_types.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_helper.c226
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_stream.h29
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_types.h67
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c59
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_aux.c9
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c184
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_services.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h40
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c41
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c484
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c432
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c417
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c432
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c418
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c386
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c392
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c193
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c86
-rw-r--r--drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h21
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c62
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c76
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c36
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c341
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c327
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h82
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/link_service.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/reg_helper.h19
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h14
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_detection.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_factory.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c14
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c51
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c258
-rw-r--r--drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h22
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile3
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c18
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.c42
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h17
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h4
-rw-r--r--drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h289
-rw-r--r--drivers/gpu/drm/amd/display/include/ddc_service_types.h1
-rw-r--r--drivers/gpu/drm/amd/display/include/gpio_types.h48
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/Makefile2
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power.c12
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_abm.c14
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_replay.c9
-rw-r--r--drivers/gpu/drm/amd/include/amd_shared.h3
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h4
-rw-r--r--drivers/gpu/drm/amd/include/kgd_kfd_interface.h3
-rw-r--r--drivers/gpu/drm/amd/include/mes_v11_api_def.h5
-rw-r--r--drivers/gpu/drm/amd/include/mes_v12_api_def.h5
-rw-r--r--drivers/gpu/drm/amd/include/soc15_hw_ip.h1
-rw-r--r--drivers/gpu/drm/amd/include/v9_structs.h4
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_pm.c116
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c21
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c38
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c11
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c755
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c446
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c58
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c81
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c761
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c7
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c7
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h17
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c34
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h37
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h6
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c8
-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.c8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c84
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c1
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c1
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c94
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c653
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c85
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c9
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c85
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c140
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h13
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c3
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c2
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c76
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h2
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c14
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c19
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h3
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras.h5
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c13
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_core.c15
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c3
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c2
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1.c19
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1.h3
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c13
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_process.c5
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc.c32
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc.h6
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h2
-rw-r--r--drivers/gpu/drm/radeon/r600_dpm.c21
-rw-r--r--include/uapi/drm/amdgpu_drm.h21
376 files changed, 33013 insertions, 21307 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index ba80542ead9d..5100e35027ec 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -70,7 +70,7 @@ amdgpu-y += amdgpu_device.o amdgpu_reg_access.o amdgpu_doorbell_mgr.o amdgpu_kms
amdgpu_umc.o smu_v11_0_i2c.o amdgpu_fru_eeprom.o amdgpu_rap.o \
amdgpu_fw_attestation.o amdgpu_securedisplay.o \
amdgpu_eeprom.o amdgpu_mca.o amdgpu_psp_ta.o amdgpu_lsdma.o amdgpu_lockdep.o \
- amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_aca.o amdgpu_dev_coredump.o \
+ amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_dev_coredump.o \
amdgpu_cper.o amdgpu_userq_fence.o amdgpu_eviction_fence.o amdgpu_ip.o
amdgpu-$(CONFIG_PROC_FS) += amdgpu_fdinfo.o
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 7b09410d6d8f..dd8ea71077af 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -44,6 +44,7 @@
#include <linux/hashtable.h>
#include <linux/dma-fence.h>
#include <linux/pci.h>
+#include <linux/xarray.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_placement.h>
@@ -103,7 +104,6 @@
#include "amdgpu_smuio.h"
#include "amdgpu_fdinfo.h"
#include "amdgpu_mca.h"
-#include "amdgpu_aca.h"
#include "amdgpu_ras.h"
#include "amdgpu_lockdep.h"
#include "amdgpu_cper.h"
@@ -113,6 +113,7 @@
#include "amdgpu_userq.h"
#include "amdgpu_eviction_fence.h"
#include "amdgpu_ip.h"
+#include "amdgpu_sa.h"
#if defined(CONFIG_DRM_AMD_ISP)
#include "amdgpu_isp.h"
#endif
@@ -272,7 +273,6 @@ extern int amdgpu_ptl;
extern uint amdgpu_hdmi_hpd_debounce_delay_ms;
-#define AMDGPU_VM_MAX_NUM_CTX 4096
#define AMDGPU_SG_THRESHOLD (256*1024*1024)
#define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000
#define AMDGPU_MAX_USEC_TIMEOUT 100000 /* 100 ms */
@@ -305,9 +305,10 @@ extern uint amdgpu_hdmi_hpd_debounce_delay_ms;
/* reset mask */
#define AMDGPU_RESET_TYPE_FULL (1 << 0) /* full adapter reset, mode1/mode2/BACO/etc. */
-#define AMDGPU_RESET_TYPE_SOFT_RESET (1 << 1) /* IP level soft reset */
+#define AMDGPU_RESET_TYPE_SOFT_RECOVERY (1 << 1) /* soft recovery, eg. kill shaders */
#define AMDGPU_RESET_TYPE_PER_QUEUE (1 << 2) /* per queue */
#define AMDGPU_RESET_TYPE_PER_PIPE (1 << 3) /* per pipe */
+#define AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET (1 << 4) /* soft-resets an IP block */
/* max cursor sizes (in pixels) */
#define CIK_CURSOR_WIDTH 128
@@ -387,37 +388,6 @@ struct amdgpu_clock {
uint32_t max_pixel_clock;
};
-/* sub-allocation manager, it has to be protected by another lock.
- * By conception this is an helper for other part of the driver
- * like the indirect buffer or semaphore, which both have their
- * locking.
- *
- * Principe is simple, we keep a list of sub allocation in offset
- * order (first entry has offset == 0, last entry has the highest
- * offset).
- *
- * When allocating new object we first check if there is room at
- * the end total_size - (last_object_offset + last_object_size) >=
- * alloc_size. If so we allocate new object there.
- *
- * When there is not enough room at the end, we start waiting for
- * each sub object until we reach object_offset+object_size >=
- * alloc_size, this object then become the sub object we return.
- *
- * Alignment can't be bigger than page size.
- *
- * Hole are not considered for allocation to keep things simple.
- * Assumption is that there won't be hole (all object on same
- * alignment).
- */
-
-struct amdgpu_sa_manager {
- struct drm_suballoc_manager base;
- struct amdgpu_bo *bo;
- uint64_t gpu_addr;
- void *cpu_ptr;
-};
-
/*
* IRQS.
*/
@@ -446,8 +416,7 @@ struct amdgpu_fpriv {
struct amdgpu_bo_va *prt_va;
struct amdgpu_bo_va *csa_va;
struct amdgpu_bo_va *seq64_va;
- struct mutex bo_list_lock;
- struct idr bo_list_handles;
+ struct xarray bo_list_handles;
struct amdgpu_ctx_mgr ctx_mgr;
struct amdgpu_userq_mgr userq_mgr;
@@ -587,8 +556,6 @@ struct amdgpu_asic_funcs {
/* invalidate hdp read cache */
void (*invalidate_hdp)(struct amdgpu_device *adev,
struct amdgpu_ring *ring);
- /* check if the asic needs a full reset of if soft reset will work */
- bool (*need_full_reset)(struct amdgpu_device *adev);
/* initialize doorbell layout for specific asic*/
void (*init_doorbell_index)(struct amdgpu_device *adev);
/* PCIe bandwidth usage */
@@ -851,6 +818,7 @@ struct amdgpu_device {
struct dev_pm_domain vga_pm_domain;
bool have_disp_power_ref;
bool have_atomics_support;
+ bool is_sw_smu;
/* BIOS */
bool is_atom_fw;
@@ -1022,9 +990,6 @@ struct amdgpu_device {
/* MCA */
struct amdgpu_mca mca;
- /* ACA */
- struct amdgpu_aca aca;
-
/* CPER */
struct amdgpu_cper cper;
@@ -1136,6 +1101,8 @@ struct amdgpu_device {
bool debug_vm_userptr;
bool debug_disable_ce_logs;
bool debug_enable_ce_cs;
+ bool debug_hibernation_thaw_resume_gpu;
+ bool debug_disable_ip_block_soft_reset;
/* Protection for the following isolation structure */
struct mutex enforce_isolation_mutex;
@@ -1356,7 +1323,6 @@ int emu_soc_asic_init(struct amdgpu_device *adev);
#define amdgpu_asic_read_bios_from_rom(adev, b, l) (adev)->asic_funcs->read_bios_from_rom((adev), (b), (l))
#define amdgpu_asic_read_register(adev, se, sh, offset, v)((adev)->asic_funcs->read_register((adev), (se), (sh), (offset), (v)))
#define amdgpu_asic_get_config_memsize(adev) (adev)->asic_funcs->get_config_memsize((adev))
-#define amdgpu_asic_need_full_reset(adev) (adev)->asic_funcs->need_full_reset((adev))
#define amdgpu_asic_init_doorbell_index(adev) (adev)->asic_funcs->init_doorbell_index((adev))
#define amdgpu_asic_get_pcie_usage(adev, cnt0, cnt1) ((adev)->asic_funcs->get_pcie_usage((adev), (cnt0), (cnt1)))
#define amdgpu_asic_need_reset_on_init(adev) (adev)->asic_funcs->need_reset_on_init((adev))
@@ -1468,6 +1434,8 @@ int amdgpu_enable_vblank_kms(struct drm_crtc *crtc);
void amdgpu_disable_vblank_kms(struct drm_crtc *crtc);
int amdgpu_info_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
+int amdgpu_proc_options_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp);
/*
* functions used by amdgpu_encoder.c
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c
deleted file mode 100644
index db7858fe0c3d..000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c
+++ /dev/null
@@ -1,985 +0,0 @@
-/*
- * Copyright 2023 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-#include <linux/list.h>
-#include "amdgpu.h"
-#include "amdgpu_aca.h"
-#include "amdgpu_ras.h"
-
-#define ACA_BANK_HWID(type, hwid, mcatype) [ACA_HWIP_TYPE_##type] = {hwid, mcatype}
-
-typedef int bank_handler_t(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, void *data);
-
-static struct aca_hwip aca_hwid_mcatypes[ACA_HWIP_TYPE_COUNT] = {
- ACA_BANK_HWID(SMU, 0x01, 0x01),
- ACA_BANK_HWID(PCS_XGMI, 0x50, 0x00),
- ACA_BANK_HWID(UMC, 0x96, 0x00),
-};
-
-static void aca_banks_init(struct aca_banks *banks)
-{
- if (!banks)
- return;
-
- memset(banks, 0, sizeof(*banks));
- INIT_LIST_HEAD(&banks->list);
-}
-
-static int aca_banks_add_bank(struct aca_banks *banks, struct aca_bank *bank)
-{
- struct aca_bank_node *node;
-
- if (!bank)
- return -EINVAL;
-
- node = kvzalloc_obj(*node);
- if (!node)
- return -ENOMEM;
-
- memcpy(&node->bank, bank, sizeof(*bank));
-
- INIT_LIST_HEAD(&node->node);
- list_add_tail(&node->node, &banks->list);
-
- banks->nr_banks++;
-
- return 0;
-}
-
-static void aca_banks_release(struct aca_banks *banks)
-{
- struct aca_bank_node *node, *tmp;
-
- if (list_empty(&banks->list))
- return;
-
- list_for_each_entry_safe(node, tmp, &banks->list, node) {
- list_del(&node->node);
- kvfree(node);
- banks->nr_banks--;
- }
-}
-
-static int aca_smu_get_valid_aca_count(struct amdgpu_device *adev, enum aca_smu_type type, u32 *count)
-{
- struct amdgpu_aca *aca = &adev->aca;
- const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
-
- if (!count)
- return -EINVAL;
-
- if (!smu_funcs || !smu_funcs->get_valid_aca_count)
- return -EOPNOTSUPP;
-
- return smu_funcs->get_valid_aca_count(adev, type, count);
-}
-
-static struct aca_regs_dump {
- const char *name;
- int reg_idx;
-} aca_regs[] = {
- {"CONTROL", ACA_REG_IDX_CTL},
- {"STATUS", ACA_REG_IDX_STATUS},
- {"ADDR", ACA_REG_IDX_ADDR},
- {"MISC", ACA_REG_IDX_MISC0},
- {"CONFIG", ACA_REG_IDX_CONFIG},
- {"IPID", ACA_REG_IDX_IPID},
- {"SYND", ACA_REG_IDX_SYND},
- {"DESTAT", ACA_REG_IDX_DESTAT},
- {"DEADDR", ACA_REG_IDX_DEADDR},
- {"CONTROL_MASK", ACA_REG_IDX_CTL_MASK},
-};
-
-static void aca_smu_bank_dump(struct amdgpu_device *adev, int idx, int total, struct aca_bank *bank,
- struct ras_query_context *qctx)
-{
- u64 event_id = qctx ? qctx->evid.event_id : RAS_EVENT_INVALID_ID;
- int i;
-
- if (adev->debug_disable_ce_logs &&
- bank->smu_err_type == ACA_SMU_TYPE_CE &&
- !ACA_BANK_ERR_IS_DEFFERED(bank))
- return;
-
- RAS_EVENT_LOG(adev, event_id, HW_ERR "Accelerator Check Architecture events logged\n");
- /* plus 1 for output format, e.g: ACA[08/08]: xxxx */
- for (i = 0; i < ARRAY_SIZE(aca_regs); i++)
- RAS_EVENT_LOG(adev, event_id, HW_ERR "ACA[%02d/%02d].%s=0x%016llx\n",
- idx + 1, total, aca_regs[i].name, bank->regs[aca_regs[i].reg_idx]);
-
- if (ACA_REG__STATUS__SCRUB(bank->regs[ACA_REG_IDX_STATUS]))
- RAS_EVENT_LOG(adev, event_id, HW_ERR "hardware error logged by the scrubber\n");
-}
-
-static bool aca_bank_hwip_is_matched(struct aca_bank *bank, enum aca_hwip_type type)
-{
-
- struct aca_hwip *hwip;
- int hwid, mcatype;
- u64 ipid;
-
- if (!bank || type == ACA_HWIP_TYPE_UNKNOW)
- return false;
-
- hwip = &aca_hwid_mcatypes[type];
- if (!hwip->hwid)
- return false;
-
- ipid = bank->regs[ACA_REG_IDX_IPID];
- hwid = ACA_REG__IPID__HARDWAREID(ipid);
- mcatype = ACA_REG__IPID__MCATYPE(ipid);
-
- return hwip->hwid == hwid && hwip->mcatype == mcatype;
-}
-
-static int aca_smu_get_valid_aca_banks(struct amdgpu_device *adev, enum aca_smu_type type,
- int start, int count,
- struct aca_banks *banks, struct ras_query_context *qctx)
-{
- struct amdgpu_aca *aca = &adev->aca;
- const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
- struct aca_bank bank;
- int i, max_count, ret;
-
- if (!count)
- return 0;
-
- if (!smu_funcs || !smu_funcs->get_valid_aca_bank)
- return -EOPNOTSUPP;
-
- switch (type) {
- case ACA_SMU_TYPE_UE:
- max_count = smu_funcs->max_ue_bank_count;
- break;
- case ACA_SMU_TYPE_CE:
- max_count = smu_funcs->max_ce_bank_count;
- break;
- default:
- return -EINVAL;
- }
-
- if (start + count > max_count)
- return -EINVAL;
-
- count = min_t(int, count, max_count);
- for (i = 0; i < count; i++) {
- memset(&bank, 0, sizeof(bank));
- ret = smu_funcs->get_valid_aca_bank(adev, type, start + i, &bank);
- if (ret)
- return ret;
-
- bank.smu_err_type = type;
-
- /*
- * Poison being consumed when injecting a UE while running background workloads,
- * which are unexpected.
- */
- if (type == ACA_SMU_TYPE_UE &&
- ACA_REG__STATUS__POISON(bank.regs[ACA_REG_IDX_STATUS]) &&
- !aca_bank_hwip_is_matched(&bank, ACA_HWIP_TYPE_UMC))
- continue;
-
- aca_smu_bank_dump(adev, i, count, &bank, qctx);
-
- ret = aca_banks_add_bank(banks, &bank);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static bool aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type)
-{
- const struct aca_bank_ops *bank_ops = handle->bank_ops;
-
- /* Parse all deferred errors with UMC aca handle */
- if (ACA_BANK_ERR_IS_DEFFERED(bank))
- return handle->hwip == ACA_HWIP_TYPE_UMC;
-
- if (!aca_bank_hwip_is_matched(bank, handle->hwip))
- return false;
-
- if (!bank_ops->aca_bank_is_valid)
- return true;
-
- return bank_ops->aca_bank_is_valid(handle, bank, type, handle->data);
-}
-
-static struct aca_bank_error *new_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
-{
- struct aca_bank_error *bank_error;
-
- bank_error = kvzalloc_obj(*bank_error);
- if (!bank_error)
- return NULL;
-
- INIT_LIST_HEAD(&bank_error->node);
- memcpy(&bank_error->info, info, sizeof(*info));
-
- mutex_lock(&aerr->lock);
- list_add_tail(&bank_error->node, &aerr->list);
- aerr->nr_errors++;
- mutex_unlock(&aerr->lock);
-
- return bank_error;
-}
-
-static struct aca_bank_error *find_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
-{
- struct aca_bank_error *bank_error = NULL;
- struct aca_bank_info *tmp_info;
- bool found = false;
-
- mutex_lock(&aerr->lock);
- list_for_each_entry(bank_error, &aerr->list, node) {
- tmp_info = &bank_error->info;
- if (tmp_info->socket_id == info->socket_id &&
- tmp_info->die_id == info->die_id) {
- found = true;
- goto out_unlock;
- }
- }
-
-out_unlock:
- mutex_unlock(&aerr->lock);
-
- return found ? bank_error : NULL;
-}
-
-static void aca_bank_error_remove(struct aca_error *aerr, struct aca_bank_error *bank_error)
-{
- if (!aerr || !bank_error)
- return;
-
- list_del(&bank_error->node);
- aerr->nr_errors--;
-
- kvfree(bank_error);
-}
-
-static struct aca_bank_error *get_bank_error(struct aca_error *aerr, struct aca_bank_info *info)
-{
- struct aca_bank_error *bank_error;
-
- if (!aerr || !info)
- return NULL;
-
- bank_error = find_bank_error(aerr, info);
- if (bank_error)
- return bank_error;
-
- return new_bank_error(aerr, info);
-}
-
-int aca_error_cache_log_bank_error(struct aca_handle *handle, struct aca_bank_info *info,
- enum aca_error_type type, u64 count)
-{
- struct aca_error_cache *error_cache = &handle->error_cache;
- struct aca_bank_error *bank_error;
- struct aca_error *aerr;
-
- if (!handle || !info || type >= ACA_ERROR_TYPE_COUNT)
- return -EINVAL;
-
- if (!count)
- return 0;
-
- aerr = &error_cache->errors[type];
- bank_error = get_bank_error(aerr, info);
- if (!bank_error)
- return -ENOMEM;
-
- bank_error->count += count;
-
- return 0;
-}
-
-static int aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type)
-{
- const struct aca_bank_ops *bank_ops = handle->bank_ops;
-
- if (!bank)
- return -EINVAL;
-
- if (!bank_ops->aca_bank_parser)
- return -EOPNOTSUPP;
-
- return bank_ops->aca_bank_parser(handle, bank, type,
- handle->data);
-}
-
-static int handler_aca_log_bank_error(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- int ret;
-
- ret = aca_bank_parser(handle, bank, type);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int aca_dispatch_bank(struct aca_handle_manager *mgr, struct aca_bank *bank,
- enum aca_smu_type type, bank_handler_t handler, void *data)
-{
- struct aca_handle *handle;
- int ret;
-
- if (list_empty(&mgr->list))
- return 0;
-
- list_for_each_entry(handle, &mgr->list, node) {
- if (!aca_bank_is_valid(handle, bank, type))
- continue;
-
- ret = handler(handle, bank, type, data);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int aca_dispatch_banks(struct aca_handle_manager *mgr, struct aca_banks *banks,
- enum aca_smu_type type, bank_handler_t handler, void *data)
-{
- struct aca_bank_node *node;
- struct aca_bank *bank;
- int ret;
-
- if (!mgr || !banks)
- return -EINVAL;
-
- /* pre check to avoid unnecessary operations */
- if (list_empty(&mgr->list) || list_empty(&banks->list))
- return 0;
-
- list_for_each_entry(node, &banks->list, node) {
- bank = &node->bank;
-
- ret = aca_dispatch_bank(mgr, bank, type, handler, data);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static bool aca_bank_should_update(struct amdgpu_device *adev, enum aca_smu_type type)
-{
- struct amdgpu_aca *aca = &adev->aca;
- bool ret = true;
-
- /*
- * Because the UE Valid MCA count will only be cleared after reset,
- * in order to avoid repeated counting of the error count,
- * the aca bank is only updated once during the gpu recovery stage.
- */
- if (type == ACA_SMU_TYPE_UE) {
- if (amdgpu_ras_intr_triggered())
- ret = atomic_cmpxchg(&aca->ue_update_flag, 0, 1) == 0;
- else
- atomic_set(&aca->ue_update_flag, 0);
- }
-
- return ret;
-}
-
-static void aca_banks_generate_cper(struct amdgpu_device *adev,
- enum aca_smu_type type,
- struct aca_banks *banks,
- int count)
-{
- struct aca_bank_node *node;
- struct aca_bank *bank;
- int r;
-
- if (!adev->cper.enabled)
- return;
-
- if (!banks || !count) {
- dev_warn(adev->dev, "fail to generate cper records\n");
- return;
- }
-
- /* UEs must be encoded into separate CPER entries */
- if (type == ACA_SMU_TYPE_UE) {
- struct aca_banks de_banks;
-
- aca_banks_init(&de_banks);
- list_for_each_entry(node, &banks->list, node) {
- bank = &node->bank;
- if (bank->aca_err_type == ACA_ERROR_TYPE_DEFERRED) {
- r = aca_banks_add_bank(&de_banks, bank);
- if (r)
- dev_warn(adev->dev, "fail to add de banks, ret = %d\n", r);
- } else {
- if (amdgpu_cper_generate_ue_record(adev, bank))
- dev_warn(adev->dev, "fail to generate ue cper records\n");
- }
- }
-
- if (!list_empty(&de_banks.list)) {
- if (amdgpu_cper_generate_ce_records(adev, &de_banks, de_banks.nr_banks))
- dev_warn(adev->dev, "fail to generate de cper records\n");
- }
-
- aca_banks_release(&de_banks);
- } else {
- /*
- * SMU_TYPE_CE banks are combined into 1 CPER entries,
- * they could be CEs or DEs or both
- */
- if (amdgpu_cper_generate_ce_records(adev, banks, count))
- dev_warn(adev->dev, "fail to generate ce cper records\n");
- }
-}
-
-static int aca_banks_update(struct amdgpu_device *adev, enum aca_smu_type type,
- bank_handler_t handler, struct ras_query_context *qctx, void *data)
-{
- struct amdgpu_aca *aca = &adev->aca;
- struct aca_banks banks;
- u32 count = 0;
- int ret;
-
- if (list_empty(&aca->mgr.list))
- return 0;
-
- if (!aca_bank_should_update(adev, type))
- return 0;
-
- ret = aca_smu_get_valid_aca_count(adev, type, &count);
- if (ret)
- return ret;
-
- if (!count)
- return 0;
-
- aca_banks_init(&banks);
-
- ret = aca_smu_get_valid_aca_banks(adev, type, 0, count, &banks, qctx);
- if (ret)
- goto err_release_banks;
-
- if (list_empty(&banks.list)) {
- ret = 0;
- goto err_release_banks;
- }
-
- ret = aca_dispatch_banks(&aca->mgr, &banks, type,
- handler, data);
- if (ret)
- goto err_release_banks;
-
- aca_banks_generate_cper(adev, type, &banks, count);
-
-err_release_banks:
- aca_banks_release(&banks);
-
- return ret;
-}
-
-static int aca_log_aca_error_data(struct aca_bank_error *bank_error, enum aca_error_type type, struct ras_err_data *err_data)
-{
- struct aca_bank_info *info;
- struct amdgpu_smuio_mcm_config_info mcm_info;
- u64 count;
-
- if (type >= ACA_ERROR_TYPE_COUNT)
- return -EINVAL;
-
- count = bank_error->count;
- if (!count)
- return 0;
-
- info = &bank_error->info;
- mcm_info.die_id = info->die_id;
- mcm_info.socket_id = info->socket_id;
-
- switch (type) {
- case ACA_ERROR_TYPE_UE:
- amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, count);
- break;
- case ACA_ERROR_TYPE_CE:
- amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, count);
- break;
- case ACA_ERROR_TYPE_DEFERRED:
- amdgpu_ras_error_statistic_de_count(err_data, &mcm_info, count);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int aca_log_aca_error(struct aca_handle *handle, enum aca_error_type type, struct ras_err_data *err_data)
-{
- struct aca_error_cache *error_cache = &handle->error_cache;
- struct aca_error *aerr = &error_cache->errors[type];
- struct aca_bank_error *bank_error, *tmp;
-
- mutex_lock(&aerr->lock);
-
- if (list_empty(&aerr->list))
- goto out_unlock;
-
- list_for_each_entry_safe(bank_error, tmp, &aerr->list, node) {
- aca_log_aca_error_data(bank_error, type, err_data);
- aca_bank_error_remove(aerr, bank_error);
- }
-
-out_unlock:
- mutex_unlock(&aerr->lock);
-
- return 0;
-}
-
-static int __aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle, enum aca_error_type type,
- struct ras_err_data *err_data, struct ras_query_context *qctx)
-{
- enum aca_smu_type smu_type;
- int ret;
-
- switch (type) {
- case ACA_ERROR_TYPE_UE:
- smu_type = ACA_SMU_TYPE_UE;
- break;
- case ACA_ERROR_TYPE_CE:
- case ACA_ERROR_TYPE_DEFERRED:
- smu_type = ACA_SMU_TYPE_CE;
- break;
- default:
- return -EINVAL;
- }
-
- /* update aca bank to aca source error_cache first */
- ret = aca_banks_update(adev, smu_type, handler_aca_log_bank_error, qctx, NULL);
- if (ret)
- return ret;
-
- /* DEs may contain in CEs or UEs */
- if (type != ACA_ERROR_TYPE_DEFERRED)
- aca_log_aca_error(handle, ACA_ERROR_TYPE_DEFERRED, err_data);
-
- return aca_log_aca_error(handle, type, err_data);
-}
-
-static bool aca_handle_is_valid(struct aca_handle *handle)
-{
- if (!handle->mask || !list_empty(&handle->node))
- return false;
-
- return true;
-}
-
-int amdgpu_aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle,
- enum aca_error_type type, struct ras_err_data *err_data,
- struct ras_query_context *qctx)
-{
- if (!handle || !err_data)
- return -EINVAL;
-
- if (aca_handle_is_valid(handle))
- return -EOPNOTSUPP;
-
- if ((type < 0) || (!(BIT(type) & handle->mask)))
- return 0;
-
- return __aca_get_error_data(adev, handle, type, err_data, qctx);
-}
-
-static void aca_error_init(struct aca_error *aerr, enum aca_error_type type)
-{
- mutex_init(&aerr->lock);
- INIT_LIST_HEAD(&aerr->list);
- aerr->type = type;
- aerr->nr_errors = 0;
-}
-
-static void aca_init_error_cache(struct aca_handle *handle)
-{
- struct aca_error_cache *error_cache = &handle->error_cache;
- int type;
-
- for (type = ACA_ERROR_TYPE_UE; type < ACA_ERROR_TYPE_COUNT; type++)
- aca_error_init(&error_cache->errors[type], type);
-}
-
-static void aca_error_fini(struct aca_error *aerr)
-{
- struct aca_bank_error *bank_error, *tmp;
-
- mutex_lock(&aerr->lock);
- if (list_empty(&aerr->list))
- goto out_unlock;
-
- list_for_each_entry_safe(bank_error, tmp, &aerr->list, node)
- aca_bank_error_remove(aerr, bank_error);
-
-out_unlock:
- mutex_unlock(&aerr->lock);
- mutex_destroy(&aerr->lock);
-}
-
-static void aca_fini_error_cache(struct aca_handle *handle)
-{
- struct aca_error_cache *error_cache = &handle->error_cache;
- int type;
-
- for (type = ACA_ERROR_TYPE_UE; type < ACA_ERROR_TYPE_COUNT; type++)
- aca_error_fini(&error_cache->errors[type]);
-}
-
-static int add_aca_handle(struct amdgpu_device *adev, struct aca_handle_manager *mgr, struct aca_handle *handle,
- const char *name, const struct aca_info *ras_info, void *data)
-{
- memset(handle, 0, sizeof(*handle));
-
- handle->adev = adev;
- handle->mgr = mgr;
- handle->name = name;
- handle->hwip = ras_info->hwip;
- handle->mask = ras_info->mask;
- handle->bank_ops = ras_info->bank_ops;
- handle->data = data;
- aca_init_error_cache(handle);
-
- INIT_LIST_HEAD(&handle->node);
- list_add_tail(&handle->node, &mgr->list);
- mgr->nr_handles++;
-
- return 0;
-}
-
-static ssize_t aca_sysfs_read(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct aca_handle *handle = container_of(attr, struct aca_handle, aca_attr);
-
- /* NOTE: the aca cache will be auto cleared once read,
- * So the driver should unify the query entry point, forward request to ras query interface directly */
- return amdgpu_ras_aca_sysfs_read(dev, attr, handle, buf, handle->data);
-}
-
-static int add_aca_sysfs(struct amdgpu_device *adev, struct aca_handle *handle)
-{
- struct device_attribute *aca_attr = &handle->aca_attr;
-
- snprintf(handle->attr_name, sizeof(handle->attr_name) - 1, "aca_%s", handle->name);
- aca_attr->show = aca_sysfs_read;
- aca_attr->attr.name = handle->attr_name;
- aca_attr->attr.mode = S_IRUGO;
- sysfs_attr_init(&aca_attr->attr);
-
- return sysfs_add_file_to_group(&adev->dev->kobj,
- &aca_attr->attr,
- "ras");
-}
-
-int amdgpu_aca_add_handle(struct amdgpu_device *adev, struct aca_handle *handle,
- const char *name, const struct aca_info *ras_info, void *data)
-{
- struct amdgpu_aca *aca = &adev->aca;
- int ret;
-
- if (!amdgpu_aca_is_enabled(adev))
- return 0;
-
- ret = add_aca_handle(adev, &aca->mgr, handle, name, ras_info, data);
- if (ret)
- return ret;
-
- return add_aca_sysfs(adev, handle);
-}
-
-static void remove_aca_handle(struct aca_handle *handle)
-{
- struct aca_handle_manager *mgr = handle->mgr;
-
- aca_fini_error_cache(handle);
- list_del(&handle->node);
- mgr->nr_handles--;
-}
-
-static void remove_aca_sysfs(struct aca_handle *handle)
-{
- struct amdgpu_device *adev = handle->adev;
- struct device_attribute *aca_attr = &handle->aca_attr;
-
- if (adev->dev->kobj.sd)
- sysfs_remove_file_from_group(&adev->dev->kobj,
- &aca_attr->attr,
- "ras");
-}
-
-void amdgpu_aca_remove_handle(struct aca_handle *handle)
-{
- if (!handle || list_empty(&handle->node))
- return;
-
- remove_aca_sysfs(handle);
- remove_aca_handle(handle);
-}
-
-static int aca_manager_init(struct aca_handle_manager *mgr)
-{
- INIT_LIST_HEAD(&mgr->list);
- mgr->nr_handles = 0;
-
- return 0;
-}
-
-static void aca_manager_fini(struct aca_handle_manager *mgr)
-{
- struct aca_handle *handle, *tmp;
-
- if (list_empty(&mgr->list))
- return;
-
- list_for_each_entry_safe(handle, tmp, &mgr->list, node)
- amdgpu_aca_remove_handle(handle);
-}
-
-bool amdgpu_aca_is_enabled(struct amdgpu_device *adev)
-{
- return (adev->aca.is_enabled ||
- adev->debug_enable_ras_aca);
-}
-
-int amdgpu_aca_init(struct amdgpu_device *adev)
-{
- struct amdgpu_aca *aca = &adev->aca;
- int ret;
-
- atomic_set(&aca->ue_update_flag, 0);
-
- ret = aca_manager_init(&aca->mgr);
- if (ret)
- return ret;
-
- return 0;
-}
-
-void amdgpu_aca_fini(struct amdgpu_device *adev)
-{
- struct amdgpu_aca *aca = &adev->aca;
-
- aca_manager_fini(&aca->mgr);
-
- atomic_set(&aca->ue_update_flag, 0);
-}
-
-int amdgpu_aca_reset(struct amdgpu_device *adev)
-{
- struct amdgpu_aca *aca = &adev->aca;
-
- atomic_set(&aca->ue_update_flag, 0);
-
- return 0;
-}
-
-void amdgpu_aca_set_smu_funcs(struct amdgpu_device *adev, const struct aca_smu_funcs *smu_funcs)
-{
- struct amdgpu_aca *aca = &adev->aca;
-
- WARN_ON(aca->smu_funcs);
- aca->smu_funcs = smu_funcs;
-}
-
-int aca_bank_info_decode(struct aca_bank *bank, struct aca_bank_info *info)
-{
- u64 ipid;
- u32 instidhi, instidlo;
-
- if (!bank || !info)
- return -EINVAL;
-
- ipid = bank->regs[ACA_REG_IDX_IPID];
- info->hwid = ACA_REG__IPID__HARDWAREID(ipid);
- info->mcatype = ACA_REG__IPID__MCATYPE(ipid);
- /*
- * Unfied DieID Format: SAASS. A:AID, S:Socket.
- * Unfied DieID[4:4] = InstanceId[0:0]
- * Unfied DieID[0:3] = InstanceIdHi[0:3]
- */
- instidhi = ACA_REG__IPID__INSTANCEIDHI(ipid);
- instidlo = ACA_REG__IPID__INSTANCEIDLO(ipid);
- info->die_id = ((instidhi >> 2) & 0x03);
- info->socket_id = ((instidlo & 0x1) << 2) | (instidhi & 0x03);
-
- return 0;
-}
-
-static int aca_bank_get_error_code(struct amdgpu_device *adev, struct aca_bank *bank)
-{
- struct amdgpu_aca *aca = &adev->aca;
- const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
-
- if (!smu_funcs || !smu_funcs->parse_error_code)
- return -EOPNOTSUPP;
-
- return smu_funcs->parse_error_code(adev, bank);
-}
-
-int aca_bank_check_error_codes(struct amdgpu_device *adev, struct aca_bank *bank, int *err_codes, int size)
-{
- int i, error_code;
-
- if (!bank || !err_codes)
- return -EINVAL;
-
- error_code = aca_bank_get_error_code(adev, bank);
- if (error_code < 0)
- return error_code;
-
- for (i = 0; i < size; i++) {
- if (err_codes[i] == error_code)
- return 0;
- }
-
- return -EINVAL;
-}
-
-int amdgpu_aca_smu_set_debug_mode(struct amdgpu_device *adev, bool en)
-{
- struct amdgpu_aca *aca = &adev->aca;
- const struct aca_smu_funcs *smu_funcs = aca->smu_funcs;
-
- if (!smu_funcs || !smu_funcs->set_debug_mode)
- return -EOPNOTSUPP;
-
- return smu_funcs->set_debug_mode(adev, en);
-}
-
-#if defined(CONFIG_DEBUG_FS)
-static int amdgpu_aca_smu_debug_mode_set(void *data, u64 val)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)data;
- int ret;
-
- ret = amdgpu_ras_set_aca_debug_mode(adev, val ? true : false);
- if (ret)
- return ret;
-
- dev_info(adev->dev, "amdgpu set smu aca debug mode %s success\n", val ? "on" : "off");
-
- return 0;
-}
-
-static void aca_dump_entry(struct seq_file *m, struct aca_bank *bank, enum aca_smu_type type, int idx)
-{
- struct aca_bank_info info;
- int i, ret;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return;
-
- seq_printf(m, "aca entry[%d].type: %s\n", idx, type == ACA_SMU_TYPE_UE ? "UE" : "CE");
- seq_printf(m, "aca entry[%d].info: socketid:%d aid:%d hwid:0x%03x mcatype:0x%04x\n",
- idx, info.socket_id, info.die_id, info.hwid, info.mcatype);
-
- for (i = 0; i < ARRAY_SIZE(aca_regs); i++)
- seq_printf(m, "aca entry[%d].regs[%d]: 0x%016llx\n", idx, aca_regs[i].reg_idx, bank->regs[aca_regs[i].reg_idx]);
-}
-
-struct aca_dump_context {
- struct seq_file *m;
- int idx;
-};
-
-static int handler_aca_bank_dump(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct aca_dump_context *ctx = (struct aca_dump_context *)data;
-
- aca_dump_entry(ctx->m, bank, type, ctx->idx++);
-
- return handler_aca_log_bank_error(handle, bank, type, NULL);
-}
-
-static int aca_dump_show(struct seq_file *m, enum aca_smu_type type)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
- struct aca_dump_context context = {
- .m = m,
- .idx = 0,
- };
-
- return aca_banks_update(adev, type, handler_aca_bank_dump, NULL, (void *)&context);
-}
-
-static int aca_dump_ce_show(struct seq_file *m, void *unused)
-{
- return aca_dump_show(m, ACA_SMU_TYPE_CE);
-}
-
-static int aca_dump_ce_open(struct inode *inode, struct file *file)
-{
- return single_open(file, aca_dump_ce_show, inode->i_private);
-}
-
-static const struct file_operations aca_ce_dump_debug_fops = {
- .owner = THIS_MODULE,
- .open = aca_dump_ce_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int aca_dump_ue_show(struct seq_file *m, void *unused)
-{
- return aca_dump_show(m, ACA_SMU_TYPE_UE);
-}
-
-static int aca_dump_ue_open(struct inode *inode, struct file *file)
-{
- return single_open(file, aca_dump_ue_show, inode->i_private);
-}
-
-static const struct file_operations aca_ue_dump_debug_fops = {
- .owner = THIS_MODULE,
- .open = aca_dump_ue_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-DEFINE_DEBUGFS_ATTRIBUTE(aca_debug_mode_fops, NULL, amdgpu_aca_smu_debug_mode_set, "%llu\n");
-#endif
-
-void amdgpu_aca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root)
-{
-#if defined(CONFIG_DEBUG_FS)
- if (!root)
- return;
-
- debugfs_create_file("aca_debug_mode", 0200, root, adev, &aca_debug_mode_fops);
- debugfs_create_file("aca_ue_dump", 0400, root, adev, &aca_ue_dump_debug_fops);
- debugfs_create_file("aca_ce_dump", 0400, root, adev, &aca_ce_dump_debug_fops);
-#endif
-}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.h
deleted file mode 100644
index 38c88897e1ec..000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright 2023 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-#ifndef __AMDGPU_ACA_H__
-#define __AMDGPU_ACA_H__
-
-#include <linux/list.h>
-
-struct ras_err_data;
-struct ras_query_context;
-
-#define ACA_MAX_REGS_COUNT (16)
-
-#define ACA_REG_FIELD(x, h, l) (((x) & GENMASK_ULL(h, l)) >> l)
-#define ACA_REG__STATUS__VAL(x) ACA_REG_FIELD(x, 63, 63)
-#define ACA_REG__STATUS__OVERFLOW(x) ACA_REG_FIELD(x, 62, 62)
-#define ACA_REG__STATUS__UC(x) ACA_REG_FIELD(x, 61, 61)
-#define ACA_REG__STATUS__EN(x) ACA_REG_FIELD(x, 60, 60)
-#define ACA_REG__STATUS__MISCV(x) ACA_REG_FIELD(x, 59, 59)
-#define ACA_REG__STATUS__ADDRV(x) ACA_REG_FIELD(x, 58, 58)
-#define ACA_REG__STATUS__PCC(x) ACA_REG_FIELD(x, 57, 57)
-#define ACA_REG__STATUS__ERRCOREIDVAL(x) ACA_REG_FIELD(x, 56, 56)
-#define ACA_REG__STATUS__TCC(x) ACA_REG_FIELD(x, 55, 55)
-#define ACA_REG__STATUS__SYNDV(x) ACA_REG_FIELD(x, 53, 53)
-#define ACA_REG__STATUS__CECC(x) ACA_REG_FIELD(x, 46, 46)
-#define ACA_REG__STATUS__UECC(x) ACA_REG_FIELD(x, 45, 45)
-#define ACA_REG__STATUS__DEFERRED(x) ACA_REG_FIELD(x, 44, 44)
-#define ACA_REG__STATUS__POISON(x) ACA_REG_FIELD(x, 43, 43)
-#define ACA_REG__STATUS__SCRUB(x) ACA_REG_FIELD(x, 40, 40)
-#define ACA_REG__STATUS__ERRCOREID(x) ACA_REG_FIELD(x, 37, 32)
-#define ACA_REG__STATUS__ADDRLSB(x) ACA_REG_FIELD(x, 29, 24)
-#define ACA_REG__STATUS__ERRORCODEEXT(x) ACA_REG_FIELD(x, 21, 16)
-#define ACA_REG__STATUS__ERRORCODE(x) ACA_REG_FIELD(x, 15, 0)
-
-#define ACA_REG__IPID__MCATYPE(x) ACA_REG_FIELD(x, 63, 48)
-#define ACA_REG__IPID__INSTANCEIDHI(x) ACA_REG_FIELD(x, 47, 44)
-#define ACA_REG__IPID__HARDWAREID(x) ACA_REG_FIELD(x, 43, 32)
-#define ACA_REG__IPID__INSTANCEIDLO(x) ACA_REG_FIELD(x, 31, 0)
-
-#define ACA_REG__MISC0__VALID(x) ACA_REG_FIELD(x, 63, 63)
-#define ACA_REG__MISC0__OVRFLW(x) ACA_REG_FIELD(x, 48, 48)
-#define ACA_REG__MISC0__ERRCNT(x) ACA_REG_FIELD(x, 43, 32)
-
-#define ACA_REG__SYND__ERRORINFORMATION(x) ACA_REG_FIELD(x, 17, 0)
-
-/* NOTE: The following codes refers to the smu header file */
-#define ACA_EXTERROR_CODE_CE 0x3a
-#define ACA_EXTERROR_CODE_FAULT 0x3b
-
-#define ACA_ERROR_UE_MASK BIT_MASK(ACA_ERROR_TYPE_UE)
-#define ACA_ERROR_CE_MASK BIT_MASK(ACA_ERROR_TYPE_CE)
-#define ACA_ERROR_DEFERRED_MASK BIT_MASK(ACA_ERROR_TYPE_DEFERRED)
-
-#define mmSMNAID_AID0_MCA_SMU 0x03b30400 /* SMN AID AID0 */
-#define mmSMNAID_XCD0_MCA_SMU 0x36430400 /* SMN AID XCD0 */
-#define mmSMNAID_XCD1_MCA_SMU 0x38430400 /* SMN AID XCD1 */
-#define mmSMNXCD_XCD0_MCA_SMU 0x40430400 /* SMN XCD XCD0 */
-
-#define ACA_BANK_ERR_IS_DEFFERED(bank) \
- (ACA_REG__STATUS__POISON((bank)->regs[ACA_REG_IDX_STATUS]) || \
- ACA_REG__STATUS__DEFERRED((bank)->regs[ACA_REG_IDX_STATUS]))
-
-enum aca_reg_idx {
- ACA_REG_IDX_CTL = 0,
- ACA_REG_IDX_STATUS = 1,
- ACA_REG_IDX_ADDR = 2,
- ACA_REG_IDX_MISC0 = 3,
- ACA_REG_IDX_CONFIG = 4,
- ACA_REG_IDX_IPID = 5,
- ACA_REG_IDX_SYND = 6,
- ACA_REG_IDX_DESTAT = 8,
- ACA_REG_IDX_DEADDR = 9,
- ACA_REG_IDX_CTL_MASK = 10,
- ACA_REG_IDX_COUNT = 16,
-};
-
-enum aca_hwip_type {
- ACA_HWIP_TYPE_UNKNOW = -1,
- ACA_HWIP_TYPE_PSP = 0,
- ACA_HWIP_TYPE_UMC,
- ACA_HWIP_TYPE_SMU,
- ACA_HWIP_TYPE_PCS_XGMI,
- ACA_HWIP_TYPE_COUNT,
-};
-
-enum aca_error_type {
- ACA_ERROR_TYPE_INVALID = -1,
- ACA_ERROR_TYPE_UE = 0,
- ACA_ERROR_TYPE_CE,
- ACA_ERROR_TYPE_DEFERRED,
- ACA_ERROR_TYPE_COUNT
-};
-
-enum aca_smu_type {
- ACA_SMU_TYPE_INVALID = -1,
- ACA_SMU_TYPE_UE = 0,
- ACA_SMU_TYPE_CE,
- ACA_SMU_TYPE_COUNT,
-};
-
-struct aca_hwip {
- int hwid;
- int mcatype;
-};
-
-struct aca_bank {
- enum aca_error_type aca_err_type;
- enum aca_smu_type smu_err_type;
- u64 regs[ACA_MAX_REGS_COUNT];
-};
-
-struct aca_bank_node {
- struct aca_bank bank;
- struct list_head node;
-};
-
-struct aca_banks {
- int nr_banks;
- struct list_head list;
-};
-
-struct aca_bank_info {
- int die_id;
- int socket_id;
- int hwid;
- int mcatype;
-};
-
-struct aca_bank_error {
- struct list_head node;
- struct aca_bank_info info;
- u64 count;
-};
-
-struct aca_error {
- struct list_head list;
- struct mutex lock;
- enum aca_error_type type;
- int nr_errors;
-};
-
-struct aca_handle_manager {
- struct list_head list;
- int nr_handles;
-};
-
-struct aca_error_cache {
- struct aca_error errors[ACA_ERROR_TYPE_COUNT];
-};
-
-struct aca_handle {
- struct list_head node;
- enum aca_hwip_type hwip;
- struct amdgpu_device *adev;
- struct aca_handle_manager *mgr;
- struct aca_error_cache error_cache;
- const struct aca_bank_ops *bank_ops;
- struct device_attribute aca_attr;
- char attr_name[64];
- const char *name;
- u32 mask;
- void *data;
-};
-
-struct aca_bank_ops {
- int (*aca_bank_parser)(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, void *data);
- bool (*aca_bank_is_valid)(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type,
- void *data);
-};
-
-struct aca_smu_funcs {
- int max_ue_bank_count;
- int max_ce_bank_count;
- int (*set_debug_mode)(struct amdgpu_device *adev, bool enable);
- int (*get_valid_aca_count)(struct amdgpu_device *adev, enum aca_smu_type type, u32 *count);
- int (*get_valid_aca_bank)(struct amdgpu_device *adev, enum aca_smu_type type, int idx, struct aca_bank *bank);
- int (*parse_error_code)(struct amdgpu_device *adev, struct aca_bank *bank);
-};
-
-struct amdgpu_aca {
- struct aca_handle_manager mgr;
- const struct aca_smu_funcs *smu_funcs;
- atomic_t ue_update_flag;
- bool is_enabled;
-};
-
-struct aca_info {
- enum aca_hwip_type hwip;
- const struct aca_bank_ops *bank_ops;
- u32 mask;
-};
-
-int amdgpu_aca_init(struct amdgpu_device *adev);
-void amdgpu_aca_fini(struct amdgpu_device *adev);
-int amdgpu_aca_reset(struct amdgpu_device *adev);
-void amdgpu_aca_set_smu_funcs(struct amdgpu_device *adev, const struct aca_smu_funcs *smu_funcs);
-bool amdgpu_aca_is_enabled(struct amdgpu_device *adev);
-
-int aca_bank_info_decode(struct aca_bank *bank, struct aca_bank_info *info);
-int aca_bank_check_error_codes(struct amdgpu_device *adev, struct aca_bank *bank, int *err_codes, int size);
-
-int amdgpu_aca_add_handle(struct amdgpu_device *adev, struct aca_handle *handle,
- const char *name, const struct aca_info *aca_info, void *data);
-void amdgpu_aca_remove_handle(struct aca_handle *handle);
-int amdgpu_aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle,
- enum aca_error_type type, struct ras_err_data *err_data,
- struct ras_query_context *qctx);
-int amdgpu_aca_smu_set_debug_mode(struct amdgpu_device *adev, bool en);
-void amdgpu_aca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root);
-int aca_error_cache_log_bank_error(struct aca_handle *handle, struct aca_bank_info *info,
- enum aca_error_type type, u64 count);
-#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
index 516ab9cf88fc..7f5abb03be1b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
@@ -140,13 +140,15 @@ static struct amdgpu_acpi_priv {
* @atif: atif structure
* @function: the ATIF function to execute
* @params: ATIF function params
+ * @min_size: minimum size of the expected output buffer in bytes
*
* Executes the requested ATIF function (all asics).
* Returns a pointer to the acpi output buffer.
*/
static union acpi_object *amdgpu_atif_call(struct amdgpu_atif *atif,
int function,
- struct acpi_buffer *params)
+ struct acpi_buffer *params,
+ size_t min_size)
{
acpi_status status;
union acpi_object *obj;
@@ -189,6 +191,28 @@ static union acpi_object *amdgpu_atif_call(struct amdgpu_atif *atif,
return NULL;
}
+ if (obj->buffer.length < sizeof(u16)) {
+ DRM_DEBUG_DRIVER("ATIF buffer too small to hold size field: %u\n",
+ obj->buffer.length);
+ kfree(obj);
+ return NULL;
+ }
+
+ if (obj->buffer.length < *(u16 *)obj->buffer.pointer) {
+ DRM_DEBUG_DRIVER("ATIF buffer length mismatch: reported %u, actual %u\n",
+ *(u16 *)obj->buffer.pointer,
+ obj->buffer.length);
+ kfree(obj);
+ return NULL;
+ }
+
+ if (*(u16 *)obj->buffer.pointer < min_size) {
+ DRM_DEBUG_DRIVER("ATIF buffer too small: expected %zu, got %u\n",
+ min_size, *(u16 *)obj->buffer.pointer);
+ kfree(obj);
+ return NULL;
+ }
+
return obj;
}
@@ -251,19 +275,14 @@ int amdgpu_atif_verify_interface(struct amdgpu_atif *atif)
size_t size;
int err = 0;
- info = amdgpu_atif_call(atif, ATIF_FUNCTION_VERIFY_INTERFACE, NULL);
+ info = amdgpu_atif_call(atif, ATIF_FUNCTION_VERIFY_INTERFACE, NULL,
+ sizeof(output));
if (!info)
return -EIO;
memset(&output, 0, sizeof(output));
- size = *(u16 *) info->buffer.pointer;
- if (size < 12) {
- DRM_INFO("ATIF buffer is too small: %zu\n", size);
- err = -EINVAL;
- goto out;
- }
- size = min(sizeof(output), size);
+ size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&output, info->buffer.pointer, size);
@@ -273,7 +292,6 @@ int amdgpu_atif_verify_interface(struct amdgpu_atif *atif)
amdgpu_atif_parse_notification(&atif->notifications, output.notification_mask);
amdgpu_atif_parse_functions(&atif->functions, output.function_bits);
-out:
kfree(info);
return err;
}
@@ -299,20 +317,14 @@ int amdgpu_atif_get_notification_params(struct amdgpu_atif *atif)
int err = 0;
info = amdgpu_atif_call(atif, ATIF_FUNCTION_GET_SYSTEM_PARAMETERS,
- NULL);
+ NULL, offsetof(struct atif_system_params, command_code));
if (!info) {
err = -EIO;
goto out;
}
- size = *(u16 *) info->buffer.pointer;
- if (size < 10) {
- err = -EINVAL;
- goto out;
- }
-
memset(&params, 0, sizeof(params));
- size = min(sizeof(params), size);
+ size = min(sizeof(params), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&params, info->buffer.pointer, size);
DRM_DEBUG_DRIVER("SYSTEM_PARAMS: mask = %#x, flags = %#x\n",
@@ -376,20 +388,14 @@ int amdgpu_atif_query_backlight_caps(struct amdgpu_atif *atif)
info = amdgpu_atif_call(atif,
ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS,
- &params);
+ &params, offsetof(struct atif_qbtc_output, data_points));
if (!info) {
err = -EIO;
goto out;
}
- size = *(u16 *) info->buffer.pointer;
- if (size < 10) {
- err = -EINVAL;
- goto out;
- }
-
memset(&characteristics, 0, sizeof(characteristics));
- size = min(sizeof(characteristics), size);
+ size = min(sizeof(characteristics), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&characteristics, info->buffer.pointer, size);
atif->backlight_caps.caps_valid = true;
@@ -427,24 +433,18 @@ static int amdgpu_atif_get_sbios_requests(struct amdgpu_atif *atif,
int count = 0;
info = amdgpu_atif_call(atif, ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS,
- NULL);
+ NULL, sizeof(*req));
if (!info)
return -EIO;
- size = *(u16 *)info->buffer.pointer;
- if (size < 0xd) {
- count = -EINVAL;
- goto out;
- }
memset(req, 0, sizeof(*req));
- size = min(sizeof(*req), size);
+ size = min(sizeof(*req), (size_t)*(u16 *)info->buffer.pointer);
memcpy(req, info->buffer.pointer, size);
DRM_DEBUG_DRIVER("SBIOS pending requests: %#x\n", req->pending);
count = hweight32(req->pending);
-out:
kfree(info);
return count;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
index da325863ad76..c693c508df1a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
@@ -957,3 +957,17 @@ int amdgpu_amdkfd_config_sq_perfmon(struct amdgpu_device *adev, uint32_t xcp_id,
return r;
}
+
+/* Reset an MES queue */
+int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev,
+ uint32_t node_id,
+ int queue_type,
+ int pipe, int queue,
+ unsigned int db)
+{
+ if (!adev->kfd.init_complete)
+ return 0;
+
+ return kgd2kfd_reset_mes_queue(adev->kfd.dev, node_id, queue_type,
+ pipe, queue, db);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
index e443a7277299..338412a750ed 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
@@ -210,6 +210,7 @@ int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni,
int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo,
uint32_t domain,
struct dma_fence *fence);
+int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms);
#else
static inline
bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm)
@@ -241,6 +242,11 @@ int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo,
{
return 0;
}
+static inline
+int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms)
+{
+ return -EOPNOTSUPP;
+}
#endif
/* Shared API */
int amdgpu_amdkfd_alloc_kernel_mem(struct amdgpu_device *adev, size_t size,
@@ -275,7 +281,11 @@ int amdgpu_amdkfd_stop_sched(struct amdgpu_device *adev, uint32_t node_id);
int amdgpu_amdkfd_config_sq_perfmon(struct amdgpu_device *adev, uint32_t xcp_id,
bool core_override_enable, bool reg_override_enable, bool perfmon_override_enable);
bool amdgpu_amdkfd_compute_active(struct amdgpu_device *adev, uint32_t node_id);
-
+int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev,
+ uint32_t node_id,
+ int queue_type,
+ int pipe, int queue,
+ unsigned int db);
/* Read user wptr from a specified user address space with page fault
* disabled. The memory must be pinned and mapped to the hardware when
@@ -326,9 +336,9 @@ int amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu(
int amdgpu_amdkfd_gpuvm_dmaunmap_mem(struct kgd_mem *mem, void *drm_priv);
int amdgpu_amdkfd_gpuvm_sync_memory(
struct amdgpu_device *adev, struct kgd_mem *mem, bool intr);
-int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
- void **kptr, uint64_t *size);
-void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem);
+int amdgpu_amdkfd_gpuvm_map_bo_to_kernel(struct kgd_mem *mem, void **kptr,
+ u64 *size, u32 domain);
+void amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(struct kgd_mem *mem);
int amdgpu_amdkfd_map_gtt_bo_to_gart(struct amdgpu_bo *bo, struct amdgpu_bo **bo_gart);
@@ -446,6 +456,9 @@ bool kgd2kfd_vmfault_fast_path(struct amdgpu_device *adev, struct amdgpu_iv_entr
bool retry_fault);
void kgd2kfd_lock_kfd(void);
void kgd2kfd_teardown_processes(struct amdgpu_device *adev);
+int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id,
+ int queue_type, int pipe, int queue,
+ unsigned int db);
#else
static inline int kgd2kfd_init(void)
@@ -576,5 +589,12 @@ static inline void kgd2kfd_teardown_processes(struct amdgpu_device *adev)
{
}
+static inline int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id,
+ int queue_type, int pipe, int queue,
+ unsigned int db)
+{
+ return 0;
+}
+
#endif
#endif /* AMDGPU_AMDKFD_H_INCLUDED */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c
index 6ed399163547..bc079b95fc52 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c
@@ -530,6 +530,66 @@ static uint32_t kgd_v9_4_3_ptl_ctrl(struct amdgpu_device *adev,
ptl_state, fmt1, fmt2);
}
+static int kgd_gfx_v9_4_3_hqd_sdma_get_counter(struct amdgpu_device *adev,
+ void *mqd, uint32_t num_sdma_queues_per_eng,
+ uint64_t *val)
+{
+ struct v9_sdma_mqd *m = get_sdma_mqd(mqd);
+ uint32_t sdma_rlc_reg_offset = 0;
+ uint32_t sdma_rlc_rb_cntl;
+ uint32_t engine_id, queue_id;
+ uint32_t engines = adev->sdma.num_instances;
+ uint32_t sdma_rlcx_rb_base, sdma_rlcx_rb_base_hi;
+ bool found = false;
+
+ if (!m)
+ return -EINVAL;
+
+ if (((amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 3) ||
+ amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4)) &&
+ adev->gfx.mec_fw_version < 194) ||
+ (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0) &&
+ adev->gfx.mec_fw_version < 44)) {
+ pr_warn_once("MEC FW doesn't support SDMA counter!\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* SDMA doesn't support over-subscription, there must be
+ * a HQD associated with a MQD, so found must be true in
+ * the finding loop.
+ */
+ for (engine_id = 0; engine_id < engines && !found; engine_id++) {
+ for (queue_id = 0; queue_id < num_sdma_queues_per_eng; queue_id++) {
+ sdma_rlc_reg_offset = get_sdma_rlc_reg_offset(adev,
+ engine_id, queue_id);
+ sdma_rlcx_rb_base = RREG32(sdma_rlc_reg_offset +
+ regSDMA_RLC0_RB_BASE);
+ sdma_rlcx_rb_base_hi = RREG32(sdma_rlc_reg_offset +
+ regSDMA_RLC0_RB_BASE_HI);
+
+ if (m->sdmax_rlcx_rb_base == sdma_rlcx_rb_base &&
+ m->sdmax_rlcx_rb_base_hi == sdma_rlcx_rb_base_hi) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ sdma_rlc_rb_cntl = RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_RB_CNTL);
+
+ /* Read sdma activity counter from utilization register
+ * if hw queue is enabled, otherwise read from MQD.
+ */
+ if (sdma_rlc_rb_cntl & SDMA_RLC0_RB_CNTL__RB_ENABLE_MASK)
+ *val = (uint64_t)RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_UTILIZATION_HI) << 32 |
+ RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_UTILIZATION_LO);
+ else
+ *val = (uint64_t)m->sdmax_rlcx_utilization_hi << 32 |
+ m->sdmax_rlcx_utilization_lo;
+
+ return 0;
+}
+
const struct kfd2kgd_calls gc_9_4_3_kfd2kgd = {
.program_sh_mem_settings = kgd_gfx_v9_program_sh_mem_settings,
.set_pasid_vmid_mapping = kgd_gfx_v9_4_3_set_pasid_vmid_mapping,
@@ -566,5 +626,6 @@ const struct kfd2kgd_calls gc_9_4_3_kfd2kgd = {
.hqd_get_pq_addr = kgd_gfx_v9_hqd_get_pq_addr,
.hqd_reset = kgd_gfx_v9_hqd_reset,
.hqd_sdma_get_doorbell = kgd_gfx_v9_4_3_hqd_sdma_get_doorbell,
- .ptl_ctrl = kgd_v9_4_3_ptl_ctrl
+ .ptl_ctrl = kgd_v9_4_3_ptl_ctrl,
+ .hqd_sdma_get_counter = kgd_gfx_v9_4_3_hqd_sdma_get_counter
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index 35fe2c974699..20831dbebc31 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -2271,11 +2271,14 @@ err_reserve_bo_failed:
return ret;
}
-/** amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel() - Map a GTT BO for kernel CPU access
+/** amdgpu_amdkfd_gpuvm_map_bo_to_kernel() - Map GTT or VRAM BO for kernel CPU access
*
* @mem: Buffer object to be mapped for CPU access
* @kptr[out]: pointer in kernel CPU address space
* @size[out]: size of the buffer
+ * @domain[IN]: domain for pinning (AMDGPU_GEM_DOMAIN_GTT, AMDGPU_GEM_DOMAIN_VRAM,
+ * or their combination to let the driver choose). CPU visibility is
+ * automatically enforced by amdgpu_bo_pin()
*
* Pins the BO and maps it for kernel CPU access. The eviction fence is removed
* from the BO, since pinned BOs cannot be evicted. The bo must remain on the
@@ -2284,8 +2287,8 @@ err_reserve_bo_failed:
*
* Return: 0 on success, error code on failure
*/
-int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
- void **kptr, uint64_t *size)
+int amdgpu_amdkfd_gpuvm_map_bo_to_kernel(struct kgd_mem *mem, void **kptr,
+ u64 *size, u32 domain)
{
int ret;
struct amdgpu_bo *bo = mem->bo;
@@ -2295,6 +2298,11 @@ int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
return -EINVAL;
}
+ if (!(domain & (AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM))) {
+ pr_debug("Invalid domain 0x%x for kernel mapping\n", domain);
+ return -EINVAL;
+ }
+
mutex_lock(&mem->process_info->lock);
ret = amdgpu_bo_reserve(bo, true);
@@ -2303,7 +2311,7 @@ int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem,
goto bo_reserve_failed;
}
- ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT);
+ ret = amdgpu_bo_pin(bo, domain);
if (ret) {
pr_err("Failed to pin bo. ret %d\n", ret);
goto pin_failed;
@@ -2336,7 +2344,7 @@ bo_reserve_failed:
return ret;
}
-/** amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel() - Unmap a GTT BO for kernel CPU access
+/** amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel() - Unmap GTT or VRAM BO for kernel CPU access
*
* @mem: Buffer object to be unmapped for CPU access
*
@@ -2344,7 +2352,7 @@ bo_reserve_failed:
* eviction fence, so this function should only be used for cleanup before the
* BO is destroyed.
*/
-void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem)
+void amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(struct kgd_mem *mem)
{
struct amdgpu_bo *bo = mem->bo;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c
index acd22bff1882..27c0dc8f6137 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c
@@ -1923,7 +1923,7 @@ int amdgpu_atombios_init(struct amdgpu_device *adev)
atom_card_info->pll_read = cail_pll_read;
atom_card_info->pll_write = cail_pll_write;
- adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios);
+ adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios, adev->bios_size);
if (!adev->mode_info.atom_context) {
amdgpu_atombios_fini(adev);
return -ENOMEM;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
index 3893e6fc2f03..e2a4644896ca 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
@@ -89,6 +89,15 @@ bool amdgpu_is_atpx_hybrid(void)
return amdgpu_atpx_priv.atpx.is_hybrid;
}
+static bool amdgpu_atpx_buffer_validate(const union acpi_object *obj,
+ size_t min_size)
+{
+ return obj && obj->type == ACPI_TYPE_BUFFER &&
+ obj->buffer.length >= sizeof(u16) &&
+ obj->buffer.length >= *(u16 *)obj->buffer.pointer &&
+ *(u16 *)obj->buffer.pointer >= min_size;
+}
+
/**
* amdgpu_atpx_call - call an ATPX method
*
@@ -179,15 +188,15 @@ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
if (!info)
return -EIO;
- memset(&output, 0, sizeof(output));
-
- size = *(u16 *) info->buffer.pointer;
- if (size < 10) {
- pr_err("ATPX buffer is too small: %zu\n", size);
+ if (!amdgpu_atpx_buffer_validate(info, sizeof(output))) {
+ pr_err("Invalid ATPX GET_PX_PARAMETERS response\n");
kfree(info);
return -EINVAL;
}
- size = min(sizeof(output), size);
+
+ memset(&output, 0, sizeof(output));
+
+ size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&output, info->buffer.pointer, size);
@@ -258,15 +267,15 @@ static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx)
if (!info)
return -EIO;
- memset(&output, 0, sizeof(output));
-
- size = *(u16 *) info->buffer.pointer;
- if (size < 8) {
- pr_err("ATPX buffer is too small: %zu\n", size);
+ if (!amdgpu_atpx_buffer_validate(info, sizeof(output))) {
+ pr_err("Invalid ATPX VERIFY_INTERFACE response\n");
err = -EINVAL;
goto out;
}
- size = min(sizeof(output), size);
+
+ memset(&output, 0, sizeof(output));
+
+ size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer);
memcpy(&output, info->buffer.pointer, size);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
index aa039e148a5e..3ebdd792feec 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
@@ -296,8 +296,14 @@ static int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
}
obj = (union acpi_object *)buffer.pointer;
- memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length);
- len = obj->buffer.length;
+ if (!obj || obj->type != ACPI_TYPE_BUFFER) {
+ DRM_ERROR("ATRM returned an invalid object\n");
+ kfree(buffer.pointer);
+ return -EINVAL;
+ }
+
+ len = min_t(size_t, obj->buffer.length, len);
+ memcpy(bios+offset, obj->buffer.pointer, len);
kfree(buffer.pointer);
return len;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
index 43864df8af04..ce1d08f112a8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c
@@ -38,14 +38,6 @@
#define AMDGPU_BO_LIST_NUM_BUCKETS (AMDGPU_BO_LIST_MAX_PRIORITY + 1)
#define AMDGPU_BO_LIST_MAX_ENTRIES (128 * 1024)
-static void amdgpu_bo_list_free_rcu(struct rcu_head *rcu)
-{
- struct amdgpu_bo_list *list = container_of(rcu, struct amdgpu_bo_list,
- rhead);
- mutex_destroy(&list->bo_list_mutex);
- kvfree(list);
-}
-
static void amdgpu_bo_list_free(struct kref *ref)
{
struct amdgpu_bo_list *list = container_of(ref, struct amdgpu_bo_list,
@@ -54,7 +46,8 @@ static void amdgpu_bo_list_free(struct kref *ref)
amdgpu_bo_list_for_each_entry(e, list)
amdgpu_bo_unref(&e->bo);
- call_rcu(&list->rhead, amdgpu_bo_list_free_rcu);
+
+ kvfree(list);
}
static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b)
@@ -66,9 +59,9 @@ static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b)
return (int)a->priority - (int)b->priority;
}
-int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
- struct drm_amdgpu_bo_list_entry *info,
- size_t num_entries, struct amdgpu_bo_list **result)
+struct amdgpu_bo_list *
+amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
+ struct drm_amdgpu_bo_list_entry *info, size_t num_entries)
{
unsigned last_entry = 0, first_userptr = num_entries;
struct amdgpu_bo_list_entry *array;
@@ -79,7 +72,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
list = kvzalloc_flex(*list, entries, num_entries);
if (!list)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
kref_init(&list->refcount);
@@ -134,9 +127,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp,
trace_amdgpu_cs_bo_status(list->num_entries, total_size);
- mutex_init(&list->bo_list_mutex);
- *result = list;
- return 0;
+ return list;
error_free:
for (i = 0; i < last_entry; ++i)
@@ -144,150 +135,125 @@ error_free:
for (i = first_userptr; i < num_entries; ++i)
amdgpu_bo_unref(&array[i].bo);
kvfree(list);
- return r;
+ return ERR_PTR(r);
}
-static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id)
+struct amdgpu_bo_list *amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, u32 id)
{
struct amdgpu_bo_list *list;
- mutex_lock(&fpriv->bo_list_lock);
- list = idr_remove(&fpriv->bo_list_handles, id);
- mutex_unlock(&fpriv->bo_list_lock);
+ xa_lock(&fpriv->bo_list_handles);
+ list = xa_load(&fpriv->bo_list_handles, id);
if (list)
- kref_put(&list->refcount, amdgpu_bo_list_free);
-}
-
-int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id,
- struct amdgpu_bo_list **result)
-{
- rcu_read_lock();
- *result = idr_find(&fpriv->bo_list_handles, id);
-
- if (*result && kref_get_unless_zero(&(*result)->refcount)) {
- rcu_read_unlock();
- return 0;
- }
+ kref_get(&list->refcount);
+ else
+ list = ERR_PTR(-ENOENT);
+ xa_unlock(&fpriv->bo_list_handles);
- rcu_read_unlock();
- *result = NULL;
- return -ENOENT;
+ return list;
}
void amdgpu_bo_list_put(struct amdgpu_bo_list *list)
{
- kref_put(&list->refcount, amdgpu_bo_list_free);
+ if (list)
+ kref_put(&list->refcount, amdgpu_bo_list_free);
}
-int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in,
- struct drm_amdgpu_bo_list_entry **info_param)
+struct drm_amdgpu_bo_list_entry *
+amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in)
{
- const uint32_t info_size = sizeof(struct drm_amdgpu_bo_list_entry);
const void __user *uptr = u64_to_user_ptr(in->bo_info_ptr);
- const uint32_t bo_info_size = in->bo_info_size;
const uint32_t bo_number = in->bo_number;
- struct drm_amdgpu_bo_list_entry *info;
if (bo_number > AMDGPU_BO_LIST_MAX_ENTRIES)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- /* copy the handle array from userspace to a kernel buffer */
- if (likely(info_size == bo_info_size)) {
- info = vmemdup_array_user(uptr, bo_number, info_size);
- if (IS_ERR(info))
- return PTR_ERR(info);
- } else {
- const uint32_t bytes = min(bo_info_size, info_size);
- unsigned i;
-
- info = kvmalloc_array(bo_number, info_size, GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- memset(info, 0, bo_number * info_size);
- for (i = 0; i < bo_number; ++i, uptr += bo_info_size) {
- if (copy_from_user(&info[i], uptr, bytes)) {
- kvfree(info);
- return -EFAULT;
- }
- }
- }
+ if (in->bo_info_size != sizeof(struct drm_amdgpu_bo_list_entry))
+ return ERR_PTR(-EINVAL);
- *info_param = info;
- return 0;
+ return vmemdup_array_user(uptr, bo_number,
+ sizeof(struct drm_amdgpu_bo_list_entry));
}
int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp)
{
- struct amdgpu_device *adev = drm_to_adev(dev);
struct amdgpu_fpriv *fpriv = filp->driver_priv;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct amdgpu_bo_list *list, *prev, *curr;
union drm_amdgpu_bo_list *args = data;
uint32_t handle = args->in.list_handle;
- struct drm_amdgpu_bo_list_entry *info = NULL;
- struct amdgpu_bo_list *list, *old;
+ struct drm_amdgpu_bo_list_entry *info;
int r;
- r = amdgpu_bo_create_list_entry_array(&args->in, &info);
- if (r)
- return r;
-
switch (args->in.operation) {
case AMDGPU_BO_LIST_OP_CREATE:
- r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number,
- &list);
- if (r)
- goto error_free;
+ case AMDGPU_BO_LIST_OP_UPDATE:
+ info = amdgpu_bo_create_list_entry_array(&args->in);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
- mutex_lock(&fpriv->bo_list_lock);
- r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL);
- mutex_unlock(&fpriv->bo_list_lock);
- if (r < 0) {
- goto error_put_list;
- }
+ list = amdgpu_bo_list_create(adev, filp, info,
+ args->in.bo_number);
+ kvfree(info);
+ if (IS_ERR(list))
+ return PTR_ERR(list);
- handle = r;
break;
case AMDGPU_BO_LIST_OP_DESTROY:
- amdgpu_bo_list_destroy(fpriv, handle);
+ list = xa_erase(&fpriv->bo_list_handles, handle);
+ amdgpu_bo_list_put(list);
handle = 0;
+
break;
- case AMDGPU_BO_LIST_OP_UPDATE:
- r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number,
- &list);
+ default:
+ return -EINVAL;
+ };
+
+ switch (args->in.operation) {
+ case AMDGPU_BO_LIST_OP_CREATE:
+ r = xa_alloc(&fpriv->bo_list_handles, &handle, list,
+ xa_limit_32b, GFP_KERNEL);
if (r)
- goto error_free;
+ goto error_put_list;
+
+ break;
- mutex_lock(&fpriv->bo_list_lock);
- old = idr_replace(&fpriv->bo_list_handles, list, handle);
- mutex_unlock(&fpriv->bo_list_lock);
+ case AMDGPU_BO_LIST_OP_UPDATE:
+ curr = xa_load(&fpriv->bo_list_handles, handle);
+ if (!curr) {
+ r = -ENOENT;
+ goto error_put_list;
+ }
- if (IS_ERR(old)) {
- r = PTR_ERR(old);
+ prev = xa_cmpxchg(&fpriv->bo_list_handles, handle, curr, list,
+ GFP_KERNEL);
+ if (xa_is_err(prev)) {
+ r = xa_err(prev);
+ goto error_put_list;
+ } else if (prev != curr) {
+ r = -ENOENT;
goto error_put_list;
}
- amdgpu_bo_list_put(old);
+ amdgpu_bo_list_put(curr);
break;
+ case AMDGPU_BO_LIST_OP_DESTROY:
default:
- r = -EINVAL;
- goto error_free;
+ /* Handled above. */
+ break;
}
memset(args, 0, sizeof(*args));
args->out.list_handle = handle;
- kvfree(info);
return 0;
error_put_list:
amdgpu_bo_list_put(list);
-
-error_free:
- kvfree(info);
return r;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
index 2b5e7c46a39d..bde912150824 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
@@ -43,7 +43,6 @@ struct amdgpu_bo_list_entry {
};
struct amdgpu_bo_list {
- struct rcu_head rhead;
struct kref refcount;
struct amdgpu_bo *gds_obj;
struct amdgpu_bo *gws_obj;
@@ -51,24 +50,19 @@ struct amdgpu_bo_list {
unsigned first_userptr;
unsigned num_entries;
- /* Protect access during command submission.
- */
- struct mutex bo_list_mutex;
-
struct amdgpu_bo_list_entry entries[] __counted_by(num_entries);
};
-int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id,
- struct amdgpu_bo_list **result);
+struct amdgpu_bo_list *amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, u32 id);
void amdgpu_bo_list_put(struct amdgpu_bo_list *list);
-int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in,
- struct drm_amdgpu_bo_list_entry **info_param);
+struct drm_amdgpu_bo_list_entry *
+amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in);
-int amdgpu_bo_list_create(struct amdgpu_device *adev,
- struct drm_file *filp,
- struct drm_amdgpu_bo_list_entry *info,
- size_t num_entries,
- struct amdgpu_bo_list **list);
+struct amdgpu_bo_list *
+amdgpu_bo_list_create(struct amdgpu_device *adev,
+ struct drm_file *filp,
+ struct drm_amdgpu_bo_list_entry *info,
+ size_t num_entries);
#define amdgpu_bo_list_for_each_entry(e, list) \
for (e = list->entries; \
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c
index d5e59c24d907..6fb129025761 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c
@@ -289,40 +289,6 @@ struct cper_hdr *amdgpu_cper_alloc_entry(struct amdgpu_device *adev,
return hdr;
}
-int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev,
- struct aca_bank *bank)
-{
- struct cper_hdr *fatal = NULL;
- struct cper_sec_crashdump_reg_data reg_data = { 0 };
- struct amdgpu_ring *ring = &adev->cper.ring_buf;
- int ret;
-
- fatal = amdgpu_cper_alloc_entry(adev, AMDGPU_CPER_TYPE_FATAL, 1);
- if (!fatal) {
- dev_err(adev->dev, "fail to alloc cper entry for ue record\n");
- return -ENOMEM;
- }
-
- reg_data.status_lo = lower_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
- reg_data.status_hi = upper_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
- reg_data.addr_lo = lower_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
- reg_data.addr_hi = upper_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
- reg_data.ipid_lo = lower_32_bits(bank->regs[ACA_REG_IDX_IPID]);
- reg_data.ipid_hi = upper_32_bits(bank->regs[ACA_REG_IDX_IPID]);
- reg_data.synd_lo = lower_32_bits(bank->regs[ACA_REG_IDX_SYND]);
- reg_data.synd_hi = upper_32_bits(bank->regs[ACA_REG_IDX_SYND]);
-
- amdgpu_cper_entry_fill_hdr(adev, fatal, AMDGPU_CPER_TYPE_FATAL, CPER_SEV_FATAL_UNCORRECTED);
- ret = amdgpu_cper_entry_fill_fatal_section(adev, fatal, 0, reg_data);
- if (ret)
- return ret;
-
- amdgpu_cper_ring_write(ring, fatal, fatal->record_length);
- kfree(fatal);
-
- return 0;
-}
-
int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev)
{
struct cper_hdr *bp_threshold = NULL;
@@ -348,83 +314,6 @@ int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev)
return 0;
}
-static enum cper_error_severity amdgpu_aca_err_type_to_cper_sev(struct amdgpu_device *adev,
- enum aca_error_type aca_err_type)
-{
- switch (aca_err_type) {
- case ACA_ERROR_TYPE_UE:
- return CPER_SEV_FATAL_UNCORRECTED;
- case ACA_ERROR_TYPE_CE:
- return CPER_SEV_NON_FATAL_CORRECTED;
- case ACA_ERROR_TYPE_DEFERRED:
- return CPER_SEV_NON_FATAL_UNCORRECTED;
- default:
- dev_err(adev->dev, "Unknown ACA error type!\n");
- return CPER_SEV_FATAL_UNCORRECTED;
- }
-}
-
-int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev,
- struct aca_banks *banks,
- uint16_t bank_count)
-{
- struct cper_hdr *corrected = NULL;
- enum cper_error_severity sev = CPER_SEV_NON_FATAL_CORRECTED;
- struct amdgpu_ring *ring = &adev->cper.ring_buf;
- uint32_t reg_data[CPER_ACA_REG_COUNT] = { 0 };
- struct aca_bank_node *node;
- struct aca_bank *bank;
- uint32_t i = 0;
- int ret;
-
- corrected = amdgpu_cper_alloc_entry(adev, AMDGPU_CPER_TYPE_RUNTIME, bank_count);
- if (!corrected) {
- dev_err(adev->dev, "fail to allocate cper entry for ce records\n");
- return -ENOMEM;
- }
-
- /* Raise severity if any DE is detected in the ACA bank list */
- list_for_each_entry(node, &banks->list, node) {
- bank = &node->bank;
- if (bank->aca_err_type == ACA_ERROR_TYPE_DEFERRED) {
- sev = CPER_SEV_NON_FATAL_UNCORRECTED;
- break;
- }
- }
-
- amdgpu_cper_entry_fill_hdr(adev, corrected, AMDGPU_CPER_TYPE_RUNTIME, sev);
-
- /* Combine CE and DE in cper record */
- list_for_each_entry(node, &banks->list, node) {
- bank = &node->bank;
- reg_data[CPER_ACA_REG_CTL_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_CTL]);
- reg_data[CPER_ACA_REG_CTL_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_CTL]);
- reg_data[CPER_ACA_REG_STATUS_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
- reg_data[CPER_ACA_REG_STATUS_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_STATUS]);
- reg_data[CPER_ACA_REG_ADDR_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
- reg_data[CPER_ACA_REG_ADDR_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_ADDR]);
- reg_data[CPER_ACA_REG_MISC0_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_MISC0]);
- reg_data[CPER_ACA_REG_MISC0_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_MISC0]);
- reg_data[CPER_ACA_REG_CONFIG_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_CONFIG]);
- reg_data[CPER_ACA_REG_CONFIG_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_CONFIG]);
- reg_data[CPER_ACA_REG_IPID_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_IPID]);
- reg_data[CPER_ACA_REG_IPID_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_IPID]);
- reg_data[CPER_ACA_REG_SYND_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_SYND]);
- reg_data[CPER_ACA_REG_SYND_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_SYND]);
-
- ret = amdgpu_cper_entry_fill_runtime_section(adev, corrected, i++,
- amdgpu_aca_err_type_to_cper_sev(adev, bank->aca_err_type),
- reg_data, CPER_ACA_REG_COUNT);
- if (ret)
- return ret;
- }
-
- amdgpu_cper_ring_write(ring, corrected, corrected->record_length);
- kfree(corrected);
-
- return 0;
-}
-
static bool amdgpu_cper_is_hdr(struct amdgpu_ring *ring, u64 pos)
{
char signature[CPER_SIGNATURE_SZ];
@@ -592,8 +481,7 @@ int amdgpu_cper_init(struct amdgpu_device *adev)
if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_cper_en(adev))
return 0;
- else if (!amdgpu_sriov_vf(adev) && !amdgpu_uniras_enabled(adev) &&
- !amdgpu_aca_is_enabled(adev))
+ else if (!amdgpu_sriov_vf(adev) && !amdgpu_uniras_enabled(adev))
return 0;
r = amdgpu_cper_ring_init(adev);
@@ -612,7 +500,7 @@ int amdgpu_cper_init(struct amdgpu_device *adev)
int amdgpu_cper_fini(struct amdgpu_device *adev)
{
- if (!amdgpu_aca_is_enabled(adev) && !amdgpu_sriov_ras_cper_en(adev))
+ if (amdgpu_sriov_vf(adev))
return 0;
adev->cper.enabled = false;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h
index 353421807387..d12c98077d9d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h
@@ -26,7 +26,6 @@
#define __AMDGPU_CPER_H__
#include "amd_cper.h"
-#include "amdgpu_aca.h"
#define CPER_MAX_ALLOWED_COUNT 0x1000
#define CPER_MAX_RING_SIZE 0X100000
@@ -88,13 +87,6 @@ int amdgpu_cper_entry_fill_bad_page_threshold_section(struct amdgpu_device *adev
struct cper_hdr *amdgpu_cper_alloc_entry(struct amdgpu_device *adev,
enum amdgpu_cper_type type,
uint16_t section_count);
-/* UE must be encoded into separated cper entries, 1 UE 1 cper */
-int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev,
- struct aca_bank *bank);
-/* CEs and DEs are combined into 1 cper entry */
-int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev,
- struct aca_banks *banks,
- uint16_t bank_count);
/* Bad page threshold is encoded into separated cper entry */
int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev);
void amdgpu_cper_ring_write(struct amdgpu_ring *ring,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index e714cee2997a..d777375e5350 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -140,24 +140,19 @@ static int amdgpu_cs_p1_bo_handles(struct amdgpu_cs_parser *p,
struct drm_amdgpu_bo_list_in *data)
{
struct drm_amdgpu_bo_list_entry *info;
- int r;
-
- r = amdgpu_bo_create_list_entry_array(data, &info);
- if (r)
- return r;
-
- r = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number,
- &p->bo_list);
- if (r)
- goto error_free;
+ struct amdgpu_bo_list *list;
- kvfree(info);
- return 0;
+ info = amdgpu_bo_create_list_entry_array(data);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
-error_free:
+ list = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number);
kvfree(info);
+ if (IS_ERR(list))
+ return PTR_ERR(list);
- return r;
+ p->bo_list = list;
+ return 0;
}
/* Copy the data from userspace and go over it the first time */
@@ -846,6 +841,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
{
struct amdgpu_fpriv *fpriv = p->filp->driver_priv;
struct ttm_operation_ctx ctx = { true, false };
+ struct amdgpu_bo_list *list = NULL;
struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_bo_list_entry *e;
struct drm_gem_object *obj;
@@ -857,25 +853,24 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
if (p->bo_list)
return -EINVAL;
- r = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle,
- &p->bo_list);
- if (r)
- return r;
+ list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle);
} else if (!p->bo_list) {
/* Create a empty bo_list when no handle is provided */
- r = amdgpu_bo_list_create(p->adev, p->filp, NULL, 0,
- &p->bo_list);
- if (r)
- return r;
+ list = amdgpu_bo_list_create(p->adev, p->filp, NULL, 0);
}
- mutex_lock(&p->bo_list->bo_list_mutex);
+ if (IS_ERR(list))
+ return PTR_ERR(list);
+ else if (list)
+ p->bo_list = list;
+ else
+ list = p->bo_list;
/* Get userptr backing pages. If pages are updated after registered
* in amdgpu_gem_userptr_ioctl(), amdgpu_cs_list_validate() will do
* amdgpu_ttm_backend_bind() to flush and invalidate new pages
*/
- amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
+ amdgpu_bo_list_for_each_userptr_entry(e, list) {
bool userpage_invalidated = false;
struct amdgpu_bo *bo = e->bo;
@@ -905,7 +900,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
if (unlikely(r))
goto out_free_user_pages;
- amdgpu_bo_list_for_each_entry(e, p->bo_list) {
+ amdgpu_bo_list_for_each_entry(e, list) {
r = drm_exec_prepare_obj(&p->exec, &e->bo->tbo.base,
TTM_NUM_MOVE_FENCES + p->gang_size);
drm_exec_retry_on_contention(&p->exec);
@@ -924,7 +919,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
}
}
- amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
+ amdgpu_bo_list_for_each_userptr_entry(e, list) {
struct mm_struct *usermm;
usermm = amdgpu_ttm_tt_get_usermm(e->bo->tbo.ttm);
@@ -977,17 +972,15 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
p->bytes_moved_vis);
for (i = 0; i < p->gang_size; ++i)
- amdgpu_job_set_resources(p->jobs[i], p->bo_list->gds_obj,
- p->bo_list->gws_obj,
- p->bo_list->oa_obj);
+ amdgpu_job_set_resources(p->jobs[i], list->gds_obj,
+ list->gws_obj, list->oa_obj);
return 0;
out_free_user_pages:
- amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
+ amdgpu_bo_list_for_each_userptr_entry(e, list) {
amdgpu_hmm_range_free(e->range);
e->range = NULL;
}
- mutex_unlock(&p->bo_list->bo_list_mutex);
return r;
}
@@ -1371,7 +1364,6 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm);
mutex_unlock(&p->adev->notifier_lock);
- mutex_unlock(&p->bo_list->bo_list_mutex);
return 0;
}
@@ -1443,28 +1435,25 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
r = amdgpu_cs_patch_jobs(&parser);
if (r)
- goto error_backoff;
+ goto error_fini;
r = amdgpu_cs_vm_handling(&parser);
if (r)
- goto error_backoff;
+ goto error_fini;
r = amdgpu_cs_sync_rings(&parser);
if (r)
- goto error_backoff;
+ goto error_fini;
trace_amdgpu_cs_ibs(&parser);
r = amdgpu_cs_submit(&parser, data);
if (r)
- goto error_backoff;
+ goto error_fini;
amdgpu_cs_parser_fini(&parser);
return 0;
-error_backoff:
- mutex_unlock(&parser.bo_list->bo_list_mutex);
-
error_fini:
amdgpu_cs_parser_fini(&parser);
return r;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
index ce35b415093d..d53259a5b82f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
@@ -283,6 +283,8 @@ static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev,
if (!entity)
return res;
+ drm_sched_entity_destroy(&entity->entity);
+
for (i = 0; i < amdgpu_sched_jobs; ++i) {
res = ktime_add(res, amdgpu_ctx_fence_time(entity->fences[i]));
dma_fence_put(entity->fences[i]);
@@ -294,32 +296,20 @@ static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev,
return res;
}
-static int amdgpu_ctx_get_stable_pstate(struct amdgpu_ctx *ctx,
- u32 *stable_pstate)
+static u32 amdgpu_get_stable_pstate(struct amdgpu_device *adev)
{
- struct amdgpu_device *adev = ctx->mgr->adev;
- enum amd_dpm_forced_level current_level;
-
- current_level = amdgpu_dpm_get_performance_level(adev);
-
- switch (current_level) {
+ switch (amdgpu_dpm_get_performance_level(adev)) {
case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
- *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_STANDARD;
- break;
+ return AMDGPU_CTX_STABLE_PSTATE_STANDARD;
case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
- *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK;
- break;
+ return AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK;
case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
- *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK;
- break;
+ return AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK;
case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
- *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_PEAK;
- break;
+ return AMDGPU_CTX_STABLE_PSTATE_PEAK;
default:
- *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE;
- break;
+ return AMDGPU_CTX_STABLE_PSTATE_NONE;
}
- return 0;
}
static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority,
@@ -383,9 +373,9 @@ static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
if (current_ctx && current_ctx != ctx)
return -EBUSY;
- r = amdgpu_ctx_get_stable_pstate(ctx, &current_stable_pstate);
- if (r || current_stable_pstate == stable_pstate)
- return r;
+ current_stable_pstate = amdgpu_get_stable_pstate(adev);
+ if (current_stable_pstate == stable_pstate)
+ return 0;
r = amdgpu_dpm_force_performance_level(adev, level);
if (r)
@@ -416,7 +406,7 @@ static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx,
return r;
}
-static void amdgpu_ctx_fini(struct kref *ref)
+void amdgpu_ctx_fini(struct kref *ref)
{
struct amdgpu_ctx *ctx = container_of(ref, struct amdgpu_ctx, refcount);
struct amdgpu_ctx_mgr *mgr = ctx->mgr;
@@ -504,53 +494,26 @@ static int amdgpu_ctx_alloc(struct amdgpu_device *adev,
if (!ctx)
return -ENOMEM;
- mutex_lock(&mgr->lock);
- r = idr_alloc(&mgr->ctx_handles, ctx, 1, AMDGPU_VM_MAX_NUM_CTX, GFP_KERNEL);
- if (r < 0) {
- mutex_unlock(&mgr->lock);
- kfree(ctx);
- return r;
- }
-
- *id = (uint32_t)r;
r = amdgpu_ctx_init(mgr, priority, filp, ctx);
if (r) {
- idr_remove(&mgr->ctx_handles, *id);
- *id = 0;
kfree(ctx);
+ return r;
}
- mutex_unlock(&mgr->lock);
- return r;
-}
-
-static void amdgpu_ctx_do_release(struct kref *ref)
-{
- struct amdgpu_ctx *ctx;
- u32 i, j;
- ctx = container_of(ref, struct amdgpu_ctx, refcount);
- for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
- for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
- if (!ctx->entities[i][j])
- continue;
-
- drm_sched_entity_destroy(&ctx->entities[i][j]->entity);
- }
- }
+ r = xa_alloc(&mgr->ctx_handles, id, ctx, xa_limit_32b, GFP_KERNEL);
+ if (r)
+ amdgpu_ctx_put(ctx);
- amdgpu_ctx_fini(ref);
+ return r;
}
static int amdgpu_ctx_free(struct amdgpu_fpriv *fpriv, uint32_t id)
{
- struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
struct amdgpu_ctx *ctx;
- mutex_lock(&mgr->lock);
- ctx = idr_remove(&mgr->ctx_handles, id);
- if (ctx)
- kref_put(&ctx->refcount, amdgpu_ctx_do_release);
- mutex_unlock(&mgr->lock);
+ ctx = xa_erase(&fpriv->ctx_mgr.ctx_handles, id);
+ amdgpu_ctx_put(ctx);
+
return ctx ? 0 : -EINVAL;
}
@@ -559,19 +522,11 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
union drm_amdgpu_ctx_out *out)
{
struct amdgpu_ctx *ctx;
- struct amdgpu_ctx_mgr *mgr;
unsigned reset_counter;
- if (!fpriv)
- return -EINVAL;
-
- mgr = &fpriv->ctx_mgr;
- mutex_lock(&mgr->lock);
- ctx = idr_find(&mgr->ctx_handles, id);
- if (!ctx) {
- mutex_unlock(&mgr->lock);
+ ctx = amdgpu_ctx_get(fpriv, id);
+ if (!ctx)
return -EINVAL;
- }
/* TODO: these two are always zero */
out->state.flags = 0x0;
@@ -586,7 +541,8 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
out->state.reset_status = AMDGPU_CTX_UNKNOWN_RESET;
ctx->reset_counter_query = reset_counter;
- mutex_unlock(&mgr->lock);
+ amdgpu_ctx_put(ctx);
+
return 0;
}
@@ -619,18 +575,10 @@ static int amdgpu_ctx_query2(struct amdgpu_device *adev,
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct amdgpu_ctx *ctx;
- struct amdgpu_ctx_mgr *mgr;
- if (!fpriv)
- return -EINVAL;
-
- mgr = &fpriv->ctx_mgr;
- mutex_lock(&mgr->lock);
- ctx = idr_find(&mgr->ctx_handles, id);
- if (!ctx) {
- mutex_unlock(&mgr->lock);
+ ctx = amdgpu_ctx_get(fpriv, id);
+ if (!ctx)
return -EINVAL;
- }
out->state.flags = 0x0;
out->state.hangs = 0x0;
@@ -671,7 +619,8 @@ static int amdgpu_ctx_query2(struct amdgpu_device *adev,
msecs_to_jiffies(AMDGPU_RAS_COUNTE_DELAY_MS));
}
- mutex_unlock(&mgr->lock);
+ amdgpu_ctx_put(ctx);
+
return 0;
}
@@ -680,26 +629,26 @@ static int amdgpu_ctx_stable_pstate(struct amdgpu_device *adev,
bool set, u32 *stable_pstate)
{
struct amdgpu_ctx *ctx;
- struct amdgpu_ctx_mgr *mgr;
- int r;
+ int r = 0;
- if (!fpriv)
+ ctx = amdgpu_ctx_get(fpriv, id);
+ if (!ctx)
return -EINVAL;
- mgr = &fpriv->ctx_mgr;
- mutex_lock(&mgr->lock);
- ctx = idr_find(&mgr->ctx_handles, id);
- if (!ctx) {
- mutex_unlock(&mgr->lock);
- return -EINVAL;
- }
+ /*
+ * The get path is odd in this uapi - it will check whether the context
+ * id exist, but otherwise does nothing with it. In other words, the
+ * uapi has historically been implemented as being able to query the
+ * global device state, as long as the caller supplies a random valid
+ * context id.
+ */
if (set)
r = amdgpu_ctx_set_stable_pstate(ctx, *stable_pstate);
else
- r = amdgpu_ctx_get_stable_pstate(ctx, stable_pstate);
+ *stable_pstate = amdgpu_get_stable_pstate(adev);
- mutex_unlock(&mgr->lock);
+ amdgpu_ctx_put(ctx);
return r;
}
@@ -778,23 +727,14 @@ struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id)
mgr = &fpriv->ctx_mgr;
- mutex_lock(&mgr->lock);
- ctx = idr_find(&mgr->ctx_handles, id);
+ xa_lock(&mgr->ctx_handles);
+ ctx = xa_load(&mgr->ctx_handles, id);
if (ctx)
kref_get(&ctx->refcount);
- mutex_unlock(&mgr->lock);
+ xa_unlock(&mgr->ctx_handles);
return ctx;
}
-int amdgpu_ctx_put(struct amdgpu_ctx *ctx)
-{
- if (ctx == NULL)
- return -EINVAL;
-
- kref_put(&ctx->refcount, amdgpu_ctx_do_release);
- return 0;
-}
-
uint64_t amdgpu_ctx_add_fence(struct amdgpu_ctx *ctx,
struct drm_sched_entity *entity,
struct dma_fence *fence)
@@ -928,8 +868,7 @@ void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr,
unsigned int i;
mgr->adev = adev;
- mutex_init(&mgr->lock);
- idr_init_base(&mgr->ctx_handles, 1);
+ xa_init_flags(&mgr->ctx_handles, XA_FLAGS_ALLOC1);
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i)
atomic64_set(&mgr->time_spend[i], 0);
@@ -938,13 +877,13 @@ void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr,
long amdgpu_ctx_mgr_entity_flush(struct amdgpu_ctx_mgr *mgr, long timeout)
{
struct amdgpu_ctx *ctx;
- struct idr *idp;
- uint32_t id, i, j;
+ unsigned long id;
+ int i, j;
- idp = &mgr->ctx_handles;
-
- mutex_lock(&mgr->lock);
- idr_for_each_entry(idp, ctx, id) {
+ xa_lock(&mgr->ctx_handles);
+ xa_for_each(&mgr->ctx_handles, id, ctx) {
+ kref_get(&ctx->refcount);
+ xa_unlock(&mgr->ctx_handles);
for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
struct drm_sched_entity *entity;
@@ -956,45 +895,21 @@ long amdgpu_ctx_mgr_entity_flush(struct amdgpu_ctx_mgr *mgr, long timeout)
timeout = drm_sched_entity_flush(entity, timeout);
}
}
+ amdgpu_ctx_put(ctx);
+ xa_lock(&mgr->ctx_handles);
}
- mutex_unlock(&mgr->lock);
+ xa_unlock(&mgr->ctx_handles);
return timeout;
}
-static void amdgpu_ctx_mgr_entity_fini(struct amdgpu_ctx_mgr *mgr)
+void amdgpu_ctx_mgr_fini(struct amdgpu_ctx_mgr *mgr)
{
struct amdgpu_ctx *ctx;
- struct idr *idp;
- uint32_t id, i, j;
-
- idp = &mgr->ctx_handles;
-
- idr_for_each_entry(idp, ctx, id) {
- if (kref_read(&ctx->refcount) != 1) {
- drm_err(adev_to_drm(mgr->adev), "ctx %p is still alive\n", ctx);
- continue;
- }
+ unsigned long id;
- for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) {
- for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) {
- struct drm_sched_entity *entity;
-
- if (!ctx->entities[i][j])
- continue;
-
- entity = &ctx->entities[i][j]->entity;
- drm_sched_entity_fini(entity);
- }
- }
- kref_put(&ctx->refcount, amdgpu_ctx_fini);
- }
-}
-
-void amdgpu_ctx_mgr_fini(struct amdgpu_ctx_mgr *mgr)
-{
- amdgpu_ctx_mgr_entity_fini(mgr);
- idr_destroy(&mgr->ctx_handles);
- mutex_destroy(&mgr->lock);
+ xa_for_each(&mgr->ctx_handles, id, ctx)
+ amdgpu_ctx_put(ctx);
+ xa_destroy(&mgr->ctx_handles);
}
void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
@@ -1002,21 +917,21 @@ void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
{
struct amdgpu_ctx *ctx;
unsigned int hw_ip, i;
- uint32_t id;
+ unsigned long id;
/*
* This is a little bit racy because it can be that a ctx or a fence are
* destroyed just in the moment we try to account them. But that is ok
* since exactly that case is explicitely allowed by the interface.
*/
- mutex_lock(&mgr->lock);
for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) {
uint64_t ns = atomic64_read(&mgr->time_spend[hw_ip]);
usage[hw_ip] = ns_to_ktime(ns);
}
- idr_for_each_entry(&mgr->ctx_handles, ctx, id) {
+ xa_lock(&mgr->ctx_handles);
+ xa_for_each(&mgr->ctx_handles, id, ctx) {
for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) {
for (i = 0; i < amdgpu_ctx_num_entities[hw_ip]; ++i) {
struct amdgpu_ctx_entity *centity;
@@ -1030,5 +945,5 @@ void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr,
}
}
}
- mutex_unlock(&mgr->lock);
+ xa_unlock(&mgr->ctx_handles);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h
index e444b2088d40..a4b89eca4169 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h
@@ -25,6 +25,7 @@
#include <linux/ktime.h>
#include <linux/types.h>
+#include <linux/xarray.h>
#include "amdgpu_ring.h"
@@ -60,16 +61,21 @@ struct amdgpu_ctx {
struct amdgpu_ctx_mgr {
struct amdgpu_device *adev;
- struct mutex lock;
- /* protected by lock */
- struct idr ctx_handles;
+ struct xarray ctx_handles;
atomic64_t time_spend[AMDGPU_HW_IP_NUM];
};
extern const unsigned int amdgpu_ctx_num_entities[AMDGPU_HW_IP_NUM];
struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id);
-int amdgpu_ctx_put(struct amdgpu_ctx *ctx);
+
+void amdgpu_ctx_fini(struct kref *kref);
+
+static inline void amdgpu_ctx_put(struct amdgpu_ctx *ctx)
+{
+ if (ctx)
+ kref_put(&ctx->refcount, amdgpu_ctx_fini);
+}
int amdgpu_ctx_get_entity(struct amdgpu_ctx *ctx, u32 hw_ip, u32 instance,
u32 ring, struct drm_sched_entity **entity);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
index 389bad724273..0455c2cd043f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
@@ -26,6 +26,7 @@
#include <linux/kthread.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
+#include <linux/security.h>
#include <linux/pm_runtime.h>
#include "amdgpu.h"
@@ -1739,6 +1740,12 @@ int amdgpu_debugfs_regs_init(struct amdgpu_device *adev)
struct dentry *ent, *root = minor->debugfs_root;
unsigned int i;
+ if (security_locked_down(LOCKDOWN_PCI_ACCESS)) {
+ drm_info(adev_to_drm(adev),
+ "amdgpu: HW debugfs nodes disabled (kernel lockdown)\n");
+ return 0;
+ }
+
for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) {
ent = debugfs_create_file(debugfs_regs_names[i],
S_IFREG | 0400, root,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
index e77db76b48b8..4fd0df3aa70d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
@@ -64,6 +64,7 @@ const char *hw_ip_names[MAX_HWIP] = {
[VCN1_HWIP] = "VCN1",
[VCE_HWIP] = "VCE",
[VPE_HWIP] = "VPE",
+ [UMSCH_HWIP] = "UMSCH",
[DF_HWIP] = "DF",
[DCE_HWIP] = "DCE",
[OSSSYS_HWIP] = "OSSSYS",
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 8d6502a94306..78c96c7102e4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -74,6 +74,7 @@
#include "amdgpu_ras.h"
#include "amdgpu_ras_mgr.h"
#include "amdgpu_pmu.h"
+#include "amdgpu_smu.h"
#include "amdgpu_fru_eeprom.h"
#include "amdgpu_reset.h"
#include "amdgpu_virt.h"
@@ -2130,6 +2131,8 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev)
adev->cg_flags &= amdgpu_cg_mask;
adev->pg_flags &= amdgpu_pg_mask;
+ amdgpu_smu_early_init(adev);
+
return 0;
}
@@ -3677,6 +3680,10 @@ static void amdgpu_device_sys_interface_fini(struct amdgpu_device *adev)
amdgpu_pm_sysfs_fini(adev);
if (adev->ucode_sysfs_en)
amdgpu_ucode_sysfs_fini(adev);
+
+ amdgpu_discovery_sysfs_fini(adev);
+ amdgpu_preempt_mgr_sysfs_fini(adev);
+
amdgpu_device_attr_sysfs_fini(adev);
amdgpu_fru_sysfs_fini(adev);
@@ -3773,6 +3780,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
spin_lock_init(&adev->irq.lock);
+ amdgpu_early_init_rlc_reg_funcs(adev);
amdgpu_device_init_apu_flags(adev);
r = amdgpu_device_check_arguments(adev);
@@ -4208,6 +4216,7 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
if (adev->mman.initialized)
drain_workqueue(adev->mman.bdev.wq);
+
adev->shutdown = true;
unregister_pm_notifier(&adev->pm_nb);
@@ -4707,161 +4716,6 @@ exit:
}
/**
- * amdgpu_device_ip_check_soft_reset - did soft reset succeed
- *
- * @adev: amdgpu_device pointer
- *
- * The list of all the hardware IPs that make up the asic is walked and
- * the check_soft_reset callbacks are run. check_soft_reset determines
- * if the asic is still hung or not.
- * Returns true if any of the IPs are still in a hung state, false if not.
- */
-static bool amdgpu_device_ip_check_soft_reset(struct amdgpu_device *adev)
-{
- int i;
- bool asic_hang = false;
-
- if (amdgpu_sriov_vf(adev))
- return true;
-
- if (amdgpu_asic_need_full_reset(adev))
- return true;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if (adev->ip_blocks[i].version->funcs->check_soft_reset)
- adev->ip_blocks[i].status.hang =
- adev->ip_blocks[i].version->funcs->check_soft_reset(
- &adev->ip_blocks[i]);
- if (adev->ip_blocks[i].status.hang) {
- dev_info(adev->dev, "IP block:%s is hung!\n", adev->ip_blocks[i].version->funcs->name);
- asic_hang = true;
- }
- }
- return asic_hang;
-}
-
-/**
- * amdgpu_device_ip_pre_soft_reset - prepare for soft reset
- *
- * @adev: amdgpu_device pointer
- *
- * The list of all the hardware IPs that make up the asic is walked and the
- * pre_soft_reset callbacks are run if the block is hung. pre_soft_reset
- * handles any IP specific hardware or software state changes that are
- * necessary for a soft reset to succeed.
- * Returns 0 on success, negative error code on failure.
- */
-static int amdgpu_device_ip_pre_soft_reset(struct amdgpu_device *adev)
-{
- int i, r = 0;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if (adev->ip_blocks[i].status.hang &&
- adev->ip_blocks[i].version->funcs->pre_soft_reset) {
- r = adev->ip_blocks[i].version->funcs->pre_soft_reset(&adev->ip_blocks[i]);
- if (r)
- return r;
- }
- }
-
- return 0;
-}
-
-/**
- * amdgpu_device_ip_need_full_reset - check if a full asic reset is needed
- *
- * @adev: amdgpu_device pointer
- *
- * Some hardware IPs cannot be soft reset. If they are hung, a full gpu
- * reset is necessary to recover.
- * Returns true if a full asic reset is required, false if not.
- */
-static bool amdgpu_device_ip_need_full_reset(struct amdgpu_device *adev)
-{
- int i;
-
- if (amdgpu_asic_need_full_reset(adev))
- return true;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if ((adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) ||
- (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_SMC) ||
- (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_ACP) ||
- (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_DCE) ||
- adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_PSP) {
- if (adev->ip_blocks[i].status.hang) {
- dev_info(adev->dev, "Some block need full reset!\n");
- return true;
- }
- }
- }
- return false;
-}
-
-/**
- * amdgpu_device_ip_soft_reset - do a soft reset
- *
- * @adev: amdgpu_device pointer
- *
- * The list of all the hardware IPs that make up the asic is walked and the
- * soft_reset callbacks are run if the block is hung. soft_reset handles any
- * IP specific hardware or software state changes that are necessary to soft
- * reset the IP.
- * Returns 0 on success, negative error code on failure.
- */
-static int amdgpu_device_ip_soft_reset(struct amdgpu_device *adev)
-{
- int i, r = 0;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if (adev->ip_blocks[i].status.hang &&
- adev->ip_blocks[i].version->funcs->soft_reset) {
- r = adev->ip_blocks[i].version->funcs->soft_reset(&adev->ip_blocks[i]);
- if (r)
- return r;
- }
- }
-
- return 0;
-}
-
-/**
- * amdgpu_device_ip_post_soft_reset - clean up from soft reset
- *
- * @adev: amdgpu_device pointer
- *
- * The list of all the hardware IPs that make up the asic is walked and the
- * post_soft_reset callbacks are run if the asic was hung. post_soft_reset
- * handles any IP specific hardware or software state changes that are
- * necessary after the IP has been soft reset.
- * Returns 0 on success, negative error code on failure.
- */
-static int amdgpu_device_ip_post_soft_reset(struct amdgpu_device *adev)
-{
- int i, r = 0;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if (adev->ip_blocks[i].status.hang &&
- adev->ip_blocks[i].version->funcs->post_soft_reset)
- r = adev->ip_blocks[i].version->funcs->post_soft_reset(&adev->ip_blocks[i]);
- if (r)
- return r;
- }
-
- return 0;
-}
-
-/**
* amdgpu_device_reset_sriov - reset ASIC for SR-IOV vf
*
* @adev: amdgpu_device pointer
@@ -5152,20 +5006,7 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev,
/* Don't suspend on bare metal if we are not going to HW reset the ASIC */
if (!amdgpu_sriov_vf(adev)) {
-
- if (!need_full_reset)
- need_full_reset = amdgpu_device_ip_need_full_reset(adev);
-
- if (!need_full_reset && amdgpu_gpu_recovery &&
- amdgpu_device_ip_check_soft_reset(adev)) {
- amdgpu_device_ip_pre_soft_reset(adev);
- r = amdgpu_device_ip_soft_reset(adev);
- amdgpu_device_ip_post_soft_reset(adev);
- if (r || amdgpu_device_ip_check_soft_reset(adev)) {
- dev_info(adev->dev, "soft reset failed, will fallback to full reset!\n");
- need_full_reset = true;
- }
- }
+ need_full_reset = true;
if (!test_bit(AMDGPU_SKIP_COREDUMP, &reset_context->flags)) {
dev_info(tmp_adev->dev, "Dumping IP State\n");
@@ -5618,8 +5459,7 @@ static void amdgpu_device_halt_activities(struct amdgpu_device *adev,
drm_client_dev_suspend(adev_to_drm(tmp_adev));
/* disable ras on ALL IPs */
- if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev) &&
- amdgpu_device_ip_need_full_reset(tmp_adev))
+ if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev))
amdgpu_ras_suspend(tmp_adev);
amdgpu_userq_pre_reset(tmp_adev);
@@ -6891,7 +6731,7 @@ ssize_t amdgpu_get_soft_full_reset_mask(struct amdgpu_ring *ring)
if (unlikely(!ring->adev->debug_disable_soft_recovery) &&
!amdgpu_sriov_vf(ring->adev) && ring->funcs->soft_recovery)
- size |= AMDGPU_RESET_TYPE_SOFT_RESET;
+ size |= AMDGPU_RESET_TYPE_SOFT_RECOVERY;
return size;
}
@@ -6907,8 +6747,8 @@ ssize_t amdgpu_show_reset_mask(char *buf, uint32_t supported_reset)
}
- if (supported_reset & AMDGPU_RESET_TYPE_SOFT_RESET)
- size += sysfs_emit_at(buf, size, "soft ");
+ if (supported_reset & AMDGPU_RESET_TYPE_SOFT_RECOVERY)
+ size += sysfs_emit_at(buf, size, "soft_recovery ");
if (supported_reset & AMDGPU_RESET_TYPE_PER_QUEUE)
size += sysfs_emit_at(buf, size, "queue ");
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
index 853365dee2a7..a015d55aa158 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
@@ -22,6 +22,7 @@
*/
#include <linux/firmware.h>
+#include <linux/kernfs.h>
#include "amdgpu.h"
#include "amdgpu_discovery.h"
@@ -148,6 +149,26 @@ MODULE_FIRMWARE("amdgpu/aldebaran_ip_discovery.bin");
#define mmDRIVER_SCRATCH_1 0x95
#define mmDRIVER_SCRATCH_2 0x96
+struct ip_discovery_top {
+ struct kobject kobj;
+ struct kset die_kset;
+ struct pci_dev *pdev;
+ struct amdgpu_device *adev;
+ uint8_t *discovery_bin;
+ uint32_t bin_size;
+ bool standalone_mode;
+};
+
+/* List to track early-initialized ip_discovery_top entries */
+struct early_ip_discovery {
+ struct list_head list;
+ struct pci_dev *pdev;
+ struct ip_discovery_top *ip_top;
+};
+
+static LIST_HEAD(early_ip_discovery_list);
+static DEFINE_MUTEX(early_ip_discovery_mutex);
+
static const char *hw_id_names[HW_ID_MAX] = {
[MP1_HWID] = "MP1",
[MP2_HWID] = "MP2",
@@ -226,6 +247,7 @@ static const char *hw_id_names[HW_ID_MAX] = {
[XGBE_HWID] = "XGBE",
[MP0_HWID] = "MP0",
[VPE_HWID] = "VPE",
+ [UMSCH_HWID] = "UMSCH",
[ATU_HWID] = "ATU",
[AIGC_HWID] = "AIGC",
};
@@ -258,6 +280,7 @@ static int hw_id_map[MAX_HWIP] = {
[DCI_HWIP] = DCI_HWID,
[PCIE_HWIP] = PCIE_HWID,
[VPE_HWIP] = VPE_HWID,
+ [UMSCH_HWIP] = UMSCH_HWID,
[ISP_HWIP] = ISP_HWID,
[ATU_HWIP] = ATU_HWID,
};
@@ -542,25 +565,37 @@ static const char *amdgpu_discovery_get_fw_name(struct amdgpu_device *adev)
}
}
-static int amdgpu_discovery_get_table_info(struct amdgpu_device *adev,
- struct table_info **info,
- uint16_t table_id)
+static struct table_info *
+amdgpu_discovery_get_table_info_from_bin(uint8_t *discovery_bin,
+ uint16_t table_id)
{
- struct binary_header *bhdr =
- (struct binary_header *)adev->discovery.bin;
+ struct binary_header *bhdr = (struct binary_header *)discovery_bin;
struct binary_header_v2 *bhdrv2;
switch (bhdr->version_major) {
case 2:
- bhdrv2 = (struct binary_header_v2 *)adev->discovery.bin;
- *info = &bhdrv2->table_list[table_id];
- break;
+ bhdrv2 = (struct binary_header_v2 *)discovery_bin;
+ return &bhdrv2->table_list[table_id];
case 1:
case 0:
- *info = &bhdr->table_list[table_id];
- break;
+ return &bhdr->table_list[table_id];
default:
- dev_err(adev->dev, "Invalid ip discovery table version %d\n",bhdr->version_major);
+ return NULL;
+ }
+}
+
+static int amdgpu_discovery_get_table_info(struct amdgpu_device *adev,
+ struct table_info **info,
+ uint16_t table_id)
+{
+ struct binary_header *bhdr =
+ (struct binary_header *)adev->discovery.bin;
+
+ *info = amdgpu_discovery_get_table_info_from_bin(adev->discovery.bin,
+ table_id);
+ if (!*info) {
+ dev_err(adev->dev, "Invalid ip discovery table version %d\n",
+ bhdr->version_major);
return -EINVAL;
}
@@ -724,11 +759,11 @@ out:
return r;
}
-static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev);
-
void amdgpu_discovery_fini(struct amdgpu_device *adev)
{
- amdgpu_discovery_sysfs_fini(adev);
+ if (adev->discovery.ip_top && !adev->discovery.ip_top->standalone_mode)
+ amdgpu_discovery_sysfs_fini(adev);
+
kfree(adev->discovery.bin);
adev->discovery.bin = NULL;
}
@@ -737,15 +772,17 @@ static int amdgpu_discovery_validate_ip(struct amdgpu_device *adev,
uint8_t instance, uint16_t hw_id)
{
if (instance >= HWIP_MAX_INSTANCE) {
- dev_err(adev->dev,
- "Unexpected instance_number (%d) from ip discovery blob\n",
- instance);
+ if (adev)
+ dev_err(adev->dev,
+ "Unexpected instance_number (%d) from ip discovery blob\n",
+ instance);
return -EINVAL;
}
if (hw_id >= HW_ID_MAX) {
- dev_err(adev->dev,
- "Unexpected hw_id (%d) from ip discovery blob\n",
- hw_id);
+ if (adev)
+ dev_err(adev->dev,
+ "Unexpected hw_id (%d) from ip discovery blob\n",
+ hw_id);
return -EINVAL;
}
@@ -1111,12 +1148,6 @@ static const struct kobj_type ip_discovery_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
};
-struct ip_discovery_top {
- struct kobject kobj; /* ip_discovery/ */
- struct kset die_kset; /* ip_discovery/die/, contains ip_die_entry */
- struct amdgpu_device *adev;
-};
-
static void die_kobj_release(struct kobject *kobj)
{
struct ip_discovery_top *ip_top = container_of(to_kset(kobj),
@@ -1132,8 +1163,14 @@ static void ip_disc_release(struct kobject *kobj)
kobj);
struct amdgpu_device *adev = ip_top->adev;
+ /* In standalone mode, discovery_bin is managed by devm and will be
+ * freed automatically when the PCI device is removed. Do not manually
+ * free it here to avoid double-free.
+ */
+
kfree(ip_top);
- adev->discovery.ip_top = NULL;
+ if (adev)
+ adev->discovery.ip_top = NULL;
}
static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev,
@@ -1141,6 +1178,10 @@ static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev,
{
uint8_t harvest = 0;
+ /* In early init mode (adev == NULL), harvest info is not available */
+ if (!adev)
+ return 0;
+
/* Until a uniform way is figured, get mask based on hwid */
switch (hw_id) {
case VCN_HWID:
@@ -1169,11 +1210,14 @@ static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev,
}
static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
+ struct ip_discovery_top *ip_top,
struct ip_die_entry *ip_die_entry,
const size_t _ip_offset, const int num_ips,
bool reg_base_64)
{
- uint8_t *discovery_bin = adev->discovery.bin;
+ uint8_t *discovery_bin = ip_top->standalone_mode ?
+ ip_top->discovery_bin :
+ adev->discovery.bin;
int ii, jj, kk, res;
uint16_t hw_id;
uint8_t inst;
@@ -1270,10 +1314,12 @@ next_ip:
return 0;
}
-static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
+static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev,
+ struct ip_discovery_top *ip_top)
{
- struct ip_discovery_top *ip_top = adev->discovery.ip_top;
- uint8_t *discovery_bin = adev->discovery.bin;
+ uint8_t *discovery_bin = ip_top->standalone_mode ?
+ ip_top->discovery_bin :
+ adev->discovery.bin;
struct table_info *info;
struct ip_discovery_header *ihdr;
struct die_header *dhdr;
@@ -1282,9 +1328,10 @@ static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
size_t ip_offset;
int ii, res;
- res = amdgpu_discovery_get_table_info(adev, &info, IP_DISCOVERY);
- if (res)
- return res;
+ info = amdgpu_discovery_get_table_info_from_bin(discovery_bin,
+ IP_DISCOVERY);
+ if (!info)
+ return -EINVAL;
ihdr = (struct ip_discovery_header
*)(discovery_bin +
le16_to_cpu(info->offset));
@@ -1322,7 +1369,8 @@ static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
return res;
}
- amdgpu_discovery_sysfs_ips(adev, ip_die_entry, ip_offset, num_ips, !!ihdr->base_addr_64_bit);
+ amdgpu_discovery_sysfs_ips(adev, ip_top, ip_die_entry, ip_offset,
+ num_ips, !!ihdr->base_addr_64_bit);
}
return 0;
@@ -1338,12 +1386,30 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
if (!discovery_bin)
return -EINVAL;
+ /* If early init already created sysfs in standalone mode, skip normal init */
+ if (adev->discovery.ip_top && adev->discovery.ip_top->standalone_mode)
+ return 0;
+
ip_top = kzalloc_obj(*ip_top);
if (!ip_top)
return -ENOMEM;
ip_top->adev = adev;
- adev->discovery.ip_top = ip_top;
+
+ /* Check if ip_discovery already exists before creating.
+ * This shouldn't normally happen but handle it gracefully.
+ */
+ if (adev->dev->kobj.sd) {
+ struct kernfs_node *existing;
+
+ existing = kernfs_find_and_get(adev->dev->kobj.sd, "ip_discovery");
+ if (existing) {
+ kernfs_put(existing);
+ kfree(ip_top);
+ return 0;
+ }
+ }
+
res = kobject_init_and_add(&ip_top->kobj, &ip_discovery_ktype,
&adev->dev->kobj, "ip_discovery");
if (res) {
@@ -1351,6 +1417,8 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
goto Err;
}
+ adev->discovery.ip_top = ip_top;
+
die_kset = &ip_top->die_kset;
kobject_set_name(&die_kset->kobj, "%s", "die");
die_kset->kobj.parent = &ip_top->kobj;
@@ -1365,7 +1433,7 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
ip_hw_instance_attrs[ii] = &ip_hw_attr[ii].attr;
ip_hw_instance_attrs[ii] = NULL;
- res = amdgpu_discovery_sysfs_recurse(adev);
+ res = amdgpu_discovery_sysfs_recurse(adev, ip_top);
return res;
Err:
@@ -1412,7 +1480,7 @@ static void amdgpu_discovery_sysfs_die_free(struct ip_die_entry *ip_die_entry)
kobject_put(&ip_die_entry->ip_kset.kobj);
}
-static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)
+void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)
{
struct ip_discovery_top *ip_top = adev->discovery.ip_top;
struct list_head *el, *tmp;
@@ -1421,6 +1489,16 @@ static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)
if (!ip_top)
return;
+ /*
+ * In standalone mode the sysfs hierarchy is tied to the PCI device
+ * lifetime and is torn down by amdgpu_discovery_sysfs_early_fini().
+ * Freeing it here would leave a dangling pointer in the early
+ * discovery list, causing a use-after-free on driver unbind.
+ */
+ if (ip_top->standalone_mode)
+ return;
+
+ adev->discovery.ip_top = NULL;
die_kset = &ip_top->die_kset;
spin_lock(&die_kset->list_lock);
list_for_each_prev_safe(el, tmp, &die_kset->list) {
@@ -1479,6 +1557,150 @@ void amdgpu_discovery_dump(struct amdgpu_device *adev, struct drm_printer *p)
spin_unlock(&die_kset->list_lock);
}
+int amdgpu_discovery_sysfs_early_init(struct amdgpu_device *adev, struct pci_dev *pdev)
+{
+ struct ip_discovery_top *ip_top;
+ struct early_ip_discovery *early_entry, *tmp;
+ struct kset *die_kset;
+ uint8_t *discovery_bin;
+ int res, ii;
+
+ if (!adev || !adev->discovery.bin)
+ return -EINVAL;
+
+ if (adev->discovery.ip_top)
+ return 0;
+
+ mutex_lock(&early_ip_discovery_mutex);
+ list_for_each_entry_safe(early_entry, tmp, &early_ip_discovery_list, list) {
+ if (early_entry->pdev == pdev) {
+ adev->discovery.ip_top = early_entry->ip_top;
+ early_entry->ip_top->adev = adev;
+ mutex_unlock(&early_ip_discovery_mutex);
+ return 0;
+ }
+ }
+ mutex_unlock(&early_ip_discovery_mutex);
+
+ discovery_bin = adev->discovery.bin;
+
+ early_entry = kzalloc(sizeof(*early_entry), GFP_KERNEL);
+ if (!early_entry)
+ return -ENOMEM;
+
+ ip_top = kzalloc(sizeof(*ip_top), GFP_KERNEL);
+ if (!ip_top) {
+ kfree(early_entry);
+ return -ENOMEM;
+ }
+
+ ip_top->discovery_bin = devm_kmemdup(&pdev->dev, discovery_bin,
+ DISCOVERY_TMR_SIZE, GFP_KERNEL);
+ if (!ip_top->discovery_bin) {
+ kfree(ip_top);
+ kfree(early_entry);
+ return -ENOMEM;
+ }
+
+ ip_top->bin_size = DISCOVERY_TMR_SIZE;
+ ip_top->pdev = pdev;
+ ip_top->adev = adev;
+ ip_top->standalone_mode = true;
+
+ /* Check if ip_discovery already exists (from previous probe attempt).
+ * This can happen if the module was unloaded and reloaded but the
+ * sysfs persisted (tied to PCI device lifetime).
+ */
+ if (pdev->dev.kobj.sd) {
+ struct kernfs_node *existing;
+
+ existing = kernfs_find_and_get(pdev->dev.kobj.sd, "ip_discovery");
+ if (existing) {
+ kernfs_put(existing);
+ kfree(ip_top);
+ kfree(early_entry);
+ return 0;
+ }
+ }
+
+ res = kobject_init_and_add(&ip_top->kobj, &ip_discovery_ktype,
+ &pdev->dev.kobj, "ip_discovery");
+ if (res)
+ goto err_put_kobj;
+
+ adev->discovery.ip_top = ip_top;
+
+ die_kset = &ip_top->die_kset;
+ kobject_set_name(&die_kset->kobj, "%s", "die");
+ die_kset->kobj.parent = &ip_top->kobj;
+ die_kset->kobj.ktype = &die_kobj_ktype;
+ res = kset_register(&ip_top->die_kset);
+ if (res)
+ goto err_put_die_kset;
+
+ for (ii = 0; ii < ARRAY_SIZE(ip_hw_attr); ii++)
+ ip_hw_instance_attrs[ii] = &ip_hw_attr[ii].attr;
+ ip_hw_instance_attrs[ii] = NULL;
+
+ res = amdgpu_discovery_sysfs_recurse(NULL, ip_top);
+ if (res)
+ goto err_put_die_kset;
+
+ early_entry->pdev = pdev;
+ early_entry->ip_top = ip_top;
+ mutex_lock(&early_ip_discovery_mutex);
+ list_add(&early_entry->list, &early_ip_discovery_list);
+ mutex_unlock(&early_ip_discovery_mutex);
+
+ return 0;
+
+err_put_die_kset:
+ kobject_put(&ip_top->die_kset.kobj);
+err_put_kobj:
+ kobject_put(&ip_top->kobj);
+ kfree(early_entry);
+ adev->discovery.ip_top = NULL;
+ return res;
+}
+
+void amdgpu_discovery_sysfs_early_fini(struct pci_dev *pdev)
+{
+ struct early_ip_discovery *entry, *tmp_entry;
+ struct ip_discovery_top *ip_top = NULL;
+ struct list_head *el, *tmp;
+ struct kset *die_kset;
+
+ /* Find the entry in our tracking list */
+ mutex_lock(&early_ip_discovery_mutex);
+ list_for_each_entry_safe(entry, tmp_entry, &early_ip_discovery_list, list) {
+ if (entry->pdev == pdev) {
+ ip_top = entry->ip_top;
+ list_del(&entry->list);
+ kfree(entry);
+ break;
+ }
+ }
+ mutex_unlock(&early_ip_discovery_mutex);
+
+ if (!ip_top)
+ return;
+
+ /* Clean up sysfs hierarchy */
+ die_kset = &ip_top->die_kset;
+
+ spin_lock(&die_kset->list_lock);
+ list_for_each_prev_safe(el, tmp, &die_kset->list) {
+ list_del_init(el);
+ spin_unlock(&die_kset->list_lock);
+ amdgpu_discovery_sysfs_die_free(to_ip_die_entry(list_to_kobj(el)));
+ spin_lock(&die_kset->list_lock);
+ }
+ spin_unlock(&die_kset->list_lock);
+
+ kobject_put(&ip_top->die_kset.kobj);
+ kobject_put(&ip_top->kobj);
+ /* ip_top itself will be freed by kobject_put via ip_disc_release */
+}
/* ================================================== */
@@ -1504,6 +1726,9 @@ static int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
r = amdgpu_discovery_init(adev);
if (r)
return r;
+
+ amdgpu_discovery_sysfs_early_init(adev, adev->pdev);
+
discovery_bin = adev->discovery.bin;
wafl_ver = 0;
adev->gfx.xcc_mask = 0;
@@ -2636,7 +2861,12 @@ static int amdgpu_discovery_set_mm_ip_blocks(struct amdgpu_device *adev)
return -EINVAL;
}
} else {
- switch (amdgpu_ip_version(adev, UVD_HWIP, 0)) {
+ uint32_t vcn_version = amdgpu_ip_version(adev, UVD_HWIP, 0);
+
+ /* no VCN discovered; nothing to add */
+ if (!vcn_version)
+ return 0;
+ switch (vcn_version) {
case IP_VERSION(1, 0, 0):
case IP_VERSION(1, 0, 1):
amdgpu_device_ip_block_add(adev, &vcn_v1_0_ip_block);
@@ -2704,7 +2934,7 @@ static int amdgpu_discovery_set_mm_ip_blocks(struct amdgpu_device *adev)
default:
dev_err(adev->dev,
"Failed to add vcn/jpeg ip block(UVD_HWIP:0x%x)\n",
- amdgpu_ip_version(adev, UVD_HWIP, 0));
+ vcn_version);
return -EINVAL;
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
index e0010f6a3eda..5b2b16f68576 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
@@ -41,6 +41,7 @@ struct amdgpu_discovery_info {
bool reserve_tmr;
};
+void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev);
void amdgpu_discovery_fini(struct amdgpu_device *adev);
int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev);
@@ -53,4 +54,9 @@ int amdgpu_discovery_get_gc_major_minor_version(struct amdgpu_device *adev,
void amdgpu_discovery_dump(struct amdgpu_device *adev, struct drm_printer *p);
+/* Early sysfs functions for persistent ip_discovery export */
+int amdgpu_discovery_sysfs_early_init(struct amdgpu_device *adev,
+ struct pci_dev *pdev);
+void amdgpu_discovery_sysfs_early_fini(struct pci_dev *pdev);
+
#endif /* __AMDGPU_DISCOVERY__ */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 4c0c77eafbd1..ad631ad31899 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -33,7 +33,6 @@
#include <drm/drm_vblank.h>
#include <linux/cc_platform.h>
-#include <linux/console.h>
#include <linux/dynamic_debug.h>
#include <linux/module.h>
#include <linux/mmu_notifier.h>
@@ -146,7 +145,9 @@ enum AMDGPU_DEBUG_MASK {
AMDGPU_DEBUG_SMU_POOL = BIT(7),
AMDGPU_DEBUG_VM_USERPTR = BIT(8),
AMDGPU_DEBUG_DISABLE_RAS_CE_LOG = BIT(9),
- AMDGPU_DEBUG_ENABLE_CE_CS = BIT(10)
+ AMDGPU_DEBUG_ENABLE_CE_CS = BIT(10),
+ AMDGPU_DEBUG_HIBERNATION_THAW_RESUME_GPU = BIT(11),
+ AMDGPU_DEBUG_DISABLE_IP_BLOCK_SOFT_RESET = BIT(12),
};
unsigned int amdgpu_vram_limit = UINT_MAX;
@@ -1939,6 +1940,7 @@ static const struct pci_device_id pciidlist[] = {
{0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY},
{0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY},
{0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},
+ {0x1002, 0x664D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},
{0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},
{0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},
{0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},
@@ -2008,6 +2010,7 @@ static const struct pci_device_id pciidlist[] = {
{0x1002, 0x6930, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA},
{0x1002, 0x6938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA},
{0x1002, 0x6939, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA},
+ {0x1002, 0x693B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA},
/* fiji */
{0x1002, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_FIJI},
{0x1002, 0x730F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_FIJI},
@@ -2036,6 +2039,7 @@ static const struct pci_device_id pciidlist[] = {
{0x1002, 0x67C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10},
{0x1002, 0x67C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10},
{0x1002, 0x67D0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10},
+ {0x1002, 0x67D4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10},
{0x1002, 0x67DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10},
{0x1002, 0x67C8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10},
{0x1002, 0x67C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10},
@@ -2049,6 +2053,7 @@ static const struct pci_device_id pciidlist[] = {
{0x1002, 0x6985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12},
{0x1002, 0x6986, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12},
{0x1002, 0x6987, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12},
+ {0x1002, 0x698F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12},
{0x1002, 0x6995, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12},
{0x1002, 0x6997, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12},
{0x1002, 0x699F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12},
@@ -2250,7 +2255,7 @@ static void amdgpu_init_debug_options(struct amdgpu_device *adev)
}
if (amdgpu_debug_mask & AMDGPU_DEBUG_DISABLE_GPU_SOFT_RECOVERY) {
- pr_info("debug: soft reset for GPU recovery disabled\n");
+ pr_info("debug: soft recovery disabled\n");
adev->debug_disable_soft_recovery = true;
}
@@ -2291,6 +2296,16 @@ static void amdgpu_init_debug_options(struct amdgpu_device *adev)
pr_info("debug: allowing command submission to CE engine\n");
adev->debug_enable_ce_cs = true;
}
+
+ if (amdgpu_debug_mask & AMDGPU_DEBUG_HIBERNATION_THAW_RESUME_GPU) {
+ pr_info("debug: resume gpu in thaw() of hibernation\n");
+ adev->debug_hibernation_thaw_resume_gpu = true;
+ }
+
+ if (amdgpu_debug_mask & AMDGPU_DEBUG_DISABLE_IP_BLOCK_SOFT_RESET) {
+ pr_info("debug: IP block soft reset disabled\n");
+ adev->debug_disable_ip_block_soft_reset = true;
+ }
}
static unsigned long amdgpu_fix_asic_type(struct pci_dev *pdev, unsigned long flags)
@@ -2552,6 +2567,8 @@ amdgpu_pci_remove(struct pci_dev *pdev)
amdgpu_driver_unload_kms(dev);
+ amdgpu_discovery_sysfs_early_fini(pdev);
+
/*
* Flush any in flight DMA operations from device.
* Clear the Bus Master Enable bit and then wait on the PCIe Device
@@ -2705,9 +2722,10 @@ static int amdgpu_pmops_freeze(struct device *dev)
static int amdgpu_pmops_thaw(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
+ struct amdgpu_device *adev = drm_to_adev(drm_dev);
/* do not resume device if it's normal hibernation */
- if (console_suspend_enabled &&
+ if (!adev->debug_hibernation_thaw_resume_gpu &&
!pm_hibernate_is_recovering() &&
!pm_hibernation_mode_is_suspend())
return 0;
@@ -3076,6 +3094,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_SIGNAL, amdgpu_userq_signal_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_WAIT, amdgpu_userq_wait_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_HANDLES, amdgpu_gem_list_handles_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(AMDGPU_PROC_OPTIONS, amdgpu_proc_options_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
};
static const struct drm_driver amdgpu_kms_driver = {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
index ea69b1bac7c6..3043ad041bb4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
@@ -727,6 +727,15 @@ void amdgpu_ring_set_fence_errors_and_reemit(struct amdgpu_ring *ring,
last_seq = amdgpu_fence_read(ring) & ring->fence_drv.num_fences_mask;
seq = ring->fence_drv.sync_seq & ring->fence_drv.num_fences_mask;
+ /* If there is nothing to reemit, return early and set an error on the fence
+ * if applicable. If all of the fences are siganlled, this will be a nop.
+ * if there are still fences and ring_backup_entries_to_copy is 0, then
+ * we are skipping it on purpose.
+ */
+ if (!ring->ring_backup_entries_to_copy) {
+ amdgpu_fence_driver_force_completion(ring, &guilty_fence->base);
+ return;
+ }
ring->reemit = true;
amdgpu_ring_alloc(ring, ring->ring_backup_entries_to_copy);
spin_lock_irqsave(&ring->fence_drv.lock, flags);
@@ -741,7 +750,8 @@ void amdgpu_ring_set_fence_errors_and_reemit(struct amdgpu_ring *ring,
if (unprocessed && !dma_fence_is_signaled_locked(unprocessed)) {
fence = container_of(unprocessed, struct amdgpu_fence, base);
is_guilty_fence = fence == guilty_fence;
- is_guilty_context = fence->context == guilty_fence->context;
+ is_guilty_context = guilty_fence ?
+ (fence->context == guilty_fence->context) : false;
/* mark all fences from the guilty context with an error */
if (is_guilty_fence)
@@ -794,6 +804,17 @@ void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring,
seq = ring->fence_drv.sync_seq & ring->fence_drv.num_fences_mask;
ring->ring_backup_entries_to_copy = 0;
+ /* if we've already seen this fence, return early.
+ * ring->ring_backup_entries_to_copy is set to 0 so
+ * the reemit helper will return early as well to
+ * avoid getting stuck in a reemit loop.
+ */
+ if (ring->guilty_fence == guilty_fence) {
+ ring->guilty_fence = NULL;
+ return;
+ }
+ ring->guilty_fence = guilty_fence;
+
do {
last_seq++;
last_seq &= ring->fence_drv.num_fences_mask;
@@ -811,6 +832,36 @@ void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring,
} while (last_seq != seq);
}
+struct amdgpu_fence *
+amdgpu_ring_find_guilty_fence(struct amdgpu_ring *ring)
+{
+ struct dma_fence *unprocessed;
+ struct dma_fence __rcu **ptr;
+ struct amdgpu_fence *fence;
+ u32 seq, last_seq;
+
+ last_seq = amdgpu_fence_read(ring) & ring->fence_drv.num_fences_mask;
+ seq = ring->fence_drv.sync_seq & ring->fence_drv.num_fences_mask;
+
+ do {
+ last_seq++;
+ last_seq &= ring->fence_drv.num_fences_mask;
+
+ ptr = &ring->fence_drv.fences[last_seq];
+ rcu_read_lock();
+ unprocessed = rcu_dereference(*ptr);
+
+ if (unprocessed && !dma_fence_is_signaled(unprocessed)) {
+ fence = container_of(unprocessed, struct amdgpu_fence, base);
+ rcu_read_unlock();
+ return fence;
+ }
+ rcu_read_unlock();
+ } while (last_seq != seq);
+
+ return NULL;
+}
+
/*
* Common fence implementation
*/
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
index 85372af1216d..96c9d4f00b27 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
@@ -34,6 +34,7 @@
#include "amdgpu_xcp.h"
#include "amdgpu_xgmi.h"
#include "amdgpu_mes.h"
+#include "mes_userqueue.h"
#include "nvd.h"
/* delay 0.1 second to enable gfx off feature */
@@ -377,6 +378,30 @@ int amdgpu_gfx_kiq_init(struct amdgpu_device *adev,
return 0;
}
+static void amdgpu_gfx_mqd_reset_restore(struct amdgpu_ring *ring)
+{
+ struct amdgpu_device *adev = ring->adev;
+ int mqd_idx, mqd_size;
+
+ /* restore mqd with the backup copy */
+ if (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) {
+ mqd_idx = ring - &adev->gfx.compute_ring[0];
+ mqd_size = adev->mqds[AMDGPU_HW_IP_COMPUTE].mqd_size;
+ if (adev->gfx.mec.mqd_backup[mqd_idx])
+ memcpy_toio(ring->mqd_ptr, adev->gfx.mec.mqd_backup[mqd_idx], mqd_size);
+ } else if (ring->funcs->type == AMDGPU_RING_TYPE_GFX) {
+ mqd_size = adev->mqds[AMDGPU_HW_IP_GFX].mqd_size;
+ mqd_idx = ring - &adev->gfx.gfx_ring[0];
+
+ if (adev->gfx.me.mqd_backup[mqd_idx])
+ memcpy_toio(ring->mqd_ptr, adev->gfx.me.mqd_backup[mqd_idx], mqd_size);
+ }
+ /* reset the ring */
+ ring->wptr = 0;
+ atomic64_set((atomic64_t *)ring->wptr_cpu_addr, 0);
+ amdgpu_ring_clear_ring(ring);
+}
+
/* create MQD for each compute/gfx queue */
int amdgpu_gfx_mqd_sw_init(struct amdgpu_device *adev,
unsigned int mqd_size, int xcc_id)
@@ -1964,6 +1989,60 @@ static ssize_t amdgpu_gfx_get_compute_reset_mask(struct device *dev,
return amdgpu_show_reset_mask(buf, adev->gfx.compute_supported_reset);
}
+static int amdgpu_gfx_mes_reset_queue_start(struct amdgpu_ring *ring,
+ unsigned int vmid,
+ struct amdgpu_fence *timedout_fence,
+ bool use_mmio)
+{
+ struct amdgpu_device *adev = ring->adev;
+ bool reinit_queue;
+ int r;
+
+ if ((ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) &&
+ adev->mes.compute_pipe_reset_enabled)
+ reinit_queue = true;
+ else if ((ring->funcs->type == AMDGPU_RING_TYPE_GFX) &&
+ adev->mes.gfx_pipe_reset_enabled)
+ reinit_queue = true;
+ else
+ reinit_queue = use_mmio;
+
+ amdgpu_ring_reset_helper_begin(ring, timedout_fence);
+
+ r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio, 0);
+ if (r)
+ return r;
+
+ if (reinit_queue) {
+ r = amdgpu_mes_unmap_legacy_queue(adev, ring,
+ RESET_QUEUES, 0, 0, 0);
+ if (r)
+ return r;
+ amdgpu_gfx_mqd_reset_restore(ring);
+
+ r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
+ if (r) {
+ dev_err(adev->dev, "failed to remap kgq\n");
+ return r;
+ }
+ }
+ return 0;
+}
+
+int amdgpu_gfx_mes_reset_queue(struct amdgpu_ring *ring,
+ unsigned int vmid,
+ struct amdgpu_fence *timedout_fence,
+ bool use_mmio)
+{
+ int r;
+
+ r = amdgpu_gfx_mes_reset_queue_start(ring, vmid, timedout_fence,
+ use_mmio);
+ if (r)
+ return r;
+ return amdgpu_ring_reset_helper_end(ring, timedout_fence);
+}
+
static DEVICE_ATTR(run_cleaner_shader, 0200,
NULL, amdgpu_gfx_set_run_cleaner_shader);
@@ -2122,6 +2201,200 @@ void amdgpu_gfx_sysfs_fini(struct amdgpu_device *adev)
}
}
+static void amdgpu_gfx_reset_start_compute_scheds(struct amdgpu_device *adev,
+ struct amdgpu_ring *guilty_ring)
+{
+ struct amdgpu_ring *ring;
+ int i;
+
+ for (i = 0; i < adev->gfx.num_compute_rings; i++) {
+ ring = &adev->gfx.compute_ring[i];
+ if (ring == guilty_ring)
+ continue;
+ drm_sched_wqueue_start(&ring->sched);
+ }
+}
+
+static void amdgpu_gfx_reset_stop_compute_scheds(struct amdgpu_device *adev,
+ struct amdgpu_ring *guilty_ring)
+{
+ struct amdgpu_ring *ring;
+ int i;
+
+ for (i = 0; i < adev->gfx.num_compute_rings; i++) {
+ ring = &adev->gfx.compute_ring[i];
+ if (ring == guilty_ring)
+ continue;
+ drm_sched_wqueue_stop(&ring->sched);
+ }
+}
+
+/*
+ * Match the MES-reported hung doorbell against a compute ring and run
+ * the reset. On hit, the matched ring and its guilty fence are returned
+ * via *out_ring / *out_fence so the caller can defer reset end until
+ * after MES has resumed all gangs.
+ */
+static int amdgpu_gfx_reset_mes_kcq(struct amdgpu_device *adev,
+ struct amdgpu_ring *guilty_ring,
+ unsigned int db,
+ struct amdgpu_ring **out_ring,
+ struct amdgpu_fence **out_fence)
+{
+ bool use_mmio = adev->gfx.mec.use_mmio_for_reset;
+ struct amdgpu_fence *fence;
+ struct amdgpu_ring *ring;
+ int i, r;
+
+ *out_ring = NULL;
+ *out_fence = NULL;
+ for (i = 0; i < adev->gfx.num_compute_rings; i++) {
+ ring = &adev->gfx.compute_ring[i];
+ if (ring == guilty_ring)
+ continue;
+ if (ring->doorbell_index == db) {
+ fence = amdgpu_ring_find_guilty_fence(ring);
+ r = amdgpu_gfx_mes_reset_queue_start(ring, 0, fence,
+ use_mmio);
+ if (r)
+ return r;
+ *out_ring = ring;
+ *out_fence = fence;
+ break;
+ }
+ }
+ return 0;
+}
+
+int amdgpu_gfx_reset_mes_compute(struct amdgpu_device *adev,
+ struct amdgpu_ring *ring,
+ struct amdgpu_fence *guilty_fence,
+ struct amdgpu_usermode_queue *uq,
+ unsigned int *hung_queue_count,
+ void *faulty_queue_input)
+{
+ struct amdgpu_mes_hung_queue_hqd_info *hqd_info =
+ (struct amdgpu_mes_hung_queue_hqd_info *)
+ &adev->gfx.mec.mes_hung_db_array[adev->mes.hung_queue_hqd_info_offset];
+ int i, r, pipe, queue, queue_type;
+ unsigned int num_hung = 0;
+ bool use_mmio = adev->gfx.mec.use_mmio_for_reset;
+ struct mes_remove_queue_input *queue_input = (struct mes_remove_queue_input *)faulty_queue_input;
+ struct amdgpu_gfx_deferred_entry deferred_end[AMDGPU_MAX_COMPUTE_RINGS + 1];
+ int n_deferred = 0;
+ int ring_err;
+
+ guard(mutex)(&adev->gfx.mec.reset_mutex);
+ /* stop the drm schedulers for all compute queues */
+ amdgpu_gfx_reset_stop_compute_scheds(adev, ring);
+ /* suspend all will determine which queues are hung.
+ * reset detect will return the array of bad queue doorbells
+ */
+ r = amdgpu_mes_suspend(adev, 0);
+ /* if suspend all success, it should no hang queue */
+ if (!r)
+ /* always reset the KCQ/userq since we need to signal the fence
+ * and we could be stuck in a loop which is preemptable.
+ */
+ goto fence_reset;
+ r = amdgpu_mes_detect_and_reset_hung_queues(adev, AMDGPU_RING_TYPE_COMPUTE,
+ true, &num_hung, adev->gfx.mec.mes_hung_db_array, 0);
+ if (r)
+ goto out;
+ if (hung_queue_count)
+ *hung_queue_count = num_hung;
+
+fence_reset:
+ /* reset the queue this came from if specified */
+ if (ring) {
+ r = amdgpu_gfx_mes_reset_queue_start(ring, 0, guilty_fence,
+ use_mmio);
+ if (r)
+ goto out;
+ deferred_end[n_deferred].ring = ring;
+ deferred_end[n_deferred].fence = guilty_fence;
+ n_deferred++;
+ }
+ if (uq) {
+ r = mes_userq_reset(uq);
+ if (r)
+ goto out;
+ }
+ for (i = 0; i < num_hung; i++) {
+ struct amdgpu_ring *hr = NULL;
+ struct amdgpu_fence *hf = NULL;
+
+ pipe = hqd_info[i].pipe_index;
+ queue = hqd_info[i].queue_index;
+ queue_type = hqd_info[i].queue_type;
+
+ /* reset any KCQs */
+ r = amdgpu_gfx_reset_mes_kcq(adev, ring,
+ adev->gfx.mec.mes_hung_db_array[i],
+ &hr, &hf);
+ if (r)
+ goto out;
+ if (hr) {
+ deferred_end[n_deferred].ring = hr;
+ deferred_end[n_deferred].fence = hf;
+ n_deferred++;
+ }
+ /* reset any KFD queues */
+ r = amdgpu_amdkfd_reset_mes_queue(adev, 0, queue_type, pipe, queue,
+ adev->gfx.mec.mes_hung_db_array[i]);
+ if (r)
+ goto out;
+ /* reset KGD user queues */
+ r = mes_userq_reset_queue(adev, uq, queue_type, pipe, queue,
+ adev->gfx.mec.mes_hung_db_array[i]);
+ if (r)
+ goto out;
+ }
+
+ /* MES doesn't detect any hung queue but we have a known bad queue
+ * and it is not KCQ
+ */
+ if (!num_hung && queue_input && !ring) {
+ /* MES suspend_all is successful means this bad queue is
+ * preempted successfuly. Remove it before resume all so it
+ * doesn't get mapped back
+ */
+ if (!down_read_trylock(&adev->reset_domain->sem)) {
+ r = -EIO;
+ goto out;
+ }
+ amdgpu_mes_lock(&adev->mes);
+ r = adev->mes.funcs->remove_hw_queue(&adev->mes, queue_input);
+ amdgpu_mes_unlock(&adev->mes);
+ up_read(&adev->reset_domain->sem);
+ }
+
+out:
+ /* resume all will enable the non-hung queues */
+ amdgpu_mes_resume(adev, 0);
+
+ /* Now CP is running again — replay backed-up commands and ring
+ * doorbells on each reset queue.
+ */
+ ring_err = r;
+ for (i = 0; i < n_deferred; i++) {
+ int er = amdgpu_ring_reset_helper_end(deferred_end[i].ring,
+ deferred_end[i].fence);
+
+ if (er && !ring_err)
+ ring_err = er;
+ }
+
+ if (!ring_err)
+ amdgpu_gfx_reset_start_compute_scheds(adev, ring);
+
+ /* If this reset is triggered by non-KCQ, the KCQ result after resume must
+ * not override the reset result; otherwise a false reset failure is returned
+ * to the non-KCQ caller
+ */
+ return ring ? ring_err : r;
+}
+
int amdgpu_gfx_cleaner_shader_sw_init(struct amdgpu_device *adev,
unsigned int cleaner_shader_size)
{
@@ -2460,9 +2733,8 @@ void amdgpu_gfx_profile_ring_begin_use(struct amdgpu_ring *ring)
else
profile = PP_SMC_POWER_PROFILE_COMPUTE;
- atomic_inc(&adev->gfx.total_submission_cnt);
-
- cancel_delayed_work_sync(&adev->gfx.idle_work);
+ if (!atomic_fetch_inc(&adev->gfx.total_submission_cnt))
+ cancel_delayed_work_sync(&adev->gfx.idle_work);
/* We can safely return early here because we've cancelled the
* the delayed work so there is no one else to set it to false
@@ -2490,9 +2762,9 @@ void amdgpu_gfx_profile_ring_end_use(struct amdgpu_ring *ring)
if (amdgpu_dpm_is_overdrive_enabled(adev))
return;
- atomic_dec(&ring->adev->gfx.total_submission_cnt);
-
- schedule_delayed_work(&ring->adev->gfx.idle_work, GFX_PROFILE_IDLE_TIMEOUT);
+ if (atomic_dec_and_test(&ring->adev->gfx.total_submission_cnt))
+ schedule_delayed_work(&ring->adev->gfx.idle_work,
+ GFX_PROFILE_IDLE_TIMEOUT);
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
index 54c1eb9c499b..aefd4f03b443 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
@@ -36,6 +36,8 @@
#include "amdgpu_ring_mux.h"
#include "amdgpu_xcp.h"
+struct amdgpu_usermode_queue;
+
/* GFX current status */
#define AMDGPU_GFX_NORMAL_MODE 0x00000000L
#define AMDGPU_GFX_SAFE_MODE 0x00000001L
@@ -116,6 +118,9 @@ struct amdgpu_mec {
u32 num_pipe_per_mec;
u32 num_queue_per_pipe;
void *mqd_backup[AMDGPU_MAX_COMPUTE_RINGS * AMDGPU_MAX_GC_INSTANCES];
+ bool use_mmio_for_reset;
+ u32 *mes_hung_db_array;
+ struct mutex reset_mutex;
};
struct amdgpu_mec_bitmap {
@@ -401,6 +406,7 @@ struct amdgpu_me {
uint32_t num_pipe_per_me;
uint32_t num_queue_per_pipe;
void *mqd_backup[AMDGPU_MAX_GFX_RINGS];
+ bool use_mmio_for_reset;
/* These are the resources for which amdgpu takes ownership */
DECLARE_BITMAP(queue_bitmap, AMDGPU_MAX_GFX_QUEUES);
@@ -479,8 +485,6 @@ struct amdgpu_gfx {
const struct amdgpu_gfx_funcs *funcs;
/* reset mask */
- uint32_t grbm_soft_reset;
- uint32_t srbm_soft_reset;
uint32_t gfx_supported_reset;
uint32_t compute_supported_reset;
@@ -543,6 +547,11 @@ struct amdgpu_gfx {
bool disable_uq;
};
+struct amdgpu_gfx_deferred_entry {
+ struct amdgpu_ring *ring;
+ struct amdgpu_fence *fence;
+};
+
struct amdgpu_gfx_ras_reg_entry {
struct amdgpu_ras_err_status_reg_entry reg_entry;
enum amdgpu_gfx_ras_mem_id_type mem_id_type;
@@ -641,6 +650,12 @@ int amdgpu_gfx_poison_consumption_handler(struct amdgpu_device *adev,
bool amdgpu_gfx_is_master_xcc(struct amdgpu_device *adev, int xcc_id);
int amdgpu_gfx_sysfs_init(struct amdgpu_device *adev);
void amdgpu_gfx_sysfs_fini(struct amdgpu_device *adev);
+int amdgpu_gfx_reset_mes_compute(struct amdgpu_device *adev,
+ struct amdgpu_ring *ring,
+ struct amdgpu_fence *guilty_fence,
+ struct amdgpu_usermode_queue *uq,
+ unsigned int *hung_queue_count,
+ void *faulty_queue_input);
void amdgpu_gfx_ras_error_func(struct amdgpu_device *adev,
void *ras_error_status,
void (*func)(struct amdgpu_device *adev, void *ras_error_status,
@@ -667,6 +682,11 @@ void amdgpu_debugfs_compute_sched_mask_init(struct amdgpu_device *adev);
int amdgpu_gfx_ring_preempt_ib(struct amdgpu_ring *ring);
+int amdgpu_gfx_mes_reset_queue(struct amdgpu_ring *ring,
+ unsigned int vmid,
+ struct amdgpu_fence *timedout_fence,
+ bool use_mmio);
+
static inline const char *amdgpu_gfx_compute_mode_desc(int mode)
{
switch (mode) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
index 5d6149ba7ab7..4000b2c6fc98 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
@@ -1763,10 +1763,15 @@ int amdgpu_gmc_init_mem_ranges(struct amdgpu_device *adev)
valid = true;
else
valid = amdgpu_gmc_validate_partition_info(adev);
- if (!valid) {
- /* TODO: handle invalid case */
+ if (!valid)
dev_warn(adev->dev,
"Mem ranges not matching with hardware config\n");
+
+ if (!adev->gmc.num_mem_partitions) {
+ dev_err(adev->dev, "num_mem_partitions is zero\n");
+ kfree(adev->gmc.mem_partitions);
+ adev->gmc.mem_partitions = NULL;
+ return -EINVAL;
}
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
index ddb0d500e0fa..3ca187f5ade8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
@@ -286,7 +286,6 @@ struct amdgpu_gmc {
struct amdgpu_irq_src vm_fault;
uint32_t vram_type;
uint8_t vram_vendor;
- uint32_t srbm_soft_reset;
bool prt_warning;
uint32_t sdpif_register;
/* apertures */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c
index 6aa54156bbc9..33a04113ed74 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c
@@ -369,43 +369,152 @@ int amdgpu_device_ip_wait_for_idle(struct amdgpu_device *adev,
}
/**
- * amdgpu_device_ip_is_hw - is the hardware IP enabled
+ * amdgpu_device_ip_is_valid - is the hardware IP valid
*
* @adev: amdgpu_device pointer
* @block_type: Type of hardware IP (SMU, GFX, UVD, etc.)
*
- * Check if the hardware IP is enable or not.
- * Returns true if it the IP is enable, false if not.
+ * Check if the hardware IP is valid or not.
+ * Returns true if it the IP is valid, false if not.
*/
-bool amdgpu_device_ip_is_hw(struct amdgpu_device *adev,
- enum amd_ip_block_type block_type)
+bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev,
+ enum amd_ip_block_type block_type)
{
struct amdgpu_ip_block *ip_block;
ip_block = amdgpu_device_ip_get_ip_block(adev, block_type);
if (ip_block)
- return ip_block->status.hw;
+ return ip_block->status.valid;
return false;
}
/**
- * amdgpu_device_ip_is_valid - is the hardware IP valid
+ * amdgpu_ip_from_ring() - Find IP block type corresponding to ring type.
*
- * @adev: amdgpu_device pointer
- * @block_type: Type of hardware IP (SMU, GFX, UVD, etc.)
+ * @ring_type: The ring type whose IP block you are looking for.
+ */
+static enum amd_ip_block_type amdgpu_ip_from_ring(const enum amdgpu_ring_type ring_type)
+{
+ switch (ring_type) {
+ case AMDGPU_RING_TYPE_GFX:
+ case AMDGPU_RING_TYPE_COMPUTE:
+ return AMD_IP_BLOCK_TYPE_GFX;
+
+ case AMDGPU_RING_TYPE_SDMA:
+ return AMD_IP_BLOCK_TYPE_SDMA;
+
+ case AMDGPU_RING_TYPE_UVD:
+ case AMDGPU_RING_TYPE_UVD_ENC:
+ return AMD_IP_BLOCK_TYPE_UVD;
+
+ case AMDGPU_RING_TYPE_VCE:
+ return AMD_IP_BLOCK_TYPE_VCE;
+
+ case AMDGPU_RING_TYPE_VCN_DEC:
+ case AMDGPU_RING_TYPE_VCN_ENC:
+ return AMD_IP_BLOCK_TYPE_VCN;
+
+ case AMDGPU_RING_TYPE_VCN_JPEG:
+ return AMD_IP_BLOCK_TYPE_JPEG;
+
+ case AMDGPU_RING_TYPE_VPE:
+ return AMD_IP_BLOCK_TYPE_VPE;
+
+ default:
+ return AMD_IP_BLOCK_TYPE_NUM;
+ }
+}
+
+/**
+ * amdgpu_ring_mask_from_ip() - Find mask of ring types corresponding to an IP block type.
*
- * Check if the hardware IP is valid or not.
- * Returns true if it the IP is valid, false if not.
+ * @ip_type: The IP block type whose rings you are looking for.
*/
-bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev,
- enum amd_ip_block_type block_type)
+static u32 amdgpu_ring_mask_from_ip(const enum amd_ip_block_type ip_type)
+{
+ switch (ip_type) {
+ case AMD_IP_BLOCK_TYPE_GFX:
+ return BIT(AMDGPU_RING_TYPE_GFX) | BIT(AMDGPU_RING_TYPE_COMPUTE);
+
+ case AMD_IP_BLOCK_TYPE_SDMA:
+ return BIT(AMDGPU_RING_TYPE_SDMA);
+
+ case AMD_IP_BLOCK_TYPE_UVD:
+ return BIT(AMDGPU_RING_TYPE_UVD) | BIT(AMDGPU_RING_TYPE_UVD_ENC);
+
+ case AMD_IP_BLOCK_TYPE_VCE:
+ return BIT(AMD_IP_BLOCK_TYPE_VCE);
+
+ case AMD_IP_BLOCK_TYPE_VCN:
+ return BIT(AMDGPU_RING_TYPE_VCN_DEC) | BIT(AMDGPU_RING_TYPE_VCN_ENC);
+
+ case AMD_IP_BLOCK_TYPE_JPEG:
+ return BIT(AMDGPU_RING_TYPE_VCN_JPEG);
+
+ case AMD_IP_BLOCK_TYPE_VPE:
+ return BIT(AMDGPU_RING_TYPE_VPE);
+
+ default:
+ return 0;
+ }
+}
+
+/**
+ * amdgpu_device_ip_soft_reset() - Perform a graceful soft reset on an IP block.
+ *
+ * @guilty_ring: The ring which is guilty of causing a reset.
+ * @guilty_fence: The fence which didn't signal.
+ *
+ * IP block soft reset is used when attempting to recover
+ * from a GPU hang in a situation where a more fine grained
+ * reset type isn't available or didn't work. This effectively
+ * resets all rings that belong to the same device IP block
+ * and re-initializes the device IP block.
+ *
+ * The reset is handled gracefully, meaning that we try to
+ * minimize collateral damage (ie. avoid rejecting non-guilty jobs)
+ * as well as back up and restore the contents of all rings
+ * so that the system can move on from the hang.
+ */
+int amdgpu_device_ip_soft_reset(struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *guilty_fence)
{
+ struct amdgpu_device *adev = guilty_ring->adev;
struct amdgpu_ip_block *ip_block;
+ enum amd_ip_block_type ip_type;
+ u32 ring_type_mask;
+ int r;
- ip_block = amdgpu_device_ip_get_ip_block(adev, block_type);
- if (ip_block)
- return ip_block->status.valid;
+ ip_type = amdgpu_ip_from_ring(guilty_ring->funcs->type);
+ ip_block = amdgpu_device_ip_get_ip_block(adev, ip_type);
- return false;
+ if (!ip_block || !ip_block->version->funcs->soft_reset) {
+ dev_warn(adev->dev, "IP block soft reset not supported on %s\n",
+ ip_block->version->funcs->name);
+ return -EOPNOTSUPP;
+ }
+
+ dev_err(adev->dev, "Starting %s IP block soft reset\n",
+ ip_block->version->funcs->name);
+
+ ring_type_mask = amdgpu_ring_mask_from_ip(ip_type);
+
+ amdgpu_device_lock_reset_domain(adev->reset_domain);
+ amdgpu_multi_ring_reset_helper_begin(ring_type_mask, guilty_ring, guilty_fence);
+
+ r = ip_block->version->funcs->soft_reset(ip_block);
+
+ r = amdgpu_multi_ring_reset_helper_end(ring_type_mask, guilty_ring, r);
+ amdgpu_device_unlock_reset_domain(adev->reset_domain);
+
+ if (r) {
+ dev_err(adev->dev, "Failed %s IP block soft reset: %d\n",
+ ip_block->version->funcs->name, r);
+ return r;
+ }
+
+ dev_err(adev->dev, "Successful %s IP block soft reset\n",
+ ip_block->version->funcs->name);
+ return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h
index 1d0df6d93957..70fc4e5db51f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h
@@ -68,6 +68,7 @@ enum amd_hw_ip_block_type {
ISP_HWIP,
ATU_HWIP,
AIGC_HWIP,
+ UMSCH_HWIP,
MAX_HWIP
};
@@ -84,6 +85,9 @@ enum amd_hw_ip_block_type {
#define IP_VERSION_SUBREV(ver) ((ver) & 0xF)
#define IP_VERSION_MAJ_MIN_REV(ver) ((ver) >> 8)
+struct amdgpu_ring;
+struct amdgpu_fence;
+
struct amdgpu_ip_map_info {
/* Map of logical to actual dev instances/mask */
uint32_t dev_inst[MAX_HWIP][HWIP_MAX_INSTANCE];
@@ -146,9 +150,9 @@ void amdgpu_device_ip_get_clockgating_state(struct amdgpu_device *adev,
u64 *flags);
int amdgpu_device_ip_wait_for_idle(struct amdgpu_device *adev,
enum amd_ip_block_type block_type);
-bool amdgpu_device_ip_is_hw(struct amdgpu_device *adev,
- enum amd_ip_block_type block_type);
bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev,
enum amd_ip_block_type block_type);
+int amdgpu_device_ip_soft_reset(struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *guilty_fence);
#endif /* __AMDGPU_IP_H__ */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index 9ecc6387c1eb..cff73f1b5a72 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -112,7 +112,7 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job)
amdgpu_job_core_dump(adev, job);
if (amdgpu_gpu_recovery &&
- amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_SOFT_RESET) &&
+ amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_SOFT_RECOVERY) &&
amdgpu_ring_soft_recovery(ring, job->vmid, s_job->s_fence->parent)) {
dev_err(adev->dev, "ring %s timeout, but soft recovered\n",
s_job->sched->name);
@@ -151,6 +151,17 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job)
dev_err(adev->dev, "Ring %s reset failed\n", ring->sched.name);
}
+ /* Attempt an IP block soft reset, if supported. */
+ if (amdgpu_gpu_recovery &&
+ amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET)) {
+ r = amdgpu_device_ip_soft_reset(ring, job->hw_fence);
+ if (!r) {
+ atomic_inc(&ring->adev->gpu_reset_counter);
+ drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, info);
+ goto exit;
+ }
+ }
+
if (dma_fence_get_status(&s_job->s_fence->finished) == 0)
dma_fence_set_error(&s_job->s_fence->finished, -ETIME);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
index 63ee6ba6a931..57935c321515 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c
@@ -134,8 +134,8 @@ void amdgpu_jpeg_ring_begin_use(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
- atomic_inc(&adev->jpeg.total_submission_cnt);
- cancel_delayed_work_sync(&adev->jpeg.idle_work);
+ if (!atomic_fetch_inc(&adev->jpeg.total_submission_cnt))
+ cancel_delayed_work_sync(&adev->jpeg.idle_work);
mutex_lock(&adev->jpeg.jpeg_pg_lock);
amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_JPEG,
@@ -145,8 +145,9 @@ void amdgpu_jpeg_ring_begin_use(struct amdgpu_ring *ring)
void amdgpu_jpeg_ring_end_use(struct amdgpu_ring *ring)
{
- atomic_dec(&ring->adev->jpeg.total_submission_cnt);
- schedule_delayed_work(&ring->adev->jpeg.idle_work, JPEG_IDLE_TIMEOUT);
+ if (atomic_dec_and_test(&ring->adev->jpeg.total_submission_cnt))
+ schedule_delayed_work(&ring->adev->jpeg.idle_work,
+ JPEG_IDLE_TIMEOUT);
}
int amdgpu_jpeg_dec_ring_test_ring(struct amdgpu_ring *ring)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h
index 346ae0ab09d3..fe95d9188713 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h
@@ -149,6 +149,9 @@ struct amdgpu_jpeg {
u32 *ip_dump;
u32 reg_count;
const struct amdgpu_hwip_reg_entry *reg_list;
+
+ bool disable_uq;
+ bool disable_kq;
};
int amdgpu_jpeg_sw_init(struct amdgpu_device *adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 71272f40feef..215aa678d1d0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -1424,6 +1424,33 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
}
/**
+ * amdgpu_proc_options_ioctl - set per-fd user options
+ *
+ * @dev: drm dev pointer
+ * @data: pointer to struct drm_amdgpu_proc_options
+ * @filp: drm file
+ *
+ * Sets options stored on the per-file amdgpu_fpriv. Currently the only
+ * supported option is %AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY which
+ * controls how KFD delivers SIGBUS for poison/RAS events to the calling
+ * process (immediate, suppressed, or delayed by N milliseconds).
+ */
+int amdgpu_proc_options_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *filp)
+{
+ struct drm_amdgpu_proc_options *args = data;
+
+ switch (args->op) {
+ case AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY:
+ return amdgpu_amdkfd_set_sigbus_delay(current,
+ args->kfd_sigbus_delay.value);
+ default:
+ DRM_DEBUG_KMS("Invalid user option op %u\n", args->op);
+ return -EINVAL;
+ }
+}
+
+/**
* amdgpu_driver_open_kms - drm callback for open
*
* @dev: drm dev pointer
@@ -1504,8 +1531,7 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
if (r)
goto error_vm;
- mutex_init(&fpriv->bo_list_lock);
- idr_init_base(&fpriv->bo_list_handles, 1);
+ xa_init_flags(&fpriv->bo_list_handles, XA_FLAGS_ALLOC1);
r = amdgpu_userq_mgr_init(&fpriv->userq_mgr, file_priv, adev);
if (r)
@@ -1550,8 +1576,8 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
struct amdgpu_fpriv *fpriv = file_priv->driver_priv;
struct amdgpu_bo_list *list;
struct amdgpu_bo *pd;
+ unsigned long handle;
u32 pasid;
- int handle;
if (!fpriv)
return;
@@ -1587,11 +1613,9 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
amdgpu_pasid_free_delayed(pd->tbo.base.resv, pasid);
amdgpu_bo_unref(&pd);
- idr_for_each_entry(&fpriv->bo_list_handles, list, handle)
+ xa_for_each(&fpriv->bo_list_handles, handle, list)
amdgpu_bo_list_put(list);
-
- idr_destroy(&fpriv->bo_list_handles);
- mutex_destroy(&fpriv->bo_list_lock);
+ xa_destroy(&fpriv->bo_list_handles);
kfree(fpriv);
file_priv->driver_priv = NULL;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c
index cc6d1a4e4c3a..9a7f7d2b2767 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c
@@ -27,16 +27,6 @@
#include "umc/umc_6_7_0_offset.h"
#include "umc/umc_6_7_0_sh_mask.h"
-static bool amdgpu_mca_is_deferred_error(struct amdgpu_device *adev,
- uint64_t mc_status)
-{
- if (adev->umc.ras->check_ecc_err_status)
- return adev->umc.ras->check_ecc_err_status(adev,
- AMDGPU_MCA_ERROR_TYPE_DE, &mc_status);
-
- return false;
-}
-
void amdgpu_mca_query_correctable_error_count(struct amdgpu_device *adev,
uint64_t mc_status_addr,
unsigned long *error_count)
@@ -155,479 +145,3 @@ int amdgpu_mca_mpio_ras_sw_init(struct amdgpu_device *adev)
return 0;
}
-
-static void amdgpu_mca_bank_set_init(struct mca_bank_set *mca_set)
-{
- if (!mca_set)
- return;
-
- memset(mca_set, 0, sizeof(*mca_set));
- INIT_LIST_HEAD(&mca_set->list);
-}
-
-static int amdgpu_mca_bank_set_add_entry(struct mca_bank_set *mca_set, struct mca_bank_entry *entry)
-{
- struct mca_bank_node *node;
-
- if (!entry)
- return -EINVAL;
-
- node = kvzalloc_obj(*node);
- if (!node)
- return -ENOMEM;
-
- memcpy(&node->entry, entry, sizeof(*entry));
-
- INIT_LIST_HEAD(&node->node);
- list_add_tail(&node->node, &mca_set->list);
-
- mca_set->nr_entries++;
-
- return 0;
-}
-
-static int amdgpu_mca_bank_set_merge(struct mca_bank_set *mca_set, struct mca_bank_set *new)
-{
- struct mca_bank_node *node;
-
- list_for_each_entry(node, &new->list, node)
- amdgpu_mca_bank_set_add_entry(mca_set, &node->entry);
-
- return 0;
-}
-
-static void amdgpu_mca_bank_set_remove_node(struct mca_bank_set *mca_set, struct mca_bank_node *node)
-{
- if (!node)
- return;
-
- list_del(&node->node);
- kvfree(node);
-
- mca_set->nr_entries--;
-}
-
-static void amdgpu_mca_bank_set_release(struct mca_bank_set *mca_set)
-{
- struct mca_bank_node *node, *tmp;
-
- if (list_empty(&mca_set->list))
- return;
-
- list_for_each_entry_safe(node, tmp, &mca_set->list, node)
- amdgpu_mca_bank_set_remove_node(mca_set, node);
-}
-
-void amdgpu_mca_smu_init_funcs(struct amdgpu_device *adev, const struct amdgpu_mca_smu_funcs *mca_funcs)
-{
- struct amdgpu_mca *mca = &adev->mca;
-
- mca->mca_funcs = mca_funcs;
-}
-
-int amdgpu_mca_init(struct amdgpu_device *adev)
-{
- struct amdgpu_mca *mca = &adev->mca;
- struct mca_bank_cache *mca_cache;
- int i;
-
- atomic_set(&mca->ue_update_flag, 0);
-
- for (i = 0; i < ARRAY_SIZE(mca->mca_caches); i++) {
- mca_cache = &mca->mca_caches[i];
- mutex_init(&mca_cache->lock);
- amdgpu_mca_bank_set_init(&mca_cache->mca_set);
- }
-
- return 0;
-}
-
-void amdgpu_mca_fini(struct amdgpu_device *adev)
-{
- struct amdgpu_mca *mca = &adev->mca;
- struct mca_bank_cache *mca_cache;
- int i;
-
- atomic_set(&mca->ue_update_flag, 0);
-
- for (i = 0; i < ARRAY_SIZE(mca->mca_caches); i++) {
- mca_cache = &mca->mca_caches[i];
- amdgpu_mca_bank_set_release(&mca_cache->mca_set);
- mutex_destroy(&mca_cache->lock);
- }
-}
-
-int amdgpu_mca_reset(struct amdgpu_device *adev)
-{
- amdgpu_mca_fini(adev);
-
- return amdgpu_mca_init(adev);
-}
-
-int amdgpu_mca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable)
-{
- const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs;
-
- if (mca_funcs && mca_funcs->mca_set_debug_mode)
- return mca_funcs->mca_set_debug_mode(adev, enable);
-
- return -EOPNOTSUPP;
-}
-
-static void amdgpu_mca_smu_mca_bank_dump(struct amdgpu_device *adev, int idx, struct mca_bank_entry *entry,
- struct ras_query_context *qctx)
-{
- u64 event_id = qctx ? qctx->evid.event_id : RAS_EVENT_INVALID_ID;
-
- RAS_EVENT_LOG(adev, event_id, HW_ERR "Accelerator Check Architecture events logged\n");
- RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].STATUS=0x%016llx\n",
- idx, entry->regs[MCA_REG_IDX_STATUS]);
- RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].ADDR=0x%016llx\n",
- idx, entry->regs[MCA_REG_IDX_ADDR]);
- RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].MISC0=0x%016llx\n",
- idx, entry->regs[MCA_REG_IDX_MISC0]);
- RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].IPID=0x%016llx\n",
- idx, entry->regs[MCA_REG_IDX_IPID]);
- RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].SYND=0x%016llx\n",
- idx, entry->regs[MCA_REG_IDX_SYND]);
-}
-
-static int amdgpu_mca_smu_get_valid_mca_count(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, uint32_t *count)
-{
- const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs;
-
- if (!count)
- return -EINVAL;
-
- if (mca_funcs && mca_funcs->mca_get_valid_mca_count)
- return mca_funcs->mca_get_valid_mca_count(adev, type, count);
-
- return -EOPNOTSUPP;
-}
-
-static int amdgpu_mca_smu_get_mca_entry(struct amdgpu_device *adev, enum amdgpu_mca_error_type type,
- int idx, struct mca_bank_entry *entry)
-{
- const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs;
- int count;
-
- if (!mca_funcs || !mca_funcs->mca_get_mca_entry)
- return -EOPNOTSUPP;
-
- switch (type) {
- case AMDGPU_MCA_ERROR_TYPE_UE:
- count = mca_funcs->max_ue_count;
- break;
- case AMDGPU_MCA_ERROR_TYPE_CE:
- count = mca_funcs->max_ce_count;
- break;
- default:
- return -EINVAL;
- }
-
- if (idx >= count)
- return -EINVAL;
-
- return mca_funcs->mca_get_mca_entry(adev, type, idx, entry);
-}
-
-static bool amdgpu_mca_bank_should_update(struct amdgpu_device *adev, enum amdgpu_mca_error_type type)
-{
- struct amdgpu_mca *mca = &adev->mca;
- bool ret = true;
-
- /*
- * Because the UE Valid MCA count will only be cleared after reset,
- * in order to avoid repeated counting of the error count,
- * the aca bank is only updated once during the gpu recovery stage.
- */
- if (type == AMDGPU_MCA_ERROR_TYPE_UE) {
- if (amdgpu_ras_intr_triggered())
- ret = atomic_cmpxchg(&mca->ue_update_flag, 0, 1) == 0;
- else
- atomic_set(&mca->ue_update_flag, 0);
- }
-
- return ret;
-}
-
-static bool amdgpu_mca_bank_should_dump(struct amdgpu_device *adev, enum amdgpu_mca_error_type type,
- struct mca_bank_entry *entry)
-{
- bool ret;
-
- switch (type) {
- case AMDGPU_MCA_ERROR_TYPE_CE:
- ret = amdgpu_mca_is_deferred_error(adev, entry->regs[MCA_REG_IDX_STATUS]);
- break;
- case AMDGPU_MCA_ERROR_TYPE_UE:
- default:
- ret = true;
- break;
- }
-
- return ret;
-}
-
-static int amdgpu_mca_smu_get_mca_set(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, struct mca_bank_set *mca_set,
- struct ras_query_context *qctx)
-{
- struct mca_bank_entry entry;
- uint32_t count = 0, i;
- int ret;
-
- if (!mca_set)
- return -EINVAL;
-
- if (!amdgpu_mca_bank_should_update(adev, type))
- return 0;
-
- ret = amdgpu_mca_smu_get_valid_mca_count(adev, type, &count);
- if (ret)
- return ret;
-
- for (i = 0; i < count; i++) {
- memset(&entry, 0, sizeof(entry));
- ret = amdgpu_mca_smu_get_mca_entry(adev, type, i, &entry);
- if (ret)
- return ret;
-
- amdgpu_mca_bank_set_add_entry(mca_set, &entry);
-
- if (amdgpu_mca_bank_should_dump(adev, type, &entry))
- amdgpu_mca_smu_mca_bank_dump(adev, i, &entry, qctx);
- }
-
- return 0;
-}
-
-static int amdgpu_mca_smu_parse_mca_error_count(struct amdgpu_device *adev, enum amdgpu_ras_block blk,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count)
-{
- const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs;
-
- if (!count || !entry)
- return -EINVAL;
-
- if (!mca_funcs || !mca_funcs->mca_parse_mca_error_count)
- return -EOPNOTSUPP;
-
- return mca_funcs->mca_parse_mca_error_count(adev, blk, type, entry, count);
-}
-
-static int amdgpu_mca_dispatch_mca_set(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type,
- struct mca_bank_set *mca_set, struct ras_err_data *err_data)
-{
- struct amdgpu_smuio_mcm_config_info mcm_info;
- struct mca_bank_node *node, *tmp;
- struct mca_bank_entry *entry;
- uint32_t count;
- int ret;
-
- if (!mca_set)
- return -EINVAL;
-
- if (!mca_set->nr_entries)
- return 0;
-
- list_for_each_entry_safe(node, tmp, &mca_set->list, node) {
- entry = &node->entry;
-
- count = 0;
- ret = amdgpu_mca_smu_parse_mca_error_count(adev, blk, type, entry, &count);
- if (ret && ret != -EOPNOTSUPP)
- return ret;
-
- if (!count)
- continue;
-
- memset(&mcm_info, 0, sizeof(mcm_info));
-
- mcm_info.socket_id = entry->info.socket_id;
- mcm_info.die_id = entry->info.aid;
-
- if (type == AMDGPU_MCA_ERROR_TYPE_UE) {
- amdgpu_ras_error_statistic_ue_count(err_data,
- &mcm_info, (uint64_t)count);
- } else {
- if (amdgpu_mca_is_deferred_error(adev, entry->regs[MCA_REG_IDX_STATUS]))
- amdgpu_ras_error_statistic_de_count(err_data,
- &mcm_info, (uint64_t)count);
- else
- amdgpu_ras_error_statistic_ce_count(err_data,
- &mcm_info, (uint64_t)count);
- }
-
- amdgpu_mca_bank_set_remove_node(mca_set, node);
- }
-
- return 0;
-}
-
-static int amdgpu_mca_add_mca_set_to_cache(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, struct mca_bank_set *new)
-{
- struct mca_bank_cache *mca_cache = &adev->mca.mca_caches[type];
- int ret;
-
- mutex_lock(&mca_cache->lock);
- ret = amdgpu_mca_bank_set_merge(&mca_cache->mca_set, new);
- mutex_unlock(&mca_cache->lock);
-
- return ret;
-}
-
-int amdgpu_mca_smu_log_ras_error(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type,
- struct ras_err_data *err_data, struct ras_query_context *qctx)
-{
- struct mca_bank_set mca_set;
- struct mca_bank_cache *mca_cache = &adev->mca.mca_caches[type];
- int ret;
-
- amdgpu_mca_bank_set_init(&mca_set);
-
- ret = amdgpu_mca_smu_get_mca_set(adev, type, &mca_set, qctx);
- if (ret)
- goto out_mca_release;
-
- ret = amdgpu_mca_dispatch_mca_set(adev, blk, type, &mca_set, err_data);
- if (ret)
- goto out_mca_release;
-
- /* add remain mca bank to mca cache */
- if (mca_set.nr_entries) {
- ret = amdgpu_mca_add_mca_set_to_cache(adev, type, &mca_set);
- if (ret)
- goto out_mca_release;
- }
-
- /* dispatch mca set again if mca cache has valid data */
- mutex_lock(&mca_cache->lock);
- if (mca_cache->mca_set.nr_entries)
- ret = amdgpu_mca_dispatch_mca_set(adev, blk, type, &mca_cache->mca_set, err_data);
- mutex_unlock(&mca_cache->lock);
-
-out_mca_release:
- amdgpu_mca_bank_set_release(&mca_set);
-
- return ret;
-}
-
-#if defined(CONFIG_DEBUG_FS)
-static int amdgpu_mca_smu_debug_mode_set(void *data, u64 val)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)data;
- int ret;
-
- ret = amdgpu_ras_set_mca_debug_mode(adev, val ? true : false);
- if (ret)
- return ret;
-
- dev_info(adev->dev, "amdgpu set smu mca debug mode %s success\n", val ? "on" : "off");
-
- return 0;
-}
-
-static void mca_dump_entry(struct seq_file *m, struct mca_bank_entry *entry)
-{
- int i, idx = entry->idx;
- int reg_idx_array[] = {
- MCA_REG_IDX_STATUS,
- MCA_REG_IDX_ADDR,
- MCA_REG_IDX_MISC0,
- MCA_REG_IDX_IPID,
- MCA_REG_IDX_SYND,
- };
-
- seq_printf(m, "mca entry[%d].type: %s\n", idx, entry->type == AMDGPU_MCA_ERROR_TYPE_UE ? "UE" : "CE");
- seq_printf(m, "mca entry[%d].ip: %d\n", idx, entry->ip);
- seq_printf(m, "mca entry[%d].info: socketid:%d aid:%d hwid:0x%03x mcatype:0x%04x\n",
- idx, entry->info.socket_id, entry->info.aid, entry->info.hwid, entry->info.mcatype);
-
- for (i = 0; i < ARRAY_SIZE(reg_idx_array); i++)
- seq_printf(m, "mca entry[%d].regs[%d]: 0x%016llx\n", idx, reg_idx_array[i], entry->regs[reg_idx_array[i]]);
-}
-
-static int mca_dump_show(struct seq_file *m, enum amdgpu_mca_error_type type)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
- struct mca_bank_node *node;
- struct mca_bank_set mca_set;
- struct ras_query_context qctx;
- int ret;
-
- amdgpu_mca_bank_set_init(&mca_set);
-
- qctx.evid.event_id = RAS_EVENT_INVALID_ID;
- ret = amdgpu_mca_smu_get_mca_set(adev, type, &mca_set, &qctx);
- if (ret)
- goto err_free_mca_set;
-
- seq_printf(m, "amdgpu smu %s valid mca count: %d\n",
- type == AMDGPU_MCA_ERROR_TYPE_UE ? "UE" : "CE", mca_set.nr_entries);
-
- if (!mca_set.nr_entries)
- goto err_free_mca_set;
-
- list_for_each_entry(node, &mca_set.list, node)
- mca_dump_entry(m, &node->entry);
-
- /* add mca bank to mca bank cache */
- ret = amdgpu_mca_add_mca_set_to_cache(adev, type, &mca_set);
-
-err_free_mca_set:
- amdgpu_mca_bank_set_release(&mca_set);
-
- return ret;
-}
-
-static int mca_dump_ce_show(struct seq_file *m, void *unused)
-{
- return mca_dump_show(m, AMDGPU_MCA_ERROR_TYPE_CE);
-}
-
-static int mca_dump_ce_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mca_dump_ce_show, inode->i_private);
-}
-
-static const struct file_operations mca_ce_dump_debug_fops = {
- .owner = THIS_MODULE,
- .open = mca_dump_ce_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int mca_dump_ue_show(struct seq_file *m, void *unused)
-{
- return mca_dump_show(m, AMDGPU_MCA_ERROR_TYPE_UE);
-}
-
-static int mca_dump_ue_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mca_dump_ue_show, inode->i_private);
-}
-
-static const struct file_operations mca_ue_dump_debug_fops = {
- .owner = THIS_MODULE,
- .open = mca_dump_ue_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-DEFINE_DEBUGFS_ATTRIBUTE(mca_debug_mode_fops, NULL, amdgpu_mca_smu_debug_mode_set, "%llu\n");
-#endif
-
-void amdgpu_mca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root)
-{
-#if defined(CONFIG_DEBUG_FS)
- if (!root)
- return;
-
- debugfs_create_file("mca_debug_mode", 0200, root, adev, &mca_debug_mode_fops);
- debugfs_create_file("mca_ue_dump", 0400, root, adev, &mca_ue_dump_debug_fops);
- debugfs_create_file("mca_ce_dump", 0400, root, adev, &mca_ce_dump_debug_fops);
-#endif
-}
-
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h
index e80323ff90c1..6d12f8a516d5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h
@@ -23,45 +23,6 @@
#include "amdgpu_ras.h"
-#define MCA_MAX_REGS_COUNT (16)
-
-#define MCA_REG_FIELD(x, h, l) (((x) & GENMASK_ULL(h, l)) >> l)
-#define MCA_REG__STATUS__VAL(x) MCA_REG_FIELD(x, 63, 63)
-#define MCA_REG__STATUS__OVERFLOW(x) MCA_REG_FIELD(x, 62, 62)
-#define MCA_REG__STATUS__UC(x) MCA_REG_FIELD(x, 61, 61)
-#define MCA_REG__STATUS__EN(x) MCA_REG_FIELD(x, 60, 60)
-#define MCA_REG__STATUS__MISCV(x) MCA_REG_FIELD(x, 59, 59)
-#define MCA_REG__STATUS__ADDRV(x) MCA_REG_FIELD(x, 58, 58)
-#define MCA_REG__STATUS__PCC(x) MCA_REG_FIELD(x, 57, 57)
-#define MCA_REG__STATUS__ERRCOREIDVAL(x) MCA_REG_FIELD(x, 56, 56)
-#define MCA_REG__STATUS__TCC(x) MCA_REG_FIELD(x, 55, 55)
-#define MCA_REG__STATUS__SYNDV(x) MCA_REG_FIELD(x, 53, 53)
-#define MCA_REG__STATUS__CECC(x) MCA_REG_FIELD(x, 46, 46)
-#define MCA_REG__STATUS__UECC(x) MCA_REG_FIELD(x, 45, 45)
-#define MCA_REG__STATUS__DEFERRED(x) MCA_REG_FIELD(x, 44, 44)
-#define MCA_REG__STATUS__POISON(x) MCA_REG_FIELD(x, 43, 43)
-#define MCA_REG__STATUS__SCRUB(x) MCA_REG_FIELD(x, 40, 40)
-#define MCA_REG__STATUS__ERRCOREID(x) MCA_REG_FIELD(x, 37, 32)
-#define MCA_REG__STATUS__ADDRLSB(x) MCA_REG_FIELD(x, 29, 24)
-#define MCA_REG__STATUS__ERRORCODEEXT(x) MCA_REG_FIELD(x, 21, 16)
-#define MCA_REG__STATUS__ERRORCODE(x) MCA_REG_FIELD(x, 15, 0)
-
-#define MCA_REG__MISC0__ERRCNT(x) MCA_REG_FIELD(x, 43, 32)
-
-#define MCA_REG__SYND__ERRORINFORMATION(x) MCA_REG_FIELD(x, 17, 0)
-
-enum amdgpu_mca_ip {
- AMDGPU_MCA_IP_UNKNOW = -1,
- AMDGPU_MCA_IP_PSP = 0,
- AMDGPU_MCA_IP_SDMA,
- AMDGPU_MCA_IP_GC,
- AMDGPU_MCA_IP_SMU,
- AMDGPU_MCA_IP_MP5,
- AMDGPU_MCA_IP_UMC,
- AMDGPU_MCA_IP_PCS_XGMI,
- AMDGPU_MCA_IP_COUNT,
-};
-
enum amdgpu_mca_error_type {
AMDGPU_MCA_ERROR_TYPE_UE = 0,
AMDGPU_MCA_ERROR_TYPE_CE,
@@ -77,77 +38,20 @@ struct amdgpu_mca_ras {
struct amdgpu_mca_ras_block *ras;
};
-struct mca_bank_set {
- int nr_entries;
- struct list_head list;
-};
-
-struct mca_bank_cache {
- struct mca_bank_set mca_set;
- struct mutex lock;
-};
-
struct amdgpu_mca {
struct amdgpu_mca_ras mp0;
struct amdgpu_mca_ras mp1;
struct amdgpu_mca_ras mpio;
- const struct amdgpu_mca_smu_funcs *mca_funcs;
- struct mca_bank_cache mca_caches[AMDGPU_MCA_ERROR_TYPE_DE];
- atomic_t ue_update_flag;
-};
-
-enum mca_reg_idx {
- MCA_REG_IDX_STATUS = 1,
- MCA_REG_IDX_ADDR = 2,
- MCA_REG_IDX_MISC0 = 3,
- MCA_REG_IDX_IPID = 5,
- MCA_REG_IDX_SYND = 6,
- MCA_REG_IDX_COUNT = 16,
-};
-
-struct mca_bank_info {
- int socket_id;
- int aid;
- int hwid;
- int mcatype;
-};
-
-struct mca_bank_entry {
- int idx;
- enum amdgpu_mca_error_type type;
- enum amdgpu_mca_ip ip;
- struct mca_bank_info info;
- uint64_t regs[MCA_MAX_REGS_COUNT];
-};
-
-struct mca_bank_node {
- struct mca_bank_entry entry;
- struct list_head node;
-};
-
-struct amdgpu_mca_smu_funcs {
- int max_ue_count;
- int max_ce_count;
- int (*mca_set_debug_mode)(struct amdgpu_device *adev, bool enable);
- int (*mca_parse_mca_error_count)(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type,
- struct mca_bank_entry *entry, uint32_t *count);
- int (*mca_get_valid_mca_count)(struct amdgpu_device *adev, enum amdgpu_mca_error_type type,
- uint32_t *count);
- int (*mca_get_mca_entry)(struct amdgpu_device *adev, enum amdgpu_mca_error_type type,
- int idx, struct mca_bank_entry *entry);
};
void amdgpu_mca_query_correctable_error_count(struct amdgpu_device *adev,
uint64_t mc_status_addr,
unsigned long *error_count);
-
void amdgpu_mca_query_uncorrectable_error_count(struct amdgpu_device *adev,
uint64_t mc_status_addr,
unsigned long *error_count);
-
void amdgpu_mca_reset_error_count(struct amdgpu_device *adev,
uint64_t mc_status_addr);
-
void amdgpu_mca_query_ras_error_count(struct amdgpu_device *adev,
uint64_t mc_status_addr,
void *ras_error_status);
@@ -155,15 +59,4 @@ int amdgpu_mca_mp0_ras_sw_init(struct amdgpu_device *adev);
int amdgpu_mca_mp1_ras_sw_init(struct amdgpu_device *adev);
int amdgpu_mca_mpio_ras_sw_init(struct amdgpu_device *adev);
-void amdgpu_mca_smu_init_funcs(struct amdgpu_device *adev, const struct amdgpu_mca_smu_funcs *mca_funcs);
-int amdgpu_mca_init(struct amdgpu_device *adev);
-void amdgpu_mca_fini(struct amdgpu_device *adev);
-int amdgpu_mca_reset(struct amdgpu_device *adev);
-int amdgpu_mca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable);
-int amdgpu_mca_smu_get_mca_set_error_count(struct amdgpu_device *adev, enum amdgpu_ras_block blk,
- enum amdgpu_mca_error_type type, uint32_t *total);
-void amdgpu_mca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root);
-int amdgpu_mca_smu_log_ras_error(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type,
- struct ras_err_data *err_data, struct ras_query_context *qctx);
-
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
index e3972673fd64..6c0dde3786e3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
@@ -217,7 +217,7 @@ int amdgpu_mes_init(struct amdgpu_device *adev)
if (r)
goto error_doorbell;
- if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(12, 1, 0)) {
+ if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(11, 0, 0)) {
/* When queue/pipe reset is done in MES instead of in the
* driver, MES passes hung queues information to the driver in
* hung_queue_hqd_info. Calculate required space to store this
@@ -252,6 +252,10 @@ int amdgpu_mes_init(struct amdgpu_device *adev)
}
}
+ adev->gfx.mec.mes_hung_db_array =
+ kcalloc(amdgpu_mes_get_hung_queue_db_array_size(adev),
+ sizeof(u32), GFP_KERNEL);
+
return 0;
error_doorbell:
@@ -279,6 +283,8 @@ void amdgpu_mes_fini(struct amdgpu_device *adev)
int i;
int num_xcc = adev->gfx.xcc_mask ? NUM_XCC(adev->gfx.xcc_mask) : 1;
+ kfree(adev->gfx.mec.mes_hung_db_array);
+
amdgpu_bo_free_kernel(&adev->mes.event_log_gpu_obj,
&adev->mes.event_log_gpu_addr,
&adev->mes.event_log_cpu_addr);
@@ -439,6 +445,59 @@ int amdgpu_mes_reset_legacy_queue(struct amdgpu_device *adev,
return r;
}
+int amdgpu_mes_reset_queue_mmio(struct amdgpu_device *adev,
+ int queue_type,
+ unsigned int vmid,
+ unsigned int me,
+ unsigned int pipe,
+ unsigned int queue,
+ uint32_t xcc_id)
+{
+ struct mes_reset_queue_input queue_input;
+ int r;
+
+ memset(&queue_input, 0, sizeof(queue_input));
+
+ queue_input.xcc_id = xcc_id;
+ queue_input.me_id = me;
+ queue_input.pipe_id = pipe;
+ queue_input.queue_id = queue;
+ queue_input.vmid = vmid;
+ queue_input.queue_type = queue_type;
+ queue_input.use_mmio = true;
+
+ amdgpu_mes_lock(&adev->mes);
+ r = adev->mes.funcs->reset_hw_queue(&adev->mes, &queue_input);
+ amdgpu_mes_unlock(&adev->mes);
+ if (r)
+ dev_err(adev->dev, "failed to reset legacy queue\n");
+
+ return r;
+}
+
+int amdgpu_mes_reset_user_queue(struct amdgpu_device *adev,
+ int queue_type,
+ unsigned int doorbell_index,
+ unsigned int xcc_id)
+{
+ struct mes_reset_queue_input queue_input;
+ int r;
+
+ memset(&queue_input, 0, sizeof(queue_input));
+
+ queue_input.xcc_id = xcc_id;
+ queue_input.queue_type = queue_type;
+ queue_input.doorbell_offset = doorbell_index;
+
+ amdgpu_mes_lock(&adev->mes);
+ r = adev->mes.funcs->reset_hw_queue(&adev->mes, &queue_input);
+ amdgpu_mes_unlock(&adev->mes);
+ if (r)
+ dev_err(adev->dev, "failed to reset user queue\n");
+
+ return r;
+}
+
int amdgpu_mes_get_hung_queue_db_array_size(struct amdgpu_device *adev)
{
return adev->mes.hung_queue_db_array_size;
@@ -805,8 +864,13 @@ bool amdgpu_mes_suspend_resume_all_supported(struct amdgpu_device *adev)
bool amdgpu_mes_queue_reset_by_mes_supported(struct amdgpu_device *adev)
{
- return (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 1, 0) &&
- (adev->mes.sched_version & AMDGPU_MES_VERSION_MASK) >= 0x73);
+ u32 ip_maj = IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0));
+ u32 ip_min = IP_VERSION_MIN(amdgpu_ip_version(adev, GC_HWIP, 0));
+ u32 mes_sched = adev->mes.sched_version & AMDGPU_MES_VERSION_MASK;
+
+ return (ip_maj == 11 && mes_sched >= 0x8c) ||
+ ((ip_maj == 12 && ip_min == 0) && mes_sched >= 0x8d) ||
+ ((ip_maj == 12 && ip_min == 1) && mes_sched >= 0x73);
}
/* Fix me -- node_id is used to identify the correct MES instances in the future */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
index 1aae49f4df49..f25cffad8efe 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
@@ -168,6 +168,9 @@ struct amdgpu_mes {
int master_xcc_ids[AMDGPU_MAX_MES_INST_PIPES];
struct amdgpu_bo *shared_cmd_buf_obj[AMDGPU_MAX_MES_INST_PIPES];
uint64_t shared_cmd_buf_gpu_addr[AMDGPU_MAX_MES_INST_PIPES];
+
+ bool compute_pipe_reset_enabled;
+ bool gfx_pipe_reset_enabled;
};
struct amdgpu_mes_hung_queue_hqd_info {
@@ -271,6 +274,7 @@ struct mes_remove_queue_input {
uint32_t xcc_id;
uint32_t doorbell_offset;
uint64_t gang_context_addr;
+ uint32_t queue_type;
bool remove_queue_after_reset;
};
@@ -461,6 +465,17 @@ int amdgpu_mes_reset_legacy_queue(struct amdgpu_device *adev,
unsigned int vmid,
bool use_mmio,
uint32_t xcc_id);
+int amdgpu_mes_reset_queue_mmio(struct amdgpu_device *adev,
+ int queue_type,
+ unsigned int vmid,
+ unsigned int me,
+ unsigned int pipe,
+ unsigned int queue,
+ uint32_t xcc_id);
+int amdgpu_mes_reset_user_queue(struct amdgpu_device *adev,
+ int queue_type,
+ unsigned int doorbell_index,
+ unsigned int xcc_id);
int amdgpu_mes_get_hung_queue_db_array_size(struct amdgpu_device *adev);
int amdgpu_mes_detect_and_reset_hung_queues(struct amdgpu_device *adev,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h
index 6b8214650e5d..c5120ba51e24 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h
@@ -21,29 +21,6 @@
#ifndef __AMDGPU_MMHUB_H__
#define __AMDGPU_MMHUB_H__
-enum amdgpu_mmhub_ras_memory_id {
- AMDGPU_MMHUB_WGMI_PAGEMEM = 0,
- AMDGPU_MMHUB_RGMI_PAGEMEM = 1,
- AMDGPU_MMHUB_WDRAM_PAGEMEM = 2,
- AMDGPU_MMHUB_RDRAM_PAGEMEM = 3,
- AMDGPU_MMHUB_WIO_CMDMEM = 4,
- AMDGPU_MMHUB_RIO_CMDMEM = 5,
- AMDGPU_MMHUB_WGMI_CMDMEM = 6,
- AMDGPU_MMHUB_RGMI_CMDMEM = 7,
- AMDGPU_MMHUB_WDRAM_CMDMEM = 8,
- AMDGPU_MMHUB_RDRAM_CMDMEM = 9,
- AMDGPU_MMHUB_MAM_DMEM0 = 10,
- AMDGPU_MMHUB_MAM_DMEM1 = 11,
- AMDGPU_MMHUB_MAM_DMEM2 = 12,
- AMDGPU_MMHUB_MAM_DMEM3 = 13,
- AMDGPU_MMHUB_WRET_TAGMEM = 19,
- AMDGPU_MMHUB_RRET_TAGMEM = 20,
- AMDGPU_MMHUB_WIO_DATAMEM = 21,
- AMDGPU_MMHUB_WGMI_DATAMEM = 22,
- AMDGPU_MMHUB_WDRAM_DATAMEM = 23,
- AMDGPU_MMHUB_MEMORY_BLOCK_LAST,
-};
-
struct amdgpu_mmhub_ras {
struct amdgpu_ras_block_object ras_block;
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
index 4d68732d6223..ff11a0903499 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
@@ -312,46 +312,6 @@ uint32_t amdgpu_bo_mem_stats_placement(struct amdgpu_bo *bo);
uint32_t amdgpu_bo_get_preferred_domain(struct amdgpu_device *adev,
uint32_t domain);
-/*
- * sub allocation
- */
-static inline struct amdgpu_sa_manager *
-to_amdgpu_sa_manager(struct drm_suballoc_manager *manager)
-{
- return container_of(manager, struct amdgpu_sa_manager, base);
-}
-
-static inline uint64_t amdgpu_sa_bo_gpu_addr(struct drm_suballoc *sa_bo)
-{
- return to_amdgpu_sa_manager(sa_bo->manager)->gpu_addr +
- drm_suballoc_soffset(sa_bo);
-}
-
-static inline void *amdgpu_sa_bo_cpu_addr(struct drm_suballoc *sa_bo)
-{
- return to_amdgpu_sa_manager(sa_bo->manager)->cpu_ptr +
- drm_suballoc_soffset(sa_bo);
-}
-
-int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev,
- struct amdgpu_sa_manager *sa_manager,
- unsigned size, u32 align, u32 domain);
-void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev,
- struct amdgpu_sa_manager *sa_manager);
-int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev,
- struct amdgpu_sa_manager *sa_manager);
-int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager,
- struct drm_suballoc **sa_bo,
- unsigned int size);
-void amdgpu_sa_bo_free(struct drm_suballoc **sa_bo,
- struct dma_fence *fence);
-#if defined(CONFIG_DEBUG_FS)
-void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager,
- struct seq_file *m);
-u64 amdgpu_bo_print_info(int id, struct amdgpu_bo *bo, struct seq_file *m);
-#endif
-void amdgpu_debugfs_sa_init(struct amdgpu_device *adev);
-
bool amdgpu_bo_support_uswc(u64 bo_flags);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c
index b1dc33301d83..e8592970aaab 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c
@@ -47,6 +47,17 @@ static ssize_t mem_info_preempt_used_show(struct device *dev,
static DEVICE_ATTR_RO(mem_info_preempt_used);
/**
+ * amdgpu_preempt_mgr_sysfs_fini - remove PREEMPT manager sysfs attributes
+ *
+ * @adev: amdgpu_device pointer
+ */
+void amdgpu_preempt_mgr_sysfs_fini(struct amdgpu_device *adev)
+{
+ if (adev->dev->kobj.sd)
+ device_remove_file(adev->dev, &dev_attr_mem_info_preempt_used);
+}
+
+/**
* amdgpu_preempt_mgr_new - allocate a new node
*
* @man: TTM memory type manager
@@ -137,9 +148,6 @@ void amdgpu_preempt_mgr_fini(struct amdgpu_device *adev)
if (ret)
return;
- if (adev->dev->kobj.sd)
- device_remove_file(adev->dev, &dev_attr_mem_info_preempt_used);
-
ttm_resource_manager_cleanup(man);
ttm_set_driver_manager(&adev->mman.bdev, AMDGPU_PL_PREEMPT, NULL);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c
index 0d3c18f04ac3..8ae72c862d11 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c
@@ -166,7 +166,8 @@ static ssize_t ta_if_load_debugfs_write(struct file *fp, const char *buf, size_t
if (ret)
return -EFAULT;
- if (ta_bin_len > PSP_1_MEG)
+ if (ta_bin_len < sizeof(struct common_firmware_header) ||
+ ta_bin_len > PSP_1_MEG)
return -EINVAL;
copy_pos += sizeof(uint32_t);
@@ -321,6 +322,8 @@ static ssize_t ta_if_invoke_debugfs_write(struct file *fp, const char *buf, size
ret = copy_from_user((void *)&shared_buf_len, &buf[copy_pos], sizeof(uint32_t));
if (ret)
return -EFAULT;
+ if (!shared_buf_len || shared_buf_len > PSP_1_MEG)
+ return -EINVAL;
copy_pos += sizeof(uint32_t);
shared_buf = memdup_user(&buf[copy_pos], shared_buf_len);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
index 764cd4950408..148bb4cb0a2d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
@@ -128,12 +128,6 @@ const char *get_ras_block_str(struct ras_common_if *ras_block)
/* typical ECC bad page rate is 1 bad page per 100MB VRAM */
#define RAS_BAD_PAGE_COVER (100 * 1024 * 1024ULL)
-#define MAX_UMC_POISON_POLLING_TIME_ASYNC 10
-
-#define AMDGPU_RAS_RETIRE_PAGE_INTERVAL 100 //ms
-
-#define MAX_FLUSH_RETIRE_DWORK_TIMES 100
-
#define BYPASS_ALLOCATED_ADDRESS 0x0
#define BYPASS_INITIALIZATION_ADDRESS 0x1
@@ -249,16 +243,12 @@ static int amdgpu_check_address_validity(struct amdgpu_device *adev,
(address >= RAS_UMC_INJECT_ADDR_LIMIT))
return -EFAULT;
- if (amdgpu_uniras_enabled(adev)) {
- if (amdgpu_sriov_vf(adev))
- count = amdgpu_virt_ras_convert_retired_address(adev, address,
- page_pfns, ARRAY_SIZE(page_pfns));
- else
- count = amdgpu_ras_mgr_lookup_bad_pages_in_a_row(adev, address,
- page_pfns, ARRAY_SIZE(page_pfns));
- } else
- count = amdgpu_umc_lookup_bad_pages_in_a_row(adev,
- address, page_pfns, ARRAY_SIZE(page_pfns));
+ if (amdgpu_sriov_vf(adev))
+ count = amdgpu_virt_ras_convert_retired_address(adev, address,
+ page_pfns, ARRAY_SIZE(page_pfns));
+ else
+ count = amdgpu_ras_mgr_lookup_bad_pages_in_a_row(adev, address,
+ page_pfns, ARRAY_SIZE(page_pfns));
if (count <= 0)
return -EPERM;
@@ -1381,76 +1371,6 @@ static void amdgpu_ras_mgr_virt_error_data_statistics_update(struct ras_manager
obj->err_data.de_count = err_data->de_count;
}
-static struct ras_manager *get_ras_manager(struct amdgpu_device *adev, enum amdgpu_ras_block blk)
-{
- struct ras_common_if head;
-
- memset(&head, 0, sizeof(head));
- head.block = blk;
-
- return amdgpu_ras_find_obj(adev, &head);
-}
-
-int amdgpu_ras_bind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk,
- const struct aca_info *aca_info, void *data)
-{
- struct ras_manager *obj;
-
- /* in resume phase, no need to create aca fs node */
- if (adev->in_suspend || amdgpu_reset_in_recovery(adev))
- return 0;
-
- obj = get_ras_manager(adev, blk);
- if (!obj)
- return -EINVAL;
-
- return amdgpu_aca_add_handle(adev, &obj->aca_handle, ras_block_str(blk), aca_info, data);
-}
-
-int amdgpu_ras_unbind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk)
-{
- struct ras_manager *obj;
-
- obj = get_ras_manager(adev, blk);
- if (!obj)
- return -EINVAL;
-
- amdgpu_aca_remove_handle(&obj->aca_handle);
-
- return 0;
-}
-
-static int amdgpu_aca_log_ras_error_data(struct amdgpu_device *adev, enum amdgpu_ras_block blk,
- enum aca_error_type type, struct ras_err_data *err_data,
- struct ras_query_context *qctx)
-{
- struct ras_manager *obj;
-
- obj = get_ras_manager(adev, blk);
- if (!obj)
- return -EINVAL;
-
- return amdgpu_aca_get_error_data(adev, &obj->aca_handle, type, err_data, qctx);
-}
-
-ssize_t amdgpu_ras_aca_sysfs_read(struct device *dev, struct device_attribute *attr,
- struct aca_handle *handle, char *buf, void *data)
-{
- struct ras_manager *obj = container_of(handle, struct ras_manager, aca_handle);
- struct ras_query_if info = {
- .head = obj->head,
- };
-
- if (!amdgpu_ras_get_error_query_ready(obj->adev))
- return sysfs_emit(buf, "Query currently inaccessible\n");
-
- if (amdgpu_ras_query_error_status(obj->adev, &info))
- return -EINVAL;
-
- return sysfs_emit(buf, "%s: %lu\n%s: %lu\n%s: %lu\n", "ue", info.ue_count,
- "ce", info.ce_count, "de", info.de_count);
-}
-
static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev,
struct ras_query_if *info,
struct ras_err_data *err_data,
@@ -1459,7 +1379,6 @@ static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev,
{
enum amdgpu_ras_block blk = info ? info->head.block : AMDGPU_RAS_BLOCK_COUNT;
struct amdgpu_ras_block_object *block_obj = NULL;
- int ret;
if (blk == AMDGPU_RAS_BLOCK_COUNT)
return -EINVAL;
@@ -1469,7 +1388,7 @@ static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev,
if (error_query_mode == AMDGPU_RAS_VIRT_ERROR_COUNT_QUERY) {
return amdgpu_virt_req_ras_err_count(adev, blk, err_data);
- } else if (error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) {
+ } else {
if (info->head.block == AMDGPU_RAS_BLOCK__UMC) {
amdgpu_ras_get_ecc_info(adev, err_data);
} else {
@@ -1490,24 +1409,6 @@ static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev,
block_obj->hw_ops->query_ras_error_status(adev);
}
}
- } else {
- if (amdgpu_aca_is_enabled(adev)) {
- ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_UE, err_data, qctx);
- if (ret)
- return ret;
-
- ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_CE, err_data, qctx);
- if (ret)
- return ret;
-
- ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_DEFERRED, err_data, qctx);
- if (ret)
- return ret;
- } else {
- /* FIXME: add code to check return value later */
- amdgpu_mca_smu_log_ras_error(adev, blk, AMDGPU_MCA_ERROR_TYPE_UE, err_data, qctx);
- amdgpu_mca_smu_log_ras_error(adev, blk, AMDGPU_MCA_ERROR_TYPE_CE, err_data, qctx);
- }
}
return 0;
@@ -1624,8 +1525,6 @@ int amdgpu_ras_reset_error_count(struct amdgpu_device *adev,
enum amdgpu_ras_block block)
{
struct amdgpu_ras_block_object *block_obj = amdgpu_ras_get_ras_block(adev, block, 0);
- const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs;
- const struct aca_smu_funcs *smu_funcs = adev->aca.smu_funcs;
if (!block_obj || !block_obj->hw_ops) {
dev_dbg_once(adev->dev, "%s doesn't config RAS function\n",
@@ -1633,17 +1532,14 @@ int amdgpu_ras_reset_error_count(struct amdgpu_device *adev,
return -EOPNOTSUPP;
}
- if (!amdgpu_ras_is_supported(adev, block) ||
- !amdgpu_ras_get_aca_debug_mode(adev))
+ if (!amdgpu_ras_is_supported(adev, block))
return -EOPNOTSUPP;
if (amdgpu_sriov_vf(adev))
return -EOPNOTSUPP;
/* skip ras error reset in gpu reset */
- if ((amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) &&
- ((smu_funcs && smu_funcs->set_debug_mode) ||
- (mca_funcs && mca_funcs->mca_set_debug_mode)))
+ if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev))
return -EOPNOTSUPP;
if (block_obj->hw_ops->reset_ras_error_count)
@@ -2090,9 +1986,6 @@ int amdgpu_ras_sysfs_create(struct amdgpu_device *adev,
{
struct ras_manager *obj = amdgpu_ras_find_obj(adev, head);
- if (amdgpu_aca_is_enabled(adev))
- return 0;
-
if (!obj || obj->attr_inuse)
return -EINVAL;
@@ -2130,9 +2023,6 @@ int amdgpu_ras_sysfs_remove(struct amdgpu_device *adev,
{
struct ras_manager *obj = amdgpu_ras_find_obj(adev, head);
- if (amdgpu_aca_is_enabled(adev))
- return 0;
-
if (!obj || !obj->attr_inuse)
return -EINVAL;
@@ -2245,25 +2135,6 @@ static void amdgpu_ras_debugfs_create(struct amdgpu_device *adev,
obj, &amdgpu_ras_debugfs_ops);
}
-static bool amdgpu_ras_aca_is_supported(struct amdgpu_device *adev)
-{
- bool ret;
-
- switch (amdgpu_ip_version(adev, MP0_HWIP, 0)) {
- case IP_VERSION(13, 0, 6):
- case IP_VERSION(13, 0, 12):
- case IP_VERSION(13, 0, 14):
- case IP_VERSION(13, 0, 15):
- ret = true;
- break;
- default:
- ret = false;
- break;
- }
-
- return ret;
-}
-
void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
@@ -2290,13 +2161,6 @@ void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev)
amdgpu_ras_debugfs_create(adev, &fs_info, dir);
}
}
-
- if (amdgpu_ras_aca_is_supported(adev)) {
- if (amdgpu_aca_is_enabled(adev))
- amdgpu_aca_smu_debugfs_init(adev, dir);
- else
- amdgpu_mca_smu_debugfs_init(adev, dir);
- }
}
/* debugfs end */
@@ -2489,14 +2353,6 @@ static void amdgpu_ras_interrupt_poison_creation_handler(struct ras_manager *obj
event_id = amdgpu_ras_acquire_event_id(adev, type);
RAS_EVENT_LOG(adev, event_id, "Poison is created\n");
- if (amdgpu_ip_version(obj->adev, UMC_HWIP, 0) >= IP_VERSION(12, 0, 0)) {
- struct amdgpu_ras *con = amdgpu_ras_get_context(obj->adev);
-
- atomic_inc(&con->page_retirement_req_cnt);
- atomic_inc(&con->poison_creation_count);
-
- wake_up(&con->page_retirement_wq);
- }
}
static void amdgpu_ras_interrupt_umc_handler(struct ras_manager *obj,
@@ -3026,77 +2882,6 @@ static int amdgpu_ras_realloc_eh_data_space(struct amdgpu_device *adev,
return 0;
}
-static int amdgpu_ras_mca2pa_by_idx(struct amdgpu_device *adev,
- struct eeprom_table_record *bps,
- struct ras_err_data *err_data)
-{
- struct ta_ras_query_address_input addr_in;
- uint32_t socket = 0;
- int ret = 0;
-
- if (adev->smuio.funcs && adev->smuio.funcs->get_socket_id)
- socket = adev->smuio.funcs->get_socket_id(adev);
-
- /* reinit err_data */
- err_data->err_addr_cnt = 0;
- err_data->err_addr_len = adev->umc.retire_unit;
-
- memset(&addr_in, 0, sizeof(addr_in));
- addr_in.ma.err_addr = bps->address;
- addr_in.ma.socket_id = socket;
- addr_in.ma.ch_inst = bps->mem_channel;
- if (!amdgpu_ras_smu_eeprom_supported(adev)) {
- /* tell RAS TA the node instance is not used */
- addr_in.ma.node_inst = TA_RAS_INV_NODE;
- } else {
- addr_in.ma.umc_inst = bps->mcumc_id;
- addr_in.ma.node_inst = bps->cu;
- }
-
- if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr)
- ret = adev->umc.ras->convert_ras_err_addr(adev, err_data,
- &addr_in, NULL, false);
-
- return ret;
-}
-
-static int amdgpu_ras_mca2pa(struct amdgpu_device *adev,
- struct eeprom_table_record *bps,
- struct ras_err_data *err_data)
-{
- struct ta_ras_query_address_input addr_in;
- uint32_t die_id, socket = 0;
-
- if (adev->smuio.funcs && adev->smuio.funcs->get_socket_id)
- socket = adev->smuio.funcs->get_socket_id(adev);
-
- /* although die id is gotten from PA in nps1 mode, the id is
- * fitable for any nps mode
- */
- if (adev->umc.ras && adev->umc.ras->get_die_id_from_pa)
- die_id = adev->umc.ras->get_die_id_from_pa(adev, bps->address,
- bps->retired_page << AMDGPU_GPU_PAGE_SHIFT);
- else
- return -EINVAL;
-
- /* reinit err_data */
- err_data->err_addr_cnt = 0;
- err_data->err_addr_len = adev->umc.retire_unit;
-
- memset(&addr_in, 0, sizeof(addr_in));
- addr_in.ma.err_addr = bps->address;
- addr_in.ma.ch_inst = bps->mem_channel;
- addr_in.ma.umc_inst = bps->mcumc_id;
- addr_in.ma.node_inst = die_id;
- addr_in.ma.socket_id = socket;
-
- if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr)
- return adev->umc.ras->convert_ras_err_addr(adev, err_data,
- &addr_in, NULL, false);
- else
- return -EINVAL;
-}
-
static bool __check_record_in_range(struct amdgpu_device *adev,
struct eeprom_table_record *bps, int count)
{
@@ -3157,117 +2942,13 @@ static int __amdgpu_ras_convert_rec_array_from_rom(struct amdgpu_device *adev,
struct eeprom_table_record *bps, struct ras_err_data *err_data,
enum amdgpu_memory_partition nps)
{
- int i = 0;
- uint64_t chan_idx_v2;
- enum amdgpu_memory_partition save_nps;
-
- save_nps = (bps[0].retired_page >> UMC_NPS_SHIFT) & UMC_NPS_MASK;
- chan_idx_v2 = bps[0].retired_page & UMC_CHANNEL_IDX_V2;
-
/*old asics just have pa in eeprom*/
- if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) < 12) {
- memcpy(err_data->err_addr, bps,
- sizeof(struct eeprom_table_record) * adev->umc.retire_unit);
- goto out;
- }
-
- for (i = 0; i < adev->umc.retire_unit; i++)
- bps[i].retired_page &= ~(UMC_NPS_MASK << UMC_NPS_SHIFT);
+ memcpy(err_data->err_addr, bps,
+ sizeof(struct eeprom_table_record) * adev->umc.retire_unit);
- if (save_nps || chan_idx_v2) {
- if (save_nps == nps) {
- if (amdgpu_umc_pages_in_a_row(adev, err_data,
- bps[0].retired_page << AMDGPU_GPU_PAGE_SHIFT))
- return -EINVAL;
- for (i = 0; i < adev->umc.retire_unit; i++) {
- err_data->err_addr[i].address = bps[0].address;
- err_data->err_addr[i].mem_channel = bps[0].mem_channel;
- err_data->err_addr[i].bank = bps[0].bank;
- err_data->err_addr[i].err_type = bps[0].err_type;
- err_data->err_addr[i].mcumc_id = bps[0].mcumc_id;
- }
- } else {
- if (amdgpu_ras_mca2pa_by_idx(adev, &bps[0], err_data))
- return -EINVAL;
- }
- } else {
- if (bps[0].address == 0) {
- /* for specific old eeprom data, mca address is not stored,
- * calc it from pa
- */
- if (amdgpu_umc_pa2mca(adev, bps[0].retired_page << AMDGPU_GPU_PAGE_SHIFT,
- &(bps[0].address), AMDGPU_NPS1_PARTITION_MODE))
- return -EINVAL;
- }
-
- if (amdgpu_ras_mca2pa(adev, &bps[0], err_data)) {
- if (nps == AMDGPU_NPS1_PARTITION_MODE)
- memcpy(err_data->err_addr, bps,
- sizeof(struct eeprom_table_record) * adev->umc.retire_unit);
- else
- return -EOPNOTSUPP;
- }
- }
-
-out:
return __amdgpu_ras_restore_bad_pages(adev, err_data->err_addr, adev->umc.retire_unit);
}
-static int __amdgpu_ras_convert_rec_from_rom(struct amdgpu_device *adev,
- struct eeprom_table_record *bps, struct ras_err_data *err_data,
- enum amdgpu_memory_partition nps)
-{
- int i = 0;
- uint64_t chan_idx_v2;
- enum amdgpu_memory_partition save_nps;
-
- if (!amdgpu_ras_smu_eeprom_supported(adev)) {
- save_nps = (bps->retired_page >> UMC_NPS_SHIFT) & UMC_NPS_MASK;
- chan_idx_v2 = bps->retired_page & UMC_CHANNEL_IDX_V2;
- bps->retired_page &= ~(UMC_NPS_MASK << UMC_NPS_SHIFT);
- } else {
- /* if pmfw manages eeprom, save_nps is not stored on eeprom,
- * we should always convert mca address into physical address,
- * make save_nps different from nps
- */
- save_nps = nps + 1;
- }
-
- if (save_nps == nps) {
- if (amdgpu_umc_pages_in_a_row(adev, err_data,
- bps->retired_page << AMDGPU_GPU_PAGE_SHIFT))
- return -EINVAL;
- for (i = 0; i < adev->umc.retire_unit; i++) {
- err_data->err_addr[i].address = bps->address;
- err_data->err_addr[i].mem_channel = bps->mem_channel;
- err_data->err_addr[i].bank = bps->bank;
- err_data->err_addr[i].err_type = bps->err_type;
- err_data->err_addr[i].mcumc_id = bps->mcumc_id;
- }
- } else {
- if (save_nps || chan_idx_v2) {
- if (amdgpu_ras_mca2pa_by_idx(adev, bps, err_data))
- return -EINVAL;
- } else {
- /* for specific old eeprom data, mca address is not stored,
- * calc it from pa
- */
- if (bps->address == 0)
- if (amdgpu_umc_pa2mca(adev,
- bps->retired_page << AMDGPU_GPU_PAGE_SHIFT,
- &(bps->address),
- AMDGPU_NPS1_PARTITION_MODE))
- return -EINVAL;
-
- if (amdgpu_ras_mca2pa(adev, bps, err_data))
- return -EOPNOTSUPP;
- }
- }
-
- return __amdgpu_ras_restore_bad_pages(adev, err_data->err_addr,
- adev->umc.retire_unit);
-}
-
/* it deal with vram only. */
int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev,
struct eeprom_table_record *bps, int pages, bool from_rom)
@@ -3300,8 +2981,7 @@ int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev,
if (from_rom) {
/* there is no pa recs in V3, so skip pa recs processing */
- if ((control->tbl_hdr.version < RAS_TABLE_VER_V3) &&
- !amdgpu_ras_smu_eeprom_supported(adev)) {
+ if (control->tbl_hdr.version < RAS_TABLE_VER_V3) {
for (i = 0; i < pages; i++) {
if (control->ras_num_recs - i >= adev->umc.retire_unit) {
if ((bps[i].address == bps[i + 1].address) &&
@@ -3318,10 +2998,8 @@ int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev,
}
}
}
- for (; i < pages; i++) {
- ret = __amdgpu_ras_convert_rec_from_rom(adev,
- &bps[i], &err_data, nps);
- }
+ for (; i < pages; i++)
+ bps[i].retired_page &= ~(UMC_NPS_MASK << UMC_NPS_SHIFT);
con->eh_data->count_saved = con->eh_data->count;
} else {
@@ -3346,7 +3024,7 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev,
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct ras_err_handler_data *data;
struct amdgpu_ras_eeprom_control *control;
- int save_count, unit_num, i;
+ int save_count, unit_num;
if (!con || !con->eh_data) {
if (new_cnt)
@@ -3367,12 +3045,7 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev,
mutex_lock(&con->recovery_lock);
control = &con->eeprom_control;
data = con->eh_data;
- if (amdgpu_ras_smu_eeprom_supported(adev))
- unit_num = control->ras_num_recs -
- control->ras_num_recs_old;
- else
- unit_num = data->count / adev->umc.retire_unit -
- control->ras_num_recs;
+ unit_num = data->count / adev->umc.retire_unit - control->ras_num_recs;
save_count = con->bad_page_num - control->ras_num_bad_pages;
mutex_unlock(&con->recovery_lock);
@@ -3383,21 +3056,10 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev,
/* only new entries are saved */
if (unit_num && save_count) {
/*old asics only save pa to eeprom like before*/
- if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) < 12) {
- if (amdgpu_ras_eeprom_append(control,
- &data->bps[data->count_saved], unit_num)) {
- dev_err(adev->dev, "Failed to save EEPROM table data!");
- return -EIO;
- }
- } else {
- for (i = 0; i < unit_num; i++) {
- if (amdgpu_ras_eeprom_append(control,
- &data->bps[data->count_saved +
- i * adev->umc.retire_unit], 1)) {
- dev_err(adev->dev, "Failed to save EEPROM table data!");
- return -EIO;
- }
- }
+ if (amdgpu_ras_eeprom_append(control,
+ &data->bps[data->count_saved], unit_num)) {
+ dev_err(adev->dev, "Failed to save EEPROM table data!");
+ return -EIO;
}
dev_info(adev->dev, "Saved %d pages to EEPROM table.\n", save_count);
@@ -3416,7 +3078,7 @@ static int amdgpu_ras_load_bad_pages(struct amdgpu_device *adev)
struct amdgpu_ras_eeprom_control *control =
&adev->psp.ras_context.ras->eeprom_control;
struct eeprom_table_record *bps;
- int ret, i = 0;
+ int ret;
/* no bad page record, skip eeprom access */
if (control->ras_num_recs == 0 || amdgpu_bad_page_threshold == 0)
@@ -3430,33 +3092,6 @@ static int amdgpu_ras_load_bad_pages(struct amdgpu_device *adev)
if (ret) {
dev_err(adev->dev, "Failed to load EEPROM table records!");
} else {
- if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr) {
- /*In V3, there is no pa recs, and some cases(when address==0) may be parsed
- as pa recs, so add verion check to avoid it.
- */
- if ((control->tbl_hdr.version < RAS_TABLE_VER_V3) &&
- !amdgpu_ras_smu_eeprom_supported(adev)) {
- for (i = 0; i < control->ras_num_recs; i++) {
- if ((control->ras_num_recs - i) >= adev->umc.retire_unit) {
- if ((bps[i].address == bps[i + 1].address) &&
- (bps[i].mem_channel == bps[i + 1].mem_channel)) {
- control->ras_num_pa_recs += adev->umc.retire_unit;
- i += (adev->umc.retire_unit - 1);
- } else {
- control->ras_num_mca_recs +=
- (control->ras_num_recs - i);
- break;
- }
- } else {
- control->ras_num_mca_recs += (control->ras_num_recs - i);
- break;
- }
- }
- } else {
- control->ras_num_mca_recs = control->ras_num_recs;
- }
- }
-
ret = amdgpu_ras_add_bad_pages(adev, bps, control->ras_num_recs, true);
if (ret)
goto out;
@@ -3550,293 +3185,6 @@ static void amdgpu_ras_validate_threshold(struct amdgpu_device *adev,
}
}
-int amdgpu_ras_put_poison_req(struct amdgpu_device *adev,
- enum amdgpu_ras_block block, uint16_t pasid,
- pasid_notify pasid_fn, void *data, uint32_t reset)
-{
- int ret = 0;
- struct ras_poison_msg poison_msg;
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
-
- memset(&poison_msg, 0, sizeof(poison_msg));
- poison_msg.block = block;
- poison_msg.pasid = pasid;
- poison_msg.reset = reset;
- poison_msg.pasid_fn = pasid_fn;
- poison_msg.data = data;
-
- ret = kfifo_put(&con->poison_fifo, poison_msg);
- if (!ret) {
- dev_err(adev->dev, "Poison message fifo is full!\n");
- return -ENOSPC;
- }
-
- return 0;
-}
-
-static int amdgpu_ras_get_poison_req(struct amdgpu_device *adev,
- struct ras_poison_msg *poison_msg)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
-
- return kfifo_get(&con->poison_fifo, poison_msg);
-}
-
-static void amdgpu_ras_ecc_log_init(struct ras_ecc_log_info *ecc_log)
-{
- mutex_init(&ecc_log->lock);
-
- INIT_RADIX_TREE(&ecc_log->de_page_tree, GFP_KERNEL);
- ecc_log->de_queried_count = 0;
- ecc_log->consumption_q_count = 0;
-}
-
-static void amdgpu_ras_ecc_log_fini(struct ras_ecc_log_info *ecc_log)
-{
- struct radix_tree_iter iter;
- void __rcu **slot;
- struct ras_ecc_err *ecc_err;
-
- mutex_lock(&ecc_log->lock);
- radix_tree_for_each_slot(slot, &ecc_log->de_page_tree, &iter, 0) {
- ecc_err = radix_tree_deref_slot(slot);
- kfree(ecc_err->err_pages.pfn);
- kfree(ecc_err);
- radix_tree_iter_delete(&ecc_log->de_page_tree, &iter, slot);
- }
- mutex_unlock(&ecc_log->lock);
-
- mutex_destroy(&ecc_log->lock);
- ecc_log->de_queried_count = 0;
- ecc_log->consumption_q_count = 0;
-}
-
-static bool amdgpu_ras_schedule_retirement_dwork(struct amdgpu_ras *con,
- uint32_t delayed_ms)
-{
- int ret;
-
- mutex_lock(&con->umc_ecc_log.lock);
- ret = radix_tree_tagged(&con->umc_ecc_log.de_page_tree,
- UMC_ECC_NEW_DETECTED_TAG);
- mutex_unlock(&con->umc_ecc_log.lock);
-
- if (ret)
- schedule_delayed_work(&con->page_retirement_dwork,
- msecs_to_jiffies(delayed_ms));
-
- return ret ? true : false;
-}
-
-static void amdgpu_ras_do_page_retirement(struct work_struct *work)
-{
- struct amdgpu_ras *con = container_of(work, struct amdgpu_ras,
- page_retirement_dwork.work);
- struct amdgpu_device *adev = con->adev;
- struct ras_err_data err_data;
-
- /* If gpu reset is ongoing, delay retiring the bad pages */
- if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) {
- amdgpu_ras_schedule_retirement_dwork(con,
- AMDGPU_RAS_RETIRE_PAGE_INTERVAL * 3);
- return;
- }
-
- amdgpu_ras_error_data_init(&err_data);
-
- amdgpu_umc_handle_bad_pages(adev, &err_data);
-
- amdgpu_ras_error_data_fini(&err_data);
-
- amdgpu_ras_schedule_retirement_dwork(con,
- AMDGPU_RAS_RETIRE_PAGE_INTERVAL);
-}
-
-static int amdgpu_ras_poison_creation_handler(struct amdgpu_device *adev,
- uint32_t poison_creation_count)
-{
- int ret = 0;
- struct ras_ecc_log_info *ecc_log;
- struct ras_query_if info;
- u32 timeout = MAX_UMC_POISON_POLLING_TIME_ASYNC;
- struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
- u64 de_queried_count;
- u64 consumption_q_count;
- enum ras_event_type type = RAS_EVENT_TYPE_POISON_CREATION;
-
- memset(&info, 0, sizeof(info));
- info.head.block = AMDGPU_RAS_BLOCK__UMC;
-
- ecc_log = &ras->umc_ecc_log;
- ecc_log->de_queried_count = 0;
- ecc_log->consumption_q_count = 0;
-
- do {
- ret = amdgpu_ras_query_error_status_with_event(adev, &info, type);
- if (ret)
- return ret;
-
- de_queried_count = ecc_log->de_queried_count;
- consumption_q_count = ecc_log->consumption_q_count;
-
- if (de_queried_count && consumption_q_count)
- break;
-
- msleep(100);
- } while (--timeout);
-
- if (de_queried_count)
- schedule_delayed_work(&ras->page_retirement_dwork, 0);
-
- if (amdgpu_ras_is_rma(adev) && atomic_cmpxchg(&ras->rma_in_recovery, 0, 1) == 0)
- amdgpu_ras_reset_gpu(adev);
-
- return 0;
-}
-
-static void amdgpu_ras_clear_poison_fifo(struct amdgpu_device *adev)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- struct ras_poison_msg msg;
- int ret;
-
- do {
- ret = kfifo_get(&con->poison_fifo, &msg);
- } while (ret);
-}
-
-static int amdgpu_ras_poison_consumption_handler(struct amdgpu_device *adev,
- uint32_t msg_count, uint32_t *gpu_reset)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- uint32_t reset_flags = 0, reset = 0;
- struct ras_poison_msg msg;
- int ret, i;
-
- kgd2kfd_set_sram_ecc_flag(adev->kfd.dev);
-
- for (i = 0; i < msg_count; i++) {
- ret = amdgpu_ras_get_poison_req(adev, &msg);
- if (!ret)
- continue;
-
- if (msg.pasid_fn)
- msg.pasid_fn(adev, msg.pasid, msg.data);
-
- reset_flags |= msg.reset;
- }
-
- /*
- * Try to ensure poison creation handler is completed first
- * to set rma if bad page exceed threshold.
- */
- flush_delayed_work(&con->page_retirement_dwork);
-
- /* for RMA, amdgpu_ras_poison_creation_handler will trigger gpu reset */
- if (reset_flags && !amdgpu_ras_is_rma(adev)) {
- if (reset_flags & AMDGPU_RAS_GPU_RESET_MODE1_RESET)
- reset = AMDGPU_RAS_GPU_RESET_MODE1_RESET;
- else if (reset_flags & AMDGPU_RAS_GPU_RESET_MODE2_RESET)
- reset = AMDGPU_RAS_GPU_RESET_MODE2_RESET;
- else
- reset = reset_flags;
-
- con->gpu_reset_flags |= reset;
- amdgpu_ras_reset_gpu(adev);
-
- *gpu_reset = reset;
-
- /* Wait for gpu recovery to complete */
- flush_work(&con->recovery_work);
- }
-
- return 0;
-}
-
-static int amdgpu_ras_page_retirement_thread(void *param)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)param;
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- uint32_t poison_creation_count, msg_count;
- uint32_t gpu_reset;
- int ret;
-
- while (!kthread_should_stop()) {
-
- wait_event_interruptible(con->page_retirement_wq,
- kthread_should_stop() ||
- atomic_read(&con->page_retirement_req_cnt));
-
- if (kthread_should_stop())
- break;
-
- mutex_lock(&con->poison_lock);
- gpu_reset = 0;
-
- do {
- poison_creation_count = atomic_read(&con->poison_creation_count);
- ret = amdgpu_ras_poison_creation_handler(adev, poison_creation_count);
- if (ret == -EIO)
- break;
-
- if (poison_creation_count) {
- atomic_sub(poison_creation_count, &con->poison_creation_count);
- atomic_sub(poison_creation_count, &con->page_retirement_req_cnt);
- }
- } while (atomic_read(&con->poison_creation_count) &&
- !atomic_read(&con->poison_consumption_count));
-
- if (ret != -EIO) {
- msg_count = kfifo_len(&con->poison_fifo);
- if (msg_count) {
- ret = amdgpu_ras_poison_consumption_handler(adev,
- msg_count, &gpu_reset);
- if ((ret != -EIO) &&
- (gpu_reset != AMDGPU_RAS_GPU_RESET_MODE1_RESET))
- atomic_sub(msg_count, &con->page_retirement_req_cnt);
- }
- }
-
- if ((ret == -EIO) || (gpu_reset == AMDGPU_RAS_GPU_RESET_MODE1_RESET)) {
- /* gpu mode-1 reset is ongoing or just completed ras mode-1 reset */
- /* Clear poison creation request */
- atomic_set(&con->poison_creation_count, 0);
- atomic_set(&con->poison_consumption_count, 0);
-
- /* Clear poison fifo */
- amdgpu_ras_clear_poison_fifo(adev);
-
- /* Clear all poison requests */
- atomic_set(&con->page_retirement_req_cnt, 0);
-
- if (ret == -EIO) {
- /* Wait for mode-1 reset to complete */
- down_read(&adev->reset_domain->sem);
- up_read(&adev->reset_domain->sem);
- }
-
- /* Wake up work to save bad pages to eeprom */
- schedule_delayed_work(&con->page_retirement_dwork, 0);
- } else if (gpu_reset) {
- /* gpu just completed mode-2 reset or other reset */
- /* Clear poison consumption messages cached in fifo */
- msg_count = kfifo_len(&con->poison_fifo);
- if (msg_count) {
- amdgpu_ras_clear_poison_fifo(adev);
- atomic_sub(msg_count, &con->page_retirement_req_cnt);
- }
-
- atomic_set(&con->poison_consumption_count, 0);
-
- /* Wake up work to save bad pages to eeprom */
- schedule_delayed_work(&con->page_retirement_dwork, 0);
- }
- mutex_unlock(&con->poison_lock);
- }
-
- return 0;
-}
-
int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
@@ -3846,7 +3194,14 @@ int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev)
if (!con || amdgpu_sriov_vf(adev))
return 0;
- if (amdgpu_uniras_enabled(adev))
+ /*
+ * For the reset-on-init path (e.g. an NPS memory partition,
+ * switch) the RAS IP block hw_init has not been enabled and
+ * the amdgpu_uniras_enabled return false, check amdgpu ras
+ * context uniras_enabled flag, eeprom init will be called
+ * during RAS IP block hw_init.
+ */
+ if (amdgpu_uniras_enabled(adev) || con->uniras_enabled)
return 0;
control = &con->eeprom_control;
@@ -3855,9 +3210,6 @@ int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev)
ret = amdgpu_ras_eeprom_init(control);
control->is_eeprom_valid = !ret;
- if (!adev->umc.ras || !adev->umc.ras->convert_ras_err_addr)
- control->ras_num_pa_recs = control->ras_num_recs;
-
if (adev->umc.ras &&
adev->umc.ras->get_retire_flip_bits)
adev->umc.ras->get_retire_flip_bits(adev);
@@ -3877,13 +3229,6 @@ int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev)
adev, control->bad_channel_bitmap);
con->update_channel_flag = false;
}
-
- /* The format action is only applied to new ASICs */
- if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) >= 12 &&
- control->tbl_hdr.version < RAS_TABLE_VER_V3)
- if (!amdgpu_ras_eeprom_reset_table(control))
- if (amdgpu_ras_save_bad_pages(adev, NULL))
- dev_warn(adev->dev, "Failed to format RAS EEPROM data in V3 version!\n");
}
return 0;
@@ -3917,10 +3262,8 @@ int amdgpu_ras_recovery_init(struct amdgpu_device *adev, bool init_bp_info)
}
mutex_init(&con->recovery_lock);
- mutex_init(&con->poison_lock);
INIT_WORK(&con->recovery_work, amdgpu_ras_do_recovery);
atomic_set(&con->in_recovery, 0);
- atomic_set(&con->rma_in_recovery, 0);
con->eeprom_control.bad_channel_bitmap = 0;
max_eeprom_records_count = amdgpu_ras_eeprom_max_record_count(&con->eeprom_control);
@@ -3933,21 +3276,8 @@ int amdgpu_ras_recovery_init(struct amdgpu_device *adev, bool init_bp_info)
}
mutex_init(&con->page_rsv_lock);
- INIT_KFIFO(con->poison_fifo);
mutex_init(&con->page_retirement_lock);
- init_waitqueue_head(&con->page_retirement_wq);
- atomic_set(&con->page_retirement_req_cnt, 0);
- atomic_set(&con->poison_creation_count, 0);
- atomic_set(&con->poison_consumption_count, 0);
- con->page_retirement_thread =
- kthread_run(amdgpu_ras_page_retirement_thread, adev, "umc_page_retirement");
- if (IS_ERR(con->page_retirement_thread)) {
- con->page_retirement_thread = NULL;
- dev_warn(adev->dev, "Failed to create umc_page_retirement thread!!!\n");
- }
-
- INIT_DELAYED_WORK(&con->page_retirement_dwork, amdgpu_ras_do_page_retirement);
- amdgpu_ras_ecc_log_init(&con->umc_ecc_log);
+
#ifdef CONFIG_X86_MCE_AMD
if ((adev->asic_type == CHIP_ALDEBARAN) &&
(adev->gmc.xgmi.connected_to_cpu))
@@ -3978,33 +3308,15 @@ static int amdgpu_ras_recovery_fini(struct amdgpu_device *adev)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct ras_err_handler_data *data = con->eh_data;
- int max_flush_timeout = MAX_FLUSH_RETIRE_DWORK_TIMES;
- bool ret;
/* recovery_init failed to init it, fini is useless */
if (!data)
return 0;
- /* Save all cached bad pages to eeprom */
- do {
- flush_delayed_work(&con->page_retirement_dwork);
- ret = amdgpu_ras_schedule_retirement_dwork(con, 0);
- } while (ret && max_flush_timeout--);
-
- if (con->page_retirement_thread)
- kthread_stop(con->page_retirement_thread);
-
- atomic_set(&con->page_retirement_req_cnt, 0);
- atomic_set(&con->poison_creation_count, 0);
-
mutex_destroy(&con->page_rsv_lock);
cancel_work_sync(&con->recovery_work);
- cancel_delayed_work_sync(&con->page_retirement_dwork);
-
- amdgpu_ras_ecc_log_fini(&con->umc_ecc_log);
-
mutex_lock(&con->recovery_lock);
con->eh_data = NULL;
kfree(data->bps);
@@ -4206,15 +3518,6 @@ init_ras_enabled_flag:
adev->ras_enabled = amdgpu_ras_enable == 0 ? 0 :
adev->ras_hw_enabled & amdgpu_ras_mask;
- /* aca is disabled by default except for psp v13_0_6/v13_0_12/v13_0_14 */
- if (!amdgpu_sriov_vf(adev)) {
- adev->aca.is_enabled =
- (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) ||
- amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) ||
- amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) ||
- amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 15));
- }
-
/* bad page feature is not applicable to specific app platform */
if (adev->gmc.is_app_apu &&
amdgpu_ip_version(adev, UMC_HWIP, 0) == IP_VERSION(12, 0, 0))
@@ -4435,15 +3738,6 @@ int amdgpu_ras_init(struct amdgpu_device *adev)
goto release_con;
}
- if (amdgpu_ras_aca_is_supported(adev)) {
- if (amdgpu_aca_is_enabled(adev))
- r = amdgpu_aca_init(adev);
- else
- r = amdgpu_mca_init(adev);
- if (r)
- goto release_con;
- }
-
con->init_task_pid = task_pid_nr(current);
get_task_comm(con->init_task_comm, current);
@@ -4541,9 +3835,9 @@ int amdgpu_ras_block_late_init(struct amdgpu_device *adev,
goto cleanup;
}
- if (ras_obj->hw_ops &&
+ if (amdgpu_uniras_enabled(adev) || (ras_obj->hw_ops &&
(ras_obj->hw_ops->query_ras_error_count ||
- ras_obj->hw_ops->query_ras_error_status)) {
+ ras_obj->hw_ops->query_ras_error_status))) {
r = amdgpu_ras_sysfs_create(adev, ras_block);
if (r)
goto interrupt;
@@ -4671,28 +3965,13 @@ int amdgpu_ras_late_init(struct amdgpu_device *adev)
amdgpu_ras_event_mgr_init(adev);
- if (amdgpu_ras_aca_is_supported(adev)) {
- if (amdgpu_reset_in_recovery(adev)) {
- if (amdgpu_aca_is_enabled(adev))
- r = amdgpu_aca_reset(adev);
- else
- r = amdgpu_mca_reset(adev);
- if (r)
- return r;
- }
-
- if (!amdgpu_sriov_vf(adev)) {
- if (amdgpu_aca_is_enabled(adev))
- amdgpu_ras_set_aca_debug_mode(adev, false);
- else
- amdgpu_ras_set_mca_debug_mode(adev, false);
- }
- }
-
/* Guest side doesn't need init ras feature */
if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_telemetry_en(adev))
return 0;
+ if (amdgpu_uniras_enabled(adev))
+ amdgpu_ras_mgr_set_debug_mode(adev, false);
+
list_for_each_entry_safe(node, tmp, &adev->ras_list, node) {
obj = node->ras_obj;
if (!obj) {
@@ -4773,13 +4052,6 @@ int amdgpu_ras_fini(struct amdgpu_device *adev)
amdgpu_ras_fs_fini(adev);
amdgpu_ras_interrupt_remove_all(adev);
- if (amdgpu_ras_aca_is_supported(adev)) {
- if (amdgpu_aca_is_enabled(adev))
- amdgpu_aca_fini(adev);
- else
- amdgpu_mca_fini(adev);
- }
-
WARN(AMDGPU_RAS_GET_FEATURES(con->features), "Feature mask is not cleared");
if (AMDGPU_RAS_GET_FEATURES(con->features))
@@ -5064,6 +4336,13 @@ static void amdgpu_register_bad_pages_mca_notifier(struct amdgpu_device *adev)
* Use this list instead of mgpu_info to find the amdgpu
* device on which the UMC error was reported.
*/
+ if (mce_adev_list.num_gpu >= MAX_GPU_INSTANCE) {
+ dev_warn_ratelimited(adev->dev,
+ "mce_adev_list full, skip notifier registration (max=%d)\n",
+ MAX_GPU_INSTANCE);
+ return;
+ }
+
mce_adev_list.devs[mce_adev_list.num_gpu++] = adev;
/*
@@ -5181,59 +4460,10 @@ int amdgpu_ras_reset_gpu(struct amdgpu_device *adev)
return 0;
}
-int amdgpu_ras_set_mca_debug_mode(struct amdgpu_device *adev, bool enable)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- int ret = 0;
-
- if (con) {
- ret = amdgpu_mca_smu_set_debug_mode(adev, enable);
- if (!ret)
- con->is_aca_debug_mode = enable;
- }
-
- return ret;
-}
-
-int amdgpu_ras_set_aca_debug_mode(struct amdgpu_device *adev, bool enable)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- int ret = 0;
-
- if (con) {
- if (amdgpu_aca_is_enabled(adev))
- ret = amdgpu_aca_smu_set_debug_mode(adev, enable);
- else
- ret = amdgpu_mca_smu_set_debug_mode(adev, enable);
- if (!ret)
- con->is_aca_debug_mode = enable;
- }
-
- return ret;
-}
-
-bool amdgpu_ras_get_aca_debug_mode(struct amdgpu_device *adev)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- const struct aca_smu_funcs *smu_funcs = adev->aca.smu_funcs;
- const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs;
-
- if (!con)
- return false;
-
- if ((amdgpu_aca_is_enabled(adev) && smu_funcs && smu_funcs->set_debug_mode) ||
- (!amdgpu_aca_is_enabled(adev) && mca_funcs && mca_funcs->mca_set_debug_mode))
- return con->is_aca_debug_mode;
- else
- return true;
-}
-
bool amdgpu_ras_get_error_query_mode(struct amdgpu_device *adev,
unsigned int *error_query_mode)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs;
- const struct aca_smu_funcs *smu_funcs = adev->aca.smu_funcs;
if (!con) {
*error_query_mode = AMDGPU_RAS_INVALID_ERROR_QUERY;
@@ -5242,9 +4472,6 @@ bool amdgpu_ras_get_error_query_mode(struct amdgpu_device *adev,
if (amdgpu_sriov_vf(adev)) {
*error_query_mode = AMDGPU_RAS_VIRT_ERROR_COUNT_QUERY;
- } else if ((smu_funcs && smu_funcs->set_debug_mode) || (mca_funcs && mca_funcs->mca_set_debug_mode)) {
- *error_query_mode =
- (con->is_aca_debug_mode) ? AMDGPU_RAS_DIRECT_ERROR_QUERY : AMDGPU_RAS_FIRMWARE_ERROR_QUERY;
} else {
*error_query_mode = AMDGPU_RAS_DIRECT_ERROR_QUERY;
}
@@ -5834,3 +5061,8 @@ void amdgpu_ras_post_reset(struct amdgpu_device *adev,
amdgpu_ras_mgr_post_reset(tmp_adev);
}
}
+
+void amdgpu_ras_resume_after_reset(struct amdgpu_device *adev)
+{
+ amdgpu_ras_mgr_resume_after_reset(adev);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
index a86ab65aa2f0..a44aed7f169e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
@@ -31,7 +31,6 @@
#include "ta_ras_if.h"
#include "amdgpu_ras_eeprom.h"
#include "amdgpu_smuio.h"
-#include "amdgpu_aca.h"
struct amdgpu_iv_entry;
@@ -466,14 +465,6 @@ struct ras_query_context {
typedef int (*pasid_notify)(struct amdgpu_device *adev,
uint16_t pasid, void *data);
-struct ras_poison_msg {
- enum amdgpu_ras_block block;
- uint16_t pasid;
- uint32_t reset;
- pasid_notify pasid_fn;
- void *data;
-};
-
struct ras_err_pages {
uint32_t count;
uint64_t *pfn;
@@ -492,8 +483,6 @@ struct ras_ecc_err {
struct ras_ecc_log_info {
struct mutex lock;
struct radix_tree_root de_page_tree;
- uint64_t de_queried_count;
- uint64_t consumption_q_count;
};
struct ras_critical_region {
@@ -549,7 +538,6 @@ struct amdgpu_ras {
/* gpu recovery */
struct work_struct recovery_work;
atomic_t in_recovery;
- atomic_t rma_in_recovery;
struct amdgpu_device *adev;
/* error handler data */
struct ras_err_handler_data *eh_data;
@@ -581,22 +569,15 @@ struct amdgpu_ras {
/* Indicates smu whether need update bad channel info */
bool update_channel_flag;
/* Record status of smu mca debug mode */
- bool is_aca_debug_mode;
+ bool is_mca_debug_mode;
bool is_rma;
/* Record special requirements of gpu reset caller */
uint32_t gpu_reset_flags;
- struct task_struct *page_retirement_thread;
- wait_queue_head_t page_retirement_wq;
struct mutex page_retirement_lock;
- atomic_t page_retirement_req_cnt;
- atomic_t poison_creation_count;
- atomic_t poison_consumption_count;
struct mutex page_rsv_lock;
- DECLARE_KFIFO(poison_fifo, struct ras_poison_msg, 128);
struct ras_ecc_log_info umc_ecc_log;
- struct delayed_work page_retirement_dwork;
/* ras errors detected */
unsigned long ras_err_state;
@@ -615,9 +596,6 @@ struct amdgpu_ras {
struct list_head critical_region_head;
struct mutex critical_region_lock;
- /* Protect poison injection */
- struct mutex poison_lock;
-
/* Disable/Enable uniras switch */
bool uniras_enabled;
const struct ras_smu_drv *ras_smu_drv;
@@ -702,8 +680,6 @@ struct ras_manager {
struct ras_ih_data ih_data;
struct ras_err_data err_data;
-
- struct aca_handle aca_handle;
};
struct ras_badpage {
@@ -964,8 +940,7 @@ struct amdgpu_ras* amdgpu_ras_get_context(struct amdgpu_device *adev);
int amdgpu_ras_set_context(struct amdgpu_device *adev, struct amdgpu_ras *ras_con);
int amdgpu_ras_set_mca_debug_mode(struct amdgpu_device *adev, bool enable);
-int amdgpu_ras_set_aca_debug_mode(struct amdgpu_device *adev, bool enable);
-bool amdgpu_ras_get_aca_debug_mode(struct amdgpu_device *adev);
+bool amdgpu_ras_get_mca_debug_mode(struct amdgpu_device *adev);
bool amdgpu_ras_get_error_query_mode(struct amdgpu_device *adev,
unsigned int *mode);
@@ -1006,12 +981,6 @@ int amdgpu_ras_error_statistic_de_count(struct ras_err_data *err_data,
struct amdgpu_smuio_mcm_config_info *mcm_info,
u64 count);
void amdgpu_ras_query_boot_status(struct amdgpu_device *adev, u32 num_instances);
-int amdgpu_ras_bind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk,
- const struct aca_info *aca_info, void *data);
-int amdgpu_ras_unbind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk);
-
-ssize_t amdgpu_ras_aca_sysfs_read(struct device *dev, struct device_attribute *attr,
- struct aca_handle *handle, char *buf, void *data);
void amdgpu_ras_set_fed(struct amdgpu_device *adev, bool status);
bool amdgpu_ras_get_fed_status(struct amdgpu_device *adev);
@@ -1029,10 +998,6 @@ int amdgpu_ras_reserve_page(struct amdgpu_device *adev, uint64_t pfn);
int amdgpu_ras_add_critical_region(struct amdgpu_device *adev, struct amdgpu_bo *bo);
bool amdgpu_ras_check_critical_address(struct amdgpu_device *adev, uint64_t addr);
-int amdgpu_ras_put_poison_req(struct amdgpu_device *adev,
- enum amdgpu_ras_block block, uint16_t pasid,
- pasid_notify pasid_fn, void *data, uint32_t reset);
-
bool amdgpu_ras_in_recovery(struct amdgpu_device *adev);
__printf(3, 4)
@@ -1045,4 +1010,5 @@ void amdgpu_ras_pre_reset(struct amdgpu_device *adev,
struct list_head *device_list);
void amdgpu_ras_post_reset(struct amdgpu_device *adev,
struct list_head *device_list);
+void amdgpu_ras_resume_after_reset(struct amdgpu_device *adev);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
index b265b4d9053f..baa8cc3646d5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
@@ -124,8 +124,6 @@
RAS_TABLE_V2_1_INFO_SIZE) \
/ RAS_TABLE_RECORD_SIZE)
-#define RAS_SMU_MESSAGE_TIMEOUT_MS 1000 /* 1s */
-
/* Given a zero-based index of an EEPROM RAS record, yields the EEPROM
* offset off of RAS_TABLE_START. That is, this is something you can
* add to control->i2c_address, and then tell I2C layer to read
@@ -159,6 +157,9 @@
static bool __is_ras_eeprom_supported(struct amdgpu_device *adev)
{
+ if (amdgpu_sriov_vf(adev))
+ return false;
+
switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {
case IP_VERSION(11, 0, 2): /* VEGA20 and ARCTURUS */
case IP_VERSION(11, 0, 7): /* Sienna cichlid */
@@ -449,57 +450,46 @@ int amdgpu_ras_eeprom_reset_table(struct amdgpu_ras_eeprom_control *control)
struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
struct amdgpu_ras_eeprom_table_ras_info *rai = &control->tbl_rai;
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- u32 erase_res = 0;
u8 csum;
int res;
mutex_lock(&control->ras_tbl_mutex);
- if (!amdgpu_ras_smu_eeprom_supported(adev)) {
- hdr->header = RAS_TABLE_HDR_VAL;
- amdgpu_ras_set_eeprom_table_version(control);
-
- if (hdr->version >= RAS_TABLE_VER_V2_1) {
- hdr->first_rec_offset = RAS_RECORD_START_V2_1;
- hdr->tbl_size = RAS_TABLE_HEADER_SIZE +
- RAS_TABLE_V2_1_INFO_SIZE;
- rai->rma_status = GPU_HEALTH_USABLE;
-
- control->ras_record_offset = RAS_RECORD_START_V2_1;
- control->ras_max_record_count = RAS_MAX_RECORD_COUNT_V2_1;
- /**
- * GPU health represented as a percentage.
- * 0 means worst health, 100 means fully health.
- */
- rai->health_percent = 100;
- /* ecc_page_threshold = 0 means disable bad page retirement */
- rai->ecc_page_threshold = con->bad_page_cnt_threshold;
- } else {
- hdr->first_rec_offset = RAS_RECORD_START;
- hdr->tbl_size = RAS_TABLE_HEADER_SIZE;
+ hdr->header = RAS_TABLE_HDR_VAL;
+ amdgpu_ras_set_eeprom_table_version(control);
- control->ras_record_offset = RAS_RECORD_START;
- control->ras_max_record_count = RAS_MAX_RECORD_COUNT;
- }
+ if (hdr->version >= RAS_TABLE_VER_V2_1) {
+ hdr->first_rec_offset = RAS_RECORD_START_V2_1;
+ hdr->tbl_size = RAS_TABLE_HEADER_SIZE +
+ RAS_TABLE_V2_1_INFO_SIZE;
+ rai->rma_status = GPU_HEALTH_USABLE;
- csum = __calc_hdr_byte_sum(control);
- if (hdr->version >= RAS_TABLE_VER_V2_1)
- csum += __calc_ras_info_byte_sum(control);
- csum = -csum;
- hdr->checksum = csum;
- res = __write_table_header(control);
- if (!res && hdr->version > RAS_TABLE_VER_V1)
- res = __write_table_ras_info(control);
+ control->ras_record_offset = RAS_RECORD_START_V2_1;
+ control->ras_max_record_count = RAS_MAX_RECORD_COUNT_V2_1;
+ /**
+ * GPU health represented as a percentage.
+ * 0 means worst health, 100 means fully health.
+ */
+ rai->health_percent = 100;
+ /* ecc_page_threshold = 0 means disable bad page retirement */
+ rai->ecc_page_threshold = con->bad_page_cnt_threshold;
} else {
- res = amdgpu_ras_smu_erase_ras_table(adev, &erase_res);
- if (res || erase_res) {
- dev_warn(adev->dev, "RAS EEPROM reset failed, res:%d result:%d",
- res, erase_res);
- if (!res)
- res = -EIO;
- }
+ hdr->first_rec_offset = RAS_RECORD_START;
+ hdr->tbl_size = RAS_TABLE_HEADER_SIZE;
+
+ control->ras_record_offset = RAS_RECORD_START;
+ control->ras_max_record_count = RAS_MAX_RECORD_COUNT;
}
+ csum = __calc_hdr_byte_sum(control);
+ if (hdr->version >= RAS_TABLE_VER_V2_1)
+ csum += __calc_ras_info_byte_sum(control);
+ csum = -csum;
+ hdr->checksum = csum;
+ res = __write_table_header(control);
+ if (!res && hdr->version > RAS_TABLE_VER_V1)
+ res = __write_table_ras_info(control);
+
control->ras_num_recs = 0;
control->ras_num_bad_pages = 0;
control->ras_num_mca_recs = 0;
@@ -662,7 +652,6 @@ amdgpu_ras_eeprom_append_table(struct amdgpu_ras_eeprom_control *control,
const u32 num)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(to_amdgpu_device(control));
- struct amdgpu_device *adev = to_amdgpu_device(control);
u32 a, b, i;
u8 *buf, *pp;
int res;
@@ -767,10 +756,7 @@ amdgpu_ras_eeprom_append_table(struct amdgpu_ras_eeprom_control *control,
% control->ras_max_record_count;
/*old asics only save pa to eeprom like before*/
- if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) < 12)
- control->ras_num_pa_recs += num;
- else
- control->ras_num_mca_recs += num;
+ control->ras_num_pa_recs += num;
control->ras_num_bad_pages = con->bad_page_num;
Out:
@@ -879,71 +865,6 @@ Out:
return res;
}
-int amdgpu_ras_eeprom_update_record_num(struct amdgpu_ras_eeprom_control *control)
-{
- struct amdgpu_device *adev = to_amdgpu_device(control);
- int ret, retry = 20;
-
- if (!amdgpu_ras_smu_eeprom_supported(adev))
- return 0;
-
- control->ras_num_recs_old = control->ras_num_recs;
-
- do {
- /* 1000ms timeout is long enough, smu_get_badpage_count won't
- * return -EBUSY before timeout.
- */
- ret = amdgpu_ras_smu_get_badpage_count(adev,
- &(control->ras_num_recs), RAS_SMU_MESSAGE_TIMEOUT_MS);
- if (!ret &&
- (control->ras_num_recs_old == control->ras_num_recs)) {
- /* record number update in PMFW needs some time,
- * smu_get_badpage_count may return immediately without
- * count update, sleep for a while and retry again.
- */
- msleep(50);
- retry--;
- } else {
- break;
- }
- } while (retry);
-
- /* no update of record number is not a real failure,
- * don't print warning here
- */
- if (!ret && (control->ras_num_recs_old == control->ras_num_recs))
- ret = -EINVAL;
-
- return ret;
-}
-
-static int amdgpu_ras_smu_eeprom_append(struct amdgpu_ras_eeprom_control *control)
-{
- struct amdgpu_device *adev = to_amdgpu_device(control);
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
-
- if (!amdgpu_ras_smu_eeprom_supported(adev) || !con)
- return 0;
-
- control->ras_num_bad_pages = con->bad_page_num;
-
- if (amdgpu_bad_page_threshold != 0 &&
- control->ras_num_bad_pages > con->bad_page_cnt_threshold) {
- dev_warn(adev->dev,
- "Saved bad pages %d reaches threshold value %d\n",
- control->ras_num_bad_pages, con->bad_page_cnt_threshold);
-
- if (adev->cper.enabled && amdgpu_cper_generate_bp_threshold_record(adev))
- dev_warn(adev->dev, "fail to generate bad page threshold cper records\n");
-
- if ((amdgpu_bad_page_threshold != -1) &&
- (amdgpu_bad_page_threshold != -2))
- con->is_rma = true;
- }
-
- return 0;
-}
-
/**
* amdgpu_ras_eeprom_append -- append records to the EEPROM RAS table
* @control: pointer to control structure
@@ -968,9 +889,6 @@ int amdgpu_ras_eeprom_append(struct amdgpu_ras_eeprom_control *control,
if (!__is_ras_eeprom_supported(adev))
return 0;
- if (amdgpu_ras_smu_eeprom_supported(adev))
- return amdgpu_ras_smu_eeprom_append(control);
-
if (num == 0) {
dev_err(adev->dev, "will not append 0 records\n");
return -EINVAL;
@@ -1046,52 +964,6 @@ static int __amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control,
return res;
}
-int amdgpu_ras_eeprom_read_idx(struct amdgpu_ras_eeprom_control *control,
- struct eeprom_table_record *record, u32 rec_idx,
- const u32 num)
-{
- struct amdgpu_device *adev = to_amdgpu_device(control);
- uint64_t ts, end_idx;
- int i, ret;
- u64 mca, ipid;
- u32 cu, mem_channel, mcumc_id;
-
- if (!amdgpu_ras_smu_eeprom_supported(adev))
- return 0;
-
- if (!adev->umc.ras || !adev->umc.ras->mca_ipid_parse)
- return -EOPNOTSUPP;
-
- end_idx = rec_idx + num;
- for (i = rec_idx; i < end_idx; i++) {
- ret = amdgpu_ras_smu_get_badpage_mca_addr(adev, i, &mca);
- if (ret)
- return ret;
-
- ret = amdgpu_ras_smu_get_badpage_ipid(adev, i, &ipid);
- if (ret)
- return ret;
-
- ret = amdgpu_ras_smu_get_timestamp(adev, i, &ts);
- if (ret)
- return ret;
-
- record[i - rec_idx].address = mca;
- /* retired_page (pa) is unused now */
- record[i - rec_idx].retired_page = 0x1ULL;
- record[i - rec_idx].ts = ts;
- record[i - rec_idx].err_type = AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE;
-
- adev->umc.ras->mca_ipid_parse(adev, ipid,
- &cu, &mem_channel, &mcumc_id, NULL);
- record[i - rec_idx].cu = (u8)cu;
- record[i - rec_idx].mem_channel = (u8)mem_channel;
- record[i - rec_idx].mcumc_id = (u8)mcumc_id;
- }
-
- return 0;
-}
-
/**
* amdgpu_ras_eeprom_read -- read EEPROM
* @control: pointer to control structure
@@ -1113,9 +985,6 @@ int amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control,
u8 *buf, *pp;
u32 g0, g1;
- if (amdgpu_ras_smu_eeprom_supported(adev))
- return amdgpu_ras_eeprom_read_idx(control, record, 0, num);
-
if (!__is_ras_eeprom_supported(adev))
return 0;
@@ -1396,6 +1265,86 @@ Out:
}
static ssize_t
+amdgpu_ras_debugfs_table_read_uniras(struct amdgpu_device *adev,
+ char __user *buf,
+ size_t size, loff_t *pos)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ struct ras_core_context *ras_core = ras_mgr ? ras_mgr->ras_core : NULL;
+ struct eeprom_umc_record *records = NULL;
+ struct ras_eeprom_control *control;
+ size_t bufsz, len = 0;
+ u32 num_recs;
+ char *kbuf;
+ ssize_t res;
+ int i;
+
+ if (!ras_core)
+ return 0;
+
+ /* pmfw manages eeprom data by itself */
+ if (ras_fw_eeprom_supported(ras_core))
+ return 0;
+
+ control = &ras_core->ras_eeprom;
+ num_recs = ras_eeprom_get_record_count(ras_core);
+
+ bufsz = strlen(tbl_hdr_str) + tbl_hdr_fmt_size +
+ strlen(rec_hdr_str) + (size_t)rec_hdr_fmt_size * num_recs + 1;
+
+ kbuf = kvmalloc(bufsz, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ if (num_recs) {
+ records = kvcalloc(num_recs, sizeof(*records), GFP_KERNEL);
+ if (!records) {
+ res = -ENOMEM;
+ goto out;
+ }
+
+ res = ras_eeprom_read(ras_core, records, num_recs);
+ if (res)
+ goto out;
+ }
+
+ len += scnprintf(kbuf + len, bufsz - len, "%s", tbl_hdr_str);
+ len += scnprintf(kbuf + len, bufsz - len, tbl_hdr_fmt,
+ control->tbl_hdr.header,
+ control->tbl_hdr.version,
+ control->tbl_hdr.first_rec_offset,
+ control->tbl_hdr.tbl_size,
+ control->tbl_hdr.checksum);
+ len += scnprintf(kbuf + len, bufsz - len, "%s", rec_hdr_str);
+
+ for (i = 0; i < num_recs; i++) {
+ u32 ai = RAS_RI_TO_AI(control, i);
+ int et = records[i].err_type;
+ const char *ets = (et >= 0 && et < AMDGPU_RAS_EEPROM_ERR_COUNT) ?
+ record_err_type_str[et] : "na";
+
+ len += scnprintf(kbuf + len, bufsz - len, rec_hdr_fmt,
+ i,
+ RAS_INDEX_TO_OFFSET(control, ai),
+ ets,
+ records[i].bank,
+ records[i].ts,
+ records[i].offset,
+ records[i].mem_channel,
+ records[i].mcumc_id,
+ records[i].retired_row_pfn);
+ }
+
+ res = simple_read_from_buffer(buf, size, pos, kbuf, len);
+
+out:
+ kvfree(records);
+ kvfree(kbuf);
+
+ return res;
+}
+
+static ssize_t
amdgpu_ras_debugfs_eeprom_table_read(struct file *f, char __user *buf,
size_t size, loff_t *pos)
{
@@ -1408,6 +1357,10 @@ amdgpu_ras_debugfs_eeprom_table_read(struct file *f, char __user *buf,
if (!size)
return size;
+ if (amdgpu_uniras_enabled(adev))
+ return amdgpu_ras_debugfs_table_read_uniras(adev, buf,
+ size, pos);
+
if (!ras || !control) {
res = snprintf(data, sizeof(data), "Not supported\n");
if (*pos >= res)
@@ -1521,42 +1474,6 @@ Out:
return res == RAS_TABLE_V2_1_INFO_SIZE ? 0 : res;
}
-static int amdgpu_ras_smu_eeprom_init(struct amdgpu_ras_eeprom_control *control)
-{
- struct amdgpu_device *adev = to_amdgpu_device(control);
- struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
- struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
- uint64_t local_time;
- int res;
-
- ras->is_rma = false;
-
- if (!__is_ras_eeprom_supported(adev))
- return 0;
- mutex_init(&control->ras_tbl_mutex);
-
- res = amdgpu_ras_smu_get_table_version(adev, &(hdr->version));
- if (res)
- return res;
-
- res = amdgpu_ras_smu_get_badpage_count(adev,
- &(control->ras_num_recs), 100);
- if (res)
- return res;
-
- local_time = (uint64_t)ktime_get_real_seconds();
- res = amdgpu_ras_smu_set_timestamp(adev, local_time);
- if (res)
- return res;
-
- control->ras_max_record_count = 4000;
-
- control->ras_num_mca_recs = 0;
- control->ras_num_pa_recs = 0;
-
- return 0;
-}
-
int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
{
struct amdgpu_device *adev = to_amdgpu_device(control);
@@ -1567,9 +1484,6 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
uint32_t vram_type = adev->gmc.vram_type;
int res;
- if (amdgpu_ras_smu_eeprom_supported(adev))
- return amdgpu_ras_smu_eeprom_init(control);
-
ras->is_rma = false;
if (!__is_ras_eeprom_supported(adev))
@@ -1663,47 +1577,6 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
return 0;
}
-static int amdgpu_ras_smu_eeprom_check(struct amdgpu_ras_eeprom_control *control)
-{
- struct amdgpu_device *adev = to_amdgpu_device(control);
- struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
-
- if (!__is_ras_eeprom_supported(adev))
- return 0;
-
- control->ras_num_bad_pages = ras->bad_page_num;
-
- if ((ras->bad_page_cnt_threshold < control->ras_num_bad_pages) &&
- amdgpu_bad_page_threshold != 0) {
- dev_warn(adev->dev,
- "RAS records:%d exceed threshold:%d\n",
- control->ras_num_bad_pages, ras->bad_page_cnt_threshold);
- if ((amdgpu_bad_page_threshold == -1) ||
- (amdgpu_bad_page_threshold == -2)) {
- dev_warn(adev->dev,
- "Please consult AMD Service Action Guide (SAG) for appropriate service procedures\n");
- } else {
- ras->is_rma = true;
- dev_warn(adev->dev,
- "User defined threshold is set, runtime service will be halt when threshold is reached\n");
- }
-
- return 0;
- }
-
- dev_dbg(adev->dev,
- "Found existing EEPROM table with %d records",
- control->ras_num_bad_pages);
-
- /* Warn if we are at 90% of the threshold or above
- */
- if (10 * control->ras_num_bad_pages >= 9 * ras->bad_page_cnt_threshold)
- dev_warn(adev->dev, "RAS records:%u exceeds 90%% of threshold:%d",
- control->ras_num_bad_pages,
- ras->bad_page_cnt_threshold);
- return 0;
-}
-
int amdgpu_ras_eeprom_check(struct amdgpu_ras_eeprom_control *control)
{
struct amdgpu_device *adev = to_amdgpu_device(control);
@@ -1711,9 +1584,6 @@ int amdgpu_ras_eeprom_check(struct amdgpu_ras_eeprom_control *control)
struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
int res = 0;
- if (amdgpu_ras_smu_eeprom_supported(adev))
- return amdgpu_ras_smu_eeprom_check(control);
-
if (!__is_ras_eeprom_supported(adev))
return 0;
@@ -1973,7 +1843,7 @@ void amdgpu_ras_check_bad_page_status(struct amdgpu_device *adev)
struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
struct amdgpu_ras_eeprom_control *control = ras ? &ras->eeprom_control : NULL;
- if (!control || amdgpu_bad_page_threshold == 0)
+ if (!__is_ras_eeprom_supported(adev) || !control || amdgpu_bad_page_threshold == 0)
return;
if (control->ras_num_bad_pages > ras->bad_page_cnt_threshold) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
index a62114800a92..3c7fcce5fe8b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
@@ -82,7 +82,6 @@ struct amdgpu_ras_eeprom_control {
/* Number of records in the table.
*/
u32 ras_num_recs;
- u32 ras_num_recs_old;
/* the bad page number is ras_num_recs or
* ras_num_recs * umc.retire_unit
@@ -191,8 +190,6 @@ int amdgpu_ras_eeprom_read_idx(struct amdgpu_ras_eeprom_control *control,
struct eeprom_table_record *record, u32 rec_idx,
const u32 num);
-int amdgpu_ras_eeprom_update_record_num(struct amdgpu_ras_eeprom_control *control);
-
void amdgpu_ras_check_bad_page_status(struct amdgpu_device *adev);
extern const struct file_operations amdgpu_ras_debugfs_eeprom_size_ops;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
index b97fa35bac23..4d417c4a5cd2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
@@ -935,6 +935,194 @@ int amdgpu_ring_reset_helper_end(struct amdgpu_ring *ring,
return 0;
}
+/**
+ * amdgpu_multi_ring_reset_helper_begin() - Prepare multiple rings for a reset.
+ *
+ * @ring_type_mask: Bitmask of affected ring types
+ * @guilty_ring: The ring which is guilty of causing a reset.
+ * @guilty_fence: The fence which didn't signal on the guilty ring.
+ *
+ * Useful when performing a GPU reset method that affects
+ * multiple rings at the same time, such as an IP block soft
+ * reset. For example, a GFX IP block soft reset will affect
+ * every graphics and compute queue.
+ *
+ * This function should be called before such a reset.
+ *
+ * Prepare the affected rings before the reset, make sure to
+ * minimize collateral damage, and backup the contents of
+ * the rings. Then the caller can call the actual HW specific
+ * reset function.
+ *
+ * After the reset is complete, the caller should then call
+ * amdgpu_multi_ring_reset_helper_end() to restore the rings.
+ */
+void amdgpu_multi_ring_reset_helper_begin(const u32 ring_type_mask,
+ struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *guilty_fence)
+{
+ struct amdgpu_device *adev = guilty_ring->adev;
+ struct amdgpu_fence *ring_guilty_fence;
+ struct amdgpu_ring *ring;
+ bool rings_busy;
+ int i;
+ u32 t;
+
+ for (i = 0; i < adev->num_rings; ++i) {
+ ring = adev->rings[i];
+
+ if (!(BIT(ring->funcs->type) & ring_type_mask))
+ continue;
+
+ /* Don't accept new submissions on the ring. */
+ if (amdgpu_ring_sched_ready(ring) && !drm_sched_is_stopped(&ring->sched))
+ drm_sched_wqueue_stop(&ring->sched);
+
+ /*
+ * Clear the preempt condition to stop the ring
+ * from starting its next submission. This ensures
+ * that only the currently executing submission
+ * can be rejected because of the reset and helps
+ * minimize collateral damage.
+ */
+ if (ring->funcs->init_cond_exec)
+ amdgpu_ring_set_preempt_cond_exec(ring, false);
+ }
+
+ /* Flush HDP cache so the GPU can see the updated COND_EXEC values */
+ amdgpu_device_flush_hdp(adev, NULL);
+
+ /*
+ * Give some time for non-guilty rings to finish their
+ * current submission, to try to minimize collateral damage.
+ *
+ * Note that this is just a best effort, but really there
+ * is no way to really know which ring is actually responsible
+ * because different rings may share resources, eg. a compute
+ * ring may hog shader engines, causing a graphics ring to hang.
+ */
+ for (t = 0; t < adev->usec_timeout; t += 10000) {
+ rings_busy = false;
+
+ /* Check if any of the non-guilty rings are busy */
+ for (i = 0; i < adev->num_rings; ++i) {
+ ring = adev->rings[i];
+
+ if (!(BIT(ring->funcs->type) & ring_type_mask))
+ continue;
+
+ if (ring == guilty_ring)
+ continue;
+
+ rings_busy |=
+ atomic_read(&ring->fence_drv.last_seq) !=
+ READ_ONCE(ring->fence_drv.sync_seq);
+ }
+
+ if (!rings_busy)
+ break;
+
+ mdelay(10);
+ }
+
+ for (i = 0; i < adev->num_rings; ++i) {
+ ring = adev->rings[i];
+
+ if (!(BIT(ring->funcs->type) & ring_type_mask))
+ continue;
+
+ /*
+ * Find guilty fences, ie. the fences that didn't signal
+ * on each ring. At this point there is no way to know
+ * which one is really responsible for the hang, and no
+ * way to save any of them, so we treat all of them as guilty.
+ */
+ ring_guilty_fence =
+ ring == guilty_ring ? guilty_fence :
+ amdgpu_ring_find_guilty_fence(ring);
+
+ /*
+ * Backup current contents of the ring.
+ * The helper takes care to only reemit unsignalled fences
+ * so we don't have to worry about that here.
+ */
+ amdgpu_ring_reset_helper_begin(ring, ring_guilty_fence);
+ }
+}
+
+/**
+ * amdgpu_multi_ring_reset_helper_end() - Prepare multiple rings for a reset.
+ *
+ * @ring_type_mask: Bitmask of affected ring types
+ * @guilty_ring: The ring which is guilty of causing a reset.
+ * @ret: Return code from the reset function.
+ *
+ * After calling amdgpu_multi_ring_reset_helper_begin()
+ * and executing the actual reset method, call this
+ * function to restore normal operation.
+ *
+ * In case the reset failed, this function should still
+ * be called to restore preemption state, but it won't attempt to
+ * fully restore the ring contents.
+ */
+int amdgpu_multi_ring_reset_helper_end(const u32 ring_type_mask,
+ struct amdgpu_ring *guilty_ring, int ret)
+{
+ struct amdgpu_device *adev = guilty_ring->adev;
+ struct amdgpu_ring *ring;
+ int i, r;
+
+ /* Set preempt condition, rings are now allowed to execute submissions */
+ for (i = 0; i < adev->num_rings; ++i) {
+ ring = adev->rings[i];
+
+ if (!(BIT(ring->funcs->type) & ring_type_mask))
+ continue;
+
+ if (ring->funcs->init_cond_exec)
+ amdgpu_ring_set_preempt_cond_exec(ring, true);
+ }
+
+ /* Flush HDP cache so the GPU can see the updated COND_EXEC values */
+ amdgpu_device_flush_hdp(adev, NULL);
+
+ /* If the reset was unsuccessful, return without restoring anything else. */
+ if (ret)
+ return ret;
+
+ /* Restore contents of all rings */
+ for (i = 0; i < adev->num_rings; ++i) {
+ ring = adev->rings[i];
+
+ if (!(BIT(ring->funcs->type) & ring_type_mask))
+ continue;
+
+ /* Restore contents of the ring */
+ r = amdgpu_ring_reset_helper_end(ring, ring->guilty_fence);
+ if (r) {
+ dev_err(adev->dev,
+ "Failed to recover ring %s after soft reset\n",
+ ring->name);
+ return r;
+ }
+ }
+
+ /* Accept submissions on all rings again */
+ for (i = 0; i < adev->num_rings; ++i) {
+ ring = adev->rings[i];
+
+ if (!(BIT(ring->funcs->type) & ring_type_mask))
+ continue;
+
+ if (!amdgpu_ring_sched_ready(ring))
+ continue;
+
+ drm_sched_wqueue_start(&ring->sched);
+ }
+
+ return 0;
+}
+
bool amdgpu_ring_is_reset_type_supported(struct amdgpu_ring *ring,
u32 reset_type)
{
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
index 8f28b3bd7010..9d3934b4f106 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
@@ -314,6 +314,7 @@ struct amdgpu_ring {
uint32_t *ring_backup;
unsigned int ring_backup_entries_to_copy;
bool reemit;
+ struct amdgpu_fence *guilty_fence;
unsigned rptr_offs;
u64 rptr_gpu_addr;
u32 *rptr_cpu_addr;
@@ -588,10 +589,17 @@ int amdgpu_ib_ring_tests(struct amdgpu_device *adev);
bool amdgpu_ring_sched_ready(struct amdgpu_ring *ring);
void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring,
struct amdgpu_fence *guilty_fence);
+struct amdgpu_fence *
+amdgpu_ring_find_guilty_fence(struct amdgpu_ring *ring);
void amdgpu_ring_reset_helper_begin(struct amdgpu_ring *ring,
struct amdgpu_fence *guilty_fence);
int amdgpu_ring_reset_helper_end(struct amdgpu_ring *ring,
struct amdgpu_fence *guilty_fence);
+void amdgpu_multi_ring_reset_helper_begin(const u32 ring_type_mask,
+ struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *guilty_fence);
+int amdgpu_multi_ring_reset_helper_end(const u32 ring_type_mask,
+ struct amdgpu_ring *guilty_ring, int ret);
bool amdgpu_ring_is_reset_type_supported(struct amdgpu_ring *ring,
u32 reset_type);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
index 572a60e1b3cb..002fae3c380e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c
@@ -583,3 +583,42 @@ int amdgpu_gfx_rlc_init_microcode(struct amdgpu_device *adev,
amdgpu_gfx_rlc_init_microcode_v2_5(adev);
return 0;
}
+
+static const struct amdgpu_rlc_reg_funcs amdgpu_sriov_rlc_reg_funcs = {
+ .rreg32 = amdgpu_sriov_rreg,
+ .wreg32 = amdgpu_sriov_wreg,
+};
+
+static u32
+amdgpu_rlc_rreg(struct amdgpu_device *adev, u32 reg, u32 acc_flags, u32 hwip,
+ u32 xcc_id)
+{
+ return amdgpu_device_rreg(adev, reg, 0);
+}
+
+static void
+amdgpu_rlc_wreg(struct amdgpu_device *adev, u32 reg, u32 value, u32 acc_flags,
+ u32 hwip, u32 xcc_id)
+{
+ amdgpu_device_wreg(adev, reg, value, 0);
+}
+
+static const struct amdgpu_rlc_reg_funcs amdgpu_rlc_reg_funcs = {
+ .rreg32 = amdgpu_rlc_rreg,
+ .wreg32 = amdgpu_rlc_wreg,
+};
+
+void amdgpu_early_init_rlc_reg_funcs(struct amdgpu_device *adev)
+{
+ adev->gfx.rlc.reg_funcs = &amdgpu_rlc_reg_funcs;
+}
+
+void amdgpu_init_rlc_reg_funcs(struct amdgpu_device *adev)
+{
+ if (amdgpu_sriov_vf(adev) &&
+ adev->gfx.rlc.funcs &&
+ adev->gfx.rlc.rlcg_reg_access_supported)
+ adev->gfx.rlc.reg_funcs = &amdgpu_sriov_rlc_reg_funcs;
+ else
+ adev->gfx.rlc.reg_funcs = &amdgpu_rlc_reg_funcs;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
index e535534237a1..959d60c90dcd 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h
@@ -262,6 +262,11 @@ struct amdgpu_rlc_funcs {
bool (*is_rlcg_access_range)(struct amdgpu_device *adev, uint32_t reg);
};
+struct amdgpu_rlc_reg_funcs {
+ u32 (*rreg32)(struct amdgpu_device *adev, u32 reg, u32 acc_flags, u32 hwip, u32 xcc_id);
+ void (*wreg32)(struct amdgpu_device *adev, u32 reg, u32 val, u32 acc_flags, u32 hwip, u32 xcc_id);
+};
+
struct amdgpu_rlcg_reg_access_ctrl {
uint32_t scratch_reg0;
uint32_t scratch_reg1;
@@ -303,6 +308,7 @@ struct amdgpu_rlc {
/* safe mode for updating CG/PG state */
bool in_safe_mode[AMDGPU_MAX_RLC_INSTANCES];
const struct amdgpu_rlc_funcs *funcs;
+ const struct amdgpu_rlc_reg_funcs *reg_funcs;
/* for firmware data */
u32 save_and_restore_offset;
@@ -374,4 +380,8 @@ void amdgpu_gfx_rlc_fini(struct amdgpu_device *adev);
int amdgpu_gfx_rlc_init_microcode(struct amdgpu_device *adev,
uint16_t version_major,
uint16_t version_minor);
+
+void amdgpu_early_init_rlc_reg_funcs(struct amdgpu_device *adev);
+void amdgpu_init_rlc_reg_funcs(struct amdgpu_device *adev);
+
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h
new file mode 100644
index 000000000000..8c85c80fc119
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef AMDGPU_SA_H_
+#define AMDGPU_SA_H_
+
+#include <drm/drm_suballoc.h>
+
+struct amdgpu_device;
+struct amdgpu_bo;
+
+struct amdgpu_sa_manager {
+ struct drm_suballoc_manager base;
+ struct amdgpu_bo *bo;
+ uint64_t gpu_addr;
+ void *cpu_ptr;
+};
+
+static inline struct amdgpu_sa_manager *
+to_amdgpu_sa_manager(struct drm_suballoc_manager *manager)
+{
+ return container_of(manager, struct amdgpu_sa_manager, base);
+}
+
+static inline uint64_t amdgpu_sa_bo_gpu_addr(struct drm_suballoc *sa_bo)
+{
+ return to_amdgpu_sa_manager(sa_bo->manager)->gpu_addr +
+ drm_suballoc_soffset(sa_bo);
+}
+
+static inline void *amdgpu_sa_bo_cpu_addr(struct drm_suballoc *sa_bo)
+{
+ return to_amdgpu_sa_manager(sa_bo->manager)->cpu_ptr +
+ drm_suballoc_soffset(sa_bo);
+}
+
+int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev,
+ struct amdgpu_sa_manager *sa_manager,
+ unsigned size, u32 align, u32 domain);
+void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev,
+ struct amdgpu_sa_manager *sa_manager);
+int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev,
+ struct amdgpu_sa_manager *sa_manager);
+int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager,
+ struct drm_suballoc **sa_bo,
+ unsigned int size);
+void amdgpu_sa_bo_free(struct drm_suballoc **sa_bo,
+ struct dma_fence *fence);
+#if defined(CONFIG_DEBUG_FS)
+void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager,
+ struct seq_file *m);
+u64 amdgpu_bo_print_info(int id, struct amdgpu_bo *bo, struct seq_file *m);
+#endif
+void amdgpu_debugfs_sa_init(struct amdgpu_device *adev);
+
+#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c
index 0eecfaa3a94c..8effb1158430 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c
@@ -39,7 +39,7 @@ static int amdgpu_sched_process_priority_override(struct amdgpu_device *adev,
struct amdgpu_fpriv *fpriv;
struct amdgpu_ctx_mgr *mgr;
struct amdgpu_ctx *ctx;
- uint32_t id;
+ unsigned long id;
int r;
if (fd_empty(f))
@@ -50,10 +50,10 @@ static int amdgpu_sched_process_priority_override(struct amdgpu_device *adev,
return r;
mgr = &fpriv->ctx_mgr;
- mutex_lock(&mgr->lock);
- idr_for_each_entry(&mgr->ctx_handles, ctx, id)
+ xa_lock(&mgr->ctx_handles);
+ xa_for_each(&mgr->ctx_handles, id, ctx)
amdgpu_ctx_priority_override(ctx, priority);
- mutex_unlock(&mgr->lock);
+ xa_unlock(&mgr->ctx_handles);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c
index fcd81242059e..fbac732f3e01 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c
@@ -553,10 +553,11 @@ static int amdgpu_sdma_soft_reset(struct amdgpu_device *adev, u32 instance_id)
int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id,
bool caller_handles_kernel_queues)
{
- int ret = 0;
struct amdgpu_sdma_instance *sdma_instance = &adev->sdma.instance[instance_id];
struct amdgpu_ring *gfx_ring = &sdma_instance->ring;
struct amdgpu_ring *page_ring = &sdma_instance->page;
+ struct amdgpu_fence *gfx_fence, *page_fence;
+ int ret = 0;
if (amdgpu_sriov_vf(adev))
return -EOPNOTSUPP;
@@ -569,9 +570,14 @@ int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id,
* the reset is in progress.
*/
drm_sched_wqueue_stop(&gfx_ring->sched);
+ gfx_fence = amdgpu_ring_find_guilty_fence(gfx_ring);
+ amdgpu_ring_reset_helper_begin(gfx_ring, gfx_fence);
- if (adev->sdma.has_page_queue)
+ if (adev->sdma.has_page_queue) {
drm_sched_wqueue_stop(&page_ring->sched);
+ page_fence = amdgpu_ring_find_guilty_fence(page_ring);
+ amdgpu_ring_reset_helper_begin(page_ring, page_fence);
+ }
}
if (sdma_instance->funcs->stop_kernel_queue) {
@@ -600,14 +606,19 @@ exit:
* to be submitted to the queues after the reset is complete.
*/
if (!ret) {
- amdgpu_fence_driver_force_completion(gfx_ring, NULL);
+ ret = amdgpu_ring_reset_helper_end(gfx_ring, gfx_fence);
+ if (ret)
+ goto unlock;
drm_sched_wqueue_start(&gfx_ring->sched);
if (adev->sdma.has_page_queue) {
- amdgpu_fence_driver_force_completion(page_ring, NULL);
+ ret = amdgpu_ring_reset_helper_end(page_ring, page_fence);
+ if (ret)
+ goto unlock;
drm_sched_wqueue_start(&page_ring->sched);
}
}
}
+unlock:
mutex_unlock(&sdma_instance->engine_reset_mutex);
return ret;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h
index 2bf365609775..4f4e56022c97 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h
@@ -85,34 +85,6 @@ struct amdgpu_sdma_instance {
const struct amdgpu_sdma_funcs *funcs;
};
-enum amdgpu_sdma_ras_memory_id {
- AMDGPU_SDMA_MBANK_DATA_BUF0 = 1,
- AMDGPU_SDMA_MBANK_DATA_BUF1 = 2,
- AMDGPU_SDMA_MBANK_DATA_BUF2 = 3,
- AMDGPU_SDMA_MBANK_DATA_BUF3 = 4,
- AMDGPU_SDMA_MBANK_DATA_BUF4 = 5,
- AMDGPU_SDMA_MBANK_DATA_BUF5 = 6,
- AMDGPU_SDMA_MBANK_DATA_BUF6 = 7,
- AMDGPU_SDMA_MBANK_DATA_BUF7 = 8,
- AMDGPU_SDMA_MBANK_DATA_BUF8 = 9,
- AMDGPU_SDMA_MBANK_DATA_BUF9 = 10,
- AMDGPU_SDMA_MBANK_DATA_BUF10 = 11,
- AMDGPU_SDMA_MBANK_DATA_BUF11 = 12,
- AMDGPU_SDMA_MBANK_DATA_BUF12 = 13,
- AMDGPU_SDMA_MBANK_DATA_BUF13 = 14,
- AMDGPU_SDMA_MBANK_DATA_BUF14 = 15,
- AMDGPU_SDMA_MBANK_DATA_BUF15 = 16,
- AMDGPU_SDMA_UCODE_BUF = 17,
- AMDGPU_SDMA_RB_CMD_BUF = 18,
- AMDGPU_SDMA_IB_CMD_BUF = 19,
- AMDGPU_SDMA_UTCL1_RD_FIFO = 20,
- AMDGPU_SDMA_UTCL1_RDBST_FIFO = 21,
- AMDGPU_SDMA_UTCL1_WR_FIFO = 22,
- AMDGPU_SDMA_DATA_LUT_FIFO = 23,
- AMDGPU_SDMA_SPLIT_DAT_BUF = 24,
- AMDGPU_SDMA_MEMORY_BLOCK_LAST,
-};
-
struct amdgpu_sdma_ras {
struct amdgpu_ras_block_object ras_block;
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
index 85724ec6aaf8..5324030a13f5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
@@ -28,6 +28,8 @@
#include <linux/types.h>
#include <linux/tracepoint.h>
+#include "amdgpu_userq_fence.h"
+
#undef TRACE_SYSTEM
#define TRACE_SYSTEM amdgpu
#define TRACE_INCLUDE_FILE amdgpu_trace
@@ -582,6 +584,154 @@ TRACE_EVENT(amdgpu_reset_reg_dumps,
__entry->value)
);
+DECLARE_EVENT_CLASS(amdgpu_userq_queue,
+ TP_PROTO(struct amdgpu_usermode_queue *queue),
+ TP_ARGS(queue),
+ TP_STRUCT__entry(
+ __field(void *, queue)
+ __field(u64, doorbell_index)
+ __field(int, queue_type)
+ __field(int, state)
+ __field(u32, xcp_id)
+ ),
+ TP_fast_assign(
+ __entry->queue = queue;
+ __entry->doorbell_index = queue->doorbell_index;
+ __entry->queue_type = queue->queue_type;
+ __entry->state = queue->state;
+ __entry->xcp_id = queue->xcp_id;
+ ),
+ TP_printk("queue=%p, doorbell=%llu, type=%d, state=%d, xcp_id=%u",
+ __entry->queue, __entry->doorbell_index,
+ __entry->queue_type, __entry->state, __entry->xcp_id)
+);
+DEFINE_EVENT(amdgpu_userq_queue, amdgpu_userq_create_start,
+ TP_PROTO(struct amdgpu_usermode_queue *queue),
+ TP_ARGS(queue));
+DEFINE_EVENT(amdgpu_userq_queue, amdgpu_userq_destroy_start,
+ TP_PROTO(struct amdgpu_usermode_queue *queue),
+ TP_ARGS(queue));
+DECLARE_EVENT_CLASS(amdgpu_userq_queue_result,
+ TP_PROTO(struct amdgpu_usermode_queue *queue, int result),
+ TP_ARGS(queue, result),
+ TP_STRUCT__entry(
+ __field(void *, queue)
+ __field(u64, doorbell_index)
+ __field(int, queue_type)
+ __field(int, state)
+ __field(u32, xcp_id)
+ __field(int, result)
+ ),
+ TP_fast_assign(
+ __entry->queue = queue;
+ __entry->doorbell_index = queue->doorbell_index;
+ __entry->queue_type = queue->queue_type;
+ __entry->state = queue->state;
+ __entry->xcp_id = queue->xcp_id;
+ __entry->result = result;
+ ),
+ TP_printk("queue=%p, doorbell=%llu, type=%d, state=%d, xcp_id=%u, result=%d",
+ __entry->queue, __entry->doorbell_index,
+ __entry->queue_type, __entry->state,
+ __entry->xcp_id, __entry->result)
+);
+DEFINE_EVENT(amdgpu_userq_queue_result, amdgpu_userq_create_end,
+ TP_PROTO(struct amdgpu_usermode_queue *queue, int result),
+ TP_ARGS(queue, result));
+DEFINE_EVENT(amdgpu_userq_queue_result, amdgpu_userq_destroy_end,
+ TP_PROTO(struct amdgpu_usermode_queue *queue, int result),
+ TP_ARGS(queue, result));
+
+TRACE_EVENT(amdgpu_userq_emit_fence,
+ TP_PROTO(struct device *device, struct amdgpu_usermode_queue *queue, struct amdgpu_userq_fence *fence),
+ TP_ARGS(device, queue, fence),
+ TP_STRUCT__entry(
+ __field(u64, fence_context)
+ __field(u64, fence_seqno)
+ __string(dev, dev_name(device))
+ __field(u64, doorbell_index)
+ __field(u64, client_id)
+ __field(u32, queue_type)
+ ),
+ TP_fast_assign(
+ __entry->fence_context = fence->base.context;
+ __entry->fence_seqno = fence->base.seqno;
+ __assign_str(dev);
+ __entry->doorbell_index = queue->doorbell_index;
+ __entry->client_id = queue->userq_mgr->file->client_id;
+ __entry->queue_type = queue->queue_type;
+ ),
+ TP_printk("dev=%s, client_id=%llu, type=%u, doorbell=%llu, fence=%llu:%llu",
+ __get_str(dev), __entry->client_id, __entry->queue_type, __entry->doorbell_index,
+ __entry->fence_context,
+ __entry->fence_seqno)
+);
+
+TRACE_EVENT(amdgpu_userq_wait_deps,
+ TP_PROTO(struct device *device, struct amdgpu_usermode_queue *queue, struct amdgpu_userq_fence *dep),
+ TP_ARGS(device, queue, dep),
+ TP_STRUCT__entry(
+ __field(u64, context)
+ __field(u64, dep_context)
+ __field(u64, dep_seqno)
+ __string(dev, dev_name(device))
+ __field(u64, doorbell_index)
+ __field(u64, client_id)
+ __field(u32, queue_type)
+ ),
+ TP_fast_assign(
+ __assign_str(dev);
+ __entry->doorbell_index = queue->doorbell_index;
+ __entry->queue_type = queue->queue_type;
+ __entry->client_id = queue->userq_mgr->file->client_id;
+ __entry->context = queue->fence_drv->context;
+ __entry->dep_context = dep->base.context;
+ __entry->dep_seqno = dep->base.seqno;
+ ),
+ TP_printk("dev=%s, client_id=%llu, type=%u, doorbell=%llu, context=%llu depends on fence=%llu:%llu",
+ __get_str(dev), __entry->client_id, __entry->queue_type, __entry->doorbell_index, __entry->context,
+ __entry->dep_context,
+ __entry->dep_seqno)
+);
+
+TRACE_EVENT(amdgpu_userq_state_start,
+ TP_PROTO(struct amdgpu_usermode_queue *queue),
+ TP_ARGS(queue),
+ TP_STRUCT__entry(
+ __field(u64, doorbell_index)
+ __field(u64, client_id)
+ __field(u32, queue_type)
+ __field(u32, from)
+ ),
+ TP_fast_assign(
+ __entry->doorbell_index = queue->doorbell_index;
+ __entry->queue_type = queue->queue_type;
+ __entry->client_id = queue->userq_mgr->file->client_id;
+ __entry->from = queue->state;
+ ),
+ TP_printk("client_id=%llu, type=%u, doorbell=%llu, from=%d",
+ __entry->client_id, __entry->queue_type, __entry->doorbell_index, __entry->from)
+);
+
+TRACE_EVENT(amdgpu_userq_state_changed,
+ TP_PROTO(struct amdgpu_usermode_queue *queue, enum amdgpu_userq_state new_state),
+ TP_ARGS(queue, new_state),
+ TP_STRUCT__entry(
+ __field(u64, doorbell_index)
+ __field(u64, client_id)
+ __field(u32, queue_type)
+ __field(u32, to)
+ ),
+ TP_fast_assign(
+ __entry->doorbell_index = queue->doorbell_index;
+ __entry->queue_type = queue->queue_type;
+ __entry->client_id = queue->userq_mgr->file->client_id;
+ __entry->to = new_state;
+ ),
+ TP_printk("client_id=%llu, type=%u, doorbell=%llu, to=%d",
+ __entry->client_id, __entry->queue_type, __entry->doorbell_index, __entry->to)
+);
+
#undef AMDGPU_JOB_GET_TIMELINE_NAME
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 025625e7e800..b10b0878df37 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -2194,7 +2194,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
return r;
}
- /* Create a boorbell page for kernel usages */
+ /* Create a doorbell page for kernel usages */
r = amdgpu_doorbell_create_kernel_doorbells(adev);
if (r) {
dev_err(adev->dev, "Failed to initialize kernel doorbells.\n");
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
index b5d938b31383..ff9e2e346609 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
@@ -140,6 +140,7 @@ int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size);
void amdgpu_gtt_mgr_fini(struct amdgpu_device *adev);
int amdgpu_preempt_mgr_init(struct amdgpu_device *adev);
void amdgpu_preempt_mgr_fini(struct amdgpu_device *adev);
+void amdgpu_preempt_mgr_sysfs_fini(struct amdgpu_device *adev);
int amdgpu_vram_mgr_init(struct amdgpu_device *adev);
void amdgpu_vram_mgr_fini(struct amdgpu_device *adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c
index b8ed931f8a40..2a5f5e6188bb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c
@@ -97,7 +97,6 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev,
{
struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status;
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- struct amdgpu_ras_eeprom_control *control = &con->eeprom_control;
unsigned int error_query_mode;
int ret = 0;
unsigned long err_count;
@@ -118,77 +117,66 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev,
err_data->err_addr_len = adev->umc.max_ras_err_cnt_per_query;
mutex_lock(&con->page_retirement_lock);
- if (!amdgpu_ras_smu_eeprom_supported(adev)) {
- ret = amdgpu_dpm_get_ecc_info(adev, (void *)&(con->umc_ecc));
- if (ret == -EOPNOTSUPP &&
- error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) {
- if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
- adev->umc.ras->ras_block.hw_ops->query_ras_error_count)
- adev->umc.ras->ras_block.hw_ops->query_ras_error_count(adev,
- ras_error_status);
-
- if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
- adev->umc.ras->ras_block.hw_ops->query_ras_error_address &&
- adev->umc.max_ras_err_cnt_per_query) {
- kfree(err_data->err_addr);
- err_data->err_addr =
- kzalloc_objs(struct eeprom_table_record,
- adev->umc.max_ras_err_cnt_per_query);
-
- /* still call query_ras_error_address to clear error status
- * even NOMEM error is encountered
- */
- if (!err_data->err_addr)
- dev_warn(adev->dev,
- "Failed to alloc memory for umc error address record!\n");
- else
- err_data->err_addr_len =
- adev->umc.max_ras_err_cnt_per_query;
-
- /* umc query_ras_error_address is also responsible for clearing
- * error status
- */
- adev->umc.ras->ras_block.hw_ops->query_ras_error_address(adev,
- ras_error_status);
- }
- } else if (error_query_mode == AMDGPU_RAS_FIRMWARE_ERROR_QUERY ||
- (!ret && error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY)) {
- if (adev->umc.ras &&
- adev->umc.ras->ecc_info_query_ras_error_count)
- adev->umc.ras->ecc_info_query_ras_error_count(adev,
- ras_error_status);
-
- if (adev->umc.ras &&
- adev->umc.ras->ecc_info_query_ras_error_address &&
- adev->umc.max_ras_err_cnt_per_query) {
- kfree(err_data->err_addr);
- err_data->err_addr =
- kzalloc_objs(struct eeprom_table_record,
- adev->umc.max_ras_err_cnt_per_query);
-
- /* still call query_ras_error_address to clear error status
- * even NOMEM error is encountered
- */
- if (!err_data->err_addr)
- dev_warn(adev->dev,
- "Failed to alloc memory for umc error address record!\n");
- else
- err_data->err_addr_len =
- adev->umc.max_ras_err_cnt_per_query;
-
- /* umc query_ras_error_address is also responsible for clearing
- * error status
- */
- adev->umc.ras->ecc_info_query_ras_error_address(adev,
- ras_error_status);
- }
+ ret = amdgpu_dpm_get_ecc_info(adev, (void *)&(con->umc_ecc));
+ if (ret == -EOPNOTSUPP &&
+ error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) {
+ if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
+ adev->umc.ras->ras_block.hw_ops->query_ras_error_count)
+ adev->umc.ras->ras_block.hw_ops->query_ras_error_count(adev,
+ ras_error_status);
+
+ if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
+ adev->umc.ras->ras_block.hw_ops->query_ras_error_address &&
+ adev->umc.max_ras_err_cnt_per_query) {
+ err_data->err_addr =
+ kzalloc_objs(struct eeprom_table_record,
+ adev->umc.max_ras_err_cnt_per_query);
+
+ /* still call query_ras_error_address to clear error status
+ * even NOMEM error is encountered
+ */
+ if (!err_data->err_addr)
+ dev_warn(adev->dev,
+ "Failed to alloc memory for umc error address record!\n");
+ else
+ err_data->err_addr_len =
+ adev->umc.max_ras_err_cnt_per_query;
+
+ /* umc query_ras_error_address is also responsible for clearing
+ * error status
+ */
+ adev->umc.ras->ras_block.hw_ops->query_ras_error_address(adev,
+ ras_error_status);
}
- } else {
- if (!amdgpu_ras_eeprom_update_record_num(control)) {
- err_data->err_addr_cnt = err_data->de_count =
- control->ras_num_recs - control->ras_num_recs_old;
- amdgpu_ras_eeprom_read_idx(control, err_data->err_addr,
- control->ras_num_recs_old, err_data->de_count);
+ } else if (error_query_mode == AMDGPU_RAS_FIRMWARE_ERROR_QUERY ||
+ (!ret && error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY)) {
+ if (adev->umc.ras &&
+ adev->umc.ras->ecc_info_query_ras_error_count)
+ adev->umc.ras->ecc_info_query_ras_error_count(adev,
+ ras_error_status);
+
+ if (adev->umc.ras &&
+ adev->umc.ras->ecc_info_query_ras_error_address &&
+ adev->umc.max_ras_err_cnt_per_query) {
+ err_data->err_addr =
+ kcalloc(adev->umc.max_ras_err_cnt_per_query,
+ sizeof(struct eeprom_table_record), GFP_KERNEL);
+
+ /* still call query_ras_error_address to clear error status
+ * even NOMEM error is encountered
+ */
+ if (!err_data->err_addr)
+ dev_warn(adev->dev,
+ "Failed to alloc memory for umc error address record!\n");
+ else
+ err_data->err_addr_len =
+ adev->umc.max_ras_err_cnt_per_query;
+
+ /* umc query_ras_error_address is also responsible for clearing
+ * error status
+ */
+ adev->umc.ras->ecc_info_query_ras_error_address(adev,
+ ras_error_status);
}
}
@@ -276,7 +264,7 @@ int amdgpu_umc_pasid_poison_handler(struct amdgpu_device *adev,
}
amdgpu_ras_error_data_fini(&err_data);
- } else if (amdgpu_uniras_enabled(adev)) {
+ } else {
struct ras_ih_info ih_info = {0};
ih_info.block = block;
@@ -285,17 +273,6 @@ int amdgpu_umc_pasid_poison_handler(struct amdgpu_device *adev,
ih_info.pasid_fn = pasid_fn;
ih_info.data = data;
amdgpu_ras_mgr_handle_consumer_interrupt(adev, &ih_info);
- } else {
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- int ret;
-
- ret = amdgpu_ras_put_poison_req(adev,
- block, pasid, pasid_fn, data, reset);
- if (!ret) {
- atomic_inc(&con->page_retirement_req_cnt);
- atomic_inc(&con->poison_consumption_count);
- wake_up(&con->page_retirement_wq);
- }
}
} else {
if (adev->virt.ops && adev->virt.ops->ras_poison_handler)
@@ -512,129 +489,3 @@ int amdgpu_umc_loop_channels(struct amdgpu_device *adev,
return 0;
}
-
-int amdgpu_umc_update_ecc_status(struct amdgpu_device *adev,
- uint64_t status, uint64_t ipid, uint64_t addr)
-{
- if (adev->umc.ras->update_ecc_status)
- return adev->umc.ras->update_ecc_status(adev,
- status, ipid, addr);
- return 0;
-}
-
-int amdgpu_umc_logs_ecc_err(struct amdgpu_device *adev,
- struct radix_tree_root *ecc_tree, struct ras_ecc_err *ecc_err)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- struct ras_ecc_log_info *ecc_log;
- int ret;
-
- ecc_log = &con->umc_ecc_log;
-
- mutex_lock(&ecc_log->lock);
- ret = radix_tree_insert(ecc_tree, ecc_err->pa_pfn, ecc_err);
- if (!ret)
- radix_tree_tag_set(ecc_tree,
- ecc_err->pa_pfn, UMC_ECC_NEW_DETECTED_TAG);
- mutex_unlock(&ecc_log->lock);
-
- return ret;
-}
-
-int amdgpu_umc_pages_in_a_row(struct amdgpu_device *adev,
- struct ras_err_data *err_data, uint64_t pa_addr)
-{
- struct ta_ras_query_address_output addr_out;
-
- /* reinit err_data */
- err_data->err_addr_cnt = 0;
- err_data->err_addr_len = adev->umc.retire_unit;
-
- addr_out.pa.pa = pa_addr;
- if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr)
- return adev->umc.ras->convert_ras_err_addr(adev, err_data, NULL,
- &addr_out, false);
- else
- return -EINVAL;
-}
-
-int amdgpu_umc_lookup_bad_pages_in_a_row(struct amdgpu_device *adev,
- uint64_t pa_addr, uint64_t *pfns, int len)
-{
- int i, ret;
- struct ras_err_data err_data;
-
- err_data.err_addr = kzalloc_objs(struct eeprom_table_record,
- adev->umc.retire_unit);
- if (!err_data.err_addr) {
- dev_warn(adev->dev, "Failed to alloc memory in bad page lookup!\n");
- return 0;
- }
-
- ret = amdgpu_umc_pages_in_a_row(adev, &err_data, pa_addr);
- if (ret)
- goto out;
-
- for (i = 0; i < adev->umc.retire_unit; i++) {
- if (i >= len)
- goto out;
-
- pfns[i] = err_data.err_addr[i].retired_page;
- }
- ret = i;
- adev->umc.err_addr_cnt = err_data.err_addr_cnt;
-
-out:
- kfree(err_data.err_addr);
- return ret;
-}
-
-int amdgpu_umc_mca_to_addr(struct amdgpu_device *adev,
- uint64_t err_addr, uint32_t ch, uint32_t umc,
- uint32_t node, uint32_t socket,
- struct ta_ras_query_address_output *addr_out, bool dump_addr)
-{
- struct ta_ras_query_address_input addr_in;
- int ret;
-
- memset(&addr_in, 0, sizeof(addr_in));
- addr_in.ma.err_addr = err_addr;
- addr_in.ma.ch_inst = ch;
- addr_in.ma.umc_inst = umc;
- addr_in.ma.node_inst = node;
- addr_in.ma.socket_id = socket;
-
- if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr) {
- ret = adev->umc.ras->convert_ras_err_addr(adev, NULL, &addr_in,
- addr_out, dump_addr);
- if (ret)
- return ret;
- } else {
- return 0;
- }
-
- return 0;
-}
-
-int amdgpu_umc_pa2mca(struct amdgpu_device *adev,
- uint64_t pa, uint64_t *mca, enum amdgpu_memory_partition nps)
-{
- struct ta_ras_query_address_input addr_in;
- struct ta_ras_query_address_output addr_out;
- int ret;
-
- /* nps: the pa belongs to */
- addr_in.pa.pa = pa | ((uint64_t)nps << 58);
- addr_in.addr_type = TA_RAS_PA_TO_MCA;
- ret = psp_ras_query_address(&adev->psp, &addr_in, &addr_out);
- if (ret) {
- dev_warn(adev->dev, "Failed to query RAS MCA address for 0x%llx",
- pa);
-
- return ret;
- }
-
- *mca = addr_out.ma.err_addr;
-
- return 0;
-}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h
index 8494a55ebf76..cf06d5f856f9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h
@@ -103,18 +103,7 @@ struct amdgpu_umc_ras {
void *ras_error_status);
bool (*check_ecc_err_status)(struct amdgpu_device *adev,
enum amdgpu_mca_error_type type, void *ras_error_status);
- int (*update_ecc_status)(struct amdgpu_device *adev,
- uint64_t status, uint64_t ipid, uint64_t addr);
- int (*convert_ras_err_addr)(struct amdgpu_device *adev,
- struct ras_err_data *err_data,
- struct ta_ras_query_address_input *addr_in,
- struct ta_ras_query_address_output *addr_out,
- bool dump_addr);
- uint32_t (*get_die_id_from_pa)(struct amdgpu_device *adev,
- uint64_t mca_addr, uint64_t retired_page);
void (*get_retire_flip_bits)(struct amdgpu_device *adev);
- void (*mca_ipid_parse)(struct amdgpu_device *adev, uint64_t ipid,
- uint32_t *did, uint32_t *ch, uint32_t *umc_inst, uint32_t *sid);
};
struct amdgpu_umc_funcs {
@@ -179,21 +168,6 @@ int amdgpu_umc_page_retirement_mca(struct amdgpu_device *adev,
int amdgpu_umc_loop_channels(struct amdgpu_device *adev,
umc_func func, void *data);
-int amdgpu_umc_update_ecc_status(struct amdgpu_device *adev,
- uint64_t status, uint64_t ipid, uint64_t addr);
-int amdgpu_umc_logs_ecc_err(struct amdgpu_device *adev,
- struct radix_tree_root *ecc_tree, struct ras_ecc_err *ecc_err);
-
void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev,
void *ras_error_status);
-int amdgpu_umc_pages_in_a_row(struct amdgpu_device *adev,
- struct ras_err_data *err_data, uint64_t pa_addr);
-int amdgpu_umc_lookup_bad_pages_in_a_row(struct amdgpu_device *adev,
- uint64_t pa_addr, uint64_t *pfns, int len);
-int amdgpu_umc_mca_to_addr(struct amdgpu_device *adev,
- uint64_t err_addr, uint32_t ch, uint32_t umc,
- uint32_t node, uint32_t socket,
- struct ta_ras_query_address_output *addr_out, bool dump_addr);
-int amdgpu_umc_pa2mca(struct amdgpu_device *adev,
- uint64_t pa, uint64_t *mca, enum amdgpu_memory_partition nps);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index ef3f0213cc46..82c8809d1d9c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -33,6 +33,7 @@
#include "amdgpu_userq.h"
#include "amdgpu_hmm.h"
#include "amdgpu_userq_fence.h"
+#include "amdgpu_trace.h"
u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev)
{
@@ -88,14 +89,7 @@ static void amdgpu_userq_mgr_reset_work(struct work_struct *work)
container_of(work, struct amdgpu_userq_mgr,
reset_work);
struct amdgpu_device *adev = uq_mgr->adev;
- const int queue_types[] = {
- AMDGPU_RING_TYPE_COMPUTE,
- AMDGPU_RING_TYPE_GFX,
- AMDGPU_RING_TYPE_SDMA
- };
- const int num_queue_types = ARRAY_SIZE(queue_types);
- bool gpu_reset = false;
- int i, r;
+ struct amdgpu_reset_context reset_context;
if (unlikely(adev->debug_disable_gpu_ring_reset)) {
dev_err(adev->dev, "userq reset disabled by debug mask\n");
@@ -109,42 +103,15 @@ static void amdgpu_userq_mgr_reset_work(struct work_struct *work)
if (!amdgpu_gpu_recovery)
return;
- /*
- * Iterate through all queue types to detect and reset problematic queues
- * Process each queue type in the defined order
- */
- for (i = 0; i < num_queue_types; i++) {
- int ring_type = queue_types[i];
- const struct amdgpu_userq_funcs *funcs =
- adev->userq_funcs[ring_type];
-
- if (!amdgpu_userq_is_reset_type_supported(adev, ring_type,
- AMDGPU_RESET_TYPE_PER_QUEUE))
- continue;
+ memset(&reset_context, 0, sizeof(reset_context));
- if (atomic_read(&uq_mgr->userq_count[ring_type]) > 0 &&
- funcs && funcs->detect_and_reset) {
- r = funcs->detect_and_reset(adev, ring_type);
- if (r) {
- gpu_reset = true;
- break;
- }
- }
- }
+ reset_context.method = AMD_RESET_METHOD_NONE;
+ reset_context.reset_req_dev = adev;
+ reset_context.src = AMDGPU_RESET_SRC_USERQ;
+ set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags);
+ /*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/
- if (gpu_reset) {
- struct amdgpu_reset_context reset_context;
-
- memset(&reset_context, 0, sizeof(reset_context));
-
- reset_context.method = AMD_RESET_METHOD_NONE;
- reset_context.reset_req_dev = adev;
- reset_context.src = AMDGPU_RESET_SRC_USERQ;
- set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags);
- /*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/
-
- amdgpu_device_gpu_recover(adev, NULL, &reset_context);
- }
+ amdgpu_device_gpu_recover(adev, NULL, &reset_context);
}
static void amdgpu_userq_hang_detect_work(struct work_struct *work)
@@ -152,12 +119,45 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work)
struct amdgpu_usermode_queue *queue =
container_of(work, struct amdgpu_usermode_queue,
hang_detect_work.work);
+ struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr;
+ struct amdgpu_device *adev = uq_mgr->adev;
+ const struct amdgpu_userq_funcs *userq_funcs =
+ adev->userq_funcs[queue->queue_type];
+ bool gpu_reset = false;
+
+ if (unlikely(adev->debug_disable_gpu_ring_reset)) {
+ dev_err(adev->dev, "userq reset disabled by debug mask\n");
+ return;
+ }
+
+ /*
+ * If GPU recovery feature is disabled system-wide,
+ * skip all reset detection logic
+ */
+ if (!amdgpu_gpu_recovery)
+ return;
+
+ if (amdgpu_userq_is_reset_type_supported(adev, queue->queue_type,
+ AMDGPU_RESET_TYPE_PER_QUEUE)) {
+ int r;
+
+ if (queue->queue_type == AMDGPU_HW_IP_COMPUTE)
+ r = amdgpu_gfx_reset_mes_compute(adev, NULL, NULL,
+ queue, NULL, NULL);
+ else
+ r = userq_funcs->reset(queue);
+ if (r)
+ gpu_reset = true;
+ } else {
+ gpu_reset = true;
+ }
/*
* Don't schedule the work here! Scheduling or queue work from one reset
* handler to another is illegal if you don't take extra precautions!
*/
- amdgpu_userq_mgr_reset_work(&queue->userq_mgr->reset_work);
+ if (gpu_reset)
+ amdgpu_userq_mgr_reset_work(&queue->userq_mgr->reset_work);
}
/*
@@ -293,11 +293,15 @@ static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue)
int r;
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
+ trace_amdgpu_userq_state_start(queue);
+
r = userq_funcs->preempt(queue);
if (r) {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
return r;
} else {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_PREEMPTED);
queue->state = AMDGPU_USERQ_STATE_PREEMPTED;
}
}
@@ -313,10 +317,14 @@ static int amdgpu_userq_restore_helper(struct amdgpu_usermode_queue *queue)
int r = 0;
if (queue->state == AMDGPU_USERQ_STATE_PREEMPTED) {
+ trace_amdgpu_userq_state_start(queue);
+
r = userq_funcs->restore(queue);
if (r) {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
} else {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED);
queue->state = AMDGPU_USERQ_STATE_MAPPED;
}
}
@@ -334,12 +342,15 @@ static int amdgpu_userq_unmap_helper(struct amdgpu_usermode_queue *queue)
if ((queue->state == AMDGPU_USERQ_STATE_MAPPED) ||
(queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) {
+ trace_amdgpu_userq_state_start(queue);
r = userq_funcs->unmap(queue);
if (r) {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
return r;
} else {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_UNMAPPED);
queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
}
}
@@ -356,11 +367,15 @@ static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue)
int r;
if (queue->state == AMDGPU_USERQ_STATE_UNMAPPED) {
+ trace_amdgpu_userq_state_start(queue);
+
r = userq_funcs->map(queue);
if (r) {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
return r;
} else {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED);
queue->state = AMDGPU_USERQ_STATE_MAPPED;
}
}
@@ -507,6 +522,8 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type];
int r = 0;
+ trace_amdgpu_userq_destroy_start(queue);
+
cancel_delayed_work_sync(&uq_mgr->resume_work);
/* Cancel any pending hang detection work and cleanup */
@@ -532,6 +549,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
amdgpu_bo_unreserve(queue->db_obj.obj);
amdgpu_bo_unref(&queue->db_obj.obj);
+ trace_amdgpu_userq_destroy_end(queue, r);
kfree(queue);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
@@ -629,6 +647,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
queue->queue_type = args->in.ip_type;
queue->vm = &fpriv->vm;
queue->priority = priority;
+ queue->xcp_id = (fpriv->xcp_id != AMDGPU_XCP_NO_PARTITION) ?
+ fpriv->xcp_id : 0;
queue->userq_mgr = uq_mgr;
INIT_DELAYED_WORK(&queue->hang_detect_work,
amdgpu_userq_hang_detect_work);
@@ -671,6 +691,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
}
queue->doorbell_index = index;
+ queue->doorbell_offset = (u32)args->in.doorbell_offset;
+ trace_amdgpu_userq_create_start(queue);
r = uq_funcs->mqd_create(queue, &args->in);
if (r) {
drm_file_err(uq_mgr->file, "Failed to create Queue\n");
@@ -694,6 +716,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
r = amdgpu_userq_map_helper(queue);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
+ trace_amdgpu_userq_create_end(queue, r);
mutex_unlock(&uq_mgr->userq_mutex);
goto erase_doorbell;
}
@@ -710,11 +733,13 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
* This drops the last reference which should take care of
* all cleanup.
*/
+ trace_amdgpu_userq_create_end(queue, r);
amdgpu_userq_put(queue);
return r;
}
amdgpu_debugfs_userq_init(filp, queue, qid);
+ trace_amdgpu_userq_create_end(queue, 0);
args->out.queue_id = qid;
return 0;
@@ -730,6 +755,7 @@ clean_doorbell_bo:
free_fence_drv:
amdgpu_userq_fence_driver_free(queue);
free_queue:
+ trace_amdgpu_userq_create_end(queue, r);
kfree(queue);
err_pm_runtime:
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
@@ -862,16 +888,10 @@ int amdgpu_userq_ioctl(struct drm_device *dev, void *data,
static int
amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr)
{
- struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr);
- struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_usermode_queue *queue;
unsigned long queue_id;
int ret = 0, r;
-
- if (amdgpu_bo_reserve(vm->root.bo, false))
- return false;
-
mutex_lock(&uq_mgr->userq_mutex);
/* Resume all the queues for this process */
xa_for_each(&uq_mgr->userq_xa, queue_id, queue) {
@@ -879,6 +899,7 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr)
if (!amdgpu_userq_buffer_vas_mapped(queue)) {
drm_file_err(uq_mgr->file,
"trying restore queue without va mapping\n");
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_INVALID_VA);
queue->state = AMDGPU_USERQ_STATE_INVALID_VA;
continue;
}
@@ -886,10 +907,8 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr)
r = amdgpu_userq_map_helper(queue);
if (r)
ret = r;
-
}
mutex_unlock(&uq_mgr->userq_mutex);
- amdgpu_bo_unreserve(vm->root.bo);
if (ret)
drm_file_err(uq_mgr->file,
@@ -923,7 +942,8 @@ amdgpu_userq_bo_validate(struct amdgpu_device *adev, struct drm_exec *exec,
spin_unlock(&vm->individual_lock);
bo = bo_va->base.bo;
- ret = drm_exec_prepare_obj(exec, &bo->tbo.base, 2);
+ ret = drm_exec_prepare_obj(exec, &bo->tbo.base,
+ TTM_NUM_MOVE_FENCES + 1);
if (unlikely(ret))
return ret;
@@ -946,7 +966,7 @@ amdgpu_userq_bo_validate(struct amdgpu_device *adev, struct drm_exec *exec,
/* Make sure the whole VM is ready to be used */
static int
-amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
+amdgpu_userq_vm_validate_and_restore_queue(struct amdgpu_userq_mgr *uq_mgr)
{
struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr);
bool invalidated = false, new_addition = false;
@@ -1072,8 +1092,12 @@ retry_lock:
dma_fence_wait(vm->last_update, false);
ret = amdgpu_evf_mgr_rearm(&fpriv->evf_mgr, &exec);
- if (ret)
+ if (ret) {
drm_file_err(uq_mgr->file, "Failed to replace eviction fence\n");
+ goto unlock_all;
+ }
+
+ ret = amdgpu_userq_restore_all(uq_mgr);
unlock_all:
drm_exec_fini(&exec);
@@ -1099,18 +1123,34 @@ static void amdgpu_userq_restore_worker(struct work_struct *work)
if (!dma_fence_is_signaled(ev_fence))
goto put_fence;
- ret = amdgpu_userq_vm_validate(uq_mgr);
+ ret = amdgpu_userq_vm_validate_and_restore_queue(uq_mgr);
if (ret) {
drm_file_err(uq_mgr->file, "Failed to validate BOs to restore ret=%d\n", ret);
goto put_fence;
}
- amdgpu_userq_restore_all(uq_mgr);
-
put_fence:
dma_fence_put(ev_fence);
}
+void amdgpu_userq_process_reset_irq(struct amdgpu_device *adev,
+ u32 pasid, u32 doorbell_offset)
+{
+ struct xarray *xa = &adev->userq_doorbell_xa;
+ struct amdgpu_usermode_queue *queue;
+ unsigned long flags, idx;
+
+ xa_lock_irqsave(xa, flags);
+ xa_for_each(xa, idx, queue) {
+ if (queue->vm && queue->vm->pasid == pasid &&
+ queue->doorbell_offset == doorbell_offset) {
+ amdgpu_userq_start_hang_detect_work(queue);
+ break;
+ }
+ }
+ xa_unlock_irqrestore(xa, flags);
+}
+
static int
amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
{
@@ -1166,6 +1206,7 @@ int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *f
xa_init_flags(&userq_mgr->userq_xa, XA_FLAGS_ALLOC);
userq_mgr->adev = adev;
userq_mgr->file = file_priv;
+ mutex_init(&userq_mgr->proc_ctx_lock);
INIT_DELAYED_WORK(&userq_mgr->resume_work, amdgpu_userq_restore_worker);
INIT_WORK(&userq_mgr->reset_work, amdgpu_userq_mgr_reset_work);
@@ -1219,6 +1260,11 @@ void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr)
*/
cancel_work_sync(&userq_mgr->reset_work);
+ amdgpu_bo_free_kernel(&userq_mgr->proc_ctx_obj.obj,
+ &userq_mgr->proc_ctx_obj.gpu_addr,
+ &userq_mgr->proc_ctx_obj.cpu_ptr);
+
+ mutex_destroy(&userq_mgr->proc_ctx_lock);
mutex_destroy(&userq_mgr->userq_mutex);
}
@@ -1370,12 +1416,14 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev)
if (queue->state != AMDGPU_USERQ_STATE_MAPPED)
continue;
+ trace_amdgpu_userq_state_start(queue);
userq_funcs = adev->userq_funcs[queue->queue_type];
userq_funcs->unmap(queue);
/* just mark all queues as hung at this point.
* if unmap succeeds, we could map again
* in amdgpu_userq_post_reset() if vram is not lost
*/
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
amdgpu_userq_fence_driver_force_completion(queue);
}
@@ -1394,6 +1442,8 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
if (queue->state == AMDGPU_USERQ_STATE_HUNG && !vram_lost) {
+ trace_amdgpu_userq_state_start(queue);
+
userq_funcs = adev->userq_funcs[queue->queue_type];
/* Re-map queue */
r = userq_funcs->map(queue);
@@ -1401,6 +1451,7 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
dev_err(adev->dev, "Failed to remap queue %ld\n", queue_id);
continue;
}
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED);
queue->state = AMDGPU_USERQ_STATE_MAPPED;
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
index d1751febaefe..61e5f8a06eb2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
@@ -53,6 +53,7 @@ struct amdgpu_usermode_queue {
enum amdgpu_userq_state state;
uint64_t doorbell_handle;
uint64_t doorbell_index;
+ u32 doorbell_offset;
uint64_t flags;
struct amdgpu_mqd_prop *userq_prop;
struct amdgpu_userq_mgr *userq_mgr;
@@ -111,8 +112,7 @@ struct amdgpu_userq_funcs {
int (*map)(struct amdgpu_usermode_queue *queue);
int (*preempt)(struct amdgpu_usermode_queue *queue);
int (*restore)(struct amdgpu_usermode_queue *queue);
- int (*detect_and_reset)(struct amdgpu_device *adev,
- int queue_type);
+ int (*reset)(struct amdgpu_usermode_queue *queue);
};
/* Usermode queues for gfx */
@@ -127,6 +127,8 @@ struct amdgpu_userq_mgr {
struct amdgpu_device *adev;
struct delayed_work resume_work;
struct drm_file *file;
+ struct mutex proc_ctx_lock;
+ struct amdgpu_userq_obj proc_ctx_obj;
/**
* @reset_work:
@@ -177,6 +179,16 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost);
void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue);
void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell);
+/*
+ * CP packs the per-process doorbell_id of the queue in
+ * CTXID0[9:0] on priv-fault (same encoding KFD uses via
+ * KFD_CTXID0_DOORBELL_ID_MASK)
+ */
+#define AMDGPU_CTXID0_DOORBELL_ID_MASK 0x3ff
+
+void amdgpu_userq_process_reset_irq(struct amdgpu_device *adev,
+ u32 pasid, u32 doorbell_offset);
+
int amdgpu_userq_input_va_validate(struct amdgpu_device *adev,
struct amdgpu_usermode_queue *queue,
u64 addr, u64 expected_size, u64 *va_out);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
index f74ad378e407..7e80442ec3e5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
@@ -30,7 +30,7 @@
#include <drm/drm_syncobj.h>
#include "amdgpu.h"
-#include "amdgpu_userq_fence.h"
+#include "amdgpu_trace.h"
#define AMDGPU_USERQ_MAX_HANDLES (1U << 16)
@@ -528,6 +528,8 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
/* Create the new fence */
amdgpu_userq_fence_init(queue, fence, wptr);
+ trace_amdgpu_userq_emit_fence(dev->dev, queue, fence);
+
mutex_unlock(&userq_mgr->userq_mutex);
/*
@@ -701,7 +703,7 @@ amdgpu_userq_wait_add_fence(struct drm_amdgpu_userq_wait *wait_info,
}
static int
-amdgpu_userq_wait_return_fence_info(struct drm_file *filp,
+amdgpu_userq_wait_return_fence_info(struct drm_device *dev, struct drm_file *filp,
struct drm_amdgpu_userq_wait *wait_info,
u32 *syncobj_handles, u64 *timeline_points,
u32 *timeline_handles,
@@ -869,6 +871,8 @@ amdgpu_userq_wait_return_fence_info(struct drm_file *filp,
amdgpu_userq_fence_driver_get(fence_drv);
+ trace_amdgpu_userq_wait_deps(dev->dev, waitq, userq_fence);
+
/* Store drm syncobj's gpu va address and value */
fence_info[cnt].va = fence_drv->va;
fence_info[cnt].value = fences[i]->seqno;
@@ -969,7 +973,7 @@ int amdgpu_userq_wait_ioctl(struct drm_device *dev, void *data,
gobj_write,
gobj_read);
} else {
- r = amdgpu_userq_wait_return_fence_info(filp, wait_info,
+ r = amdgpu_userq_wait_return_fence_info(dev, filp, wait_info,
syncobj_handles,
timeline_points,
timeline_handles,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
index 616967519869..fe504f1a3fc8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c
@@ -506,9 +506,8 @@ void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring)
struct amdgpu_device *adev = ring->adev;
struct amdgpu_vcn_inst *vcn_inst = &adev->vcn.inst[ring->me];
- atomic_inc(&vcn_inst->total_submission_cnt);
-
- cancel_delayed_work_sync(&vcn_inst->idle_work);
+ if (!atomic_fetch_inc(&vcn_inst->total_submission_cnt))
+ cancel_delayed_work_sync(&vcn_inst->idle_work);
mutex_lock(&vcn_inst->vcn_pg_lock);
vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_UNGATE);
@@ -550,10 +549,9 @@ void amdgpu_vcn_ring_end_use(struct amdgpu_ring *ring)
!adev->vcn.inst[ring->me].using_unified_queue)
atomic_dec(&ring->adev->vcn.inst[ring->me].dpg_enc_submission_cnt);
- atomic_dec(&ring->adev->vcn.inst[ring->me].total_submission_cnt);
-
- schedule_delayed_work(&ring->adev->vcn.inst[ring->me].idle_work,
- VCN_IDLE_TIMEOUT);
+ if (atomic_dec_and_test(&ring->adev->vcn.inst[ring->me].total_submission_cnt))
+ schedule_delayed_work(&ring->adev->vcn.inst[ring->me].idle_work,
+ VCN_IDLE_TIMEOUT);
}
int amdgpu_vcn_dec_ring_test_ring(struct amdgpu_ring *ring)
@@ -1485,6 +1483,37 @@ int vcn_set_powergating_state(struct amdgpu_ip_block *ip_block,
return ret;
}
+static struct amdgpu_fence *
+amdgpu_vcn_ring_reset_begin_helper(struct amdgpu_ring *ring,
+ struct amdgpu_ring *guilty_ring,
+ struct amdgpu_fence *timedout_fence)
+{
+ struct amdgpu_fence *fence;
+
+ drm_sched_wqueue_stop(&ring->sched);
+ if (ring == guilty_ring)
+ fence = timedout_fence;
+ else
+ fence = amdgpu_ring_find_guilty_fence(ring);
+ amdgpu_ring_reset_helper_begin(ring, fence);
+
+ return fence;
+}
+
+static int
+amdgpu_vcn_ring_reset_end_helper(struct amdgpu_ring *ring,
+ struct amdgpu_fence *fence)
+{
+ int r;
+
+ r = amdgpu_ring_reset_helper_end(ring, fence);
+ if (r)
+ return r;
+
+ drm_sched_wqueue_start(&ring->sched);
+ return 0;
+}
+
/**
* amdgpu_vcn_ring_reset - Reset a VCN ring
* @ring: ring to reset
@@ -1502,48 +1531,33 @@ int amdgpu_vcn_ring_reset(struct amdgpu_ring *ring,
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_vcn_inst *vinst = &adev->vcn.inst[ring->me];
+ struct amdgpu_fence *dec_fence;
+ struct amdgpu_fence *enc_fence[AMDGPU_VCN_MAX_ENC_RINGS];
int r, i;
if (adev->vcn.inst[ring->me].using_unified_queue)
return -EINVAL;
mutex_lock(&vinst->engine_reset_mutex);
- /* Stop the scheduler's work queue for the dec and enc rings if they are running.
- * This ensures that no new tasks are submitted to the queues while
- * the reset is in progress.
- */
- drm_sched_wqueue_stop(&vinst->ring_dec.sched);
+ dec_fence = amdgpu_vcn_ring_reset_begin_helper(&vinst->ring_dec, ring,
+ timedout_fence);
for (i = 0; i < vinst->num_enc_rings; i++)
- drm_sched_wqueue_stop(&vinst->ring_enc[i].sched);
+ enc_fence[i] = amdgpu_vcn_ring_reset_begin_helper(&vinst->ring_enc[i], ring,
+ timedout_fence);
/* Perform the VCN reset for the specified instance */
r = vinst->reset(vinst);
if (r)
goto unlock;
- r = amdgpu_ring_test_ring(&vinst->ring_dec);
+
+ r = amdgpu_vcn_ring_reset_end_helper(&vinst->ring_dec, dec_fence);
if (r)
goto unlock;
for (i = 0; i < vinst->num_enc_rings; i++) {
- r = amdgpu_ring_test_ring(&vinst->ring_enc[i]);
+ r = amdgpu_vcn_ring_reset_end_helper(&vinst->ring_enc[i], enc_fence[i]);
if (r)
goto unlock;
}
- amdgpu_fence_driver_force_completion(&vinst->ring_dec,
- (&vinst->ring_dec == ring) ?
- &timedout_fence->base : NULL);
- for (i = 0; i < vinst->num_enc_rings; i++)
- amdgpu_fence_driver_force_completion(&vinst->ring_enc[i],
- (&vinst->ring_enc[i] == ring) ?
- &timedout_fence->base : NULL);
-
- /* Restart the scheduler's work queue for the dec and enc rings
- * if they were stopped by this function. This allows new tasks
- * to be submitted to the queues after the reset is complete.
- */
- drm_sched_wqueue_start(&vinst->ring_dec.sched);
- for (i = 0; i < vinst->num_enc_rings; i++)
- drm_sched_wqueue_start(&vinst->ring_enc[i].sched);
-
unlock:
mutex_unlock(&vinst->engine_reset_mutex);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
index 82624b44e661..bea95307fd42 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
@@ -368,6 +368,9 @@ struct amdgpu_vcn {
struct mutex workload_profile_mutex;
u32 reg_count;
const struct amdgpu_hwip_reg_entry *reg_list;
+
+ bool disable_uq;
+ bool disable_kq;
};
struct amdgpu_fw_shared_rb_ptrs_struct {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
index 409e103ffe8c..35faea0ff17f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
@@ -381,7 +381,8 @@ int amdgpu_xcp_get_inst_details(struct amdgpu_xcp *xcp,
enum AMDGPU_XCP_IP_BLOCK ip,
uint32_t *inst_mask)
{
- if (!xcp->valid || !inst_mask || !(xcp->ip[ip].valid))
+ if (!xcp->valid || !inst_mask || ip >= AMDGPU_XCP_MAX_BLOCKS ||
+ !(xcp->ip[ip].valid))
return -EINVAL;
*inst_mask = xcp->ip[ip].inst_mask;
@@ -468,14 +469,18 @@ void amdgpu_xcp_release_sched(struct amdgpu_device *adev,
{
struct drm_gpu_scheduler *sched =
container_of(entity->entity.rq, typeof(*sched), rq);
+ struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
- if (!adev->xcp_mgr)
+ if (!xcp_mgr)
return;
if (drm_sched_wqueue_ready(sched)) {
struct amdgpu_ring *ring = to_amdgpu_ring(sched);
- atomic_dec(&adev->xcp_mgr->xcp[ring->xcp_id].ref_cnt);
+ mutex_lock(&xcp_mgr->xcp_lock);
+ if (ring->xcp_id < xcp_mgr->num_xcps && xcp_mgr->xcp[ring->xcp_id].valid)
+ atomic_dec(&xcp_mgr->xcp[ring->xcp_id].ref_cnt);
+ mutex_unlock(&xcp_mgr->xcp_lock);
}
}
@@ -488,7 +493,9 @@ int amdgpu_xcp_select_scheds(struct amdgpu_device *adev,
u32 sel_xcp_id;
int i;
struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr;
+ int r = 0;
+ mutex_lock(&xcp_mgr->xcp_lock);
if (fpriv->xcp_id == AMDGPU_XCP_NO_PARTITION) {
u32 least_ref_cnt = ~0;
@@ -505,19 +512,27 @@ int amdgpu_xcp_select_scheds(struct amdgpu_device *adev,
}
sel_xcp_id = fpriv->xcp_id;
+ if (sel_xcp_id >= xcp_mgr->num_xcps || !xcp_mgr->xcp[sel_xcp_id].valid) {
+ dev_err(adev->dev, "Selected partition #%d is not valid.", sel_xcp_id);
+ r = -ENODEV;
+ goto out;
+ }
+
if (xcp_mgr->xcp[sel_xcp_id].gpu_sched[hw_ip][hw_prio].num_scheds) {
*num_scheds =
- xcp_mgr->xcp[fpriv->xcp_id].gpu_sched[hw_ip][hw_prio].num_scheds;
+ xcp_mgr->xcp[sel_xcp_id].gpu_sched[hw_ip][hw_prio].num_scheds;
*scheds =
- xcp_mgr->xcp[fpriv->xcp_id].gpu_sched[hw_ip][hw_prio].sched;
- atomic_inc(&adev->xcp_mgr->xcp[sel_xcp_id].ref_cnt);
+ xcp_mgr->xcp[sel_xcp_id].gpu_sched[hw_ip][hw_prio].sched;
+ atomic_inc(&xcp_mgr->xcp[sel_xcp_id].ref_cnt);
dev_dbg(adev->dev, "Selected partition #%d", sel_xcp_id);
} else {
dev_err(adev->dev, "Failed to schedule partition #%d.", sel_xcp_id);
- return -ENOENT;
+ r = -ENOENT;
}
- return 0;
+out:
+ mutex_unlock(&xcp_mgr->xcp_lock);
+ return r;
}
static void amdgpu_set_xcp_id(struct amdgpu_device *adev,
@@ -574,6 +589,9 @@ static void amdgpu_xcp_gpu_sched_update(struct amdgpu_device *adev,
{
unsigned int *num_gpu_sched;
+ if (sel_xcp_id >= MAX_XCP || sel_xcp_id == AMDGPU_XCP_NO_PARTITION)
+ return;
+
num_gpu_sched = &adev->xcp_mgr->xcp[sel_xcp_id]
.gpu_sched[ring->funcs->type][ring->hw_prio].num_scheds;
adev->xcp_mgr->xcp[sel_xcp_id].gpu_sched[ring->funcs->type][ring->hw_prio]
@@ -903,7 +921,7 @@ static void amdgpu_xcp_cfg_sysfs_init(struct amdgpu_device *adev)
{
struct amdgpu_xcp_res_details *xcp_res;
struct amdgpu_xcp_cfg *xcp_cfg;
- int i, r, j, rid, mode;
+ int i, r, rid, mode;
if (!adev->xcp_mgr)
return;
@@ -949,14 +967,16 @@ static void amdgpu_xcp_cfg_sysfs_init(struct amdgpu_device *adev)
&xcp_cfg_res_sysfs_ktype,
&xcp_cfg->kobj, "%s",
xcp_res_names[rid]);
- if (r)
+ if (r) {
+ kobject_put(&xcp_res->kobj);
goto err;
+ }
}
adev->xcp_mgr->xcp_cfg = xcp_cfg;
return;
err:
- for (j = 0; j < i; j++) {
+ while (i--) {
xcp_res = &xcp_cfg->xcp_res[i];
kobject_put(&xcp_res->kobj);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c
index e63d05c477a0..d2c5bb50d94a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c
@@ -106,53 +106,6 @@ static const int walf_pcs_err_noncorrectable_mask_reg_aldebaran[] = {
smnPCS_GOPX1_PCS_ERROR_NONCORRECTABLE_MASK + 0x100000
};
-static const int xgmi3x16_pcs_err_status_reg_v6_4[] = {
- smnPCS_XGMI3X16_PCS_ERROR_STATUS,
- smnPCS_XGMI3X16_PCS_ERROR_STATUS + 0x100000
-};
-
-static const int xgmi3x16_pcs_err_noncorrectable_mask_reg_v6_4[] = {
- smnPCS_XGMI3X16_PCS_ERROR_NONCORRECTABLE_MASK,
- smnPCS_XGMI3X16_PCS_ERROR_NONCORRECTABLE_MASK + 0x100000
-};
-
-static const u64 xgmi_v6_4_0_mca_base_array[] = {
- 0x11a09200,
- 0x11b09200,
-};
-
-static const char *xgmi_v6_4_0_ras_error_code_ext[32] = {
- [0x00] = "XGMI PCS DataLossErr",
- [0x01] = "XGMI PCS TrainingErr",
- [0x02] = "XGMI PCS FlowCtrlAckErr",
- [0x03] = "XGMI PCS RxFifoUnderflowErr",
- [0x04] = "XGMI PCS RxFifoOverflowErr",
- [0x05] = "XGMI PCS CRCErr",
- [0x06] = "XGMI PCS BERExceededErr",
- [0x07] = "XGMI PCS TxMetaDataErr",
- [0x08] = "XGMI PCS ReplayBufParityErr",
- [0x09] = "XGMI PCS DataParityErr",
- [0x0a] = "XGMI PCS ReplayFifoOverflowErr",
- [0x0b] = "XGMI PCS ReplayFifoUnderflowErr",
- [0x0c] = "XGMI PCS ElasticFifoOverflowErr",
- [0x0d] = "XGMI PCS DeskewErr",
- [0x0e] = "XGMI PCS FlowCtrlCRCErr",
- [0x0f] = "XGMI PCS DataStartupLimitErr",
- [0x10] = "XGMI PCS FCInitTimeoutErr",
- [0x11] = "XGMI PCS RecoveryTimeoutErr",
- [0x12] = "XGMI PCS ReadySerialTimeoutErr",
- [0x13] = "XGMI PCS ReadySerialAttemptErr",
- [0x14] = "XGMI PCS RecoveryAttemptErr",
- [0x15] = "XGMI PCS RecoveryRelockAttemptErr",
- [0x16] = "XGMI PCS ReplayAttemptErr",
- [0x17] = "XGMI PCS SyncHdrErr",
- [0x18] = "XGMI PCS TxReplayTimeoutErr",
- [0x19] = "XGMI PCS RxReplayTimeoutErr",
- [0x1a] = "XGMI PCS LinkSubTxTimeoutErr",
- [0x1b] = "XGMI PCS LinkSubRxTimeoutErr",
- [0x1c] = "XGMI PCS RxCMDPktErr",
-};
-
static const struct amdgpu_pcs_ras_field xgmi_pcs_ras_fields[] = {
{"XGMI PCS DataLossErr",
SOC15_REG_FIELD(XGMI0_PCS_GOPX16_PCS_ERROR_STATUS, DataLossErr)},
@@ -1152,91 +1105,15 @@ int amdgpu_xgmi_remove_device(struct amdgpu_device *adev)
return 0;
}
-static int xgmi_v6_4_0_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct amdgpu_device *adev = handle->adev;
- struct aca_bank_info info;
- const char *error_str;
- u64 status, count;
- int ret, ext_error_code;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- status = bank->regs[ACA_REG_IDX_STATUS];
- ext_error_code = ACA_REG__STATUS__ERRORCODEEXT(status);
-
- error_str = ext_error_code < ARRAY_SIZE(xgmi_v6_4_0_ras_error_code_ext) ?
- xgmi_v6_4_0_ras_error_code_ext[ext_error_code] : NULL;
- if (error_str)
- dev_info(adev->dev, "%s detected\n", error_str);
-
- count = ACA_REG__MISC0__ERRCNT(bank->regs[ACA_REG_IDX_MISC0]);
-
- switch (type) {
- case ACA_SMU_TYPE_UE:
- if (ext_error_code != 0 && ext_error_code != 1 && ext_error_code != 9)
- count = 0ULL;
-
- bank->aca_err_type = ACA_ERROR_TYPE_UE;
- ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE, count);
- break;
- case ACA_SMU_TYPE_CE:
- count = ext_error_code == 6 ? count : 0ULL;
- bank->aca_err_type = ACA_ERROR_TYPE_CE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, count);
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
-static const struct aca_bank_ops xgmi_v6_4_0_aca_bank_ops = {
- .aca_bank_parser = xgmi_v6_4_0_aca_bank_parser,
-};
-
-static const struct aca_info xgmi_v6_4_0_aca_info = {
- .hwip = ACA_HWIP_TYPE_PCS_XGMI,
- .mask = ACA_ERROR_UE_MASK | ACA_ERROR_CE_MASK,
- .bank_ops = &xgmi_v6_4_0_aca_bank_ops,
-};
-
static int amdgpu_xgmi_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
{
- int r;
-
if (!adev->gmc.xgmi.supported ||
adev->gmc.xgmi.num_physical_nodes == 0)
return 0;
amdgpu_ras_reset_error_count(adev, AMDGPU_RAS_BLOCK__XGMI_WAFL);
- r = amdgpu_ras_block_late_init(adev, ras_block);
- if (r)
- return r;
-
- switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) {
- case IP_VERSION(6, 4, 0):
- case IP_VERSION(6, 4, 1):
- r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__XGMI_WAFL,
- &xgmi_v6_4_0_aca_info, NULL);
- if (r)
- goto late_fini;
- break;
- default:
- break;
- }
-
- return 0;
-
-late_fini:
- amdgpu_ras_block_late_fini(adev, ras_block);
-
- return r;
+ return amdgpu_ras_block_late_init(adev, ras_block);
}
uint64_t amdgpu_xgmi_get_relative_phy_addr(struct amdgpu_device *adev,
@@ -1252,7 +1129,7 @@ static void pcs_clear_status(struct amdgpu_device *adev, uint32_t pcs_status_reg
WREG32_PCIE(pcs_status_reg, 0);
}
-static void amdgpu_xgmi_legacy_reset_ras_error_count(struct amdgpu_device *adev)
+static void amdgpu_xgmi_reset_ras_error_count(struct amdgpu_device *adev)
{
uint32_t i;
@@ -1278,54 +1155,6 @@ static void amdgpu_xgmi_legacy_reset_ras_error_count(struct amdgpu_device *adev)
default:
break;
}
-
- switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) {
- case IP_VERSION(6, 4, 0):
- case IP_VERSION(6, 4, 1):
- for (i = 0; i < ARRAY_SIZE(xgmi3x16_pcs_err_status_reg_v6_4); i++)
- pcs_clear_status(adev,
- xgmi3x16_pcs_err_status_reg_v6_4[i]);
- break;
- default:
- break;
- }
-}
-
-static void __xgmi_v6_4_0_reset_error_count(struct amdgpu_device *adev, int xgmi_inst, u64 mca_base)
-{
- uint64_t smn_base =
- amdgpu_reg_get_smn_base64(adev, XGMI_HWIP, xgmi_inst);
-
- WREG64_MCA(smn_base, mca_base, ACA_REG_IDX_STATUS, 0ULL);
-}
-
-static void xgmi_v6_4_0_reset_error_count(struct amdgpu_device *adev, int xgmi_inst)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(xgmi_v6_4_0_mca_base_array); i++)
- __xgmi_v6_4_0_reset_error_count(adev, xgmi_inst, xgmi_v6_4_0_mca_base_array[i]);
-}
-
-static void xgmi_v6_4_0_reset_ras_error_count(struct amdgpu_device *adev)
-{
- int i;
-
- for_each_inst(i, adev->aid_mask)
- xgmi_v6_4_0_reset_error_count(adev, i);
-}
-
-static void amdgpu_xgmi_reset_ras_error_count(struct amdgpu_device *adev)
-{
- switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) {
- case IP_VERSION(6, 4, 0):
- case IP_VERSION(6, 4, 1):
- xgmi_v6_4_0_reset_ras_error_count(adev);
- break;
- default:
- amdgpu_xgmi_legacy_reset_ras_error_count(adev);
- break;
- }
}
static int amdgpu_xgmi_query_pcs_error_status(struct amdgpu_device *adev,
@@ -1343,11 +1172,7 @@ static int amdgpu_xgmi_query_pcs_error_status(struct amdgpu_device *adev,
if (is_xgmi_pcs) {
if (amdgpu_ip_version(adev, XGMI_HWIP, 0) ==
- IP_VERSION(6, 1, 0) ||
- amdgpu_ip_version(adev, XGMI_HWIP, 0) ==
- IP_VERSION(6, 4, 0) ||
- amdgpu_ip_version(adev, XGMI_HWIP, 0) ==
- IP_VERSION(6, 4, 1)) {
+ IP_VERSION(6, 1, 0)) {
pcs_ras_fields = &xgmi3x16_pcs_ras_fields[0];
field_array_size = ARRAY_SIZE(xgmi3x16_pcs_ras_fields);
} else {
@@ -1381,11 +1206,11 @@ static int amdgpu_xgmi_query_pcs_error_status(struct amdgpu_device *adev,
return 0;
}
-static void amdgpu_xgmi_legacy_query_ras_error_count(struct amdgpu_device *adev,
+static void amdgpu_xgmi_query_ras_error_count(struct amdgpu_device *adev,
void *ras_error_status)
{
struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status;
- int i, supported = 1;
+ int i;
uint32_t data, mask_data = 0;
uint32_t ue_cnt = 0, ce_cnt = 0;
@@ -1449,26 +1274,6 @@ static void amdgpu_xgmi_legacy_query_ras_error_count(struct amdgpu_device *adev,
}
break;
default:
- supported = 0;
- break;
- }
-
- switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) {
- case IP_VERSION(6, 4, 0):
- case IP_VERSION(6, 4, 1):
- /* check xgmi3x16 pcs error */
- for (i = 0; i < ARRAY_SIZE(xgmi3x16_pcs_err_status_reg_v6_4); i++) {
- data = RREG32_PCIE(xgmi3x16_pcs_err_status_reg_v6_4[i]);
- mask_data =
- RREG32_PCIE(xgmi3x16_pcs_err_noncorrectable_mask_reg_v6_4[i]);
- if (data)
- amdgpu_xgmi_query_pcs_error_status(adev, data,
- mask_data, &ue_cnt, &ce_cnt, true, true);
- }
- break;
- default:
- if (!supported)
- dev_warn(adev->dev, "XGMI RAS error query not supported");
break;
}
@@ -1478,90 +1283,6 @@ static void amdgpu_xgmi_legacy_query_ras_error_count(struct amdgpu_device *adev,
err_data->ce_count += ce_cnt;
}
-static enum aca_error_type xgmi_v6_4_0_pcs_mca_get_error_type(struct amdgpu_device *adev, u64 status)
-{
- const char *error_str;
- int ext_error_code;
-
- ext_error_code = ACA_REG__STATUS__ERRORCODEEXT(status);
-
- error_str = ext_error_code < ARRAY_SIZE(xgmi_v6_4_0_ras_error_code_ext) ?
- xgmi_v6_4_0_ras_error_code_ext[ext_error_code] : NULL;
- if (error_str)
- dev_info(adev->dev, "%s detected\n", error_str);
-
- switch (ext_error_code) {
- case 0:
- return ACA_ERROR_TYPE_UE;
- case 6:
- return ACA_ERROR_TYPE_CE;
- default:
- return -EINVAL;
- }
-
- return -EINVAL;
-}
-
-static void __xgmi_v6_4_0_query_error_count(struct amdgpu_device *adev, struct amdgpu_smuio_mcm_config_info *mcm_info,
- u64 mca_base, struct ras_err_data *err_data)
-{
- int xgmi_inst = mcm_info->die_id;
- uint64_t smn_base;
- u64 status = 0;
-
- status = RREG64_MCA(xgmi_inst, mca_base, ACA_REG_IDX_STATUS);
- if (!ACA_REG__STATUS__VAL(status))
- return;
-
- switch (xgmi_v6_4_0_pcs_mca_get_error_type(adev, status)) {
- case ACA_ERROR_TYPE_UE:
- amdgpu_ras_error_statistic_ue_count(err_data, mcm_info, 1ULL);
- break;
- case ACA_ERROR_TYPE_CE:
- amdgpu_ras_error_statistic_ce_count(err_data, mcm_info, 1ULL);
- break;
- default:
- break;
- }
- smn_base = amdgpu_reg_get_smn_base64(adev, XGMI_HWIP, xgmi_inst);
- WREG64_MCA(smn_base, mca_base, ACA_REG_IDX_STATUS, 0ULL);
-}
-
-static void xgmi_v6_4_0_query_error_count(struct amdgpu_device *adev, int xgmi_inst, struct ras_err_data *err_data)
-{
- struct amdgpu_smuio_mcm_config_info mcm_info = {
- .socket_id = adev->smuio.funcs->get_socket_id(adev),
- .die_id = xgmi_inst,
- };
- int i;
-
- for (i = 0; i < ARRAY_SIZE(xgmi_v6_4_0_mca_base_array); i++)
- __xgmi_v6_4_0_query_error_count(adev, &mcm_info, xgmi_v6_4_0_mca_base_array[i], err_data);
-}
-
-static void xgmi_v6_4_0_query_ras_error_count(struct amdgpu_device *adev, void *ras_error_status)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status;
- int i;
-
- for_each_inst(i, adev->aid_mask)
- xgmi_v6_4_0_query_error_count(adev, i, err_data);
-}
-
-static void amdgpu_xgmi_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_error_status)
-{
- switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) {
- case IP_VERSION(6, 4, 0):
- case IP_VERSION(6, 4, 1):
- xgmi_v6_4_0_query_ras_error_count(adev, ras_error_status);
- break;
- default:
- amdgpu_xgmi_legacy_query_ras_error_count(adev, ras_error_status);
- break;
- }
-}
-
/* Trigger XGMI/WAFL error */
static int amdgpu_ras_error_inject_xgmi(struct amdgpu_device *adev,
void *inject_if, uint32_t instance_mask)
@@ -1663,6 +1384,16 @@ static void amdgpu_xgmi_reset_on_init_work(struct work_struct *work)
if (r && r != -EHWPOISON)
dev_err(tmp_adev->dev,
"error during bad page data initialization");
+
+ /*
+ * For the reset-on-init path (e.g. an NPS memory partition
+ * switch) the RAS IP block hw_init was skipped under the
+ * minimal init level, so uniras was never enabled. Bring it
+ * up now that the reset domain has been unlocked. This is a
+ * no-op for any other reset path where RAS is already
+ * initialized, and for non-uniras devices.
+ */
+ amdgpu_ras_resume_after_reset(tmp_adev);
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c
index 72ea37dbfea8..cddfe4015f53 100644
--- a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c
+++ b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c
@@ -273,8 +273,10 @@ static int aqua_vanjaram_get_xcp_res_info(struct amdgpu_xcp_mgr *xcp_mgr,
xcp_cfg->num_res = ARRAY_SIZE(max_res);
for (i = 0; i < xcp_cfg->num_res; i++) {
- res_lt_xcp = max_res[i] < num_xcp;
xcp_cfg->xcp_res[i].id = i;
+ if (!max_res[i])
+ continue;
+ res_lt_xcp = max_res[i] < num_xcp;
xcp_cfg->xcp_res[i].num_inst =
res_lt_xcp ? 1 : max_res[i] / num_xcp;
xcp_cfg->xcp_res[i].num_inst =
@@ -589,6 +591,29 @@ static struct aqua_reg_list pcie_reg_addrs[] = {
{ smreg_0x1A380088, 6, DW_ADDR_INCR },
};
+/*
+ * Return the GPU's internal US switch port, or NULL if it is not visible
+ * (e.g. passthrough) or the EP is parented under an unrelated bridge.
+ */
+static struct pci_dev *aqua_vanjaram_get_us_pdev(struct amdgpu_device *adev)
+{
+ struct pci_dev *ds_pdev, *us_pdev;
+
+ ds_pdev = pci_upstream_bridge(adev->pdev);
+ if (!ds_pdev || ds_pdev->vendor != PCI_VENDOR_ID_ATI ||
+ pci_pcie_type(ds_pdev) != PCI_EXP_TYPE_DOWNSTREAM)
+ return NULL;
+
+ us_pdev = pci_upstream_bridge(ds_pdev);
+ if (!us_pdev ||
+ (us_pdev->vendor != PCI_VENDOR_ID_ATI &&
+ us_pdev->vendor != PCI_VENDOR_ID_AMD) ||
+ pci_pcie_type(us_pdev) != PCI_EXP_TYPE_UPSTREAM)
+ return NULL;
+
+ return us_pdev;
+}
+
static ssize_t aqua_vanjaram_read_pcie_state(struct amdgpu_device *adev,
void *buf, size_t max_size)
{
@@ -596,7 +621,7 @@ static ssize_t aqua_vanjaram_read_pcie_state(struct amdgpu_device *adev,
uint32_t start_addr, incrx, num_regs, szbuf;
struct amdgpu_regs_pcie_v1_0 *pcie_regs;
struct amdgpu_smn_reg_data *reg_data;
- struct pci_dev *us_pdev, *ds_pdev;
+ struct pci_dev *us_pdev;
int aer_cap, r, n;
if (!buf || !max_size)
@@ -628,25 +653,27 @@ static ssize_t aqua_vanjaram_read_pcie_state(struct amdgpu_device *adev,
}
}
- ds_pdev = pci_upstream_bridge(adev->pdev);
- us_pdev = pci_upstream_bridge(ds_pdev);
+ us_pdev = aqua_vanjaram_get_us_pdev(adev);
+ if (us_pdev) {
+ pcie_capability_read_word(us_pdev, PCI_EXP_DEVSTA,
+ &pcie_regs->device_status);
+ pcie_capability_read_word(us_pdev, PCI_EXP_LNKSTA,
+ &pcie_regs->link_status);
+
+ aer_cap = pci_find_ext_capability(us_pdev, PCI_EXT_CAP_ID_ERR);
+ if (aer_cap) {
+ pci_read_config_dword(us_pdev,
+ aer_cap + PCI_ERR_COR_STATUS,
+ &pcie_regs->pcie_corr_err_status);
+ pci_read_config_dword(us_pdev,
+ aer_cap + PCI_ERR_UNCOR_STATUS,
+ &pcie_regs->pcie_uncorr_err_status);
+ }
- pcie_capability_read_word(us_pdev, PCI_EXP_DEVSTA,
- &pcie_regs->device_status);
- pcie_capability_read_word(us_pdev, PCI_EXP_LNKSTA,
- &pcie_regs->link_status);
-
- aer_cap = pci_find_ext_capability(us_pdev, PCI_EXT_CAP_ID_ERR);
- if (aer_cap) {
- pci_read_config_dword(us_pdev, aer_cap + PCI_ERR_COR_STATUS,
- &pcie_regs->pcie_corr_err_status);
- pci_read_config_dword(us_pdev, aer_cap + PCI_ERR_UNCOR_STATUS,
- &pcie_regs->pcie_uncorr_err_status);
+ pci_read_config_dword(us_pdev, PCI_PRIMARY_BUS,
+ &pcie_regs->sub_bus_number_latency);
}
- pci_read_config_dword(us_pdev, PCI_PRIMARY_BUS,
- &pcie_regs->sub_bus_number_latency);
-
pcie_reg_state->common_header.structure_size = szbuf;
pcie_reg_state->common_header.format_revision = 1;
pcie_reg_state->common_header.content_revision = 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/atom.c b/drivers/gpu/drm/amd/amdgpu/atom.c
index ca5d091549e1..e0e585f280e2 100644
--- a/drivers/gpu/drm/amd/amdgpu/atom.c
+++ b/drivers/gpu/drm/amd/amdgpu/atom.c
@@ -114,8 +114,10 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
uint32_t index, uint32_t data)
{
uint32_t temp = 0xCDCDCDCD;
+ int start = base;
- while (1)
+ /* IIO opcodes read up to base+3; keep within the BIOS image */
+ while (base + 3 < ctx->bios_size)
switch (CU8(base)) {
case ATOM_IIO_NOP:
base++;
@@ -180,6 +182,9 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
pr_info("Unknown IIO opcode\n");
return 0;
}
+
+ pr_info("IIO method starting at offset %d runs past BIOS image\n", start);
+ return 0;
}
static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
@@ -1327,11 +1332,25 @@ static void atom_index_iio(struct atom_context *ctx, int base)
ctx->iio = kzalloc(2 * 256, GFP_KERNEL);
if (!ctx->iio)
return;
- while (CU8(base) == ATOM_IIO_START) {
- ctx->iio[CU8(base + 1)] = base + 2;
+ while (base + 1 < ctx->bios_size && CU8(base) == ATOM_IIO_START) {
+ uint8_t index = CU8(base + 1);
+ int start = base + 2;
base += 2;
- while (CU8(base) != ATOM_IIO_END)
- base += atom_iio_len[CU8(base)];
+ while (base < ctx->bios_size && CU8(base) != ATOM_IIO_END) {
+ uint8_t op = CU8(base);
+
+ /*
+ * Unknown opcode: its length is unknown so the byte
+ * stream cannot be resynced reliably.
+ */
+ if (op >= ARRAY_SIZE(atom_iio_len))
+ return;
+ base += atom_iio_len[op];
+ }
+ if (base >= ctx->bios_size)
+ return;
+ /* Only index well-formed methods, others stay 0 */
+ ctx->iio[index] = start;
base += 3;
}
}
@@ -1339,6 +1358,7 @@ static void atom_index_iio(struct atom_context *ctx, int base)
static void atom_get_vbios_name(struct atom_context *ctx)
{
unsigned char *p_rom;
+ unsigned char *p_end;
unsigned char str_num;
unsigned short off_to_vbios_str;
unsigned char *c_ptr;
@@ -1349,39 +1369,48 @@ static void atom_get_vbios_name(struct atom_context *ctx)
char *back;
p_rom = ctx->bios;
+ p_end = p_rom + ctx->bios_size;
+
+ if (p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START + 1 >= p_end)
+ goto no_name;
str_num = *(p_rom + OFFSET_TO_GET_ATOMBIOS_NUMBER_OF_STRINGS);
- if (str_num != 0) {
- off_to_vbios_str =
- *(unsigned short *)(p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START);
+ if (!str_num)
+ goto no_name;
- c_ptr = (unsigned char *)(p_rom + off_to_vbios_str);
- } else {
- /* do not know where to find name */
- memcpy(ctx->name, na, 7);
- ctx->name[7] = 0;
- return;
- }
+ off_to_vbios_str =
+ *(unsigned short *)(p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START);
+
+ c_ptr = (unsigned char *)(p_rom + off_to_vbios_str);
+ if (c_ptr >= p_end)
+ goto no_name;
/*
* skip the atombios strings, usually 4
* 1st is P/N, 2nd is ASIC, 3rd is PCI type, 4th is Memory type
*/
for (i = 0; i < str_num; i++) {
- while (*c_ptr != 0)
+ while (c_ptr < p_end && *c_ptr != 0)
c_ptr++;
c_ptr++;
}
/* skip the following 2 chars: 0x0D 0x0A */
c_ptr += 2;
+ if (c_ptr >= p_end)
+ goto no_name;
- name_size = strnlen(c_ptr, STRLEN_LONG - 1);
+ name_size = strnlen(c_ptr, min(STRLEN_LONG - 1, (int)(p_end - c_ptr)));
memcpy(ctx->name, c_ptr, name_size);
back = ctx->name + name_size;
while ((*--back) == ' ')
;
*(back + 1) = '\0';
+ return;
+
+no_name:
+ /* do not know where to find name */
+ strscpy(ctx->name, na, sizeof(ctx->name));
}
static void atom_get_vbios_date(struct atom_context *ctx)
@@ -1553,7 +1582,7 @@ static inline void atom_print_vbios_info(struct atom_context *ctx)
drm_info(ctx->card->dev, "ATOM BIOS: %s\n", vbios_info);
}
-struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios)
+struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios, uint32_t bios_size)
{
int base;
struct atom_context *ctx =
@@ -1567,6 +1596,7 @@ struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios)
ctx->card = card;
ctx->bios = bios;
+ ctx->bios_size = bios_size;
if (CU16(0) != ATOM_BIOS_MAGIC) {
pr_info("Invalid BIOS magic\n");
diff --git a/drivers/gpu/drm/amd/amdgpu/atom.h b/drivers/gpu/drm/amd/amdgpu/atom.h
index bb3d9eb7eb6b..4687c019cbe3 100644
--- a/drivers/gpu/drm/amd/amdgpu/atom.h
+++ b/drivers/gpu/drm/amd/amdgpu/atom.h
@@ -133,6 +133,7 @@ struct atom_context {
struct card_info *card;
struct mutex mutex;
void *bios;
+ uint32_t bios_size;
uint32_t cmd_table, data_table;
uint16_t *iio;
@@ -160,7 +161,7 @@ struct atom_context {
extern int amdgpu_atom_debug;
-struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios);
+struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios, uint32_t bios_size);
int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size);
int amdgpu_atom_asic_init(struct atom_context *ctx);
void amdgpu_atom_destroy(struct atom_context *ctx);
diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c
index 29954c7d61b0..77e120a72815 100644
--- a/drivers/gpu/drm/amd/amdgpu/cik.c
+++ b/drivers/gpu/drm/amd/amdgpu/cik.c
@@ -1876,12 +1876,6 @@ static void cik_invalidate_hdp(struct amdgpu_device *adev,
}
}
-static bool cik_need_full_reset(struct amdgpu_device *adev)
-{
- /* change this when we support soft reset */
- return true;
-}
-
static void cik_get_pcie_usage(struct amdgpu_device *adev, uint64_t *count0,
uint64_t *count1)
{
@@ -1971,7 +1965,6 @@ static const struct amdgpu_asic_funcs cik_asic_funcs =
.get_config_memsize = &cik_get_config_memsize,
.flush_hdp = &cik_flush_hdp,
.invalidate_hdp = &cik_invalidate_hdp,
- .need_full_reset = &cik_need_full_reset,
.init_doorbell_index = &legacy_doorbell_index_init,
.get_pcie_usage = &cik_get_pcie_usage,
.need_reset_on_init = &cik_need_reset_on_init,
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
index c8f465158e71..f2977fe6d824 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
@@ -410,36 +410,6 @@ static u32 dce_v10_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
return mmDC_GPIO_HPD_A;
}
-static bool dce_v10_0_is_display_hung(struct amdgpu_device *adev)
-{
- u32 crtc_hung = 0;
- u32 crtc_status[6];
- u32 i, j, tmp;
-
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- tmp = RREG32(mmCRTC_CONTROL + crtc_offsets[i]);
- if (REG_GET_FIELD(tmp, CRTC_CONTROL, CRTC_MASTER_EN)) {
- crtc_status[i] = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]);
- crtc_hung |= (1 << i);
- }
- }
-
- for (j = 0; j < 10; j++) {
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- if (crtc_hung & (1 << i)) {
- tmp = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]);
- if (tmp != crtc_status[i])
- crtc_hung &= ~(1 << i);
- }
- }
- if (crtc_hung == 0)
- return false;
- udelay(100);
- }
-
- return true;
-}
-
static void dce_v10_0_set_vga_render_state(struct amdgpu_device *adev,
bool render)
{
@@ -2956,40 +2926,6 @@ static bool dce_v10_0_is_idle(struct amdgpu_ip_block *ip_block)
return true;
}
-static bool dce_v10_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
-
- return dce_v10_0_is_display_hung(adev);
-}
-
-static int dce_v10_0_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- u32 srbm_soft_reset = 0, tmp;
- struct amdgpu_device *adev = ip_block->adev;
-
- if (dce_v10_0_is_display_hung(adev))
- srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK;
-
- if (srbm_soft_reset) {
- tmp = RREG32(mmSRBM_SOFT_RESET);
- tmp |= srbm_soft_reset;
- dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
- WREG32(mmSRBM_SOFT_RESET, tmp);
- tmp = RREG32(mmSRBM_SOFT_RESET);
-
- udelay(50);
-
- tmp &= ~srbm_soft_reset;
- WREG32(mmSRBM_SOFT_RESET, tmp);
- tmp = RREG32(mmSRBM_SOFT_RESET);
-
- /* Wait a little for things to settle down */
- udelay(50);
- }
- return 0;
-}
-
static void dce_v10_0_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev,
int crtc,
enum amdgpu_interrupt_state state)
@@ -3332,8 +3268,6 @@ static const struct amd_ip_funcs dce_v10_0_ip_funcs = {
.suspend = dce_v10_0_suspend,
.resume = dce_v10_0_resume,
.is_idle = dce_v10_0_is_idle,
- .check_soft_reset = dce_v10_0_check_soft_reset,
- .soft_reset = dce_v10_0_soft_reset,
.set_clockgating_state = dce_v10_0_set_clockgating_state,
.set_powergating_state = dce_v10_0_set_powergating_state,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
index 58d0da5c2a74..c68de0fe1d7d 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
@@ -378,35 +378,6 @@ static u32 dce_v6_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
return mmDC_GPIO_HPD_A;
}
-static bool dce_v6_0_is_display_hung(struct amdgpu_device *adev)
-{
- u32 crtc_hung = 0;
- u32 crtc_status[6];
- u32 i, j, tmp;
-
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- if (RREG32(mmCRTC_CONTROL + crtc_offsets[i]) & CRTC_CONTROL__CRTC_MASTER_EN_MASK) {
- crtc_status[i] = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]);
- crtc_hung |= (1 << i);
- }
- }
-
- for (j = 0; j < 10; j++) {
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- if (crtc_hung & (1 << i)) {
- tmp = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]);
- if (tmp != crtc_status[i])
- crtc_hung &= ~(1 << i);
- }
- }
- if (crtc_hung == 0)
- return false;
- udelay(100);
- }
-
- return true;
-}
-
static void dce_v6_0_set_vga_render_state(struct amdgpu_device *adev,
bool render)
{
@@ -2901,33 +2872,6 @@ static bool dce_v6_0_is_idle(struct amdgpu_ip_block *ip_block)
return true;
}
-static int dce_v6_0_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- u32 srbm_soft_reset = 0, tmp;
- struct amdgpu_device *adev = ip_block->adev;
-
- if (dce_v6_0_is_display_hung(adev))
- srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK;
-
- if (srbm_soft_reset) {
- tmp = RREG32(mmSRBM_SOFT_RESET);
- tmp |= srbm_soft_reset;
- dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
- WREG32(mmSRBM_SOFT_RESET, tmp);
- tmp = RREG32(mmSRBM_SOFT_RESET);
-
- udelay(50);
-
- tmp &= ~srbm_soft_reset;
- WREG32(mmSRBM_SOFT_RESET, tmp);
- tmp = RREG32(mmSRBM_SOFT_RESET);
-
- /* Wait a little for things to settle down */
- udelay(50);
- }
- return 0;
-}
-
static void dce_v6_0_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev,
int crtc,
enum amdgpu_interrupt_state state)
@@ -3224,7 +3168,6 @@ static const struct amd_ip_funcs dce_v6_0_ip_funcs = {
.suspend = dce_v6_0_suspend,
.resume = dce_v6_0_resume,
.is_idle = dce_v6_0_is_idle,
- .soft_reset = dce_v6_0_soft_reset,
.set_clockgating_state = dce_v6_0_set_clockgating_state,
.set_powergating_state = dce_v6_0_set_powergating_state,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
index 6d19f6d94d25..c3906270f25e 100644
--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
@@ -362,35 +362,6 @@ static u32 dce_v8_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
return mmDC_GPIO_HPD_A;
}
-static bool dce_v8_0_is_display_hung(struct amdgpu_device *adev)
-{
- u32 crtc_hung = 0;
- u32 crtc_status[6];
- u32 i, j, tmp;
-
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- if (RREG32(mmCRTC_CONTROL + crtc_offsets[i]) & CRTC_CONTROL__CRTC_MASTER_EN_MASK) {
- crtc_status[i] = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]);
- crtc_hung |= (1 << i);
- }
- }
-
- for (j = 0; j < 10; j++) {
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- if (crtc_hung & (1 << i)) {
- tmp = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]);
- if (tmp != crtc_status[i])
- crtc_hung &= ~(1 << i);
- }
- }
- if (crtc_hung == 0)
- return false;
- udelay(100);
- }
-
- return true;
-}
-
static void dce_v8_0_set_vga_render_state(struct amdgpu_device *adev,
bool render)
{
@@ -2873,33 +2844,6 @@ static bool dce_v8_0_is_idle(struct amdgpu_ip_block *ip_block)
return true;
}
-static int dce_v8_0_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- u32 srbm_soft_reset = 0, tmp;
- struct amdgpu_device *adev = ip_block->adev;
-
- if (dce_v8_0_is_display_hung(adev))
- srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK;
-
- if (srbm_soft_reset) {
- tmp = RREG32(mmSRBM_SOFT_RESET);
- tmp |= srbm_soft_reset;
- dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
- WREG32(mmSRBM_SOFT_RESET, tmp);
- tmp = RREG32(mmSRBM_SOFT_RESET);
-
- udelay(50);
-
- tmp &= ~srbm_soft_reset;
- WREG32(mmSRBM_SOFT_RESET, tmp);
- tmp = RREG32(mmSRBM_SOFT_RESET);
-
- /* Wait a little for things to settle down */
- udelay(50);
- }
- return 0;
-}
-
static void dce_v8_0_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev,
int crtc,
enum amdgpu_interrupt_state state)
@@ -3241,7 +3185,6 @@ static const struct amd_ip_funcs dce_v8_0_ip_funcs = {
.suspend = dce_v8_0_suspend,
.resume = dce_v8_0_resume,
.is_idle = dce_v8_0_is_idle,
- .soft_reset = dce_v8_0_soft_reset,
.set_clockgating_state = dce_v8_0_set_clockgating_state,
.set_powergating_state = dce_v8_0_set_powergating_state,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
index b4b27e4c495d..ddf190672530 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
@@ -7852,6 +7852,8 @@ static int gfx_v10_0_early_init(struct amdgpu_ip_block *ip_block)
/* init rlcg reg access ctrl */
gfx_v10_0_init_rlcg_reg_access_ctrl(adev);
+ amdgpu_init_rlc_reg_funcs(adev);
+
return gfx_v10_0_init_microcode(adev);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
index 3b12eb27a253..2a121df90574 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
@@ -1923,6 +1923,11 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ adev->gfx.me.use_mmio_for_reset = false;
+ adev->gfx.mec.use_mmio_for_reset = true;
+
+ mutex_init(&adev->gfx.mec.reset_mutex);
+
return 0;
}
@@ -4233,13 +4238,13 @@ static int gfx_v11_0_gfx_mqd_init(struct amdgpu_device *adev, void *m,
return 0;
}
-static int gfx_v11_0_kgq_init_queue(struct amdgpu_ring *ring, bool reset)
+static int gfx_v11_0_kgq_init_queue(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct v11_gfx_mqd *mqd = ring->mqd_ptr;
int mqd_idx = ring - &adev->gfx.gfx_ring[0];
- if (!reset && !amdgpu_in_reset(adev) && !adev->in_suspend) {
+ if (!amdgpu_in_reset(adev) && !adev->in_suspend) {
memset((void *)mqd, 0, sizeof(*mqd));
mutex_lock(&adev->srbm_mutex);
soc21_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
@@ -4266,7 +4271,7 @@ static int gfx_v11_0_cp_async_gfx_ring_resume(struct amdgpu_device *adev)
int r, i;
for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
- r = gfx_v11_0_kgq_init_queue(&adev->gfx.gfx_ring[i], false);
+ r = gfx_v11_0_kgq_init_queue(&adev->gfx.gfx_ring[i]);
if (r)
return r;
}
@@ -4603,13 +4608,13 @@ static int gfx_v11_0_kiq_init_queue(struct amdgpu_ring *ring)
return 0;
}
-static int gfx_v11_0_kcq_init_queue(struct amdgpu_ring *ring, bool reset)
+static int gfx_v11_0_kcq_init_queue(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct v11_compute_mqd *mqd = ring->mqd_ptr;
int mqd_idx = ring - &adev->gfx.compute_ring[0];
- if (!reset && !amdgpu_in_reset(adev) && !adev->in_suspend) {
+ if (!amdgpu_in_reset(adev) && !adev->in_suspend) {
memset((void *)mqd, 0, sizeof(*mqd));
mutex_lock(&adev->srbm_mutex);
soc21_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
@@ -4646,7 +4651,7 @@ static int gfx_v11_0_kcq_resume(struct amdgpu_device *adev)
gfx_v11_0_cp_compute_enable(adev, true);
for (i = 0; i < adev->gfx.num_compute_rings; i++) {
- r = gfx_v11_0_kcq_init_queue(&adev->gfx.compute_ring[i], false);
+ r = gfx_v11_0_kcq_init_queue(&adev->gfx.compute_ring[i]);
if (r)
return r;
}
@@ -5265,38 +5270,12 @@ static int gfx_v11_0_soft_reset(struct amdgpu_ip_block *ip_block)
amdgpu_gfx_rlc_exit_safe_mode(adev, 0);
- return gfx_v11_0_cp_resume(adev);
-}
-
-static bool gfx_v11_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- int i, r;
- struct amdgpu_device *adev = ip_block->adev;
- struct amdgpu_ring *ring;
- long tmo = msecs_to_jiffies(1000);
-
- for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
- ring = &adev->gfx.gfx_ring[i];
- r = amdgpu_ring_test_ib(ring, tmo);
- if (r)
- return true;
- }
-
- for (i = 0; i < adev->gfx.num_compute_rings; i++) {
- ring = &adev->gfx.compute_ring[i];
- r = amdgpu_ring_test_ib(ring, tmo);
- if (r)
- return true;
- }
-
- return false;
-}
+ r = gfx_v11_0_cp_resume(adev);
+ if (r)
+ return r;
-static int gfx_v11_0_post_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
/**
- * GFX soft reset will impact MES, need resume MES when do GFX soft reset
+ * GFX soft reset impacts MES, resume MES after GFX soft reset is finished
*/
return amdgpu_mes_resume(adev, 0);
}
@@ -5420,6 +5399,8 @@ static int gfx_v11_0_early_init(struct amdgpu_ip_block *ip_block)
gfx_v11_0_init_rlcg_reg_access_ctrl(adev);
+ amdgpu_init_rlc_reg_funcs(adev);
+
return gfx_v11_0_init_microcode(adev);
}
@@ -6708,22 +6689,29 @@ static int gfx_v11_0_set_priv_inst_fault_state(struct amdgpu_device *adev,
static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
- u8 me_id, pipe_id, queue_id;
- struct amdgpu_ring *ring;
- int i;
-
- me_id = (entry->ring_id & 0x0c) >> 2;
- pipe_id = (entry->ring_id & 0x03) >> 0;
- queue_id = (entry->ring_id & 0x70) >> 4;
+ u32 doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK;
+ /*
+ * Try KQ first by ring_id (HW slot is authoritative). The
+ * KMD compute_hqd_mask contract guarantees KCQ and user queues
+ * never share a HW slot.
+ */
if (!adev->gfx.disable_kq) {
+ u8 me_id = (entry->ring_id & 0x0c) >> 2;
+ u8 pipe_id = (entry->ring_id & 0x03) >> 0;
+ u8 queue_id = (entry->ring_id & 0x70) >> 4;
+ struct amdgpu_ring *ring;
+ int i;
+
switch (me_id) {
case 0:
for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
ring = &adev->gfx.gfx_ring[i];
if (ring->me == me_id && ring->pipe == pipe_id &&
- ring->queue == queue_id)
+ ring->queue == queue_id) {
drm_sched_fault(&ring->sched);
+ return;
+ }
}
break;
case 1:
@@ -6731,8 +6719,10 @@ static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev,
for (i = 0; i < adev->gfx.num_compute_rings; i++) {
ring = &adev->gfx.compute_ring[i];
if (ring->me == me_id && ring->pipe == pipe_id &&
- ring->queue == queue_id)
+ ring->queue == queue_id) {
drm_sched_fault(&ring->sched);
+ return;
+ }
}
break;
default:
@@ -6740,6 +6730,11 @@ static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev,
break;
}
}
+
+ /* No KQ matched: HW slot is a MES-scheduled user queue. */
+ if (adev->enable_mes && doorbell_offset)
+ amdgpu_userq_process_reset_irq(adev, entry->pasid,
+ doorbell_offset);
}
static int gfx_v11_0_priv_reg_irq(struct amdgpu_device *adev,
@@ -6846,233 +6841,14 @@ static void gfx_v11_0_emit_mem_sync(struct amdgpu_ring *ring)
amdgpu_ring_write(ring, gcr_cntl); /* GCR_CNTL */
}
-static bool gfx_v11_pipe_reset_support(struct amdgpu_device *adev)
-{
- /* Disable the pipe reset until the CPFW fully support it.*/
- dev_warn_once(adev->dev, "The CPFW hasn't support pipe reset yet.\n");
- return false;
-}
-
-
-static int gfx_v11_reset_gfx_pipe(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- uint32_t reset_pipe = 0, clean_pipe = 0;
- int r;
-
- if (!gfx_v11_pipe_reset_support(adev))
- return -EOPNOTSUPP;
-
- gfx_v11_0_set_safe_mode(adev, 0);
- mutex_lock(&adev->srbm_mutex);
- soc21_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
-
- switch (ring->pipe) {
- case 0:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
- PFP_PIPE0_RESET, 1);
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
- ME_PIPE0_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
- PFP_PIPE0_RESET, 0);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
- ME_PIPE0_RESET, 0);
- break;
- case 1:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
- PFP_PIPE1_RESET, 1);
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
- ME_PIPE1_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
- PFP_PIPE1_RESET, 0);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
- ME_PIPE1_RESET, 0);
- break;
- default:
- break;
- }
-
- WREG32_SOC15(GC, 0, regCP_ME_CNTL, reset_pipe);
- WREG32_SOC15(GC, 0, regCP_ME_CNTL, clean_pipe);
-
- r = (RREG32(SOC15_REG_OFFSET(GC, 0, regCP_GFX_RS64_INSTR_PNTR1)) << 2) -
- RS64_FW_UC_START_ADDR_LO;
- soc21_grbm_select(adev, 0, 0, 0, 0);
- mutex_unlock(&adev->srbm_mutex);
- gfx_v11_0_unset_safe_mode(adev, 0);
-
- dev_info(adev->dev, "The ring %s pipe reset to the ME firmware start PC: %s\n", ring->name,
- r == 0 ? "successfully" : "failed");
- /* FIXME: Sometimes driver can't cache the ME firmware start PC correctly,
- * so the pipe reset status relies on the later gfx ring test result.
- */
- return 0;
-}
-
static int gfx_v11_0_reset_kgq(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
- bool use_mmio = false;
- int r;
+ bool use_mmio = adev->gfx.me.use_mmio_for_reset;
- amdgpu_ring_reset_helper_begin(ring, timedout_fence);
-
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio, 0);
- if (r) {
-
- dev_warn(adev->dev, "reset via MES failed and try pipe reset %d\n", r);
- r = gfx_v11_reset_gfx_pipe(ring);
- if (r)
- return r;
- }
-
- if (use_mmio) {
- r = gfx_v11_0_kgq_init_queue(ring, true);
- if (r) {
- dev_err(adev->dev, "failed to init kgq\n");
- return r;
- }
-
- r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
- if (r) {
- dev_err(adev->dev, "failed to remap kgq\n");
- return r;
- }
- }
-
- return amdgpu_ring_reset_helper_end(ring, timedout_fence);
-}
-
-static int gfx_v11_0_reset_compute_pipe(struct amdgpu_ring *ring)
-{
-
- struct amdgpu_device *adev = ring->adev;
- uint32_t reset_pipe = 0, clean_pipe = 0;
- int r;
-
- if (!gfx_v11_pipe_reset_support(adev))
- return -EOPNOTSUPP;
-
- gfx_v11_0_set_safe_mode(adev, 0);
- mutex_lock(&adev->srbm_mutex);
- soc21_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
-
- reset_pipe = RREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL);
- clean_pipe = reset_pipe;
-
- if (adev->gfx.rs64_enable) {
-
- switch (ring->pipe) {
- case 0:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE0_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE0_RESET, 0);
- break;
- case 1:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE1_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE1_RESET, 0);
- break;
- case 2:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE2_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE2_RESET, 0);
- break;
- case 3:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE3_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE3_RESET, 0);
- break;
- default:
- break;
- }
- WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, reset_pipe);
- WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, clean_pipe);
- r = (RREG32_SOC15(GC, 0, regCP_MEC_RS64_INSTR_PNTR) << 2) -
- RS64_FW_UC_START_ADDR_LO;
- } else {
- if (ring->me == 1) {
- switch (ring->pipe) {
- case 0:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE0_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE0_RESET, 0);
- break;
- case 1:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE1_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE1_RESET, 0);
- break;
- case 2:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE2_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE2_RESET, 0);
- break;
- case 3:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE3_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE3_RESET, 0);
- break;
- default:
- break;
- }
- /* mec1 fw pc: CP_MEC1_INSTR_PNTR */
- } else {
- switch (ring->pipe) {
- case 0:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME2_PIPE0_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME2_PIPE0_RESET, 0);
- break;
- case 1:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME2_PIPE1_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME2_PIPE1_RESET, 0);
- break;
- case 2:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME2_PIPE2_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME2_PIPE2_RESET, 0);
- break;
- case 3:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME2_PIPE3_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME2_PIPE3_RESET, 0);
- break;
- default:
- break;
- }
- /* mec2 fw pc: CP:CP_MEC2_INSTR_PNTR */
- }
- WREG32_SOC15(GC, 0, regCP_MEC_CNTL, reset_pipe);
- WREG32_SOC15(GC, 0, regCP_MEC_CNTL, clean_pipe);
- r = RREG32(SOC15_REG_OFFSET(GC, 0, regCP_MEC1_INSTR_PNTR));
- }
-
- soc21_grbm_select(adev, 0, 0, 0, 0);
- mutex_unlock(&adev->srbm_mutex);
- gfx_v11_0_unset_safe_mode(adev, 0);
-
- dev_info(adev->dev, "The ring %s pipe resets to MEC FW start PC: %s\n", ring->name,
- r == 0 ? "successfully" : "failed");
- /*FIXME:Sometimes driver can't cache the MEC firmware start PC correctly, so the pipe
- * reset status relies on the compute ring test result.
- */
- return 0;
+ return amdgpu_gfx_mes_reset_queue(ring, vmid, timedout_fence, use_mmio);
}
static int gfx_v11_0_reset_kcq(struct amdgpu_ring *ring,
@@ -7080,30 +6856,8 @@ static int gfx_v11_0_reset_kcq(struct amdgpu_ring *ring,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
- int r = 0;
-
- amdgpu_ring_reset_helper_begin(ring, timedout_fence);
-
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, true, 0);
- if (r) {
- dev_warn(adev->dev, "fail(%d) to reset kcq and try pipe reset\n", r);
- r = gfx_v11_0_reset_compute_pipe(ring);
- if (r)
- return r;
- }
-
- r = gfx_v11_0_kcq_init_queue(ring, true);
- if (r) {
- dev_err(adev->dev, "fail to init kcq\n");
- return r;
- }
- r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
- if (r) {
- dev_err(adev->dev, "failed to remap kcq\n");
- return r;
- }
- return amdgpu_ring_reset_helper_end(ring, timedout_fence);
+ return amdgpu_gfx_reset_mes_compute(adev, ring, timedout_fence, NULL, NULL, NULL);
}
static void gfx_v11_ip_print(struct amdgpu_ip_block *ip_block, struct drm_printer *p)
@@ -7281,8 +7035,6 @@ static const struct amd_ip_funcs gfx_v11_0_ip_funcs = {
.is_idle = gfx_v11_0_is_idle,
.wait_for_idle = gfx_v11_0_wait_for_idle,
.soft_reset = gfx_v11_0_soft_reset,
- .check_soft_reset = gfx_v11_0_check_soft_reset,
- .post_soft_reset = gfx_v11_0_post_soft_reset,
.set_clockgating_state = gfx_v11_0_set_clockgating_state,
.set_powergating_state = gfx_v11_0_set_powergating_state,
.get_clockgating_state = gfx_v11_0_get_clockgating_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index da668a8d6abd..c765af54669c 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@ -1603,6 +1603,11 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ adev->gfx.me.use_mmio_for_reset = false;
+ adev->gfx.mec.use_mmio_for_reset = true;
+
+ mutex_init(&adev->gfx.mec.reset_mutex);
+
return 0;
}
@@ -3071,13 +3076,13 @@ static int gfx_v12_0_gfx_mqd_init(struct amdgpu_device *adev, void *m,
return 0;
}
-static int gfx_v12_0_kgq_init_queue(struct amdgpu_ring *ring, bool reset)
+static int gfx_v12_0_kgq_init_queue(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct v12_gfx_mqd *mqd = ring->mqd_ptr;
int mqd_idx = ring - &adev->gfx.gfx_ring[0];
- if (!reset && !amdgpu_in_reset(adev) && !adev->in_suspend) {
+ if (!amdgpu_in_reset(adev) && !adev->in_suspend) {
memset((void *)mqd, 0, sizeof(*mqd));
mutex_lock(&adev->srbm_mutex);
soc24_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
@@ -3104,7 +3109,7 @@ static int gfx_v12_0_cp_async_gfx_ring_resume(struct amdgpu_device *adev)
int i, r;
for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
- r = gfx_v12_0_kgq_init_queue(&adev->gfx.gfx_ring[i], false);
+ r = gfx_v12_0_kgq_init_queue(&adev->gfx.gfx_ring[i]);
if (r)
return r;
}
@@ -3441,13 +3446,13 @@ static int gfx_v12_0_kiq_init_queue(struct amdgpu_ring *ring)
return 0;
}
-static int gfx_v12_0_kcq_init_queue(struct amdgpu_ring *ring, bool reset)
+static int gfx_v12_0_kcq_init_queue(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct v12_compute_mqd *mqd = ring->mqd_ptr;
int mqd_idx = ring - &adev->gfx.compute_ring[0];
- if (!reset && !amdgpu_in_reset(adev) && !adev->in_suspend) {
+ if (!amdgpu_in_reset(adev) && !adev->in_suspend) {
memset((void *)mqd, 0, sizeof(*mqd));
mutex_lock(&adev->srbm_mutex);
soc24_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
@@ -3485,7 +3490,7 @@ static int gfx_v12_0_kcq_resume(struct amdgpu_device *adev)
gfx_v12_0_cp_compute_enable(adev, true);
for (i = 0; i < adev->gfx.num_compute_rings; i++) {
- r = gfx_v12_0_kcq_init_queue(&adev->gfx.compute_ring[i], false);
+ r = gfx_v12_0_kcq_init_queue(&adev->gfx.compute_ring[i]);
if (r)
return r;
}
@@ -3986,6 +3991,8 @@ static int gfx_v12_0_early_init(struct amdgpu_ip_block *ip_block)
gfx_v12_0_init_rlcg_reg_access_ctrl(adev);
+ amdgpu_init_rlc_reg_funcs(adev);
+
return gfx_v12_0_init_microcode(adev);
}
@@ -5025,22 +5032,30 @@ static int gfx_v12_0_set_priv_inst_fault_state(struct amdgpu_device *adev,
static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
- u8 me_id, pipe_id, queue_id;
- struct amdgpu_ring *ring;
- int i;
-
- me_id = (entry->ring_id & 0x0c) >> 2;
- pipe_id = (entry->ring_id & 0x03) >> 0;
- queue_id = (entry->ring_id & 0x70) >> 4;
+ u32 doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK;
+ /*
+ * Try KQ first by ring_id; UQ as fallback. KCQ and UQ never share
+ * a HW slot (compute_hqd_mask contract).
+ */
if (!adev->gfx.disable_kq) {
+ u8 me_id, pipe_id, queue_id;
+ struct amdgpu_ring *ring;
+ int i;
+
+ me_id = (entry->ring_id & 0x0c) >> 2;
+ pipe_id = (entry->ring_id & 0x03) >> 0;
+ queue_id = (entry->ring_id & 0x70) >> 4;
+
switch (me_id) {
case 0:
for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
ring = &adev->gfx.gfx_ring[i];
if (ring->me == me_id && ring->pipe == pipe_id &&
- ring->queue == queue_id)
+ ring->queue == queue_id) {
drm_sched_fault(&ring->sched);
+ return;
+ }
}
break;
case 1:
@@ -5048,8 +5063,10 @@ static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev,
for (i = 0; i < adev->gfx.num_compute_rings; i++) {
ring = &adev->gfx.compute_ring[i];
if (ring->me == me_id && ring->pipe == pipe_id &&
- ring->queue == queue_id)
+ ring->queue == queue_id) {
drm_sched_fault(&ring->sched);
+ return;
+ }
}
break;
default:
@@ -5057,6 +5074,11 @@ static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev,
break;
}
}
+
+ /* No KQ matched: HW slot is a MES-scheduled user queue. */
+ if (adev->enable_mes && doorbell_offset)
+ amdgpu_userq_process_reset_irq(adev, entry->pasid,
+ doorbell_offset);
}
static int gfx_v12_0_priv_reg_irq(struct amdgpu_device *adev,
@@ -5261,185 +5283,14 @@ static void gfx_v12_ip_dump(struct amdgpu_ip_block *ip_block)
amdgpu_gfx_off_ctrl(adev, true);
}
-static bool gfx_v12_pipe_reset_support(struct amdgpu_device *adev)
-{
- /* Disable the pipe reset until the CPFW fully support it.*/
- dev_warn_once(adev->dev, "The CPFW hasn't support pipe reset yet.\n");
- return false;
-}
-
-static int gfx_v12_reset_gfx_pipe(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- uint32_t reset_pipe = 0, clean_pipe = 0;
- int r;
-
- if (!gfx_v12_pipe_reset_support(adev))
- return -EOPNOTSUPP;
-
- gfx_v12_0_set_safe_mode(adev, 0);
- mutex_lock(&adev->srbm_mutex);
- soc24_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
-
- switch (ring->pipe) {
- case 0:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
- PFP_PIPE0_RESET, 1);
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
- ME_PIPE0_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
- PFP_PIPE0_RESET, 0);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
- ME_PIPE0_RESET, 0);
- break;
- case 1:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
- PFP_PIPE1_RESET, 1);
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
- ME_PIPE1_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
- PFP_PIPE1_RESET, 0);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
- ME_PIPE1_RESET, 0);
- break;
- default:
- break;
- }
-
- WREG32_SOC15(GC, 0, regCP_ME_CNTL, reset_pipe);
- WREG32_SOC15(GC, 0, regCP_ME_CNTL, clean_pipe);
-
- r = (RREG32(SOC15_REG_OFFSET(GC, 0, regCP_GFX_RS64_INSTR_PNTR1)) << 2) -
- RS64_FW_UC_START_ADDR_LO;
- soc24_grbm_select(adev, 0, 0, 0, 0);
- mutex_unlock(&adev->srbm_mutex);
- gfx_v12_0_unset_safe_mode(adev, 0);
-
- dev_info(adev->dev, "The ring %s pipe reset: %s\n", ring->name,
- r == 0 ? "successfully" : "failed");
- /* Sometimes the ME start pc counter can't cache correctly, so the
- * PC check only as a reference and pipe reset result rely on the
- * later ring test.
- */
- return 0;
-}
-
static int gfx_v12_0_reset_kgq(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
- bool use_mmio = false;
- int r;
-
- amdgpu_ring_reset_helper_begin(ring, timedout_fence);
-
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio, 0);
- if (r) {
- dev_warn(adev->dev, "reset via MES failed and try pipe reset %d\n", r);
- r = gfx_v12_reset_gfx_pipe(ring);
- if (r)
- return r;
- }
-
- if (use_mmio) {
- r = gfx_v12_0_kgq_init_queue(ring, true);
- if (r) {
- dev_err(adev->dev, "failed to init kgq\n");
- return r;
- }
-
- r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
- if (r) {
- dev_err(adev->dev, "failed to remap kgq\n");
- return r;
- }
- }
-
- return amdgpu_ring_reset_helper_end(ring, timedout_fence);
-}
-
-static int gfx_v12_0_reset_compute_pipe(struct amdgpu_ring *ring)
-{
- struct amdgpu_device *adev = ring->adev;
- uint32_t reset_pipe = 0, clean_pipe = 0;
- int r = 0;
-
- if (!gfx_v12_pipe_reset_support(adev))
- return -EOPNOTSUPP;
-
- gfx_v12_0_set_safe_mode(adev, 0);
- mutex_lock(&adev->srbm_mutex);
- soc24_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
-
- reset_pipe = RREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL);
- clean_pipe = reset_pipe;
+ bool use_mmio = adev->gfx.me.use_mmio_for_reset;
- if (adev->gfx.rs64_enable) {
- switch (ring->pipe) {
- case 0:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE0_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE0_RESET, 0);
- break;
- case 1:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE1_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE1_RESET, 0);
- break;
- case 2:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE2_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE2_RESET, 0);
- break;
- case 3:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE3_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL,
- MEC_PIPE3_RESET, 0);
- break;
- default:
- break;
- }
- WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, reset_pipe);
- WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, clean_pipe);
- r = (RREG32_SOC15(GC, 0, regCP_MEC_RS64_INSTR_PNTR) << 2) -
- RS64_FW_UC_START_ADDR_LO;
- } else {
- switch (ring->pipe) {
- case 0:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE0_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE0_RESET, 0);
- break;
- case 1:
- reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE1_RESET, 1);
- clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL,
- MEC_ME1_PIPE1_RESET, 0);
- break;
- default:
- break;
- }
- WREG32_SOC15(GC, 0, regCP_MEC_CNTL, reset_pipe);
- WREG32_SOC15(GC, 0, regCP_MEC_CNTL, clean_pipe);
- /* Doesn't find the F32 MEC instruction pointer register, and suppose
- * the driver won't run into the F32 mode.
- */
- }
-
- soc24_grbm_select(adev, 0, 0, 0, 0);
- mutex_unlock(&adev->srbm_mutex);
- gfx_v12_0_unset_safe_mode(adev, 0);
-
- dev_info(adev->dev, "The ring %s pipe resets: %s\n", ring->name,
- r == 0 ? "successfully" : "failed");
- /* Need the ring test to verify the pipe reset result.*/
- return 0;
+ return amdgpu_gfx_mes_reset_queue(ring, vmid, timedout_fence, use_mmio);
}
static int gfx_v12_0_reset_kcq(struct amdgpu_ring *ring,
@@ -5447,30 +5298,8 @@ static int gfx_v12_0_reset_kcq(struct amdgpu_ring *ring,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
- int r;
-
- amdgpu_ring_reset_helper_begin(ring, timedout_fence);
-
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, true, 0);
- if (r) {
- dev_warn(adev->dev, "fail(%d) to reset kcq and try pipe reset\n", r);
- r = gfx_v12_0_reset_compute_pipe(ring);
- if (r)
- return r;
- }
-
- r = gfx_v12_0_kcq_init_queue(ring, true);
- if (r) {
- dev_err(adev->dev, "failed to init kcq\n");
- return r;
- }
- r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
- if (r) {
- dev_err(adev->dev, "failed to remap kcq\n");
- return r;
- }
- return amdgpu_ring_reset_helper_end(ring, timedout_fence);
+ return amdgpu_gfx_reset_mes_compute(adev, ring, timedout_fence, NULL, NULL, NULL);
}
static void gfx_v12_0_ring_begin_use(struct amdgpu_ring *ring)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c
index e7e9f11b9754..e87f1baf5cb6 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c
@@ -1287,6 +1287,8 @@ static int gfx_v12_1_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ mutex_init(&adev->gfx.mec.reset_mutex);
+
return 0;
}
@@ -3004,6 +3006,8 @@ static int gfx_v12_1_early_init(struct amdgpu_ip_block *ip_block)
gfx_v12_1_init_rlcg_reg_access_ctrl(adev);
+ amdgpu_init_rlc_reg_funcs(adev);
+
return gfx_v12_1_init_microcode(adev);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
index 70ba81e6b4d4..bee2ff6865f9 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
@@ -1487,7 +1487,14 @@ static int gfx_v8_0_do_edc_gpr_workarounds(struct amdgpu_device *adev)
/* bail if the compute ring is not ready */
if (!ring->sched.ready)
- return 0;
+ return -EBUSY;
+
+ if (amdgpu_in_reset(adev)) {
+ /* Set preempt condition to execute IB */
+ amdgpu_ring_set_preempt_cond_exec(ring, true);
+ /* Flush HDP cache so the GPU can see the updated COND_EXEC value */
+ amdgpu_device_flush_hdp(adev, NULL);
+ }
tmp = RREG32(mmGB_EDC_MODE);
WREG32(mmGB_EDC_MODE, 0);
@@ -2028,6 +2035,11 @@ static int gfx_v8_0_sw_init(struct amdgpu_ip_block *ip_block)
adev->gfx.compute_supported_reset =
amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]);
+ if (!amdgpu_sriov_vf(adev) && !adev->debug_disable_ip_block_soft_reset) {
+ adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET;
+ adev->gfx.gfx_supported_reset |= AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET;
+ }
+
return 0;
}
@@ -4703,12 +4715,14 @@ static int gfx_v8_0_cp_test_all_rings(struct amdgpu_device *adev)
if (r)
return r;
+ r = 0;
+
for (i = 0; i < adev->gfx.num_compute_rings; i++) {
ring = &adev->gfx.compute_ring[i];
- amdgpu_ring_test_helper(ring);
+ r |= amdgpu_ring_test_helper(ring);
}
- return 0;
+ return r;
}
static int gfx_v8_0_cp_resume(struct amdgpu_device *adev)
@@ -4868,14 +4882,12 @@ static int gfx_v8_0_hw_fini(struct amdgpu_ip_block *ip_block)
}
amdgpu_gfx_rlc_enter_safe_mode(adev, 0);
- if (!gfx_v8_0_wait_for_idle(ip_block))
- gfx_v8_0_cp_enable(adev, false);
- else
+ if (!amdgpu_in_reset(adev) && gfx_v8_0_wait_for_idle(ip_block))
pr_err("cp is busy, skip halt cp\n");
- if (!gfx_v8_0_wait_for_rlc_idle(adev))
- adev->gfx.rlc.funcs->stop(adev);
- else
- pr_err("rlc is busy, skip halt rlc\n");
+ if (!amdgpu_in_reset(adev) && gfx_v8_0_wait_for_rlc_idle(adev))
+ pr_err("rlc is busy\n");
+ gfx_v8_0_cp_enable(adev, false);
+ adev->gfx.rlc.funcs->stop(adev);
amdgpu_gfx_rlc_exit_safe_mode(adev, 0);
return 0;
@@ -4891,128 +4903,49 @@ static int gfx_v8_0_resume(struct amdgpu_ip_block *ip_block)
return gfx_v8_0_hw_init(ip_block);
}
-static bool gfx_v8_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
+static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
u32 tmp;
+ int i;
+ int r;
- /* GRBM_STATUS */
- tmp = RREG32(mmGRBM_STATUS);
- if (tmp & (GRBM_STATUS__PA_BUSY_MASK | GRBM_STATUS__SC_BUSY_MASK |
- GRBM_STATUS__BCI_BUSY_MASK | GRBM_STATUS__SX_BUSY_MASK |
- GRBM_STATUS__TA_BUSY_MASK | GRBM_STATUS__VGT_BUSY_MASK |
- GRBM_STATUS__DB_BUSY_MASK | GRBM_STATUS__CB_BUSY_MASK |
- GRBM_STATUS__GDS_BUSY_MASK | GRBM_STATUS__SPI_BUSY_MASK |
- GRBM_STATUS__IA_BUSY_MASK | GRBM_STATUS__IA_BUSY_NO_DMA_MASK |
- GRBM_STATUS__CP_BUSY_MASK | GRBM_STATUS__CP_COHERENCY_BUSY_MASK)) {
- grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset,
- GRBM_SOFT_RESET, SOFT_RESET_CP, 1);
- grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset,
- GRBM_SOFT_RESET, SOFT_RESET_GFX, 1);
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
- SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1);
- }
-
- /* GRBM_STATUS2 */
- tmp = RREG32(mmGRBM_STATUS2);
- if (REG_GET_FIELD(tmp, GRBM_STATUS2, RLC_BUSY))
- grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset,
- GRBM_SOFT_RESET, SOFT_RESET_RLC, 1);
-
- if (REG_GET_FIELD(tmp, GRBM_STATUS2, CPF_BUSY) ||
- REG_GET_FIELD(tmp, GRBM_STATUS2, CPC_BUSY) ||
- REG_GET_FIELD(tmp, GRBM_STATUS2, CPG_BUSY)) {
- grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET,
- SOFT_RESET_CPF, 1);
- grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET,
- SOFT_RESET_CPC, 1);
- grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET,
- SOFT_RESET_CPG, 1);
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
- SOFT_RESET_GRBM, 1);
- }
-
- /* SRBM_STATUS */
- tmp = RREG32(mmSRBM_STATUS);
- if (REG_GET_FIELD(tmp, SRBM_STATUS, GRBM_RQ_PENDING))
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
- SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1);
- if (REG_GET_FIELD(tmp, SRBM_STATUS, SEM_BUSY))
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
- SRBM_SOFT_RESET, SOFT_RESET_SEM, 1);
-
- if (grbm_soft_reset || srbm_soft_reset) {
- adev->gfx.grbm_soft_reset = grbm_soft_reset;
- adev->gfx.srbm_soft_reset = srbm_soft_reset;
- return true;
- } else {
- adev->gfx.grbm_soft_reset = 0;
- adev->gfx.srbm_soft_reset = 0;
- return false;
- }
-}
-
-static int gfx_v8_0_pre_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 grbm_soft_reset = 0;
-
- if ((!adev->gfx.grbm_soft_reset) &&
- (!adev->gfx.srbm_soft_reset))
- return 0;
-
- grbm_soft_reset = adev->gfx.grbm_soft_reset;
-
- /* stop the rlc */
- adev->gfx.rlc.funcs->stop(adev);
+ grbm_soft_reset =
+ REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_RLC, 1) |
+ REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_GFX, 1) |
+ REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CP, 1) |
+ REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPF, 1) |
+ REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPC, 1) |
+ REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPG, 1);
- if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) ||
- REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_GFX))
- /* Disable GFX parsing/prefetching */
- gfx_v8_0_cp_gfx_enable(adev, false);
+ srbm_soft_reset =
+ REG_SET_FIELD(0, SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1) |
+ REG_SET_FIELD(0, SRBM_SOFT_RESET, SOFT_RESET_SEM, 1);
- if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) ||
- REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPF) ||
- REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPC) ||
- REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPG)) {
- int i;
+ for (i = 0; i < adev->gfx.num_compute_rings; i++) {
+ struct amdgpu_ring *ring = &adev->gfx.compute_ring[i];
- for (i = 0; i < adev->gfx.num_compute_rings; i++) {
- struct amdgpu_ring *ring = &adev->gfx.compute_ring[i];
+ mutex_lock(&adev->srbm_mutex);
+ vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
+ gfx_v8_0_deactivate_hqd(adev, 2);
+ vi_srbm_select(adev, 0, 0, 0, 0);
+ mutex_unlock(&adev->srbm_mutex);
- mutex_lock(&adev->srbm_mutex);
- vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
- gfx_v8_0_deactivate_hqd(adev, 2);
- vi_srbm_select(adev, 0, 0, 0, 0);
- mutex_unlock(&adev->srbm_mutex);
- }
- /* Disable MEC parsing/prefetching */
- gfx_v8_0_cp_compute_enable(adev, false);
+ udelay(50);
}
- return 0;
-}
-
-static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
- u32 tmp;
-
- if ((!adev->gfx.grbm_soft_reset) &&
- (!adev->gfx.srbm_soft_reset))
- return 0;
-
- grbm_soft_reset = adev->gfx.grbm_soft_reset;
- srbm_soft_reset = adev->gfx.srbm_soft_reset;
+ ip_block->version->funcs->set_clockgating_state(ip_block, AMD_CG_STATE_UNGATE);
+ ip_block->version->funcs->set_powergating_state(ip_block, AMD_PG_STATE_UNGATE);
+ ip_block->version->funcs->suspend(ip_block);
if (grbm_soft_reset || srbm_soft_reset) {
tmp = RREG32(mmGMCON_DEBUG);
tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_STALL, 1);
tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_CLEAR, 1);
WREG32(mmGMCON_DEBUG, tmp);
- udelay(50);
+
+ udelay(100);
}
if (grbm_soft_reset) {
@@ -5022,11 +4955,13 @@ static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block)
WREG32(mmGRBM_SOFT_RESET, tmp);
tmp = RREG32(mmGRBM_SOFT_RESET);
- udelay(50);
+ udelay(100);
tmp &= ~grbm_soft_reset;
WREG32(mmGRBM_SOFT_RESET, tmp);
tmp = RREG32(mmGRBM_SOFT_RESET);
+
+ udelay(100);
}
if (srbm_soft_reset) {
@@ -5036,11 +4971,13 @@ static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block)
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
- udelay(50);
+ udelay(100);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
+
+ udelay(100);
}
if (grbm_soft_reset || srbm_soft_reset) {
@@ -5051,48 +4988,15 @@ static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block)
}
/* Wait a little for things to settle down */
- udelay(50);
+ udelay(100);
- return 0;
-}
-
-static int gfx_v8_0_post_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 grbm_soft_reset = 0;
-
- if ((!adev->gfx.grbm_soft_reset) &&
- (!adev->gfx.srbm_soft_reset))
- return 0;
-
- grbm_soft_reset = adev->gfx.grbm_soft_reset;
-
- if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) ||
- REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPF) ||
- REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPC) ||
- REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPG)) {
- int i;
-
- for (i = 0; i < adev->gfx.num_compute_rings; i++) {
- struct amdgpu_ring *ring = &adev->gfx.compute_ring[i];
-
- mutex_lock(&adev->srbm_mutex);
- vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
- gfx_v8_0_deactivate_hqd(adev, 2);
- vi_srbm_select(adev, 0, 0, 0, 0);
- mutex_unlock(&adev->srbm_mutex);
- }
- gfx_v8_0_kiq_resume(adev);
- gfx_v8_0_kcq_resume(adev);
- }
-
- if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) ||
- REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_GFX))
- gfx_v8_0_cp_gfx_resume(adev);
+ r = ip_block->version->funcs->resume(ip_block);
+ r |= ip_block->version->funcs->late_init(ip_block);
+ if (r)
+ return r;
- gfx_v8_0_cp_test_all_rings(adev);
-
- adev->gfx.rlc.funcs->start(adev);
+ ip_block->version->funcs->set_clockgating_state(ip_block, AMD_CG_STATE_GATE);
+ ip_block->version->funcs->set_powergating_state(ip_block, AMD_PG_STATE_GATE);
return 0;
}
@@ -6859,10 +6763,7 @@ static const struct amd_ip_funcs gfx_v8_0_ip_funcs = {
.resume = gfx_v8_0_resume,
.is_idle = gfx_v8_0_is_idle,
.wait_for_idle = gfx_v8_0_wait_for_idle,
- .check_soft_reset = gfx_v8_0_check_soft_reset,
- .pre_soft_reset = gfx_v8_0_pre_soft_reset,
.soft_reset = gfx_v8_0_soft_reset,
- .post_soft_reset = gfx_v8_0_post_soft_reset,
.set_clockgating_state = gfx_v8_0_set_clockgating_state,
.set_powergating_state = gfx_v8_0_set_powergating_state,
.get_clockgating_state = gfx_v8_0_get_clockgating_state,
@@ -6923,10 +6824,12 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_compute = {
.get_wptr = gfx_v8_0_ring_get_wptr_compute,
.set_wptr = gfx_v8_0_ring_set_wptr_compute,
.emit_frame_size =
+ 5 + /* gfx_v8_0_ring_emit_init_cond_exec (from amdgpu_ib_schedule) */
20 + /* gfx_v8_0_ring_emit_gds_switch */
7 + /* gfx_v8_0_ring_emit_hdp_flush */
5 + /* hdp_invalidate */
7 + /* gfx_v8_0_ring_emit_pipeline_sync */
+ 5 + /* gfx_v8_0_ring_emit_init_cond_exec (from amdgpu_vm_flush) */
VI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + /* gfx_v8_0_ring_emit_vm_flush */
7 + 7 + 7 + /* gfx_v8_0_ring_emit_fence_compute x3 for user fence, vm fence */
7 + /* gfx_v8_0_emit_mem_sync_compute */
@@ -6947,6 +6850,7 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_compute = {
.soft_recovery = gfx_v8_0_ring_soft_recovery,
.emit_mem_sync = gfx_v8_0_emit_mem_sync_compute,
.emit_wave_limit = gfx_v8_0_emit_wave_limit,
+ .init_cond_exec = gfx_v8_0_ring_emit_init_cond_exec,
};
static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_kiq = {
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
index 3370f542e990..9f81fd715418 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
@@ -4875,6 +4875,8 @@ static int gfx_v9_0_early_init(struct amdgpu_ip_block *ip_block)
/* init rlcg reg access ctrl */
gfx_v9_0_init_rlcg_reg_access_ctrl(adev);
+ amdgpu_init_rlc_reg_funcs(adev);
+
return gfx_v9_0_init_microcode(adev);
}
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 2a36647b975a..b89cbc2df951 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c
@@ -39,7 +39,6 @@
#include "gfx_v9_4_3.h"
#include "gfx_v9_4_3_cleaner_shader.h"
#include "amdgpu_xcp.h"
-#include "amdgpu_aca.h"
MODULE_FIRMWARE("amdgpu/gc_9_4_3_mec.bin");
MODULE_FIRMWARE("amdgpu/gc_9_4_4_mec.bin");
@@ -851,73 +850,6 @@ static const struct amdgpu_gfx_funcs gfx_v9_4_3_gfx_funcs = {
.get_hdp_flush_mask = &amdgpu_gfx_get_hdp_flush_mask,
};
-static int gfx_v9_4_3_aca_bank_parser(struct aca_handle *handle,
- struct aca_bank *bank, enum aca_smu_type type,
- void *data)
-{
- struct aca_bank_info info;
- u64 misc0;
- u32 instlo;
- int ret;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- /* NOTE: overwrite info.die_id with xcd id for gfx */
- instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]);
- instlo &= GENMASK(31, 1);
- info.die_id = instlo == mmSMNAID_XCD0_MCA_SMU ? 0 : 1;
-
- misc0 = bank->regs[ACA_REG_IDX_MISC0];
-
- switch (type) {
- case ACA_SMU_TYPE_UE:
- bank->aca_err_type = ACA_ERROR_TYPE_UE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, 1ULL);
- break;
- case ACA_SMU_TYPE_CE:
- bank->aca_err_type = ACA_ERROR_TYPE_CE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type,
- ACA_REG__MISC0__ERRCNT(misc0));
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
-static bool gfx_v9_4_3_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- u32 instlo;
-
- instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]);
- instlo &= GENMASK(31, 1);
- switch (instlo) {
- case mmSMNAID_XCD0_MCA_SMU:
- case mmSMNAID_XCD1_MCA_SMU:
- case mmSMNXCD_XCD0_MCA_SMU:
- return true;
- default:
- break;
- }
-
- return false;
-}
-
-static const struct aca_bank_ops gfx_v9_4_3_aca_bank_ops = {
- .aca_bank_parser = gfx_v9_4_3_aca_bank_parser,
- .aca_bank_is_valid = gfx_v9_4_3_aca_bank_is_valid,
-};
-
-static const struct aca_info gfx_v9_4_3_aca_info = {
- .hwip = ACA_HWIP_TYPE_SMU,
- .mask = ACA_ERROR_UE_MASK | ACA_ERROR_CE_MASK,
- .bank_ops = &gfx_v9_4_3_aca_bank_ops,
-};
-
static int gfx_v9_4_3_gpu_early_init(struct amdgpu_device *adev)
{
adev->gfx.funcs = &gfx_v9_4_3_gfx_funcs;
@@ -1107,22 +1039,24 @@ static int gfx_v9_4_3_sw_init(struct amdgpu_ip_block *ip_block)
/* set up the compute queues - allocate horizontally across pipes */
for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) {
ring_id = 0;
- for (i = 0; i < adev->gfx.mec.num_mec; ++i) {
- for (j = 0; j < adev->gfx.mec.num_queue_per_pipe; j++) {
- for (k = 0; k < adev->gfx.mec.num_pipe_per_mec;
- k++) {
- if (!amdgpu_gfx_is_mec_queue_enabled(
- adev, xcc_id, i, k, j))
- continue;
-
- r = gfx_v9_4_3_compute_ring_init(adev,
- ring_id,
- xcc_id,
- i, k, j);
- if (r)
- return r;
-
- ring_id++;
+ if (!adev->gfx.disable_kq) {
+ for (i = 0; i < adev->gfx.mec.num_mec; ++i) {
+ for (j = 0; j < adev->gfx.mec.num_queue_per_pipe; j++) {
+ for (k = 0; k < adev->gfx.mec.num_pipe_per_mec;
+ k++) {
+ if (!amdgpu_gfx_is_mec_queue_enabled(
+ adev, xcc_id, i, k, j))
+ continue;
+
+ r = gfx_v9_4_3_compute_ring_init(adev,
+ ring_id,
+ xcc_id,
+ i, k, j);
+ if (r)
+ return r;
+
+ ring_id++;
+ }
}
}
}
@@ -2350,6 +2284,65 @@ static void gfx_v9_4_3_xcc_fini(struct amdgpu_device *adev, int xcc_id)
gfx_v9_4_3_xcc_cp_compute_enable(adev, false, xcc_id);
}
+static int gfx_v9_4_3_set_userq_eop_interrupts(struct amdgpu_device *adev,
+ bool enable)
+{
+ int num_xcc = NUM_XCC(adev->gfx.xcc_mask);
+ unsigned int irq_type;
+ int m, p, xcc_id, r;
+
+ if (adev->gfx.disable_kq) {
+ for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) {
+ for (m = 0; m < adev->gfx.mec.num_mec; ++m) {
+ for (p = 0; p < adev->gfx.mec.num_pipe_per_mec; p++) {
+ irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP
+ + (m * adev->gfx.mec.num_pipe_per_mec)
+ + p;
+
+ if (enable)
+ r = amdgpu_irq_get(adev, &adev->gfx.eop_irq,
+ irq_type);
+ else
+ r = amdgpu_irq_put(adev, &adev->gfx.eop_irq,
+ irq_type);
+ if (r) {
+ if (!enable)
+ return r;
+ goto err_compute;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+
+err_compute:
+ for (p--; p >= 0; p--) {
+ irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP
+ + (m * adev->gfx.mec.num_pipe_per_mec) + p;
+ amdgpu_irq_put(adev, &adev->gfx.eop_irq, irq_type);
+ }
+ for (m--; m >= 0; m--) {
+ for (p = adev->gfx.mec.num_pipe_per_mec - 1; p >= 0; p--) {
+ irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP
+ + (m * adev->gfx.mec.num_pipe_per_mec) + p;
+ amdgpu_irq_put(adev, &adev->gfx.eop_irq, irq_type);
+ }
+ }
+ for (xcc_id--; xcc_id >= 0; xcc_id--) {
+ for (m = adev->gfx.mec.num_mec - 1; m <= 0; m--) {
+ for (p = adev->gfx.mec.num_pipe_per_mec - 1; p >= 0; p--) {
+ irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP
+ + (m * adev->gfx.mec.num_pipe_per_mec) + p;
+ amdgpu_irq_put(adev, &adev->gfx.eop_irq, irq_type);
+ }
+ }
+ }
+
+ return r;
+}
+
static int gfx_v9_4_3_hw_init(struct amdgpu_ip_block *ip_block)
{
int r;
@@ -2382,9 +2375,14 @@ static int gfx_v9_4_3_hw_init(struct amdgpu_ip_block *ip_block)
r = amdgpu_irq_get(adev, &adev->gfx.bad_op_irq, 0);
if (r)
goto err_bad_op;
+ r = gfx_v9_4_3_set_userq_eop_interrupts(adev, true);
+ if (r)
+ goto err_bad_eop;
return 0;
+err_bad_eop:
+ amdgpu_irq_put(adev, &adev->gfx.bad_op_irq, 0);
err_bad_op:
amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0);
err_priv_inst:
@@ -2467,6 +2465,7 @@ static int gfx_v9_4_3_hw_fini(struct amdgpu_ip_block *ip_block)
amdgpu_irq_put(adev, &adev->gfx.bad_op_irq, 0);
amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0);
amdgpu_irq_put(adev, &adev->gfx.priv_reg_irq, 0);
+ gfx_v9_4_3_set_userq_eop_interrupts(adev, false);
num_xcc = NUM_XCC(adev->gfx.xcc_mask);
for (i = 0; i < num_xcc; i++) {
@@ -2612,8 +2611,24 @@ static int gfx_v9_4_3_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
- adev->gfx.num_compute_rings = min(amdgpu_gfx_get_num_kcq(adev),
- AMDGPU_MAX_COMPUTE_RINGS);
+ switch (amdgpu_user_queue) {
+ case -1:
+ case 0:
+ default:
+ adev->gfx.disable_kq = false;
+ adev->gfx.disable_uq = true;
+ break;
+ case 2:
+ adev->gfx.disable_kq = true;
+ adev->gfx.disable_uq = true;
+ break;
+ }
+
+ if (adev->gfx.disable_kq)
+ adev->gfx.num_compute_rings = 0;
+ else
+ adev->gfx.num_compute_rings = min(amdgpu_gfx_get_num_kcq(adev),
+ AMDGPU_MAX_COMPUTE_RINGS);
gfx_v9_4_3_set_kiq_pm4_funcs(adev);
gfx_v9_4_3_set_ring_funcs(adev);
gfx_v9_4_3_set_irq_funcs(adev);
@@ -2623,6 +2638,8 @@ static int gfx_v9_4_3_early_init(struct amdgpu_ip_block *ip_block)
/* init rlcg reg access ctrl */
gfx_v9_4_3_init_rlcg_reg_access_ctrl(adev);
+ amdgpu_init_rlc_reg_funcs(adev);
+
return gfx_v9_4_3_init_microcode(adev);
}
@@ -3709,872 +3726,6 @@ pipe_reset:
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}
-enum amdgpu_gfx_cp_ras_mem_id {
- AMDGPU_GFX_CP_MEM1 = 1,
- AMDGPU_GFX_CP_MEM2,
- AMDGPU_GFX_CP_MEM3,
- AMDGPU_GFX_CP_MEM4,
- AMDGPU_GFX_CP_MEM5,
-};
-
-enum amdgpu_gfx_gcea_ras_mem_id {
- AMDGPU_GFX_GCEA_IOWR_CMDMEM = 4,
- AMDGPU_GFX_GCEA_IORD_CMDMEM,
- AMDGPU_GFX_GCEA_GMIWR_CMDMEM,
- AMDGPU_GFX_GCEA_GMIRD_CMDMEM,
- AMDGPU_GFX_GCEA_DRAMWR_CMDMEM,
- AMDGPU_GFX_GCEA_DRAMRD_CMDMEM,
- AMDGPU_GFX_GCEA_MAM_DMEM0,
- AMDGPU_GFX_GCEA_MAM_DMEM1,
- AMDGPU_GFX_GCEA_MAM_DMEM2,
- AMDGPU_GFX_GCEA_MAM_DMEM3,
- AMDGPU_GFX_GCEA_MAM_AMEM0,
- AMDGPU_GFX_GCEA_MAM_AMEM1,
- AMDGPU_GFX_GCEA_MAM_AMEM2,
- AMDGPU_GFX_GCEA_MAM_AMEM3,
- AMDGPU_GFX_GCEA_MAM_AFLUSH_BUFFER,
- AMDGPU_GFX_GCEA_WRET_TAGMEM,
- AMDGPU_GFX_GCEA_RRET_TAGMEM,
- AMDGPU_GFX_GCEA_IOWR_DATAMEM,
- AMDGPU_GFX_GCEA_GMIWR_DATAMEM,
- AMDGPU_GFX_GCEA_DRAM_DATAMEM,
-};
-
-enum amdgpu_gfx_gc_cane_ras_mem_id {
- AMDGPU_GFX_GC_CANE_MEM0 = 0,
-};
-
-enum amdgpu_gfx_gcutcl2_ras_mem_id {
- AMDGPU_GFX_GCUTCL2_MEM2P512X95 = 160,
-};
-
-enum amdgpu_gfx_gds_ras_mem_id {
- AMDGPU_GFX_GDS_MEM0 = 0,
-};
-
-enum amdgpu_gfx_lds_ras_mem_id {
- AMDGPU_GFX_LDS_BANK0 = 0,
- AMDGPU_GFX_LDS_BANK1,
- AMDGPU_GFX_LDS_BANK2,
- AMDGPU_GFX_LDS_BANK3,
- AMDGPU_GFX_LDS_BANK4,
- AMDGPU_GFX_LDS_BANK5,
- AMDGPU_GFX_LDS_BANK6,
- AMDGPU_GFX_LDS_BANK7,
- AMDGPU_GFX_LDS_BANK8,
- AMDGPU_GFX_LDS_BANK9,
- AMDGPU_GFX_LDS_BANK10,
- AMDGPU_GFX_LDS_BANK11,
- AMDGPU_GFX_LDS_BANK12,
- AMDGPU_GFX_LDS_BANK13,
- AMDGPU_GFX_LDS_BANK14,
- AMDGPU_GFX_LDS_BANK15,
- AMDGPU_GFX_LDS_BANK16,
- AMDGPU_GFX_LDS_BANK17,
- AMDGPU_GFX_LDS_BANK18,
- AMDGPU_GFX_LDS_BANK19,
- AMDGPU_GFX_LDS_BANK20,
- AMDGPU_GFX_LDS_BANK21,
- AMDGPU_GFX_LDS_BANK22,
- AMDGPU_GFX_LDS_BANK23,
- AMDGPU_GFX_LDS_BANK24,
- AMDGPU_GFX_LDS_BANK25,
- AMDGPU_GFX_LDS_BANK26,
- AMDGPU_GFX_LDS_BANK27,
- AMDGPU_GFX_LDS_BANK28,
- AMDGPU_GFX_LDS_BANK29,
- AMDGPU_GFX_LDS_BANK30,
- AMDGPU_GFX_LDS_BANK31,
- AMDGPU_GFX_LDS_SP_BUFFER_A,
- AMDGPU_GFX_LDS_SP_BUFFER_B,
-};
-
-enum amdgpu_gfx_rlc_ras_mem_id {
- AMDGPU_GFX_RLC_GPMF32 = 1,
- AMDGPU_GFX_RLC_RLCVF32,
- AMDGPU_GFX_RLC_SCRATCH,
- AMDGPU_GFX_RLC_SRM_ARAM,
- AMDGPU_GFX_RLC_SRM_DRAM,
- AMDGPU_GFX_RLC_TCTAG,
- AMDGPU_GFX_RLC_SPM_SE,
- AMDGPU_GFX_RLC_SPM_GRBMT,
-};
-
-enum amdgpu_gfx_sp_ras_mem_id {
- AMDGPU_GFX_SP_SIMDID0 = 0,
-};
-
-enum amdgpu_gfx_spi_ras_mem_id {
- AMDGPU_GFX_SPI_MEM0 = 0,
- AMDGPU_GFX_SPI_MEM1,
- AMDGPU_GFX_SPI_MEM2,
- AMDGPU_GFX_SPI_MEM3,
-};
-
-enum amdgpu_gfx_sqc_ras_mem_id {
- AMDGPU_GFX_SQC_INST_CACHE_A = 100,
- AMDGPU_GFX_SQC_INST_CACHE_B = 101,
- AMDGPU_GFX_SQC_INST_CACHE_TAG_A = 102,
- AMDGPU_GFX_SQC_INST_CACHE_TAG_B = 103,
- AMDGPU_GFX_SQC_INST_CACHE_MISS_FIFO_A = 104,
- AMDGPU_GFX_SQC_INST_CACHE_MISS_FIFO_B = 105,
- AMDGPU_GFX_SQC_INST_CACHE_GATCL1_MISS_FIFO_A = 106,
- AMDGPU_GFX_SQC_INST_CACHE_GATCL1_MISS_FIFO_B = 107,
- AMDGPU_GFX_SQC_DATA_CACHE_A = 200,
- AMDGPU_GFX_SQC_DATA_CACHE_B = 201,
- AMDGPU_GFX_SQC_DATA_CACHE_TAG_A = 202,
- AMDGPU_GFX_SQC_DATA_CACHE_TAG_B = 203,
- AMDGPU_GFX_SQC_DATA_CACHE_MISS_FIFO_A = 204,
- AMDGPU_GFX_SQC_DATA_CACHE_MISS_FIFO_B = 205,
- AMDGPU_GFX_SQC_DATA_CACHE_HIT_FIFO_A = 206,
- AMDGPU_GFX_SQC_DATA_CACHE_HIT_FIFO_B = 207,
- AMDGPU_GFX_SQC_DIRTY_BIT_A = 208,
- AMDGPU_GFX_SQC_DIRTY_BIT_B = 209,
- AMDGPU_GFX_SQC_WRITE_DATA_BUFFER_CU0 = 210,
- AMDGPU_GFX_SQC_WRITE_DATA_BUFFER_CU1 = 211,
- AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_DATA_CACHE_A = 212,
- AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_DATA_CACHE_B = 213,
- AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_INST_CACHE = 108,
-};
-
-enum amdgpu_gfx_sq_ras_mem_id {
- AMDGPU_GFX_SQ_SGPR_MEM0 = 0,
- AMDGPU_GFX_SQ_SGPR_MEM1,
- AMDGPU_GFX_SQ_SGPR_MEM2,
- AMDGPU_GFX_SQ_SGPR_MEM3,
-};
-
-enum amdgpu_gfx_ta_ras_mem_id {
- AMDGPU_GFX_TA_FS_AFIFO_RAM_LO = 1,
- AMDGPU_GFX_TA_FS_AFIFO_RAM_HI,
- AMDGPU_GFX_TA_FS_CFIFO_RAM,
- AMDGPU_GFX_TA_FSX_LFIFO,
- AMDGPU_GFX_TA_FS_DFIFO_RAM,
-};
-
-enum amdgpu_gfx_tcc_ras_mem_id {
- AMDGPU_GFX_TCC_MEM1 = 1,
-};
-
-enum amdgpu_gfx_tca_ras_mem_id {
- AMDGPU_GFX_TCA_MEM1 = 1,
-};
-
-enum amdgpu_gfx_tci_ras_mem_id {
- AMDGPU_GFX_TCIW_MEM = 1,
-};
-
-enum amdgpu_gfx_tcp_ras_mem_id {
- AMDGPU_GFX_TCP_LFIFO0 = 1,
- AMDGPU_GFX_TCP_SET0BANK0_RAM,
- AMDGPU_GFX_TCP_SET0BANK1_RAM,
- AMDGPU_GFX_TCP_SET0BANK2_RAM,
- AMDGPU_GFX_TCP_SET0BANK3_RAM,
- AMDGPU_GFX_TCP_SET1BANK0_RAM,
- AMDGPU_GFX_TCP_SET1BANK1_RAM,
- AMDGPU_GFX_TCP_SET1BANK2_RAM,
- AMDGPU_GFX_TCP_SET1BANK3_RAM,
- AMDGPU_GFX_TCP_SET2BANK0_RAM,
- AMDGPU_GFX_TCP_SET2BANK1_RAM,
- AMDGPU_GFX_TCP_SET2BANK2_RAM,
- AMDGPU_GFX_TCP_SET2BANK3_RAM,
- AMDGPU_GFX_TCP_SET3BANK0_RAM,
- AMDGPU_GFX_TCP_SET3BANK1_RAM,
- AMDGPU_GFX_TCP_SET3BANK2_RAM,
- AMDGPU_GFX_TCP_SET3BANK3_RAM,
- AMDGPU_GFX_TCP_VM_FIFO,
- AMDGPU_GFX_TCP_DB_TAGRAM0,
- AMDGPU_GFX_TCP_DB_TAGRAM1,
- AMDGPU_GFX_TCP_DB_TAGRAM2,
- AMDGPU_GFX_TCP_DB_TAGRAM3,
- AMDGPU_GFX_TCP_UTCL1_LFIFO_PROBE0,
- AMDGPU_GFX_TCP_UTCL1_LFIFO_PROBE1,
- AMDGPU_GFX_TCP_CMD_FIFO,
-};
-
-enum amdgpu_gfx_td_ras_mem_id {
- AMDGPU_GFX_TD_UTD_CS_FIFO_MEM = 1,
- AMDGPU_GFX_TD_UTD_SS_FIFO_LO_MEM,
- AMDGPU_GFX_TD_UTD_SS_FIFO_HI_MEM,
-};
-
-enum amdgpu_gfx_tcx_ras_mem_id {
- AMDGPU_GFX_TCX_FIFOD0 = 0,
- AMDGPU_GFX_TCX_FIFOD1,
- AMDGPU_GFX_TCX_FIFOD2,
- AMDGPU_GFX_TCX_FIFOD3,
- AMDGPU_GFX_TCX_FIFOD4,
- AMDGPU_GFX_TCX_FIFOD5,
- AMDGPU_GFX_TCX_FIFOD6,
- AMDGPU_GFX_TCX_FIFOD7,
- AMDGPU_GFX_TCX_FIFOB0,
- AMDGPU_GFX_TCX_FIFOB1,
- AMDGPU_GFX_TCX_FIFOB2,
- AMDGPU_GFX_TCX_FIFOB3,
- AMDGPU_GFX_TCX_FIFOB4,
- AMDGPU_GFX_TCX_FIFOB5,
- AMDGPU_GFX_TCX_FIFOB6,
- AMDGPU_GFX_TCX_FIFOB7,
- AMDGPU_GFX_TCX_FIFOA0,
- AMDGPU_GFX_TCX_FIFOA1,
- AMDGPU_GFX_TCX_FIFOA2,
- AMDGPU_GFX_TCX_FIFOA3,
- AMDGPU_GFX_TCX_FIFOA4,
- AMDGPU_GFX_TCX_FIFOA5,
- AMDGPU_GFX_TCX_FIFOA6,
- AMDGPU_GFX_TCX_FIFOA7,
- AMDGPU_GFX_TCX_CFIFO0,
- AMDGPU_GFX_TCX_CFIFO1,
- AMDGPU_GFX_TCX_CFIFO2,
- AMDGPU_GFX_TCX_CFIFO3,
- AMDGPU_GFX_TCX_CFIFO4,
- AMDGPU_GFX_TCX_CFIFO5,
- AMDGPU_GFX_TCX_CFIFO6,
- AMDGPU_GFX_TCX_CFIFO7,
- AMDGPU_GFX_TCX_FIFO_ACKB0,
- AMDGPU_GFX_TCX_FIFO_ACKB1,
- AMDGPU_GFX_TCX_FIFO_ACKB2,
- AMDGPU_GFX_TCX_FIFO_ACKB3,
- AMDGPU_GFX_TCX_FIFO_ACKB4,
- AMDGPU_GFX_TCX_FIFO_ACKB5,
- AMDGPU_GFX_TCX_FIFO_ACKB6,
- AMDGPU_GFX_TCX_FIFO_ACKB7,
- AMDGPU_GFX_TCX_FIFO_ACKD0,
- AMDGPU_GFX_TCX_FIFO_ACKD1,
- AMDGPU_GFX_TCX_FIFO_ACKD2,
- AMDGPU_GFX_TCX_FIFO_ACKD3,
- AMDGPU_GFX_TCX_FIFO_ACKD4,
- AMDGPU_GFX_TCX_FIFO_ACKD5,
- AMDGPU_GFX_TCX_FIFO_ACKD6,
- AMDGPU_GFX_TCX_FIFO_ACKD7,
- AMDGPU_GFX_TCX_DST_FIFOA0,
- AMDGPU_GFX_TCX_DST_FIFOA1,
- AMDGPU_GFX_TCX_DST_FIFOA2,
- AMDGPU_GFX_TCX_DST_FIFOA3,
- AMDGPU_GFX_TCX_DST_FIFOA4,
- AMDGPU_GFX_TCX_DST_FIFOA5,
- AMDGPU_GFX_TCX_DST_FIFOA6,
- AMDGPU_GFX_TCX_DST_FIFOA7,
- AMDGPU_GFX_TCX_DST_FIFOB0,
- AMDGPU_GFX_TCX_DST_FIFOB1,
- AMDGPU_GFX_TCX_DST_FIFOB2,
- AMDGPU_GFX_TCX_DST_FIFOB3,
- AMDGPU_GFX_TCX_DST_FIFOB4,
- AMDGPU_GFX_TCX_DST_FIFOB5,
- AMDGPU_GFX_TCX_DST_FIFOB6,
- AMDGPU_GFX_TCX_DST_FIFOB7,
- AMDGPU_GFX_TCX_DST_FIFOD0,
- AMDGPU_GFX_TCX_DST_FIFOD1,
- AMDGPU_GFX_TCX_DST_FIFOD2,
- AMDGPU_GFX_TCX_DST_FIFOD3,
- AMDGPU_GFX_TCX_DST_FIFOD4,
- AMDGPU_GFX_TCX_DST_FIFOD5,
- AMDGPU_GFX_TCX_DST_FIFOD6,
- AMDGPU_GFX_TCX_DST_FIFOD7,
- AMDGPU_GFX_TCX_DST_FIFO_ACKB0,
- AMDGPU_GFX_TCX_DST_FIFO_ACKB1,
- AMDGPU_GFX_TCX_DST_FIFO_ACKB2,
- AMDGPU_GFX_TCX_DST_FIFO_ACKB3,
- AMDGPU_GFX_TCX_DST_FIFO_ACKB4,
- AMDGPU_GFX_TCX_DST_FIFO_ACKB5,
- AMDGPU_GFX_TCX_DST_FIFO_ACKB6,
- AMDGPU_GFX_TCX_DST_FIFO_ACKB7,
- AMDGPU_GFX_TCX_DST_FIFO_ACKD0,
- AMDGPU_GFX_TCX_DST_FIFO_ACKD1,
- AMDGPU_GFX_TCX_DST_FIFO_ACKD2,
- AMDGPU_GFX_TCX_DST_FIFO_ACKD3,
- AMDGPU_GFX_TCX_DST_FIFO_ACKD4,
- AMDGPU_GFX_TCX_DST_FIFO_ACKD5,
- AMDGPU_GFX_TCX_DST_FIFO_ACKD6,
- AMDGPU_GFX_TCX_DST_FIFO_ACKD7,
-};
-
-enum amdgpu_gfx_atc_l2_ras_mem_id {
- AMDGPU_GFX_ATC_L2_MEM0 = 0,
-};
-
-enum amdgpu_gfx_utcl2_ras_mem_id {
- AMDGPU_GFX_UTCL2_MEM0 = 0,
-};
-
-enum amdgpu_gfx_vml2_ras_mem_id {
- AMDGPU_GFX_VML2_MEM0 = 0,
-};
-
-enum amdgpu_gfx_vml2_walker_ras_mem_id {
- AMDGPU_GFX_VML2_WALKER_MEM0 = 0,
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_cp_mem_list[] = {
- {AMDGPU_GFX_CP_MEM1, "CP_MEM1"},
- {AMDGPU_GFX_CP_MEM2, "CP_MEM2"},
- {AMDGPU_GFX_CP_MEM3, "CP_MEM3"},
- {AMDGPU_GFX_CP_MEM4, "CP_MEM4"},
- {AMDGPU_GFX_CP_MEM5, "CP_MEM5"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_gcea_mem_list[] = {
- {AMDGPU_GFX_GCEA_IOWR_CMDMEM, "GCEA_IOWR_CMDMEM"},
- {AMDGPU_GFX_GCEA_IORD_CMDMEM, "GCEA_IORD_CMDMEM"},
- {AMDGPU_GFX_GCEA_GMIWR_CMDMEM, "GCEA_GMIWR_CMDMEM"},
- {AMDGPU_GFX_GCEA_GMIRD_CMDMEM, "GCEA_GMIRD_CMDMEM"},
- {AMDGPU_GFX_GCEA_DRAMWR_CMDMEM, "GCEA_DRAMWR_CMDMEM"},
- {AMDGPU_GFX_GCEA_DRAMRD_CMDMEM, "GCEA_DRAMRD_CMDMEM"},
- {AMDGPU_GFX_GCEA_MAM_DMEM0, "GCEA_MAM_DMEM0"},
- {AMDGPU_GFX_GCEA_MAM_DMEM1, "GCEA_MAM_DMEM1"},
- {AMDGPU_GFX_GCEA_MAM_DMEM2, "GCEA_MAM_DMEM2"},
- {AMDGPU_GFX_GCEA_MAM_DMEM3, "GCEA_MAM_DMEM3"},
- {AMDGPU_GFX_GCEA_MAM_AMEM0, "GCEA_MAM_AMEM0"},
- {AMDGPU_GFX_GCEA_MAM_AMEM1, "GCEA_MAM_AMEM1"},
- {AMDGPU_GFX_GCEA_MAM_AMEM2, "GCEA_MAM_AMEM2"},
- {AMDGPU_GFX_GCEA_MAM_AMEM3, "GCEA_MAM_AMEM3"},
- {AMDGPU_GFX_GCEA_MAM_AFLUSH_BUFFER, "GCEA_MAM_AFLUSH_BUFFER"},
- {AMDGPU_GFX_GCEA_WRET_TAGMEM, "GCEA_WRET_TAGMEM"},
- {AMDGPU_GFX_GCEA_RRET_TAGMEM, "GCEA_RRET_TAGMEM"},
- {AMDGPU_GFX_GCEA_IOWR_DATAMEM, "GCEA_IOWR_DATAMEM"},
- {AMDGPU_GFX_GCEA_GMIWR_DATAMEM, "GCEA_GMIWR_DATAMEM"},
- {AMDGPU_GFX_GCEA_DRAM_DATAMEM, "GCEA_DRAM_DATAMEM"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_gc_cane_mem_list[] = {
- {AMDGPU_GFX_GC_CANE_MEM0, "GC_CANE_MEM0"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_gcutcl2_mem_list[] = {
- {AMDGPU_GFX_GCUTCL2_MEM2P512X95, "GCUTCL2_MEM2P512X95"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_gds_mem_list[] = {
- {AMDGPU_GFX_GDS_MEM0, "GDS_MEM"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_lds_mem_list[] = {
- {AMDGPU_GFX_LDS_BANK0, "LDS_BANK0"},
- {AMDGPU_GFX_LDS_BANK1, "LDS_BANK1"},
- {AMDGPU_GFX_LDS_BANK2, "LDS_BANK2"},
- {AMDGPU_GFX_LDS_BANK3, "LDS_BANK3"},
- {AMDGPU_GFX_LDS_BANK4, "LDS_BANK4"},
- {AMDGPU_GFX_LDS_BANK5, "LDS_BANK5"},
- {AMDGPU_GFX_LDS_BANK6, "LDS_BANK6"},
- {AMDGPU_GFX_LDS_BANK7, "LDS_BANK7"},
- {AMDGPU_GFX_LDS_BANK8, "LDS_BANK8"},
- {AMDGPU_GFX_LDS_BANK9, "LDS_BANK9"},
- {AMDGPU_GFX_LDS_BANK10, "LDS_BANK10"},
- {AMDGPU_GFX_LDS_BANK11, "LDS_BANK11"},
- {AMDGPU_GFX_LDS_BANK12, "LDS_BANK12"},
- {AMDGPU_GFX_LDS_BANK13, "LDS_BANK13"},
- {AMDGPU_GFX_LDS_BANK14, "LDS_BANK14"},
- {AMDGPU_GFX_LDS_BANK15, "LDS_BANK15"},
- {AMDGPU_GFX_LDS_BANK16, "LDS_BANK16"},
- {AMDGPU_GFX_LDS_BANK17, "LDS_BANK17"},
- {AMDGPU_GFX_LDS_BANK18, "LDS_BANK18"},
- {AMDGPU_GFX_LDS_BANK19, "LDS_BANK19"},
- {AMDGPU_GFX_LDS_BANK20, "LDS_BANK20"},
- {AMDGPU_GFX_LDS_BANK21, "LDS_BANK21"},
- {AMDGPU_GFX_LDS_BANK22, "LDS_BANK22"},
- {AMDGPU_GFX_LDS_BANK23, "LDS_BANK23"},
- {AMDGPU_GFX_LDS_BANK24, "LDS_BANK24"},
- {AMDGPU_GFX_LDS_BANK25, "LDS_BANK25"},
- {AMDGPU_GFX_LDS_BANK26, "LDS_BANK26"},
- {AMDGPU_GFX_LDS_BANK27, "LDS_BANK27"},
- {AMDGPU_GFX_LDS_BANK28, "LDS_BANK28"},
- {AMDGPU_GFX_LDS_BANK29, "LDS_BANK29"},
- {AMDGPU_GFX_LDS_BANK30, "LDS_BANK30"},
- {AMDGPU_GFX_LDS_BANK31, "LDS_BANK31"},
- {AMDGPU_GFX_LDS_SP_BUFFER_A, "LDS_SP_BUFFER_A"},
- {AMDGPU_GFX_LDS_SP_BUFFER_B, "LDS_SP_BUFFER_B"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_rlc_mem_list[] = {
- {AMDGPU_GFX_RLC_GPMF32, "RLC_GPMF32"},
- {AMDGPU_GFX_RLC_RLCVF32, "RLC_RLCVF32"},
- {AMDGPU_GFX_RLC_SCRATCH, "RLC_SCRATCH"},
- {AMDGPU_GFX_RLC_SRM_ARAM, "RLC_SRM_ARAM"},
- {AMDGPU_GFX_RLC_SRM_DRAM, "RLC_SRM_DRAM"},
- {AMDGPU_GFX_RLC_TCTAG, "RLC_TCTAG"},
- {AMDGPU_GFX_RLC_SPM_SE, "RLC_SPM_SE"},
- {AMDGPU_GFX_RLC_SPM_GRBMT, "RLC_SPM_GRBMT"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_sp_mem_list[] = {
- {AMDGPU_GFX_SP_SIMDID0, "SP_SIMDID0"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_spi_mem_list[] = {
- {AMDGPU_GFX_SPI_MEM0, "SPI_MEM0"},
- {AMDGPU_GFX_SPI_MEM1, "SPI_MEM1"},
- {AMDGPU_GFX_SPI_MEM2, "SPI_MEM2"},
- {AMDGPU_GFX_SPI_MEM3, "SPI_MEM3"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_sqc_mem_list[] = {
- {AMDGPU_GFX_SQC_INST_CACHE_A, "SQC_INST_CACHE_A"},
- {AMDGPU_GFX_SQC_INST_CACHE_B, "SQC_INST_CACHE_B"},
- {AMDGPU_GFX_SQC_INST_CACHE_TAG_A, "SQC_INST_CACHE_TAG_A"},
- {AMDGPU_GFX_SQC_INST_CACHE_TAG_B, "SQC_INST_CACHE_TAG_B"},
- {AMDGPU_GFX_SQC_INST_CACHE_MISS_FIFO_A, "SQC_INST_CACHE_MISS_FIFO_A"},
- {AMDGPU_GFX_SQC_INST_CACHE_MISS_FIFO_B, "SQC_INST_CACHE_MISS_FIFO_B"},
- {AMDGPU_GFX_SQC_INST_CACHE_GATCL1_MISS_FIFO_A, "SQC_INST_CACHE_GATCL1_MISS_FIFO_A"},
- {AMDGPU_GFX_SQC_INST_CACHE_GATCL1_MISS_FIFO_B, "SQC_INST_CACHE_GATCL1_MISS_FIFO_B"},
- {AMDGPU_GFX_SQC_DATA_CACHE_A, "SQC_DATA_CACHE_A"},
- {AMDGPU_GFX_SQC_DATA_CACHE_B, "SQC_DATA_CACHE_B"},
- {AMDGPU_GFX_SQC_DATA_CACHE_TAG_A, "SQC_DATA_CACHE_TAG_A"},
- {AMDGPU_GFX_SQC_DATA_CACHE_TAG_B, "SQC_DATA_CACHE_TAG_B"},
- {AMDGPU_GFX_SQC_DATA_CACHE_MISS_FIFO_A, "SQC_DATA_CACHE_MISS_FIFO_A"},
- {AMDGPU_GFX_SQC_DATA_CACHE_MISS_FIFO_B, "SQC_DATA_CACHE_MISS_FIFO_B"},
- {AMDGPU_GFX_SQC_DATA_CACHE_HIT_FIFO_A, "SQC_DATA_CACHE_HIT_FIFO_A"},
- {AMDGPU_GFX_SQC_DATA_CACHE_HIT_FIFO_B, "SQC_DATA_CACHE_HIT_FIFO_B"},
- {AMDGPU_GFX_SQC_DIRTY_BIT_A, "SQC_DIRTY_BIT_A"},
- {AMDGPU_GFX_SQC_DIRTY_BIT_B, "SQC_DIRTY_BIT_B"},
- {AMDGPU_GFX_SQC_WRITE_DATA_BUFFER_CU0, "SQC_WRITE_DATA_BUFFER_CU0"},
- {AMDGPU_GFX_SQC_WRITE_DATA_BUFFER_CU1, "SQC_WRITE_DATA_BUFFER_CU1"},
- {AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_DATA_CACHE_A, "SQC_UTCL1_MISS_LFIFO_DATA_CACHE_A"},
- {AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_DATA_CACHE_B, "SQC_UTCL1_MISS_LFIFO_DATA_CACHE_B"},
- {AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_INST_CACHE, "SQC_UTCL1_MISS_LFIFO_INST_CACHE"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_sq_mem_list[] = {
- {AMDGPU_GFX_SQ_SGPR_MEM0, "SQ_SGPR_MEM0"},
- {AMDGPU_GFX_SQ_SGPR_MEM1, "SQ_SGPR_MEM1"},
- {AMDGPU_GFX_SQ_SGPR_MEM2, "SQ_SGPR_MEM2"},
- {AMDGPU_GFX_SQ_SGPR_MEM3, "SQ_SGPR_MEM3"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_ta_mem_list[] = {
- {AMDGPU_GFX_TA_FS_AFIFO_RAM_LO, "TA_FS_AFIFO_RAM_LO"},
- {AMDGPU_GFX_TA_FS_AFIFO_RAM_HI, "TA_FS_AFIFO_RAM_HI"},
- {AMDGPU_GFX_TA_FS_CFIFO_RAM, "TA_FS_CFIFO_RAM"},
- {AMDGPU_GFX_TA_FSX_LFIFO, "TA_FSX_LFIFO"},
- {AMDGPU_GFX_TA_FS_DFIFO_RAM, "TA_FS_DFIFO_RAM"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tcc_mem_list[] = {
- {AMDGPU_GFX_TCC_MEM1, "TCC_MEM1"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tca_mem_list[] = {
- {AMDGPU_GFX_TCA_MEM1, "TCA_MEM1"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tci_mem_list[] = {
- {AMDGPU_GFX_TCIW_MEM, "TCIW_MEM"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tcp_mem_list[] = {
- {AMDGPU_GFX_TCP_LFIFO0, "TCP_LFIFO0"},
- {AMDGPU_GFX_TCP_SET0BANK0_RAM, "TCP_SET0BANK0_RAM"},
- {AMDGPU_GFX_TCP_SET0BANK1_RAM, "TCP_SET0BANK1_RAM"},
- {AMDGPU_GFX_TCP_SET0BANK2_RAM, "TCP_SET0BANK2_RAM"},
- {AMDGPU_GFX_TCP_SET0BANK3_RAM, "TCP_SET0BANK3_RAM"},
- {AMDGPU_GFX_TCP_SET1BANK0_RAM, "TCP_SET1BANK0_RAM"},
- {AMDGPU_GFX_TCP_SET1BANK1_RAM, "TCP_SET1BANK1_RAM"},
- {AMDGPU_GFX_TCP_SET1BANK2_RAM, "TCP_SET1BANK2_RAM"},
- {AMDGPU_GFX_TCP_SET1BANK3_RAM, "TCP_SET1BANK3_RAM"},
- {AMDGPU_GFX_TCP_SET2BANK0_RAM, "TCP_SET2BANK0_RAM"},
- {AMDGPU_GFX_TCP_SET2BANK1_RAM, "TCP_SET2BANK1_RAM"},
- {AMDGPU_GFX_TCP_SET2BANK2_RAM, "TCP_SET2BANK2_RAM"},
- {AMDGPU_GFX_TCP_SET2BANK3_RAM, "TCP_SET2BANK3_RAM"},
- {AMDGPU_GFX_TCP_SET3BANK0_RAM, "TCP_SET3BANK0_RAM"},
- {AMDGPU_GFX_TCP_SET3BANK1_RAM, "TCP_SET3BANK1_RAM"},
- {AMDGPU_GFX_TCP_SET3BANK2_RAM, "TCP_SET3BANK2_RAM"},
- {AMDGPU_GFX_TCP_SET3BANK3_RAM, "TCP_SET3BANK3_RAM"},
- {AMDGPU_GFX_TCP_VM_FIFO, "TCP_VM_FIFO"},
- {AMDGPU_GFX_TCP_DB_TAGRAM0, "TCP_DB_TAGRAM0"},
- {AMDGPU_GFX_TCP_DB_TAGRAM1, "TCP_DB_TAGRAM1"},
- {AMDGPU_GFX_TCP_DB_TAGRAM2, "TCP_DB_TAGRAM2"},
- {AMDGPU_GFX_TCP_DB_TAGRAM3, "TCP_DB_TAGRAM3"},
- {AMDGPU_GFX_TCP_UTCL1_LFIFO_PROBE0, "TCP_UTCL1_LFIFO_PROBE0"},
- {AMDGPU_GFX_TCP_UTCL1_LFIFO_PROBE1, "TCP_UTCL1_LFIFO_PROBE1"},
- {AMDGPU_GFX_TCP_CMD_FIFO, "TCP_CMD_FIFO"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_td_mem_list[] = {
- {AMDGPU_GFX_TD_UTD_CS_FIFO_MEM, "TD_UTD_CS_FIFO_MEM"},
- {AMDGPU_GFX_TD_UTD_SS_FIFO_LO_MEM, "TD_UTD_SS_FIFO_LO_MEM"},
- {AMDGPU_GFX_TD_UTD_SS_FIFO_HI_MEM, "TD_UTD_SS_FIFO_HI_MEM"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tcx_mem_list[] = {
- {AMDGPU_GFX_TCX_FIFOD0, "TCX_FIFOD0"},
- {AMDGPU_GFX_TCX_FIFOD1, "TCX_FIFOD1"},
- {AMDGPU_GFX_TCX_FIFOD2, "TCX_FIFOD2"},
- {AMDGPU_GFX_TCX_FIFOD3, "TCX_FIFOD3"},
- {AMDGPU_GFX_TCX_FIFOD4, "TCX_FIFOD4"},
- {AMDGPU_GFX_TCX_FIFOD5, "TCX_FIFOD5"},
- {AMDGPU_GFX_TCX_FIFOD6, "TCX_FIFOD6"},
- {AMDGPU_GFX_TCX_FIFOD7, "TCX_FIFOD7"},
- {AMDGPU_GFX_TCX_FIFOB0, "TCX_FIFOB0"},
- {AMDGPU_GFX_TCX_FIFOB1, "TCX_FIFOB1"},
- {AMDGPU_GFX_TCX_FIFOB2, "TCX_FIFOB2"},
- {AMDGPU_GFX_TCX_FIFOB3, "TCX_FIFOB3"},
- {AMDGPU_GFX_TCX_FIFOB4, "TCX_FIFOB4"},
- {AMDGPU_GFX_TCX_FIFOB5, "TCX_FIFOB5"},
- {AMDGPU_GFX_TCX_FIFOB6, "TCX_FIFOB6"},
- {AMDGPU_GFX_TCX_FIFOB7, "TCX_FIFOB7"},
- {AMDGPU_GFX_TCX_FIFOA0, "TCX_FIFOA0"},
- {AMDGPU_GFX_TCX_FIFOA1, "TCX_FIFOA1"},
- {AMDGPU_GFX_TCX_FIFOA2, "TCX_FIFOA2"},
- {AMDGPU_GFX_TCX_FIFOA3, "TCX_FIFOA3"},
- {AMDGPU_GFX_TCX_FIFOA4, "TCX_FIFOA4"},
- {AMDGPU_GFX_TCX_FIFOA5, "TCX_FIFOA5"},
- {AMDGPU_GFX_TCX_FIFOA6, "TCX_FIFOA6"},
- {AMDGPU_GFX_TCX_FIFOA7, "TCX_FIFOA7"},
- {AMDGPU_GFX_TCX_CFIFO0, "TCX_CFIFO0"},
- {AMDGPU_GFX_TCX_CFIFO1, "TCX_CFIFO1"},
- {AMDGPU_GFX_TCX_CFIFO2, "TCX_CFIFO2"},
- {AMDGPU_GFX_TCX_CFIFO3, "TCX_CFIFO3"},
- {AMDGPU_GFX_TCX_CFIFO4, "TCX_CFIFO4"},
- {AMDGPU_GFX_TCX_CFIFO5, "TCX_CFIFO5"},
- {AMDGPU_GFX_TCX_CFIFO6, "TCX_CFIFO6"},
- {AMDGPU_GFX_TCX_CFIFO7, "TCX_CFIFO7"},
- {AMDGPU_GFX_TCX_FIFO_ACKB0, "TCX_FIFO_ACKB0"},
- {AMDGPU_GFX_TCX_FIFO_ACKB1, "TCX_FIFO_ACKB1"},
- {AMDGPU_GFX_TCX_FIFO_ACKB2, "TCX_FIFO_ACKB2"},
- {AMDGPU_GFX_TCX_FIFO_ACKB3, "TCX_FIFO_ACKB3"},
- {AMDGPU_GFX_TCX_FIFO_ACKB4, "TCX_FIFO_ACKB4"},
- {AMDGPU_GFX_TCX_FIFO_ACKB5, "TCX_FIFO_ACKB5"},
- {AMDGPU_GFX_TCX_FIFO_ACKB6, "TCX_FIFO_ACKB6"},
- {AMDGPU_GFX_TCX_FIFO_ACKB7, "TCX_FIFO_ACKB7"},
- {AMDGPU_GFX_TCX_FIFO_ACKD0, "TCX_FIFO_ACKD0"},
- {AMDGPU_GFX_TCX_FIFO_ACKD1, "TCX_FIFO_ACKD1"},
- {AMDGPU_GFX_TCX_FIFO_ACKD2, "TCX_FIFO_ACKD2"},
- {AMDGPU_GFX_TCX_FIFO_ACKD3, "TCX_FIFO_ACKD3"},
- {AMDGPU_GFX_TCX_FIFO_ACKD4, "TCX_FIFO_ACKD4"},
- {AMDGPU_GFX_TCX_FIFO_ACKD5, "TCX_FIFO_ACKD5"},
- {AMDGPU_GFX_TCX_FIFO_ACKD6, "TCX_FIFO_ACKD6"},
- {AMDGPU_GFX_TCX_FIFO_ACKD7, "TCX_FIFO_ACKD7"},
- {AMDGPU_GFX_TCX_DST_FIFOA0, "TCX_DST_FIFOA0"},
- {AMDGPU_GFX_TCX_DST_FIFOA1, "TCX_DST_FIFOA1"},
- {AMDGPU_GFX_TCX_DST_FIFOA2, "TCX_DST_FIFOA2"},
- {AMDGPU_GFX_TCX_DST_FIFOA3, "TCX_DST_FIFOA3"},
- {AMDGPU_GFX_TCX_DST_FIFOA4, "TCX_DST_FIFOA4"},
- {AMDGPU_GFX_TCX_DST_FIFOA5, "TCX_DST_FIFOA5"},
- {AMDGPU_GFX_TCX_DST_FIFOA6, "TCX_DST_FIFOA6"},
- {AMDGPU_GFX_TCX_DST_FIFOA7, "TCX_DST_FIFOA7"},
- {AMDGPU_GFX_TCX_DST_FIFOB0, "TCX_DST_FIFOB0"},
- {AMDGPU_GFX_TCX_DST_FIFOB1, "TCX_DST_FIFOB1"},
- {AMDGPU_GFX_TCX_DST_FIFOB2, "TCX_DST_FIFOB2"},
- {AMDGPU_GFX_TCX_DST_FIFOB3, "TCX_DST_FIFOB3"},
- {AMDGPU_GFX_TCX_DST_FIFOB4, "TCX_DST_FIFOB4"},
- {AMDGPU_GFX_TCX_DST_FIFOB5, "TCX_DST_FIFOB5"},
- {AMDGPU_GFX_TCX_DST_FIFOB6, "TCX_DST_FIFOB6"},
- {AMDGPU_GFX_TCX_DST_FIFOB7, "TCX_DST_FIFOB7"},
- {AMDGPU_GFX_TCX_DST_FIFOD0, "TCX_DST_FIFOD0"},
- {AMDGPU_GFX_TCX_DST_FIFOD1, "TCX_DST_FIFOD1"},
- {AMDGPU_GFX_TCX_DST_FIFOD2, "TCX_DST_FIFOD2"},
- {AMDGPU_GFX_TCX_DST_FIFOD3, "TCX_DST_FIFOD3"},
- {AMDGPU_GFX_TCX_DST_FIFOD4, "TCX_DST_FIFOD4"},
- {AMDGPU_GFX_TCX_DST_FIFOD5, "TCX_DST_FIFOD5"},
- {AMDGPU_GFX_TCX_DST_FIFOD6, "TCX_DST_FIFOD6"},
- {AMDGPU_GFX_TCX_DST_FIFOD7, "TCX_DST_FIFOD7"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKB0, "TCX_DST_FIFO_ACKB0"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKB1, "TCX_DST_FIFO_ACKB1"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKB2, "TCX_DST_FIFO_ACKB2"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKB3, "TCX_DST_FIFO_ACKB3"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKB4, "TCX_DST_FIFO_ACKB4"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKB5, "TCX_DST_FIFO_ACKB5"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKB6, "TCX_DST_FIFO_ACKB6"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKB7, "TCX_DST_FIFO_ACKB7"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKD0, "TCX_DST_FIFO_ACKD0"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKD1, "TCX_DST_FIFO_ACKD1"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKD2, "TCX_DST_FIFO_ACKD2"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKD3, "TCX_DST_FIFO_ACKD3"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKD4, "TCX_DST_FIFO_ACKD4"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKD5, "TCX_DST_FIFO_ACKD5"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKD6, "TCX_DST_FIFO_ACKD6"},
- {AMDGPU_GFX_TCX_DST_FIFO_ACKD7, "TCX_DST_FIFO_ACKD7"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_atc_l2_mem_list[] = {
- {AMDGPU_GFX_ATC_L2_MEM, "ATC_L2_MEM"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_utcl2_mem_list[] = {
- {AMDGPU_GFX_UTCL2_MEM, "UTCL2_MEM"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_vml2_mem_list[] = {
- {AMDGPU_GFX_VML2_MEM, "VML2_MEM"},
-};
-
-static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_vml2_walker_mem_list[] = {
- {AMDGPU_GFX_VML2_WALKER_MEM, "VML2_WALKER_MEM"},
-};
-
-static const struct amdgpu_gfx_ras_mem_id_entry gfx_v9_4_3_ras_mem_list_array[AMDGPU_GFX_MEM_TYPE_NUM] = {
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_cp_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_gcea_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_gc_cane_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_gcutcl2_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_gds_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_lds_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_rlc_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_sp_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_spi_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_sqc_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_sq_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_ta_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tcc_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tca_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tci_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tcp_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_td_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tcx_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_atc_l2_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_utcl2_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_vml2_mem_list)
- AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_vml2_walker_mem_list)
-};
-
-static const struct amdgpu_gfx_ras_reg_entry gfx_v9_4_3_ce_reg_list[] = {
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regRLC_CE_ERR_STATUS_LOW, regRLC_CE_ERR_STATUS_HIGH),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "RLC"},
- AMDGPU_GFX_RLC_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPC_CE_ERR_STATUS_LO, regCPC_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPC"},
- AMDGPU_GFX_CP_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPF_CE_ERR_STATUS_LO, regCPF_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPF"},
- AMDGPU_GFX_CP_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPG_CE_ERR_STATUS_LO, regCPG_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPG"},
- AMDGPU_GFX_CP_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGDS_CE_ERR_STATUS_LO, regGDS_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "GDS"},
- AMDGPU_GFX_GDS_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGC_CANE_CE_ERR_STATUS_LO, regGC_CANE_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CANE"},
- AMDGPU_GFX_GC_CANE_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSPI_CE_ERR_STATUS_LO, regSPI_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SPI"},
- AMDGPU_GFX_SPI_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSP0_CE_ERR_STATUS_LO, regSP0_CE_ERR_STATUS_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SP0"},
- AMDGPU_GFX_SP_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSP1_CE_ERR_STATUS_LO, regSP1_CE_ERR_STATUS_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SP1"},
- AMDGPU_GFX_SP_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSQ_CE_ERR_STATUS_LO, regSQ_CE_ERR_STATUS_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SQ"},
- AMDGPU_GFX_SQ_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSQC_CE_EDC_LO, regSQC_CE_EDC_HI),
- 5, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SQC"},
- AMDGPU_GFX_SQC_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCX_CE_ERR_STATUS_LO, regTCX_CE_ERR_STATUS_HI),
- 2, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCX"},
- AMDGPU_GFX_TCX_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCC_CE_ERR_STATUS_LO, regTCC_CE_ERR_STATUS_HI),
- 16, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCC"},
- AMDGPU_GFX_TCC_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTA_CE_EDC_LO, regTA_CE_EDC_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TA"},
- AMDGPU_GFX_TA_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCI_CE_EDC_LO_REG, regTCI_CE_EDC_HI_REG),
- 27, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCI"},
- AMDGPU_GFX_TCI_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCP_CE_EDC_LO_REG, regTCP_CE_EDC_HI_REG),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCP"},
- AMDGPU_GFX_TCP_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTD_CE_EDC_LO, regTD_CE_EDC_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TD"},
- AMDGPU_GFX_TD_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGCEA_CE_ERR_STATUS_LO, regGCEA_CE_ERR_STATUS_HI),
- 16, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "GCEA"},
- AMDGPU_GFX_GCEA_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regLDS_CE_ERR_STATUS_LO, regLDS_CE_ERR_STATUS_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "LDS"},
- AMDGPU_GFX_LDS_MEM, 4},
-};
-
-static const struct amdgpu_gfx_ras_reg_entry gfx_v9_4_3_ue_reg_list[] = {
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regRLC_UE_ERR_STATUS_LOW, regRLC_UE_ERR_STATUS_HIGH),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "RLC"},
- AMDGPU_GFX_RLC_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPC_UE_ERR_STATUS_LO, regCPC_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPC"},
- AMDGPU_GFX_CP_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPF_UE_ERR_STATUS_LO, regCPF_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPF"},
- AMDGPU_GFX_CP_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPG_UE_ERR_STATUS_LO, regCPG_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPG"},
- AMDGPU_GFX_CP_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGDS_UE_ERR_STATUS_LO, regGDS_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "GDS"},
- AMDGPU_GFX_GDS_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGC_CANE_UE_ERR_STATUS_LO, regGC_CANE_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CANE"},
- AMDGPU_GFX_GC_CANE_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSPI_UE_ERR_STATUS_LO, regSPI_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SPI"},
- AMDGPU_GFX_SPI_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSP0_UE_ERR_STATUS_LO, regSP0_UE_ERR_STATUS_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SP0"},
- AMDGPU_GFX_SP_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSP1_UE_ERR_STATUS_LO, regSP1_UE_ERR_STATUS_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SP1"},
- AMDGPU_GFX_SP_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSQ_UE_ERR_STATUS_LO, regSQ_UE_ERR_STATUS_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SQ"},
- AMDGPU_GFX_SQ_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSQC_UE_EDC_LO, regSQC_UE_EDC_HI),
- 5, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SQC"},
- AMDGPU_GFX_SQC_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCX_UE_ERR_STATUS_LO, regTCX_UE_ERR_STATUS_HI),
- 2, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCX"},
- AMDGPU_GFX_TCX_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCC_UE_ERR_STATUS_LO, regTCC_UE_ERR_STATUS_HI),
- 16, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCC"},
- AMDGPU_GFX_TCC_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTA_UE_EDC_LO, regTA_UE_EDC_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TA"},
- AMDGPU_GFX_TA_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCI_UE_EDC_LO_REG, regTCI_UE_EDC_HI_REG),
- 27, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCI"},
- AMDGPU_GFX_TCI_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCP_UE_EDC_LO_REG, regTCP_UE_EDC_HI_REG),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCP"},
- AMDGPU_GFX_TCP_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTD_UE_EDC_LO, regTD_UE_EDC_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TD"},
- AMDGPU_GFX_TD_MEM, 4},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCA_UE_ERR_STATUS_LO, regTCA_UE_ERR_STATUS_HI),
- 2, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCA"},
- AMDGPU_GFX_TCA_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGCEA_UE_ERR_STATUS_LO, regGCEA_UE_ERR_STATUS_HI),
- 16, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "GCEA"},
- AMDGPU_GFX_GCEA_MEM, 1},
- {{AMDGPU_RAS_REG_ENTRY(GC, 0, regLDS_UE_ERR_STATUS_LO, regLDS_UE_ERR_STATUS_HI),
- 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "LDS"},
- AMDGPU_GFX_LDS_MEM, 4},
-};
-
-static void gfx_v9_4_3_inst_query_ras_err_count(struct amdgpu_device *adev,
- void *ras_error_status, int xcc_id)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status;
- unsigned long ce_count = 0, ue_count = 0;
- uint32_t i, j, k;
-
- /* NOTE: convert xcc_id to physical XCD ID (XCD0 or XCD1) */
- struct amdgpu_smuio_mcm_config_info mcm_info = {
- .socket_id = adev->smuio.funcs->get_socket_id(adev),
- .die_id = xcc_id & 0x01 ? 1 : 0,
- };
-
- mutex_lock(&adev->grbm_idx_mutex);
-
- for (i = 0; i < ARRAY_SIZE(gfx_v9_4_3_ce_reg_list); i++) {
- for (j = 0; j < gfx_v9_4_3_ce_reg_list[i].se_num; j++) {
- for (k = 0; k < gfx_v9_4_3_ce_reg_list[i].reg_entry.reg_inst; k++) {
- /* no need to select if instance number is 1 */
- if (gfx_v9_4_3_ce_reg_list[i].se_num > 1 ||
- gfx_v9_4_3_ce_reg_list[i].reg_entry.reg_inst > 1)
- gfx_v9_4_3_xcc_select_se_sh(adev, j, 0, k, xcc_id);
-
- amdgpu_ras_inst_query_ras_error_count(adev,
- &(gfx_v9_4_3_ce_reg_list[i].reg_entry),
- 1,
- gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ce_reg_list[i].mem_id_type].mem_id_ent,
- gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ce_reg_list[i].mem_id_type].size,
- GET_INST(GC, xcc_id),
- AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE,
- &ce_count);
-
- amdgpu_ras_inst_query_ras_error_count(adev,
- &(gfx_v9_4_3_ue_reg_list[i].reg_entry),
- 1,
- gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ue_reg_list[i].mem_id_type].mem_id_ent,
- gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ue_reg_list[i].mem_id_type].size,
- GET_INST(GC, xcc_id),
- AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE,
- &ue_count);
- }
- }
- }
-
- /* handle extra register entries of UE */
- for (; i < ARRAY_SIZE(gfx_v9_4_3_ue_reg_list); i++) {
- for (j = 0; j < gfx_v9_4_3_ue_reg_list[i].se_num; j++) {
- for (k = 0; k < gfx_v9_4_3_ue_reg_list[i].reg_entry.reg_inst; k++) {
- /* no need to select if instance number is 1 */
- if (gfx_v9_4_3_ue_reg_list[i].se_num > 1 ||
- gfx_v9_4_3_ue_reg_list[i].reg_entry.reg_inst > 1)
- gfx_v9_4_3_xcc_select_se_sh(adev, j, 0, k, xcc_id);
-
- amdgpu_ras_inst_query_ras_error_count(adev,
- &(gfx_v9_4_3_ue_reg_list[i].reg_entry),
- 1,
- gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ue_reg_list[i].mem_id_type].mem_id_ent,
- gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ue_reg_list[i].mem_id_type].size,
- GET_INST(GC, xcc_id),
- AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE,
- &ue_count);
- }
- }
- }
-
- gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff,
- xcc_id);
- mutex_unlock(&adev->grbm_idx_mutex);
-
- /* the caller should make sure initialize value of
- * err_data->ue_count and err_data->ce_count
- */
- amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, ue_count);
- amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, ce_count);
-}
-
-static void gfx_v9_4_3_inst_reset_ras_err_count(struct amdgpu_device *adev,
- void *ras_error_status, int xcc_id)
-{
- uint32_t i, j, k;
-
- mutex_lock(&adev->grbm_idx_mutex);
-
- for (i = 0; i < ARRAY_SIZE(gfx_v9_4_3_ce_reg_list); i++) {
- for (j = 0; j < gfx_v9_4_3_ce_reg_list[i].se_num; j++) {
- for (k = 0; k < gfx_v9_4_3_ce_reg_list[i].reg_entry.reg_inst; k++) {
- /* no need to select if instance number is 1 */
- if (gfx_v9_4_3_ce_reg_list[i].se_num > 1 ||
- gfx_v9_4_3_ce_reg_list[i].reg_entry.reg_inst > 1)
- gfx_v9_4_3_xcc_select_se_sh(adev, j, 0, k, xcc_id);
-
- amdgpu_ras_inst_reset_ras_error_count(adev,
- &(gfx_v9_4_3_ce_reg_list[i].reg_entry),
- 1,
- GET_INST(GC, xcc_id));
-
- amdgpu_ras_inst_reset_ras_error_count(adev,
- &(gfx_v9_4_3_ue_reg_list[i].reg_entry),
- 1,
- GET_INST(GC, xcc_id));
- }
- }
- }
-
- /* handle extra register entries of UE */
- for (; i < ARRAY_SIZE(gfx_v9_4_3_ue_reg_list); i++) {
- for (j = 0; j < gfx_v9_4_3_ue_reg_list[i].se_num; j++) {
- for (k = 0; k < gfx_v9_4_3_ue_reg_list[i].reg_entry.reg_inst; k++) {
- /* no need to select if instance number is 1 */
- if (gfx_v9_4_3_ue_reg_list[i].se_num > 1 ||
- gfx_v9_4_3_ue_reg_list[i].reg_entry.reg_inst > 1)
- gfx_v9_4_3_xcc_select_se_sh(adev, j, 0, k, xcc_id);
-
- amdgpu_ras_inst_reset_ras_error_count(adev,
- &(gfx_v9_4_3_ue_reg_list[i].reg_entry),
- 1,
- GET_INST(GC, xcc_id));
- }
- }
- }
-
- gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff,
- xcc_id);
- mutex_unlock(&adev->grbm_idx_mutex);
-}
-
static void gfx_v9_4_3_inst_enable_watchdog_timer(struct amdgpu_device *adev,
void *ras_error_status, int xcc_id)
{
@@ -4607,18 +3758,6 @@ static void gfx_v9_4_3_inst_enable_watchdog_timer(struct amdgpu_device *adev,
mutex_unlock(&adev->grbm_idx_mutex);
}
-static void gfx_v9_4_3_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_error_status)
-{
- amdgpu_gfx_ras_error_func(adev, ras_error_status,
- gfx_v9_4_3_inst_query_ras_err_count);
-}
-
-static void gfx_v9_4_3_reset_ras_error_count(struct amdgpu_device *adev)
-{
- amdgpu_gfx_ras_error_func(adev, NULL, gfx_v9_4_3_inst_reset_ras_err_count);
-}
-
static void gfx_v9_4_3_enable_watchdog_timer(struct amdgpu_device *adev)
{
amdgpu_gfx_ras_error_func(adev, NULL, gfx_v9_4_3_inst_enable_watchdog_timer);
@@ -5099,37 +4238,9 @@ struct amdgpu_xcp_ip_funcs gfx_v9_4_3_xcp_funcs = {
.resume = &gfx_v9_4_3_xcp_resume
};
-struct amdgpu_ras_block_hw_ops gfx_v9_4_3_ras_ops = {
- .query_ras_error_count = &gfx_v9_4_3_query_ras_error_count,
- .reset_ras_error_count = &gfx_v9_4_3_reset_ras_error_count,
-};
-
-static int gfx_v9_4_3_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
-{
- int r;
-
- r = amdgpu_ras_block_late_init(adev, ras_block);
- if (r)
- return r;
-
- r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__GFX,
- &gfx_v9_4_3_aca_info,
- NULL);
- if (r)
- goto late_fini;
-
- return 0;
-
-late_fini:
- amdgpu_ras_block_late_fini(adev, ras_block);
-
- return r;
-}
-
struct amdgpu_gfx_ras gfx_v9_4_3_ras = {
.ras_block = {
- .hw_ops = &gfx_v9_4_3_ras_ops,
- .ras_late_init = &gfx_v9_4_3_ras_late_init,
+ .hw_ops = NULL,
},
.enable_watchdog_timer = &gfx_v9_4_3_enable_watchdog_timer,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
index c2a41fa3a396..64ebedc595b5 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
@@ -167,44 +167,6 @@ static void gmc_v8_0_init_golden_registers(struct amdgpu_device *adev)
}
}
-static void gmc_v8_0_mc_stop(struct amdgpu_device *adev)
-{
- u32 blackout;
- struct amdgpu_ip_block *ip_block;
-
- ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_GMC);
- if (!ip_block)
- return;
-
- gmc_v8_0_wait_for_idle(ip_block);
-
- blackout = RREG32(mmMC_SHARED_BLACKOUT_CNTL);
- if (REG_GET_FIELD(blackout, MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE) != 1) {
- /* Block CPU access */
- WREG32(mmBIF_FB_EN, 0);
- /* blackout the MC */
- blackout = REG_SET_FIELD(blackout,
- MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE, 1);
- WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout);
- }
- /* wait for the MC to settle */
- udelay(100);
-}
-
-static void gmc_v8_0_mc_resume(struct amdgpu_device *adev)
-{
- u32 tmp;
-
- /* unblackout the MC */
- tmp = RREG32(mmMC_SHARED_BLACKOUT_CNTL);
- tmp = REG_SET_FIELD(tmp, MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE, 0);
- WREG32(mmMC_SHARED_BLACKOUT_CNTL, tmp);
- /* allow CPU access */
- tmp = REG_SET_FIELD(0, BIF_FB_EN, FB_READ_EN, 1);
- tmp = REG_SET_FIELD(tmp, BIF_FB_EN, FB_WRITE_EN, 1);
- WREG32(mmBIF_FB_EN, tmp);
-}
-
/**
* gmc_v8_0_init_microcode - load ucode images from disk
*
@@ -1293,89 +1255,6 @@ static int gmc_v8_0_wait_for_idle(struct amdgpu_ip_block *ip_block)
}
-static bool gmc_v8_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- u32 srbm_soft_reset = 0;
- struct amdgpu_device *adev = ip_block->adev;
- u32 tmp = RREG32(mmSRBM_STATUS);
-
- if (tmp & SRBM_STATUS__VMC_BUSY_MASK)
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
- SRBM_SOFT_RESET, SOFT_RESET_VMC, 1);
-
- if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
- SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK)) {
- if (!(adev->flags & AMD_IS_APU))
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
- SRBM_SOFT_RESET, SOFT_RESET_MC, 1);
- }
-
- if (srbm_soft_reset) {
- adev->gmc.srbm_soft_reset = srbm_soft_reset;
- return true;
- }
-
- adev->gmc.srbm_soft_reset = 0;
-
- return false;
-}
-
-static int gmc_v8_0_pre_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
-
- if (!adev->gmc.srbm_soft_reset)
- return 0;
-
- gmc_v8_0_mc_stop(adev);
- if (gmc_v8_0_wait_for_idle(ip_block))
- dev_warn(adev->dev, "Wait for GMC idle timed out !\n");
-
- return 0;
-}
-
-static int gmc_v8_0_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 srbm_soft_reset;
-
- if (!adev->gmc.srbm_soft_reset)
- return 0;
- srbm_soft_reset = adev->gmc.srbm_soft_reset;
-
- if (srbm_soft_reset) {
- u32 tmp;
-
- tmp = RREG32(mmSRBM_SOFT_RESET);
- tmp |= srbm_soft_reset;
- dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
- WREG32(mmSRBM_SOFT_RESET, tmp);
- tmp = RREG32(mmSRBM_SOFT_RESET);
-
- udelay(50);
-
- tmp &= ~srbm_soft_reset;
- WREG32(mmSRBM_SOFT_RESET, tmp);
- tmp = RREG32(mmSRBM_SOFT_RESET);
-
- /* Wait a little for things to settle down */
- udelay(50);
- }
-
- return 0;
-}
-
-static int gmc_v8_0_post_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
-
- if (!adev->gmc.srbm_soft_reset)
- return 0;
-
- gmc_v8_0_mc_resume(adev);
- return 0;
-}
-
static int gmc_v8_0_vm_fault_interrupt_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *src,
unsigned int type,
@@ -1715,10 +1594,6 @@ static const struct amd_ip_funcs gmc_v8_0_ip_funcs = {
.resume = gmc_v8_0_resume,
.is_idle = gmc_v8_0_is_idle,
.wait_for_idle = gmc_v8_0_wait_for_idle,
- .check_soft_reset = gmc_v8_0_check_soft_reset,
- .pre_soft_reset = gmc_v8_0_pre_soft_reset,
- .soft_reset = gmc_v8_0_soft_reset,
- .post_soft_reset = gmc_v8_0_post_soft_reset,
.set_clockgating_state = gmc_v8_0_set_clockgating_state,
.set_powergating_state = gmc_v8_0_set_powergating_state,
.get_clockgating_state = gmc_v8_0_get_clockgating_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
index 8a5c44810ba1..1fcc0594fd0a 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
@@ -57,6 +57,7 @@
#include "umc_v6_0.h"
#include "umc_v6_7.h"
#include "umc_v12_0.h"
+#include "ras_umc_v12_0.h"
#include "hdp_v4_0.h"
#include "mca_v3_0.h"
@@ -1382,7 +1383,7 @@ static void gmc_v9_0_set_umc_funcs(struct amdgpu_device *adev)
case IP_VERSION(12, 0, 0):
case IP_VERSION(12, 5, 0):
adev->umc.max_ras_err_cnt_per_query =
- UMC_V12_0_TOTAL_CHANNEL_NUM(adev) * UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL;
+ UMC_V12_0_TOTAL_CHANNEL_NUM * UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL;
adev->umc.channel_inst_num = UMC_V12_0_CHANNEL_INSTANCE_NUM;
adev->umc.umc_inst_num = UMC_V12_0_UMC_INSTANCE_NUM;
adev->umc.node_inst_num /= UMC_V12_0_UMC_INSTANCE_NUM;
@@ -2025,11 +2026,19 @@ static int gmc_v9_0_sw_init(struct amdgpu_ip_block *ip_block)
* The first KFD VMID is 8 for GPUs with graphics, 3 for
* compute-only GPUs. On compute-only GPUs that leaves 2 VMIDs
* for video processing.
+ *
+ * If kernel queues are disabled, allow KFD to use all vmids.
*/
- adev->vm_manager.first_kfd_vmid =
- (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 1) ||
- amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 2) ||
- amdgpu_is_multi_aid(adev)) ?
+ if (adev->gfx.disable_kq &&
+ adev->jpeg.disable_kq &&
+ adev->vcn.disable_kq &&
+ adev->sdma.no_user_submission)
+ adev->vm_manager.first_kfd_vmid = 1;
+ else
+ adev->vm_manager.first_kfd_vmid =
+ (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 1) ||
+ amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 2) ||
+ amdgpu_is_multi_aid(adev)) ?
3 :
8;
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
index d8204fbc198d..0fdc32b3ae91 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c
@@ -119,6 +119,19 @@ static int jpeg_v4_0_3_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
+ switch (amdgpu_user_queue) {
+ case -1:
+ case 0:
+ default:
+ adev->jpeg.disable_kq = false;
+ adev->jpeg.disable_uq = true;
+ break;
+ case 2:
+ adev->jpeg.disable_kq = true;
+ adev->jpeg.disable_uq = true;
+ break;
+ }
+
adev->jpeg.num_jpeg_rings = AMDGPU_MAX_JPEG_RINGS_4_0_3;
jpeg_v4_0_3_set_dec_ring_funcs(adev);
@@ -175,6 +188,10 @@ static int jpeg_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block)
for (j = 0; j < adev->jpeg.num_jpeg_rings; ++j) {
ring = &adev->jpeg.inst[i].ring_dec[j];
ring->use_doorbell = true;
+ if (adev->jpeg.disable_kq) {
+ ring->no_scheduler = true;
+ ring->no_user_submission = true;
+ }
ring->vm_hub = AMDGPU_MMHUB0(adev->jpeg.inst[i].aid_id);
if (!amdgpu_sriov_vf(adev)) {
ring->doorbell_index =
@@ -1425,72 +1442,6 @@ static const struct amdgpu_ras_block_hw_ops jpeg_v4_0_3_ras_hw_ops = {
.query_poison_status = jpeg_v4_0_3_query_ras_poison_status,
};
-static int jpeg_v4_0_3_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct aca_bank_info info;
- u64 misc0;
- int ret;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- misc0 = bank->regs[ACA_REG_IDX_MISC0];
- switch (type) {
- case ACA_SMU_TYPE_UE:
- bank->aca_err_type = ACA_ERROR_TYPE_UE;
- ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE,
- 1ULL);
- break;
- case ACA_SMU_TYPE_CE:
- bank->aca_err_type = ACA_ERROR_TYPE_CE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type,
- ACA_REG__MISC0__ERRCNT(misc0));
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
-/* reference to smu driver if header file */
-static int jpeg_v4_0_3_err_codes[] = {
- 16, 17, 18, 19, 20, 21, 22, 23, /* JPEG[0-7][S|D] */
- 24, 25, 26, 27, 28, 29, 30, 31
-};
-
-static bool jpeg_v4_0_3_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- u32 instlo;
-
- instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]);
- instlo &= GENMASK(31, 1);
-
- if (instlo != mmSMNAID_AID0_MCA_SMU)
- return false;
-
- if (aca_bank_check_error_codes(handle->adev, bank,
- jpeg_v4_0_3_err_codes,
- ARRAY_SIZE(jpeg_v4_0_3_err_codes)))
- return false;
-
- return true;
-}
-
-static const struct aca_bank_ops jpeg_v4_0_3_aca_bank_ops = {
- .aca_bank_parser = jpeg_v4_0_3_aca_bank_parser,
- .aca_bank_is_valid = jpeg_v4_0_3_aca_bank_is_valid,
-};
-
-static const struct aca_info jpeg_v4_0_3_aca_info = {
- .hwip = ACA_HWIP_TYPE_SMU,
- .mask = ACA_ERROR_UE_MASK,
- .bank_ops = &jpeg_v4_0_3_aca_bank_ops,
-};
-
static int jpeg_v4_0_3_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
{
int r;
@@ -1506,11 +1457,6 @@ static int jpeg_v4_0_3_ras_late_init(struct amdgpu_device *adev, struct ras_comm
goto late_fini;
}
- r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__JPEG,
- &jpeg_v4_0_3_aca_info, NULL);
- if (r)
- goto late_fini;
-
return 0;
late_fini:
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
index ae3afc7ab326..8846cb3ed12b 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
@@ -118,6 +118,19 @@ static int jpeg_v5_0_1_early_init(struct amdgpu_ip_block *ip_block)
if (!adev->jpeg.num_jpeg_inst || adev->jpeg.num_jpeg_inst > AMDGPU_MAX_JPEG_INSTANCES)
return -ENOENT;
+ switch (amdgpu_user_queue) {
+ case -1:
+ case 0:
+ default:
+ adev->jpeg.disable_kq = false;
+ adev->jpeg.disable_uq = true;
+ break;
+ case 2:
+ adev->jpeg.disable_kq = true;
+ adev->jpeg.disable_uq = true;
+ break;
+ }
+
adev->jpeg.num_jpeg_rings = AMDGPU_MAX_JPEG_RINGS;
jpeg_v5_0_1_set_dec_ring_funcs(adev);
jpeg_v5_0_1_set_irq_funcs(adev);
@@ -172,6 +185,10 @@ static int jpeg_v5_0_1_sw_init(struct amdgpu_ip_block *ip_block)
for (j = 0; j < adev->jpeg.num_jpeg_rings; ++j) {
ring = &adev->jpeg.inst[i].ring_dec[j];
ring->use_doorbell = true;
+ if (adev->jpeg.disable_kq) {
+ ring->no_scheduler = true;
+ ring->no_user_submission = true;
+ }
ring->vm_hub = AMDGPU_MMHUB0(adev->jpeg.inst[i].aid_id);
if (!amdgpu_sriov_vf(adev)) {
ring->doorbell_index =
@@ -871,10 +888,7 @@ static const struct amd_ip_funcs jpeg_v5_0_1_ip_funcs = {
.resume = jpeg_v5_0_1_resume,
.is_idle = jpeg_v5_0_1_is_idle,
.wait_for_idle = jpeg_v5_0_1_wait_for_idle,
- .check_soft_reset = NULL,
- .pre_soft_reset = NULL,
.soft_reset = NULL,
- .post_soft_reset = NULL,
.set_clockgating_state = jpeg_v5_0_1_set_clockgating_state,
.set_powergating_state = jpeg_v5_0_1_set_powergating_state,
.dump_ip_state = amdgpu_jpeg_dump_ip_state,
@@ -1003,73 +1017,6 @@ static const struct amdgpu_ras_block_hw_ops jpeg_v5_0_1_ras_hw_ops = {
.query_poison_status = jpeg_v5_0_1_query_ras_poison_status,
};
-static int jpeg_v5_0_1_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct aca_bank_info info;
- u64 misc0;
- int ret;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- misc0 = bank->regs[ACA_REG_IDX_MISC0];
- switch (type) {
- case ACA_SMU_TYPE_UE:
- bank->aca_err_type = ACA_ERROR_TYPE_UE;
- ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE,
- 1ULL);
- break;
- case ACA_SMU_TYPE_CE:
- bank->aca_err_type = ACA_ERROR_TYPE_CE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type,
- ACA_REG__MISC0__ERRCNT(misc0));
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
-/* reference to smu driver if header file */
-static int jpeg_v5_0_1_err_codes[] = {
- 16, 17, 18, 19, 20, 21, 22, 23, /* JPEG[0-9][S|D] */
- 24, 25, 26, 27, 28, 29, 30, 31,
- 48, 49, 50, 51,
-};
-
-static bool jpeg_v5_0_1_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- u32 instlo;
-
- instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]);
- instlo &= GENMASK(31, 1);
-
- if (instlo != mmSMNAID_AID0_MCA_SMU)
- return false;
-
- if (aca_bank_check_error_codes(handle->adev, bank,
- jpeg_v5_0_1_err_codes,
- ARRAY_SIZE(jpeg_v5_0_1_err_codes)))
- return false;
-
- return true;
-}
-
-static const struct aca_bank_ops jpeg_v5_0_1_aca_bank_ops = {
- .aca_bank_parser = jpeg_v5_0_1_aca_bank_parser,
- .aca_bank_is_valid = jpeg_v5_0_1_aca_bank_is_valid,
-};
-
-static const struct aca_info jpeg_v5_0_1_aca_info = {
- .hwip = ACA_HWIP_TYPE_SMU,
- .mask = ACA_ERROR_UE_MASK,
- .bank_ops = &jpeg_v5_0_1_aca_bank_ops,
-};
-
static int jpeg_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
{
int r;
@@ -1078,11 +1025,6 @@ static int jpeg_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_comm
if (r)
return r;
- r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__JPEG,
- &jpeg_v5_0_1_aca_info, NULL);
- if (r)
- goto late_fini;
-
if (amdgpu_ras_is_supported(adev, ras_block->block) &&
adev->jpeg.inst->ras_poison_irq.funcs) {
r = amdgpu_irq_get(adev, &adev->jpeg.inst->ras_poison_irq, 0);
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
index 7a4ecea6b39a..ff02f72352a8 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c
@@ -690,10 +690,7 @@ static const struct amd_ip_funcs jpeg_v5_0_2_ip_funcs = {
.resume = jpeg_v5_0_2_resume,
.is_idle = jpeg_v5_0_2_is_idle,
.wait_for_idle = jpeg_v5_0_2_wait_for_idle,
- .check_soft_reset = NULL,
- .pre_soft_reset = NULL,
.soft_reset = NULL,
- .post_soft_reset = NULL,
.set_clockgating_state = jpeg_v5_0_2_set_clockgating_state,
.set_powergating_state = jpeg_v5_0_2_set_powergating_state,
.dump_ip_state = amdgpu_jpeg_dump_ip_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
index 16625c31bfd3..e947c16e694d 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
@@ -133,8 +133,8 @@ static int mes_userq_map(struct amdgpu_usermode_queue *queue)
queue_input.gang_quantum = 10000;
queue_input.paging = false;
- queue_input.process_context_addr = ctx->gpu_addr;
- queue_input.gang_context_addr = ctx->gpu_addr + AMDGPU_USERQ_PROC_CTX_SZ;
+ queue_input.process_context_addr = uq_mgr->proc_ctx_obj.gpu_addr;
+ queue_input.gang_context_addr = ctx->gpu_addr;
queue_input.inprocess_gang_priority = AMDGPU_MES_PRIORITY_LEVEL_NORMAL;
queue_input.gang_global_priority_level = convert_to_mes_priority(queue->priority);
@@ -169,7 +169,8 @@ static int mes_userq_unmap(struct amdgpu_usermode_queue *queue)
memset(&queue_input, 0x0, sizeof(struct mes_remove_queue_input));
queue_input.doorbell_offset = queue->doorbell_index;
- queue_input.gang_context_addr = ctx->gpu_addr + AMDGPU_USERQ_PROC_CTX_SZ;
+ queue_input.gang_context_addr = ctx->gpu_addr;
+ queue_input.queue_type = queue->queue_type;
amdgpu_mes_lock(&adev->mes);
r = adev->mes.funcs->remove_hw_queue(&adev->mes, &queue_input);
@@ -179,6 +180,63 @@ static int mes_userq_unmap(struct amdgpu_usermode_queue *queue)
return r;
}
+int mes_userq_reset(struct amdgpu_usermode_queue *queue)
+{
+ struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr;
+ struct amdgpu_device *adev = uq_mgr->adev;
+ struct mes_reset_queue_input queue_input;
+ int r;
+
+ /* XXX: add a FW version check for SDMA per queue reset */
+ memset(&queue_input, 0x0, sizeof(struct mes_reset_queue_input));
+ queue_input.doorbell_offset = queue->doorbell_index;
+ queue_input.queue_type = queue->queue_type;
+
+ amdgpu_mes_lock(&adev->mes);
+ r = adev->mes.funcs->reset_hw_queue(&adev->mes, &queue_input);
+ amdgpu_mes_unlock(&adev->mes);
+ if (r)
+ return r;
+ return mes_userq_unmap(queue);
+}
+
+int mes_userq_reset_queue(struct amdgpu_device *adev,
+ struct amdgpu_usermode_queue *guilty_uq,
+ int queue_type,
+ unsigned int pipe,
+ unsigned int queue,
+ unsigned int db)
+{
+ struct amdgpu_usermode_queue *uq;
+ bool use_mmio = adev->gfx.mec.use_mmio_for_reset;
+ unsigned long uq_id;
+ int r;
+
+ xa_for_each(&adev->userq_doorbell_xa, uq_id, uq) {
+ if (uq->queue_type == queue_type) {
+ if (uq == guilty_uq)
+ continue;
+ if (uq->doorbell_index == db) {
+ uq->state = AMDGPU_USERQ_STATE_HUNG;
+ if (use_mmio)
+ r = amdgpu_mes_reset_queue_mmio(adev, queue_type, 0, 1, pipe, queue, 0);
+ else
+ r = amdgpu_mes_reset_user_queue(adev, queue_type, db, 0);
+ if (r)
+ return r;
+ r = mes_userq_unmap(uq);
+ if (r)
+ return r;
+ atomic_inc(&adev->gpu_reset_counter);
+ amdgpu_userq_fence_driver_force_completion(uq);
+ drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_usermode_queue *queue,
struct drm_amdgpu_userq_in *mqd_user)
@@ -186,12 +244,8 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_userq_obj *ctx = &queue->fw_obj;
int r, size;
- /*
- * The FW expects at least one page space allocated for
- * process ctx and gang ctx each. Create an object
- * for the same.
- */
- size = AMDGPU_USERQ_PROC_CTX_SZ + AMDGPU_USERQ_GANG_CTX_SZ;
+ /* The FW expects at least one page space allocated for gang ctx. */
+ size = AMDGPU_USERQ_GANG_CTX_SZ;
r = amdgpu_bo_create_kernel(uq_mgr->adev, size, 0,
AMDGPU_GEM_DOMAIN_GTT,
&ctx->obj, &ctx->gpu_addr,
@@ -205,54 +259,26 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr,
return 0;
}
-static int mes_userq_detect_and_reset(struct amdgpu_device *adev,
- int queue_type)
+static int mes_userq_create_proc_ctx_space(struct amdgpu_userq_mgr *uq_mgr)
{
- int db_array_size = amdgpu_mes_get_hung_queue_db_array_size(adev);
- struct mes_detect_and_reset_queue_input input;
- struct amdgpu_usermode_queue *queue;
- unsigned int hung_db_num = 0;
- unsigned long queue_id;
- u32 db_array[8];
- bool found_hung_queue = false;
- int r, i;
-
- if (db_array_size > 8) {
- dev_err(adev->dev, "DB array size (%d vs 8) too small\n",
- db_array_size);
- return -EINVAL;
- }
-
- memset(&input, 0x0, sizeof(struct mes_detect_and_reset_queue_input));
+ int r = 0;
- input.queue_type = queue_type;
-
- amdgpu_mes_lock(&adev->mes);
- r = amdgpu_mes_detect_and_reset_hung_queues(adev, queue_type, false,
- &hung_db_num, db_array, 0);
- amdgpu_mes_unlock(&adev->mes);
- if (r) {
- dev_err(adev->dev, "Failed to detect and reset queues, err (%d)\n", r);
- } else if (hung_db_num) {
- xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
- if (queue->queue_type == queue_type) {
- for (i = 0; i < hung_db_num; i++) {
- if (queue->doorbell_index == db_array[i]) {
- queue->state = AMDGPU_USERQ_STATE_HUNG;
- found_hung_queue = true;
- atomic_inc(&adev->gpu_reset_counter);
- amdgpu_userq_fence_driver_force_completion(queue);
- drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL);
- }
- }
- }
- }
+ mutex_lock(&uq_mgr->proc_ctx_lock);
+ /* This check is a necessary because amdgpu_bo_create_kernel()
+ * calls helpers like amdgpu_bo_pin() and memset() unconditionally
+ */
+ if (!uq_mgr->proc_ctx_obj.obj) {
+ r = amdgpu_bo_create_kernel(uq_mgr->adev, AMDGPU_USERQ_PROC_CTX_SZ,
+ 0, AMDGPU_GEM_DOMAIN_GTT,
+ &uq_mgr->proc_ctx_obj.obj,
+ &uq_mgr->proc_ctx_obj.gpu_addr,
+ &uq_mgr->proc_ctx_obj.cpu_ptr);
+
+ if (!r)
+ memset(uq_mgr->proc_ctx_obj.cpu_ptr, 0, AMDGPU_USERQ_PROC_CTX_SZ);
}
- if (found_hung_queue) {
- /* Resume scheduling after hang recovery */
- r = amdgpu_mes_resume(adev, input.xcc_id);
- }
+ mutex_unlock(&uq_mgr->proc_ctx_lock);
return r;
}
@@ -429,7 +455,14 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
goto free_mqd;
}
- /* Create BO for FW operations */
+ /* Create per-process MES process context BO */
+ r = mes_userq_create_proc_ctx_space(uq_mgr);
+ if (r) {
+ DRM_ERROR("Failed to allocate MES process context space bo, error: %d\n", r);
+ goto free_mqd;
+ }
+
+ /* Create BO of a gang for FW operations */
r = mes_userq_create_ctx_space(uq_mgr, queue, mqd_user);
if (r) {
DRM_ERROR("Failed to allocate BO for userqueue (%d)", r);
@@ -497,7 +530,7 @@ static int mes_userq_preempt(struct amdgpu_usermode_queue *queue)
*fence_ptr = 0;
memset(&queue_input, 0x0, sizeof(struct mes_suspend_gang_input));
- queue_input.gang_context_addr = ctx->gpu_addr + AMDGPU_USERQ_PROC_CTX_SZ;
+ queue_input.gang_context_addr = ctx->gpu_addr;
queue_input.suspend_fence_addr = fence_gpu_addr;
queue_input.suspend_fence_value = 1;
amdgpu_mes_lock(&adev->mes);
@@ -534,7 +567,7 @@ static int mes_userq_restore(struct amdgpu_usermode_queue *queue)
return 0;
memset(&queue_input, 0x0, sizeof(struct mes_resume_gang_input));
- queue_input.gang_context_addr = ctx->gpu_addr + AMDGPU_USERQ_PROC_CTX_SZ;
+ queue_input.gang_context_addr = ctx->gpu_addr;
amdgpu_mes_lock(&adev->mes);
r = adev->mes.funcs->resume_gang(&adev->mes, &queue_input);
@@ -549,7 +582,7 @@ const struct amdgpu_userq_funcs userq_mes_funcs = {
.mqd_destroy = mes_userq_mqd_destroy,
.unmap = mes_userq_unmap,
.map = mes_userq_map,
- .detect_and_reset = mes_userq_detect_and_reset,
.preempt = mes_userq_preempt,
.restore = mes_userq_restore,
+ .reset = mes_userq_reset,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.h b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.h
index 090ae8897770..a473360d6a8b 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.h
+++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.h
@@ -27,4 +27,13 @@
#include "amdgpu_userq.h"
extern const struct amdgpu_userq_funcs userq_mes_funcs;
+
+int mes_userq_reset(struct amdgpu_usermode_queue *queue);
+int mes_userq_reset_queue(struct amdgpu_device *adev,
+ struct amdgpu_usermode_queue *guilty_uq,
+ int queue_type,
+ unsigned int pipe,
+ unsigned int queue,
+ unsigned int db);
+
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
index 1b071a3de173..8f136ff7d96f 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
@@ -387,6 +387,8 @@ static int mes_v11_0_remove_hw_queue(struct amdgpu_mes *mes,
mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset;
mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr;
+ mes_remove_queue_pkt.queue_type =
+ convert_to_mes_queue_type(input->queue_type);
if (mes_rev >= 0x60)
mes_remove_queue_pkt.remove_queue_after_reset = input->remove_queue_after_reset;
@@ -396,6 +398,230 @@ static int mes_v11_0_remove_hw_queue(struct amdgpu_mes *mes,
offsetof(union MESAPI__REMOVE_QUEUE, api_status));
}
+static bool mes_v11_0_pipe_reset_support(struct amdgpu_device *adev)
+{
+ /* Disable the pipe reset until the CPFW fully support it.*/
+ dev_warn_once(adev->dev, "The CPFW hasn't support pipe reset yet.\n");
+ return false;
+}
+static int mes_v11_0_reset_gfx_pipe_mmio(struct amdgpu_device *adev,
+ u32 me, u32 pipe, u32 queue)
+{
+ uint32_t reset_pipe = 0, clean_pipe = 0;
+ int r;
+
+ if (!mes_v11_0_pipe_reset_support(adev))
+ return -EOPNOTSUPP;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev, 0);
+ mutex_lock(&adev->srbm_mutex);
+ soc21_grbm_select(adev, me, pipe, queue, 0);
+
+ switch (pipe) {
+ case 0:
+ reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
+ PFP_PIPE0_RESET, 1);
+ reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
+ ME_PIPE0_RESET, 1);
+ clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
+ PFP_PIPE0_RESET, 0);
+ clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
+ ME_PIPE0_RESET, 0);
+ break;
+ case 1:
+ reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
+ PFP_PIPE1_RESET, 1);
+ reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
+ ME_PIPE1_RESET, 1);
+ clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
+ PFP_PIPE1_RESET, 0);
+ clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
+ ME_PIPE1_RESET, 0);
+ break;
+ default:
+ break;
+ }
+
+ WREG32_SOC15(GC, 0, regCP_ME_CNTL, reset_pipe);
+ WREG32_SOC15(GC, 0, regCP_ME_CNTL, clean_pipe);
+
+ r = (RREG32(SOC15_REG_OFFSET(GC, 0, regCP_GFX_RS64_INSTR_PNTR1)) << 2) -
+ RS64_FW_UC_START_ADDR_LO;
+ soc21_grbm_select(adev, 0, 0, 0, 0);
+ mutex_unlock(&adev->srbm_mutex);
+ amdgpu_gfx_rlc_exit_safe_mode(adev, 0);
+
+ dev_info(adev->dev, "The gfx pipe reset to the ME firmware start PC: %s\n",
+ r == 0 ? "successfully" : "failed");
+ /* FIXME: Sometimes driver can't cache the ME firmware start PC correctly,
+ * so the pipe reset status relies on the later gfx ring test result.
+ */
+ return 0;
+}
+
+/*
+ * With MEC pipe reset asserted, clear CP_HQD_ACTIVE / CP_HQD_DEQUEUE_REQUEST for
+ * every queue on (me, pipe). HQDs must be torn down while pipe reset stays
+ * asserted; only then clear the pipe reset bit.
+ * Caller must hold adev->srbm_mutex.
+ */
+static void mes_v11_0_clear_hqds_on_mec_pipe(struct amdgpu_device *adev, u32 me,
+ u32 pipe)
+{
+ unsigned int q;
+
+ for (q = 0; q < adev->gfx.mec.num_queue_per_pipe; q++) {
+ soc21_grbm_select(adev, me, pipe, q, 0);
+ /* Start from a clean HQD dequeue state before forcing HQD inactive. */
+ WREG32_SOC15(GC, 0, regCP_HQD_ACTIVE, 0);
+ WREG32_SOC15(GC, 0, regCP_HQD_DEQUEUE_REQUEST, 0);
+ }
+}
+
+static int mes_v11_0_reset_compute_pipe_mmio(struct amdgpu_device *adev,
+ u32 me, u32 pipe, u32 queue)
+{
+ uint32_t reset_val, clean_val;
+ int r;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev, 0);
+ mutex_lock(&adev->srbm_mutex);
+ soc21_grbm_select(adev, me, pipe, queue, 0);
+
+ if (adev->gfx.rs64_enable) {
+ reset_val = RREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL);
+ clean_val = reset_val;
+
+ switch (pipe) {
+ case 0:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE0_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE0_RESET, 0);
+ break;
+ case 1:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE1_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE1_RESET, 0);
+ break;
+ case 2:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE2_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE2_RESET, 0);
+ break;
+ case 3:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE3_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE3_RESET, 0);
+ break;
+ default:
+ break;
+ }
+ WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, reset_val);
+ mes_v11_0_clear_hqds_on_mec_pipe(adev, me, pipe);
+ WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, clean_val);
+ r = (RREG32_SOC15(GC, 0, regCP_MEC_RS64_INSTR_PNTR) << 2) -
+ RS64_FW_UC_START_ADDR_LO;
+ } else {
+ reset_val = RREG32_SOC15(GC, 0, regCP_MEC_CNTL);
+ clean_val = reset_val;
+
+ if (me == 1) {
+ switch (pipe) {
+ case 0:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE0_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE0_RESET, 0);
+ break;
+ case 1:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE1_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE1_RESET, 0);
+ break;
+ case 2:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE2_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE2_RESET, 0);
+ break;
+ case 3:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE3_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE3_RESET, 0);
+ break;
+ default:
+ break;
+ }
+ /* mec1 fw pc: CP_MEC1_INSTR_PNTR */
+ } else {
+ switch (pipe) {
+ case 0:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME2_PIPE0_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME2_PIPE0_RESET, 0);
+ break;
+ case 1:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME2_PIPE1_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME2_PIPE1_RESET, 0);
+ break;
+ case 2:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME2_PIPE2_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME2_PIPE2_RESET, 0);
+ break;
+ case 3:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME2_PIPE3_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME2_PIPE3_RESET, 0);
+ break;
+ default:
+ break;
+ }
+ /* mec2 fw pc: CP:CP_MEC2_INSTR_PNTR */
+ }
+ WREG32_SOC15(GC, 0, regCP_MEC_CNTL, reset_val);
+ mes_v11_0_clear_hqds_on_mec_pipe(adev, me, pipe);
+ WREG32_SOC15(GC, 0, regCP_MEC_CNTL, clean_val);
+ r = RREG32(SOC15_REG_OFFSET(GC, 0, regCP_MEC1_INSTR_PNTR));
+ }
+
+ soc21_grbm_select(adev, 0, 0, 0, 0);
+ mutex_unlock(&adev->srbm_mutex);
+ amdgpu_gfx_rlc_exit_safe_mode(adev, 0);
+
+ dev_dbg(adev->dev, "MEC pipe me%u pipe%u queue%u resets to MEC FW start PC: %s\n",
+ me, pipe, queue, r == 0 ? "successfully" : "failed");
+ /*FIXME:Sometimes driver can't cache the MEC firmware start PC correctly, so the pipe
+ * reset status relies on the compute ring test result.
+ */
+ return 0;
+}
+
+static int mes_v11_0_reset_pipe_mmio(struct amdgpu_mes *mes, uint32_t queue_type,
+ uint32_t me_id, uint32_t pipe_id,
+ uint32_t queue_id, uint32_t vmid)
+{
+ struct amdgpu_device *adev = mes->adev;
+
+ if (queue_type == AMDGPU_RING_TYPE_GFX)
+ return mes_v11_0_reset_gfx_pipe_mmio(adev, me_id, pipe_id, queue_id);
+ else if (queue_type == AMDGPU_RING_TYPE_COMPUTE)
+ return mes_v11_0_reset_compute_pipe_mmio(adev, me_id, pipe_id, queue_id);
+ else
+ return -EOPNOTSUPP;
+}
+
static int mes_v11_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_type,
uint32_t me_id, uint32_t pipe_id,
uint32_t queue_id, uint32_t vmid)
@@ -770,10 +996,16 @@ static int mes_v11_0_reset_hw_queue(struct amdgpu_mes *mes,
{
union MESAPI__RESET mes_reset_queue_pkt;
- if (input->use_mmio)
- return mes_v11_0_reset_queue_mmio(mes, input->queue_type,
- input->me_id, input->pipe_id,
- input->queue_id, input->vmid);
+ if (input->use_mmio) {
+ int r = mes_v11_0_reset_queue_mmio(mes, input->queue_type,
+ input->me_id, input->pipe_id,
+ input->queue_id, input->vmid);
+ if (r)
+ return mes_v11_0_reset_pipe_mmio(mes, input->queue_type,
+ input->me_id, input->pipe_id,
+ input->queue_id, input->vmid);
+ return 0;
+ }
memset(&mes_reset_queue_pkt, 0, sizeof(mes_reset_queue_pkt));
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c
index b6cbc25e1ab4..ce5064200743 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c
@@ -26,7 +26,7 @@
#include "amdgpu.h"
#include "gfx_v12_0.h"
#include "soc15_common.h"
-#include "soc21.h"
+#include "soc24.h"
#include "gc/gc_12_0_0_offset.h"
#include "gc/gc_12_0_0_sh_mask.h"
#include "gc/gc_11_0_0_default.h"
@@ -371,6 +371,8 @@ static int mes_v12_0_remove_hw_queue(struct amdgpu_mes *mes,
mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset;
mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr;
+ mes_remove_queue_pkt.queue_type =
+ convert_to_mes_queue_type(input->queue_type);
if (mes_rev >= 0x5a)
mes_remove_queue_pkt.remove_queue_after_reset = input->remove_queue_after_reset;
@@ -413,6 +415,171 @@ int gfx_v12_0_request_gfx_index_mutex(struct amdgpu_device *adev,
return 0;
}
+static bool mes_v12_0_pipe_reset_support(struct amdgpu_device *adev)
+{
+ /* Disable the pipe reset until the CPFW fully support it.*/
+ dev_warn_once(adev->dev, "The CPFW hasn't support pipe reset yet.\n");
+ return false;
+}
+
+static int mes_v12_0_reset_gfx_pipe_mmio(struct amdgpu_device *adev,
+ u32 me, u32 pipe, u32 queue)
+{
+ uint32_t reset_pipe = 0, clean_pipe = 0;
+ int r;
+
+ if (!mes_v12_0_pipe_reset_support(adev))
+ return -EOPNOTSUPP;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev, 0);
+ mutex_lock(&adev->srbm_mutex);
+ soc24_grbm_select(adev, me, pipe, queue, 0);
+
+ switch (pipe) {
+ case 0:
+ reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
+ PFP_PIPE0_RESET, 1);
+ reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
+ ME_PIPE0_RESET, 1);
+ clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
+ PFP_PIPE0_RESET, 0);
+ clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
+ ME_PIPE0_RESET, 0);
+ break;
+ case 1:
+ reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
+ PFP_PIPE1_RESET, 1);
+ reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL,
+ ME_PIPE1_RESET, 1);
+ clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
+ PFP_PIPE1_RESET, 0);
+ clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL,
+ ME_PIPE1_RESET, 0);
+ break;
+ default:
+ break;
+ }
+
+ WREG32_SOC15(GC, 0, regCP_ME_CNTL, reset_pipe);
+ WREG32_SOC15(GC, 0, regCP_ME_CNTL, clean_pipe);
+
+ r = (RREG32(SOC15_REG_OFFSET(GC, 0, regCP_GFX_RS64_INSTR_PNTR1)) << 2) -
+ RS64_FW_UC_START_ADDR_LO;
+ soc24_grbm_select(adev, 0, 0, 0, 0);
+ mutex_unlock(&adev->srbm_mutex);
+ amdgpu_gfx_rlc_exit_safe_mode(adev, 0);
+
+ dev_info(adev->dev, "The gfx pipe reset: %s\n",
+ r == 0 ? "successfully" : "failed");
+ /* Sometimes the ME start pc counter can't cache correctly, so the
+ * PC check only as a reference and pipe reset result rely on the
+ * later ring test.
+ */
+ return 0;
+}
+
+/*
+ * With MEC pipe reset asserted, clear CP_HQD_ACTIVE / CP_HQD_DEQUEUE_REQUEST for
+ * every queue on (me, pipe). HQDs must be torn down while pipe reset stays
+ * asserted; only then clear the pipe reset bit.
+ * Caller must hold adev->srbm_mutex.
+ */
+static void mes_v12_0_clear_hqds_on_mec_pipe(struct amdgpu_device *adev, u32 me,
+ u32 pipe)
+{
+ unsigned int q;
+
+ for (q = 0; q < adev->gfx.mec.num_queue_per_pipe; q++) {
+ soc24_grbm_select(adev, me, pipe, q, 0);
+ /* Start from a clean HQD dequeue state before forcing HQD inactive. */
+ WREG32_SOC15(GC, 0, regCP_HQD_ACTIVE, 0);
+ WREG32_SOC15(GC, 0, regCP_HQD_DEQUEUE_REQUEST, 0);
+ }
+}
+
+static int mes_v12_0_reset_compute_pipe_mmio(struct amdgpu_device *adev,
+ u32 me, u32 pipe, u32 queue)
+{
+ uint32_t reset_val, clean_val;
+ int r = 0;
+
+ amdgpu_gfx_rlc_enter_safe_mode(adev, 0);
+ mutex_lock(&adev->srbm_mutex);
+ soc24_grbm_select(adev, me, pipe, queue, 0);
+ if (adev->gfx.rs64_enable) {
+ reset_val = RREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL);
+ clean_val = reset_val;
+
+ switch (pipe) {
+ case 0:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE0_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE0_RESET, 0);
+ break;
+ case 1:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE1_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE1_RESET, 0);
+ break;
+ case 2:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE2_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE2_RESET, 0);
+ break;
+ case 3:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE3_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL,
+ MEC_PIPE3_RESET, 0);
+ break;
+ default:
+ break;
+ }
+ WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, reset_val);
+ mes_v12_0_clear_hqds_on_mec_pipe(adev, me, pipe);
+ soc24_grbm_select(adev, me, pipe, queue, 0);
+ WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, clean_val);
+ r = (RREG32_SOC15(GC, 0, regCP_MEC_RS64_INSTR_PNTR) << 2) -
+ RS64_FW_UC_START_ADDR_LO;
+ } else {
+ reset_val = RREG32_SOC15(GC, 0, regCP_MEC_CNTL);
+ clean_val = reset_val;
+
+ switch (pipe) {
+ case 0:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE0_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE0_RESET, 0);
+ break;
+ case 1:
+ reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE1_RESET, 1);
+ clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL,
+ MEC_ME1_PIPE1_RESET, 0);
+ break;
+ default:
+ break;
+ }
+
+ WREG32_SOC15(GC, 0, regCP_MEC_CNTL, reset_val);
+ mes_v12_0_clear_hqds_on_mec_pipe(adev, me, pipe);
+ soc24_grbm_select(adev, me, pipe, queue, 0);
+ WREG32_SOC15(GC, 0, regCP_MEC_CNTL, clean_val);
+ }
+
+ soc24_grbm_select(adev, 0, 0, 0, 0);
+ mutex_unlock(&adev->srbm_mutex);
+ amdgpu_gfx_rlc_exit_safe_mode(adev, 0);
+
+ dev_dbg(adev->dev, "MEC pipe me%u pipe%u queue%u resets to MEC FW start PC: %s\n",
+ me, pipe, queue, r == 0 ? "successfully" : "failed");
+ return 0;
+}
+
static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_type,
uint32_t me_id, uint32_t pipe_id,
uint32_t queue_id, uint32_t vmid)
@@ -442,7 +609,7 @@ static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_typ
mutex_unlock(&adev->gfx.reset_sem_mutex);
mutex_lock(&adev->srbm_mutex);
- soc21_grbm_select(adev, me_id, pipe_id, queue_id, 0);
+ soc24_grbm_select(adev, me_id, pipe_id, queue_id, 0);
/* wait till dequeue take effects */
for (i = 0; i < adev->usec_timeout; i++) {
if (!(RREG32_SOC15(GC, 0, regCP_GFX_HQD_ACTIVE) & 1))
@@ -454,13 +621,13 @@ static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_typ
r = -ETIMEDOUT;
}
- soc21_grbm_select(adev, 0, 0, 0, 0);
+ soc24_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
} else if (queue_type == AMDGPU_RING_TYPE_COMPUTE) {
dev_info(adev->dev, "reset compute queue (%d:%d:%d)\n",
me_id, pipe_id, queue_id);
mutex_lock(&adev->srbm_mutex);
- soc21_grbm_select(adev, me_id, pipe_id, queue_id, 0);
+ soc24_grbm_select(adev, me_id, pipe_id, queue_id, 0);
WREG32_SOC15(GC, 0, regCP_HQD_DEQUEUE_REQUEST, 0x2);
WREG32_SOC15(GC, 0, regSPI_COMPUTE_QUEUE_RESET, 0x1);
@@ -474,7 +641,7 @@ static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_typ
dev_err(adev->dev, "failed to wait on hqd deactivate\n");
r = -ETIMEDOUT;
}
- soc21_grbm_select(adev, 0, 0, 0, 0);
+ soc24_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
} else if (queue_type == AMDGPU_RING_TYPE_SDMA) {
dev_info(adev->dev, "reset sdma queue (%d:%d:%d)\n",
@@ -507,6 +674,20 @@ static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_typ
return r;
}
+static int mes_v12_0_reset_pipe_mmio(struct amdgpu_mes *mes, uint32_t queue_type,
+ uint32_t me_id, uint32_t pipe_id,
+ uint32_t queue_id, uint32_t vmid)
+{
+ struct amdgpu_device *adev = mes->adev;
+
+ if (queue_type == AMDGPU_RING_TYPE_GFX)
+ return mes_v12_0_reset_gfx_pipe_mmio(adev, me_id, pipe_id, queue_id);
+ else if (queue_type == AMDGPU_RING_TYPE_COMPUTE)
+ return mes_v12_0_reset_compute_pipe_mmio(adev, me_id, pipe_id, queue_id);
+ else
+ return -EOPNOTSUPP;
+}
+
static int mes_v12_0_map_legacy_queue(struct amdgpu_mes *mes,
struct mes_map_legacy_queue_input *input)
{
@@ -528,10 +709,15 @@ static int mes_v12_0_map_legacy_queue(struct amdgpu_mes *mes,
convert_to_mes_queue_type(input->queue_type);
mes_add_queue_pkt.map_legacy_kq = 1;
- if (mes->adev->enable_uni_mes)
- pipe = AMDGPU_MES_KIQ_PIPE;
- else
+ if (mes->adev->enable_uni_mes) {
+ /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */
+ if (input->queue_type == AMDGPU_RING_TYPE_MES)
+ pipe = AMDGPU_MES_KIQ_PIPE;
+ else
+ pipe = AMDGPU_MES_SCHED_PIPE;
+ } else {
pipe = AMDGPU_MES_SCHED_PIPE;
+ }
return mes_v12_0_submit_pkt_and_poll_completion(mes, pipe,
&mes_add_queue_pkt, sizeof(mes_add_queue_pkt),
@@ -565,12 +751,28 @@ static int mes_v12_0_unmap_legacy_queue(struct amdgpu_mes *mes,
mes_remove_queue_pkt.unmap_legacy_queue = 1;
mes_remove_queue_pkt.queue_type =
convert_to_mes_queue_type(input->queue_type);
+ /*
+ * A reset-time unmap: the queue was already reset via MMIO while
+ * gangs are suspended and it is on the MES hung/fail list. Tell
+ * MES to just drop its internal state for it. Without this flag
+ * MES asks CP to unmap the already-reset (still wedged) queue
+ * again, which times out and forces a GPU reset.
+ */
+ if (input->action == RESET_QUEUES &&
+ (mes->sched_version & AMDGPU_MES_VERSION_MASK) >= 0x5a)
+ mes_remove_queue_pkt.remove_queue_after_reset = 1;
+
}
- if (mes->adev->enable_uni_mes)
- pipe = AMDGPU_MES_KIQ_PIPE;
- else
+ if (mes->adev->enable_uni_mes) {
+ /* Keep scheduler queue on KIQ pipe; unmap all other kernel queues on sched pipe. */
+ if (input->queue_type == AMDGPU_RING_TYPE_MES)
+ pipe = AMDGPU_MES_KIQ_PIPE;
+ else
+ pipe = AMDGPU_MES_SCHED_PIPE;
+ } else {
pipe = AMDGPU_MES_SCHED_PIPE;
+ }
return mes_v12_0_submit_pkt_and_poll_completion(mes, pipe,
&mes_remove_queue_pkt, sizeof(mes_remove_queue_pkt),
@@ -888,10 +1090,16 @@ static int mes_v12_0_reset_hw_queue(struct amdgpu_mes *mes,
union MESAPI__RESET mes_reset_queue_pkt;
int pipe;
- if (input->use_mmio)
- return mes_v12_0_reset_queue_mmio(mes, input->queue_type,
- input->me_id, input->pipe_id,
- input->queue_id, input->vmid);
+ if (input->use_mmio) {
+ int r = mes_v12_0_reset_queue_mmio(mes, input->queue_type,
+ input->me_id, input->pipe_id,
+ input->queue_id, input->vmid);
+ if (r)
+ return mes_v12_0_reset_pipe_mmio(mes, input->queue_type,
+ input->me_id, input->pipe_id,
+ input->queue_id, input->vmid);
+ return 0;
+ }
memset(&mes_reset_queue_pkt, 0, sizeof(mes_reset_queue_pkt));
@@ -915,10 +1123,7 @@ static int mes_v12_0_reset_hw_queue(struct amdgpu_mes *mes,
mes_reset_queue_pkt.doorbell_offset = input->doorbell_offset;
}
- if (input->is_kq)
- pipe = AMDGPU_MES_KIQ_PIPE;
- else
- pipe = AMDGPU_MES_SCHED_PIPE;
+ pipe = AMDGPU_MES_SCHED_PIPE;
return mes_v12_0_submit_pkt_and_poll_completion(mes, pipe,
&mes_reset_queue_pkt, sizeof(mes_reset_queue_pkt),
@@ -1094,7 +1299,7 @@ static void mes_v12_0_enable(struct amdgpu_device *adev, bool enable)
if (enable) {
mutex_lock(&adev->srbm_mutex);
for (pipe = 0; pipe < AMDGPU_MAX_MES_PIPES; pipe++) {
- soc21_grbm_select(adev, 3, pipe, 0, 0);
+ soc24_grbm_select(adev, 3, pipe, 0, 0);
if (amdgpu_mes_log_enable) {
u32 log_size = AMDGPU_MES_LOG_BUFFER_SIZE + AMDGPU_MES_MSCRATCH_SIZE;
/* In case uni mes is not enabled, only program for pipe 0 */
@@ -1133,7 +1338,7 @@ static void mes_v12_0_enable(struct amdgpu_device *adev, bool enable)
WREG32_SOC15(GC, 0, regCP_MES_CNTL, data);
}
- soc21_grbm_select(adev, 0, 0, 0, 0);
+ soc24_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
if (amdgpu_emu_mode)
@@ -1165,7 +1370,7 @@ static void mes_v12_0_set_ucode_start_addr(struct amdgpu_device *adev)
mutex_lock(&adev->srbm_mutex);
for (pipe = 0; pipe < AMDGPU_MAX_MES_PIPES; pipe++) {
/* me=3, queue=0 */
- soc21_grbm_select(adev, 3, pipe, 0, 0);
+ soc24_grbm_select(adev, 3, pipe, 0, 0);
/* set ucode start address */
ucode_addr = adev->mes.uc_start_addr[pipe] >> 2;
@@ -1174,7 +1379,7 @@ static void mes_v12_0_set_ucode_start_addr(struct amdgpu_device *adev)
WREG32_SOC15(GC, 0, regCP_MES_PRGRM_CNTR_START_HI,
upper_32_bits(ucode_addr));
- soc21_grbm_select(adev, 0, 0, 0, 0);
+ soc24_grbm_select(adev, 0, 0, 0, 0);
}
mutex_unlock(&adev->srbm_mutex);
}
@@ -1203,7 +1408,7 @@ static int mes_v12_0_load_microcode(struct amdgpu_device *adev,
mutex_lock(&adev->srbm_mutex);
/* me=3, pipe=0, queue=0 */
- soc21_grbm_select(adev, 3, pipe, 0, 0);
+ soc24_grbm_select(adev, 3, pipe, 0, 0);
WREG32_SOC15(GC, 0, regCP_MES_IC_BASE_CNTL, 0);
@@ -1238,7 +1443,7 @@ static int mes_v12_0_load_microcode(struct amdgpu_device *adev,
WREG32_SOC15(GC, 0, regCP_MES_IC_OP_CNTL, data);
}
- soc21_grbm_select(adev, 0, 0, 0, 0);
+ soc24_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
return 0;
@@ -1385,7 +1590,7 @@ static void mes_v12_0_queue_init_register(struct amdgpu_ring *ring)
uint32_t data = 0;
mutex_lock(&adev->srbm_mutex);
- soc21_grbm_select(adev, 3, ring->pipe, 0, 0);
+ soc24_grbm_select(adev, 3, ring->pipe, 0, 0);
/* set CP_HQD_VMID.VMID = 0. */
data = RREG32_SOC15(GC, 0, regCP_HQD_VMID);
@@ -1436,7 +1641,7 @@ static void mes_v12_0_queue_init_register(struct amdgpu_ring *ring)
/* set CP_HQD_ACTIVE.ACTIVE=1 */
WREG32_SOC15(GC, 0, regCP_HQD_ACTIVE, mqd->cp_hqd_active);
- soc21_grbm_select(adev, 0, 0, 0, 0);
+ soc24_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
}
@@ -1502,14 +1707,14 @@ static int mes_v12_0_queue_init(struct amdgpu_device *adev,
((pipe == AMDGPU_MES_KIQ_PIPE) && !adev->mes.kiq_version)) {
/* get MES scheduler/KIQ versions */
mutex_lock(&adev->srbm_mutex);
- soc21_grbm_select(adev, 3, pipe, 0, 0);
+ soc24_grbm_select(adev, 3, pipe, 0, 0);
if (pipe == AMDGPU_MES_SCHED_PIPE)
adev->mes.sched_version = RREG32_SOC15(GC, 0, regCP_MES_GP3_LO);
else if (pipe == AMDGPU_MES_KIQ_PIPE && adev->enable_mes_kiq)
adev->mes.kiq_version = RREG32_SOC15(GC, 0, regCP_MES_GP3_LO);
- soc21_grbm_select(adev, 0, 0, 0, 0);
+ soc24_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
}
@@ -1697,7 +1902,7 @@ static void mes_v12_0_kiq_dequeue_sched(struct amdgpu_device *adev)
int i;
mutex_lock(&adev->srbm_mutex);
- soc21_grbm_select(adev, 3, AMDGPU_MES_SCHED_PIPE, 0, 0);
+ soc24_grbm_select(adev, 3, AMDGPU_MES_SCHED_PIPE, 0, 0);
/* disable the queue if it's active */
if (RREG32_SOC15(GC, 0, regCP_HQD_ACTIVE) & 1) {
@@ -1721,7 +1926,7 @@ static void mes_v12_0_kiq_dequeue_sched(struct amdgpu_device *adev)
WREG32_SOC15(GC, 0, regCP_HQD_PQ_WPTR_HI, 0);
WREG32_SOC15(GC, 0, regCP_HQD_PQ_RPTR, 0);
- soc21_grbm_select(adev, 0, 0, 0, 0);
+ soc24_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
adev->mes.ring[0].sched.ready = false;
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c
index e13535d94c51..f7d5879c6e44 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c
@@ -362,6 +362,8 @@ static int mes_v12_1_remove_hw_queue(struct amdgpu_mes *mes,
mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset;
mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr;
+ mes_remove_queue_pkt.queue_type =
+ convert_to_mes_queue_type(input->queue_type);
return mes_v12_1_submit_pkt_and_poll_completion(mes,
xcc_id, AMDGPU_MES_SCHED_PIPE,
@@ -417,10 +419,15 @@ static int mes_v12_1_map_legacy_queue(struct amdgpu_mes *mes,
convert_to_mes_queue_type(input->queue_type);
mes_add_queue_pkt.map_legacy_kq = 1;
- if (mes->adev->enable_uni_mes)
- pipe = AMDGPU_MES_KIQ_PIPE;
- else
+ if (mes->adev->enable_uni_mes) {
+ /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */
+ if (input->queue_type == AMDGPU_RING_TYPE_MES)
+ pipe = AMDGPU_MES_KIQ_PIPE;
+ else
+ pipe = AMDGPU_MES_SCHED_PIPE;
+ } else {
pipe = AMDGPU_MES_SCHED_PIPE;
+ }
return mes_v12_1_submit_pkt_and_poll_completion(mes,
input->xcc_id, pipe,
@@ -457,10 +464,15 @@ static int mes_v12_1_unmap_legacy_queue(struct amdgpu_mes *mes,
convert_to_mes_queue_type(input->queue_type);
}
- if (mes->adev->enable_uni_mes)
- pipe = AMDGPU_MES_KIQ_PIPE;
- else
+ if (mes->adev->enable_uni_mes) {
+ /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */
+ if (input->queue_type == AMDGPU_RING_TYPE_MES)
+ pipe = AMDGPU_MES_KIQ_PIPE;
+ else
+ pipe = AMDGPU_MES_SCHED_PIPE;
+ } else {
pipe = AMDGPU_MES_SCHED_PIPE;
+ }
return mes_v12_1_submit_pkt_and_poll_completion(mes,
input->xcc_id, pipe,
@@ -2262,6 +2274,7 @@ static int mes_v12_1_test_queue(struct amdgpu_device *adev, int xcc_id,
remove_queue.xcc_id = xcc_id;
remove_queue.doorbell_offset = doorbell_idx;
remove_queue.gang_context_addr = add_queue.gang_context_addr;
+ remove_queue.queue_type = queue_type;
r = mes_v12_1_remove_hw_queue(&adev->mes, &remove_queue);
error:
diff --git a/drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c b/drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c
index cc688ae79e84..47d07cd25fc4 100644
--- a/drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c
+++ b/drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c
@@ -29,7 +29,6 @@
#include "soc15_common.h"
#include "soc15.h"
-#include "amdgpu_ras.h"
#include "amdgpu_psp.h"
#define regVM_L2_CNTL3_DEFAULT 0x80100007
@@ -636,236 +635,8 @@ const struct amdgpu_mmhub_funcs mmhub_v1_8_funcs = {
.get_clockgating = mmhub_v1_8_get_clockgating,
};
-static const struct amdgpu_ras_err_status_reg_entry mmhub_v1_8_ce_reg_list[] = {
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA0_CE_ERR_STATUS_LO, regMMEA0_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA0"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA1_CE_ERR_STATUS_LO, regMMEA1_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA1"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA2_CE_ERR_STATUS_LO, regMMEA2_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA2"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA3_CE_ERR_STATUS_LO, regMMEA3_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA3"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA4_CE_ERR_STATUS_LO, regMMEA4_CE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA4"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMM_CANE_CE_ERR_STATUS_LO, regMM_CANE_CE_ERR_STATUS_HI),
- 1, 0, "MM_CANE"},
-};
-
-static const struct amdgpu_ras_err_status_reg_entry mmhub_v1_8_ue_reg_list[] = {
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA0_UE_ERR_STATUS_LO, regMMEA0_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA0"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA1_UE_ERR_STATUS_LO, regMMEA1_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA1"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA2_UE_ERR_STATUS_LO, regMMEA2_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA2"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA3_UE_ERR_STATUS_LO, regMMEA3_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA3"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA4_UE_ERR_STATUS_LO, regMMEA4_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA4"},
- {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMM_CANE_UE_ERR_STATUS_LO, regMM_CANE_UE_ERR_STATUS_HI),
- 1, 0, "MM_CANE"},
-};
-
-static const struct amdgpu_ras_memory_id_entry mmhub_v1_8_ras_memory_list[] = {
- {AMDGPU_MMHUB_WGMI_PAGEMEM, "MMEA_WGMI_PAGEMEM"},
- {AMDGPU_MMHUB_RGMI_PAGEMEM, "MMEA_RGMI_PAGEMEM"},
- {AMDGPU_MMHUB_WDRAM_PAGEMEM, "MMEA_WDRAM_PAGEMEM"},
- {AMDGPU_MMHUB_RDRAM_PAGEMEM, "MMEA_RDRAM_PAGEMEM"},
- {AMDGPU_MMHUB_WIO_CMDMEM, "MMEA_WIO_CMDMEM"},
- {AMDGPU_MMHUB_RIO_CMDMEM, "MMEA_RIO_CMDMEM"},
- {AMDGPU_MMHUB_WGMI_CMDMEM, "MMEA_WGMI_CMDMEM"},
- {AMDGPU_MMHUB_RGMI_CMDMEM, "MMEA_RGMI_CMDMEM"},
- {AMDGPU_MMHUB_WDRAM_CMDMEM, "MMEA_WDRAM_CMDMEM"},
- {AMDGPU_MMHUB_RDRAM_CMDMEM, "MMEA_RDRAM_CMDMEM"},
- {AMDGPU_MMHUB_MAM_DMEM0, "MMEA_MAM_DMEM0"},
- {AMDGPU_MMHUB_MAM_DMEM1, "MMEA_MAM_DMEM1"},
- {AMDGPU_MMHUB_MAM_DMEM2, "MMEA_MAM_DMEM2"},
- {AMDGPU_MMHUB_MAM_DMEM3, "MMEA_MAM_DMEM3"},
- {AMDGPU_MMHUB_WRET_TAGMEM, "MMEA_WRET_TAGMEM"},
- {AMDGPU_MMHUB_RRET_TAGMEM, "MMEA_RRET_TAGMEM"},
- {AMDGPU_MMHUB_WIO_DATAMEM, "MMEA_WIO_DATAMEM"},
- {AMDGPU_MMHUB_WGMI_DATAMEM, "MMEA_WGMI_DATAMEM"},
- {AMDGPU_MMHUB_WDRAM_DATAMEM, "MMEA_WDRAM_DATAMEM"},
-};
-
-static void mmhub_v1_8_inst_query_ras_error_count(struct amdgpu_device *adev,
- uint32_t mmhub_inst,
- void *ras_err_status)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_err_status;
- unsigned long ue_count = 0, ce_count = 0;
-
- /* NOTE: mmhub is converted by aid_mask and the range is 0-3,
- * which can be used as die ID directly */
- struct amdgpu_smuio_mcm_config_info mcm_info = {
- .socket_id = adev->smuio.funcs->get_socket_id(adev),
- .die_id = mmhub_inst,
- };
-
- amdgpu_ras_inst_query_ras_error_count(adev,
- mmhub_v1_8_ce_reg_list,
- ARRAY_SIZE(mmhub_v1_8_ce_reg_list),
- mmhub_v1_8_ras_memory_list,
- ARRAY_SIZE(mmhub_v1_8_ras_memory_list),
- mmhub_inst,
- AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE,
- &ce_count);
- amdgpu_ras_inst_query_ras_error_count(adev,
- mmhub_v1_8_ue_reg_list,
- ARRAY_SIZE(mmhub_v1_8_ue_reg_list),
- mmhub_v1_8_ras_memory_list,
- ARRAY_SIZE(mmhub_v1_8_ras_memory_list),
- mmhub_inst,
- AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE,
- &ue_count);
-
- amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, ce_count);
- amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, ue_count);
-}
-
-static void mmhub_v1_8_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_err_status)
-{
- uint32_t inst_mask;
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__MMHUB)) {
- dev_warn(adev->dev, "MMHUB RAS is not supported\n");
- return;
- }
-
- inst_mask = adev->aid_mask;
- for_each_inst(i, inst_mask)
- mmhub_v1_8_inst_query_ras_error_count(adev, i, ras_err_status);
-}
-
-static void mmhub_v1_8_inst_reset_ras_error_count(struct amdgpu_device *adev,
- uint32_t mmhub_inst)
-{
- amdgpu_ras_inst_reset_ras_error_count(adev,
- mmhub_v1_8_ce_reg_list,
- ARRAY_SIZE(mmhub_v1_8_ce_reg_list),
- mmhub_inst);
- amdgpu_ras_inst_reset_ras_error_count(adev,
- mmhub_v1_8_ue_reg_list,
- ARRAY_SIZE(mmhub_v1_8_ue_reg_list),
- mmhub_inst);
-}
-
-static void mmhub_v1_8_reset_ras_error_count(struct amdgpu_device *adev)
-{
- uint32_t inst_mask;
- uint32_t i;
-
- if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__MMHUB)) {
- dev_warn(adev->dev, "MMHUB RAS is not supported\n");
- return;
- }
-
- inst_mask = adev->aid_mask;
- for_each_inst(i, inst_mask)
- mmhub_v1_8_inst_reset_ras_error_count(adev, i);
-}
-
-static const struct amdgpu_ras_block_hw_ops mmhub_v1_8_ras_hw_ops = {
- .query_ras_error_count = mmhub_v1_8_query_ras_error_count,
- .reset_ras_error_count = mmhub_v1_8_reset_ras_error_count,
-};
-
-static int mmhub_v1_8_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct aca_bank_info info;
- u64 misc0;
- int ret;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- misc0 = bank->regs[ACA_REG_IDX_MISC0];
- switch (type) {
- case ACA_SMU_TYPE_UE:
- bank->aca_err_type = ACA_ERROR_TYPE_UE;
- ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE,
- 1ULL);
- break;
- case ACA_SMU_TYPE_CE:
- bank->aca_err_type = ACA_ERROR_TYPE_CE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type,
- ACA_REG__MISC0__ERRCNT(misc0));
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
-/* reference to smu driver if header file */
-static int mmhub_v1_8_err_codes[] = {
- 0, 1, 2, 3, 4, /* CODE_DAGB0 - 4 */
- 5, 6, 7, 8, 9, /* CODE_EA0 - 4 */
- 10, /* CODE_UTCL2_ROUTER */
- 11, /* CODE_VML2 */
- 12, /* CODE_VML2_WALKER */
- 13, /* CODE_MMCANE */
-};
-
-static bool mmhub_v1_8_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- u32 instlo;
-
- instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]);
- instlo &= GENMASK(31, 1);
-
- if (instlo != mmSMNAID_AID0_MCA_SMU)
- return false;
-
- if (aca_bank_check_error_codes(handle->adev, bank,
- mmhub_v1_8_err_codes,
- ARRAY_SIZE(mmhub_v1_8_err_codes)))
- return false;
-
- return true;
-}
-
-static const struct aca_bank_ops mmhub_v1_8_aca_bank_ops = {
- .aca_bank_parser = mmhub_v1_8_aca_bank_parser,
- .aca_bank_is_valid = mmhub_v1_8_aca_bank_is_valid,
-};
-
-static const struct aca_info mmhub_v1_8_aca_info = {
- .hwip = ACA_HWIP_TYPE_SMU,
- .mask = ACA_ERROR_UE_MASK,
- .bank_ops = &mmhub_v1_8_aca_bank_ops,
-};
-
-static int mmhub_v1_8_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
-{
- int r;
-
- r = amdgpu_ras_block_late_init(adev, ras_block);
- if (r)
- return r;
-
- r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__MMHUB,
- &mmhub_v1_8_aca_info, NULL);
- if (r)
- goto late_fini;
-
- return 0;
-
-late_fini:
- amdgpu_ras_block_late_fini(adev, ras_block);
-
- return r;
-}
-
struct amdgpu_mmhub_ras mmhub_v1_8_ras = {
.ras_block = {
- .hw_ops = &mmhub_v1_8_ras_hw_ops,
- .ras_late_init = mmhub_v1_8_ras_late_init,
+ .hw_ops = NULL,
},
};
diff --git a/drivers/gpu/drm/amd/amdgpu/nv.c b/drivers/gpu/drm/amd/amdgpu/nv.c
index 72edf5326b05..77557ee3ca16 100644
--- a/drivers/gpu/drm/amd/amdgpu/nv.c
+++ b/drivers/gpu/drm/amd/amdgpu/nv.c
@@ -507,11 +507,6 @@ void nv_set_virt_ops(struct amdgpu_device *adev)
adev->virt.ops = &xgpu_nv_virt_ops;
}
-static bool nv_need_full_reset(struct amdgpu_device *adev)
-{
- return true;
-}
-
static bool nv_need_reset_on_init(struct amdgpu_device *adev)
{
u32 sol_reg;
@@ -595,7 +590,6 @@ static const struct amdgpu_asic_funcs nv_asic_funcs = {
.set_vce_clocks = &nv_set_vce_clocks,
.get_config_memsize = &nv_get_config_memsize,
.init_doorbell_index = &nv_init_doorbell_index,
- .need_full_reset = &nv_need_full_reset,
.need_reset_on_init = &nv_need_reset_on_init,
.get_pcie_replay_count = &amdgpu_nbio_get_pcie_replay_count,
.supports_baco = &amdgpu_dpm_is_baco_supported,
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c
index 3fde9be74690..c2d098cd72ce 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c
@@ -1237,65 +1237,6 @@ static int sdma_v3_0_wait_for_idle(struct amdgpu_ip_block *ip_block)
return -ETIMEDOUT;
}
-static bool sdma_v3_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 srbm_soft_reset = 0;
- u32 tmp = RREG32(mmSRBM_STATUS2);
-
- if ((tmp & SRBM_STATUS2__SDMA_BUSY_MASK) ||
- (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK)) {
- srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA_MASK;
- srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA1_MASK;
- }
-
- if (srbm_soft_reset) {
- adev->sdma.srbm_soft_reset = srbm_soft_reset;
- return true;
- } else {
- adev->sdma.srbm_soft_reset = 0;
- return false;
- }
-}
-
-static int sdma_v3_0_pre_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 srbm_soft_reset = 0;
-
- if (!adev->sdma.srbm_soft_reset)
- return 0;
-
- srbm_soft_reset = adev->sdma.srbm_soft_reset;
-
- if (REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA) ||
- REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA1)) {
- sdma_v3_0_ctx_switch_enable(adev, false);
- sdma_v3_0_enable(adev, false);
- }
-
- return 0;
-}
-
-static int sdma_v3_0_post_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 srbm_soft_reset = 0;
-
- if (!adev->sdma.srbm_soft_reset)
- return 0;
-
- srbm_soft_reset = adev->sdma.srbm_soft_reset;
-
- if (REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA) ||
- REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA1)) {
- sdma_v3_0_gfx_resume(adev);
- sdma_v3_0_rlc_resume(adev);
- }
-
- return 0;
-}
-
static int sdma_v3_0_soft_reset(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -1552,9 +1493,6 @@ static const struct amd_ip_funcs sdma_v3_0_ip_funcs = {
.resume = sdma_v3_0_resume,
.is_idle = sdma_v3_0_is_idle,
.wait_for_idle = sdma_v3_0_wait_for_idle,
- .check_soft_reset = sdma_v3_0_check_soft_reset,
- .pre_soft_reset = sdma_v3_0_pre_soft_reset,
- .post_soft_reset = sdma_v3_0_post_soft_reset,
.soft_reset = sdma_v3_0_soft_reset,
.set_clockgating_state = sdma_v3_0_set_clockgating_state,
.set_powergating_state = sdma_v3_0_set_powergating_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
index 8652928861ad..484f1a6b5fbc 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
@@ -95,8 +95,6 @@ static const struct amdgpu_hwip_reg_entry sdma_reg_list_4_4_2[] = {
SOC15_REG_ENTRY_STR(GC, 0, regSDMA_VM_CNTL)
};
-#define mmSMNAID_AID0_MCA_SMU 0x03b30400
-
#define WREG32_SDMA(instance, offset, value) \
WREG32(sdma_v4_4_2_get_reg_offset(adev, (instance), (offset)), value)
#define RREG32_SDMA(instance, offset) \
@@ -1359,6 +1357,19 @@ static int sdma_v4_4_2_early_init(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int r;
+ switch (amdgpu_user_queue) {
+ case -1:
+ case 0:
+ default:
+ adev->sdma.no_user_submission = false;
+ adev->sdma.disable_uq = true;
+ break;
+ case 2:
+ adev->sdma.no_user_submission = true;
+ adev->sdma.disable_uq = true;
+ break;
+ }
+
r = sdma_v4_4_2_init_microcode(adev);
if (r)
return r;
@@ -1478,6 +1489,7 @@ static int sdma_v4_4_2_sw_init(struct amdgpu_ip_block *ip_block)
/* doorbell size is 2 dwords, get DWORD offset */
ring->doorbell_index = adev->doorbell_index.sdma_engine[i] << 1;
ring->vm_hub = AMDGPU_MMHUB0(aid_id);
+ ring->no_user_submission = adev->sdma.no_user_submission;
sprintf(ring->name, "sdma%d.%d", aid_id,
i % adev->sdma.num_inst_per_aid);
@@ -2404,187 +2416,9 @@ struct amdgpu_xcp_ip_funcs sdma_v4_4_2_xcp_funcs = {
.resume = &sdma_v4_4_2_xcp_resume
};
-static const struct amdgpu_ras_err_status_reg_entry sdma_v4_2_2_ue_reg_list[] = {
- {AMDGPU_RAS_REG_ENTRY(SDMA0, 0, regSDMA_UE_ERR_STATUS_LO, regSDMA_UE_ERR_STATUS_HI),
- 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SDMA"},
-};
-
-static const struct amdgpu_ras_memory_id_entry sdma_v4_4_2_ras_memory_list[] = {
- {AMDGPU_SDMA_MBANK_DATA_BUF0, "SDMA_MBANK_DATA_BUF0"},
- {AMDGPU_SDMA_MBANK_DATA_BUF1, "SDMA_MBANK_DATA_BUF1"},
- {AMDGPU_SDMA_MBANK_DATA_BUF2, "SDMA_MBANK_DATA_BUF2"},
- {AMDGPU_SDMA_MBANK_DATA_BUF3, "SDMA_MBANK_DATA_BUF3"},
- {AMDGPU_SDMA_MBANK_DATA_BUF4, "SDMA_MBANK_DATA_BUF4"},
- {AMDGPU_SDMA_MBANK_DATA_BUF5, "SDMA_MBANK_DATA_BUF5"},
- {AMDGPU_SDMA_MBANK_DATA_BUF6, "SDMA_MBANK_DATA_BUF6"},
- {AMDGPU_SDMA_MBANK_DATA_BUF7, "SDMA_MBANK_DATA_BUF7"},
- {AMDGPU_SDMA_MBANK_DATA_BUF8, "SDMA_MBANK_DATA_BUF8"},
- {AMDGPU_SDMA_MBANK_DATA_BUF9, "SDMA_MBANK_DATA_BUF9"},
- {AMDGPU_SDMA_MBANK_DATA_BUF10, "SDMA_MBANK_DATA_BUF10"},
- {AMDGPU_SDMA_MBANK_DATA_BUF11, "SDMA_MBANK_DATA_BUF11"},
- {AMDGPU_SDMA_MBANK_DATA_BUF12, "SDMA_MBANK_DATA_BUF12"},
- {AMDGPU_SDMA_MBANK_DATA_BUF13, "SDMA_MBANK_DATA_BUF13"},
- {AMDGPU_SDMA_MBANK_DATA_BUF14, "SDMA_MBANK_DATA_BUF14"},
- {AMDGPU_SDMA_MBANK_DATA_BUF15, "SDMA_MBANK_DATA_BUF15"},
- {AMDGPU_SDMA_UCODE_BUF, "SDMA_UCODE_BUF"},
- {AMDGPU_SDMA_RB_CMD_BUF, "SDMA_RB_CMD_BUF"},
- {AMDGPU_SDMA_IB_CMD_BUF, "SDMA_IB_CMD_BUF"},
- {AMDGPU_SDMA_UTCL1_RD_FIFO, "SDMA_UTCL1_RD_FIFO"},
- {AMDGPU_SDMA_UTCL1_RDBST_FIFO, "SDMA_UTCL1_RDBST_FIFO"},
- {AMDGPU_SDMA_UTCL1_WR_FIFO, "SDMA_UTCL1_WR_FIFO"},
- {AMDGPU_SDMA_DATA_LUT_FIFO, "SDMA_DATA_LUT_FIFO"},
- {AMDGPU_SDMA_SPLIT_DAT_BUF, "SDMA_SPLIT_DAT_BUF"},
-};
-
-static void sdma_v4_4_2_inst_query_ras_error_count(struct amdgpu_device *adev,
- uint32_t sdma_inst,
- void *ras_err_status)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_err_status;
- uint32_t sdma_dev_inst = GET_INST(SDMA0, sdma_inst);
- unsigned long ue_count = 0;
- struct amdgpu_smuio_mcm_config_info mcm_info = {
- .socket_id = adev->smuio.funcs->get_socket_id(adev),
- .die_id = adev->sdma.instance[sdma_inst].aid_id,
- };
-
- /* sdma v4_4_2 doesn't support query ce counts */
- amdgpu_ras_inst_query_ras_error_count(adev,
- sdma_v4_2_2_ue_reg_list,
- ARRAY_SIZE(sdma_v4_2_2_ue_reg_list),
- sdma_v4_4_2_ras_memory_list,
- ARRAY_SIZE(sdma_v4_4_2_ras_memory_list),
- sdma_dev_inst,
- AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE,
- &ue_count);
-
- amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, ue_count);
-}
-
-static void sdma_v4_4_2_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_err_status)
-{
- uint32_t inst_mask;
- int i = 0;
-
- inst_mask = GENMASK(adev->sdma.num_instances - 1, 0);
- if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__SDMA)) {
- for_each_inst(i, inst_mask)
- sdma_v4_4_2_inst_query_ras_error_count(adev, i, ras_err_status);
- } else {
- dev_warn(adev->dev, "SDMA RAS is not supported\n");
- }
-}
-
-static void sdma_v4_4_2_inst_reset_ras_error_count(struct amdgpu_device *adev,
- uint32_t sdma_inst)
-{
- uint32_t sdma_dev_inst = GET_INST(SDMA0, sdma_inst);
-
- amdgpu_ras_inst_reset_ras_error_count(adev,
- sdma_v4_2_2_ue_reg_list,
- ARRAY_SIZE(sdma_v4_2_2_ue_reg_list),
- sdma_dev_inst);
-}
-
-static void sdma_v4_4_2_reset_ras_error_count(struct amdgpu_device *adev)
-{
- uint32_t inst_mask;
- int i = 0;
-
- inst_mask = GENMASK(adev->sdma.num_instances - 1, 0);
- if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__SDMA)) {
- for_each_inst(i, inst_mask)
- sdma_v4_4_2_inst_reset_ras_error_count(adev, i);
- } else {
- dev_warn(adev->dev, "SDMA RAS is not supported\n");
- }
-}
-
-static const struct amdgpu_ras_block_hw_ops sdma_v4_4_2_ras_hw_ops = {
- .query_ras_error_count = sdma_v4_4_2_query_ras_error_count,
- .reset_ras_error_count = sdma_v4_4_2_reset_ras_error_count,
-};
-
-static int sdma_v4_4_2_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct aca_bank_info info;
- u64 misc0;
- int ret;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- misc0 = bank->regs[ACA_REG_IDX_MISC0];
- switch (type) {
- case ACA_SMU_TYPE_UE:
- bank->aca_err_type = ACA_ERROR_TYPE_UE;
- ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE,
- 1ULL);
- break;
- case ACA_SMU_TYPE_CE:
- bank->aca_err_type = ACA_ERROR_TYPE_CE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type,
- ACA_REG__MISC0__ERRCNT(misc0));
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
-/* CODE_SDMA0 - CODE_SDMA4, reference to smu driver if header file */
-static int sdma_v4_4_2_err_codes[] = { 33, 34, 35, 36 };
-
-static bool sdma_v4_4_2_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- u32 instlo;
-
- instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]);
- instlo &= GENMASK(31, 1);
-
- if (instlo != mmSMNAID_AID0_MCA_SMU)
- return false;
-
- if (aca_bank_check_error_codes(handle->adev, bank,
- sdma_v4_4_2_err_codes,
- ARRAY_SIZE(sdma_v4_4_2_err_codes)))
- return false;
-
- return true;
-}
-
-static const struct aca_bank_ops sdma_v4_4_2_aca_bank_ops = {
- .aca_bank_parser = sdma_v4_4_2_aca_bank_parser,
- .aca_bank_is_valid = sdma_v4_4_2_aca_bank_is_valid,
-};
-
-static const struct aca_info sdma_v4_4_2_aca_info = {
- .hwip = ACA_HWIP_TYPE_SMU,
- .mask = ACA_ERROR_UE_MASK,
- .bank_ops = &sdma_v4_4_2_aca_bank_ops,
-};
-
-static int sdma_v4_4_2_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
-{
- int r;
-
- r = amdgpu_sdma_ras_late_init(adev, ras_block);
- if (r)
- return r;
-
- return amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__SDMA,
- &sdma_v4_4_2_aca_info, NULL);
-}
-
static struct amdgpu_sdma_ras sdma_v4_4_2_ras = {
.ras_block = {
- .hw_ops = &sdma_v4_4_2_ras_hw_ops,
- .ras_late_init = sdma_v4_4_2_ras_late_init,
+ .hw_ops = NULL,
},
};
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
index d7537888e60c..7a3f1a60b014 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
@@ -793,23 +793,6 @@ static int sdma_v6_0_soft_reset(struct amdgpu_ip_block *ip_block)
return sdma_v6_0_start(adev);
}
-static bool sdma_v6_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- struct amdgpu_ring *ring;
- int i, r;
- long tmo = msecs_to_jiffies(1000);
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- ring = &adev->sdma.instance[i].ring;
- r = amdgpu_ring_test_ib(ring, tmo);
- if (r)
- return true;
- }
-
- return false;
-}
-
/**
* sdma_v6_0_start - setup and start the async dma engines
*
@@ -1747,7 +1730,6 @@ const struct amd_ip_funcs sdma_v6_0_ip_funcs = {
.is_idle = sdma_v6_0_is_idle,
.wait_for_idle = sdma_v6_0_wait_for_idle,
.soft_reset = sdma_v6_0_soft_reset,
- .check_soft_reset = sdma_v6_0_check_soft_reset,
.set_clockgating_state = sdma_v6_0_set_clockgating_state,
.set_powergating_state = sdma_v6_0_set_powergating_state,
.get_clockgating_state = sdma_v6_0_get_clockgating_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
index 49c57a38151b..84305b6800fe 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
@@ -784,23 +784,6 @@ static int sdma_v7_0_soft_reset(struct amdgpu_ip_block *ip_block)
return sdma_v7_0_start(adev);
}
-static bool sdma_v7_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- struct amdgpu_ring *ring;
- int i, r;
- long tmo = msecs_to_jiffies(1000);
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- ring = &adev->sdma.instance[i].ring;
- r = amdgpu_ring_test_ib(ring, tmo);
- if (r)
- return true;
- }
-
- return false;
-}
-
static int sdma_v7_0_reset_queue(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
@@ -1679,7 +1662,6 @@ const struct amd_ip_funcs sdma_v7_0_ip_funcs = {
.is_idle = sdma_v7_0_is_idle,
.wait_for_idle = sdma_v7_0_wait_for_idle,
.soft_reset = sdma_v7_0_soft_reset,
- .check_soft_reset = sdma_v7_0_check_soft_reset,
.set_clockgating_state = sdma_v7_0_set_clockgating_state,
.set_powergating_state = sdma_v7_0_set_powergating_state,
.get_clockgating_state = sdma_v7_0_get_clockgating_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
index b06001f6b536..322e6f4dd121 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
@@ -775,23 +775,6 @@ static int sdma_v7_1_soft_reset(struct amdgpu_ip_block *ip_block)
return sdma_v7_1_inst_start(adev, inst_mask);
}
-static bool sdma_v7_1_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- struct amdgpu_ring *ring;
- int i, r;
- long tmo = msecs_to_jiffies(1000);
-
- for (i = 0; i < adev->sdma.num_instances; i++) {
- ring = &adev->sdma.instance[i].ring;
- r = amdgpu_ring_test_ib(ring, tmo);
- if (r)
- return true;
- }
-
- return false;
-}
-
static int sdma_v7_1_reset_queue(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
@@ -1644,7 +1627,6 @@ const struct amd_ip_funcs sdma_v7_1_ip_funcs = {
.is_idle = sdma_v7_1_is_idle,
.wait_for_idle = sdma_v7_1_wait_for_idle,
.soft_reset = sdma_v7_1_soft_reset,
- .check_soft_reset = sdma_v7_1_check_soft_reset,
.set_clockgating_state = sdma_v7_1_set_clockgating_state,
.set_powergating_state = sdma_v7_1_set_powergating_state,
.get_clockgating_state = sdma_v7_1_get_clockgating_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c
index c26cb3e8bff6..b104469c38ec 100644
--- a/drivers/gpu/drm/amd/amdgpu/si.c
+++ b/drivers/gpu/drm/amd/amdgpu/si.c
@@ -1509,12 +1509,6 @@ static void si_invalidate_hdp(struct amdgpu_device *adev,
}
}
-static bool si_need_full_reset(struct amdgpu_device *adev)
-{
- /* change this when we support soft reset */
- return true;
-}
-
static bool si_need_reset_on_init(struct amdgpu_device *adev)
{
return false;
@@ -2019,7 +2013,6 @@ static const struct amdgpu_asic_funcs si_asic_funcs =
.get_config_memsize = &si_get_config_memsize,
.flush_hdp = &si_flush_hdp,
.invalidate_hdp = &si_invalidate_hdp,
- .need_full_reset = &si_need_full_reset,
.get_pcie_usage = &si_get_pcie_usage,
.need_reset_on_init = &si_need_reset_on_init,
.get_pcie_replay_count = &si_get_pcie_replay_count,
diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c
index 87b398dd0769..ed3fd58b78d0 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc15.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc15.c
@@ -721,12 +721,6 @@ void soc15_set_virt_ops(struct amdgpu_device *adev)
soc15_reg_base_init(adev);
}
-static bool soc15_need_full_reset(struct amdgpu_device *adev)
-{
- /* change this when we implement soft reset */
- return true;
-}
-
static void soc15_get_pcie_usage(struct amdgpu_device *adev, uint64_t *count0,
uint64_t *count1)
{
@@ -878,7 +872,6 @@ static const struct amdgpu_asic_funcs soc15_asic_funcs =
.set_uvd_clocks = &soc15_set_uvd_clocks,
.set_vce_clocks = &soc15_set_vce_clocks,
.get_config_memsize = &soc15_get_config_memsize,
- .need_full_reset = &soc15_need_full_reset,
.init_doorbell_index = &vega10_doorbell_index_init,
.get_pcie_usage = &soc15_get_pcie_usage,
.need_reset_on_init = &soc15_need_reset_on_init,
@@ -899,7 +892,6 @@ static const struct amdgpu_asic_funcs vega20_asic_funcs =
.set_uvd_clocks = &soc15_set_uvd_clocks,
.set_vce_clocks = &soc15_set_vce_clocks,
.get_config_memsize = &soc15_get_config_memsize,
- .need_full_reset = &soc15_need_full_reset,
.init_doorbell_index = &vega20_doorbell_index_init,
.get_pcie_usage = &vega20_get_pcie_usage,
.need_reset_on_init = &soc15_need_reset_on_init,
@@ -920,7 +912,6 @@ static const struct amdgpu_asic_funcs aqua_vanjaram_asic_funcs =
.set_uvd_clocks = &soc15_set_uvd_clocks,
.set_vce_clocks = &soc15_set_vce_clocks,
.get_config_memsize = &soc15_get_config_memsize,
- .need_full_reset = &soc15_need_full_reset,
.init_doorbell_index = &aqua_vanjaram_doorbell_index_init,
.need_reset_on_init = &soc15_need_reset_on_init,
.get_pcie_replay_count = &amdgpu_nbio_get_pcie_replay_count,
diff --git a/drivers/gpu/drm/amd/amdgpu/soc15_common.h b/drivers/gpu/drm/amd/amdgpu/soc15_common.h
index a7b5a95ebebb..47e0329b6f3f 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc15_common.h
+++ b/drivers/gpu/drm/amd/amdgpu/soc15_common.h
@@ -38,30 +38,30 @@
(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + (reg)+(offset))
#define __WREG32_SOC15_RLC__(reg, value, flag, hwip, inst) \
- ((amdgpu_sriov_vf(adev) && adev->gfx.rlc.funcs && adev->gfx.rlc.rlcg_reg_access_supported) ? \
- amdgpu_sriov_wreg(adev, reg, value, flag, hwip, inst) : \
- WREG32(reg, value))
+ adev->gfx.rlc.reg_funcs->wreg32(adev, reg, value, flag, hwip, inst)
#define __RREG32_SOC15_RLC__(reg, flag, hwip, inst) \
- ((amdgpu_sriov_vf(adev) && adev->gfx.rlc.funcs && adev->gfx.rlc.rlcg_reg_access_supported) ? \
- amdgpu_sriov_rreg(adev, reg, flag, hwip, inst) : \
- RREG32(reg))
-
-#define WREG32_FIELD15(ip, idx, reg, field, val) \
- __WREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][idx][mm##reg##_BASE_IDX] + mm##reg, \
- (__RREG32_SOC15_RLC__( \
- adev->reg_offset[ip##_HWIP][idx][mm##reg##_BASE_IDX] + mm##reg, \
- 0, ip##_HWIP, idx) & \
- ~REG_FIELD_MASK(reg, field)) | (val) << REG_FIELD_SHIFT(reg, field), \
- 0, ip##_HWIP, idx)
-
-#define WREG32_FIELD15_PREREG(ip, idx, reg_name, field, val) \
- __WREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][idx][reg##reg_name##_BASE_IDX] + reg##reg_name, \
- (__RREG32_SOC15_RLC__( \
- adev->reg_offset[ip##_HWIP][idx][reg##reg_name##_BASE_IDX] + reg##reg_name, \
- 0, ip##_HWIP, idx) & \
- ~REG_FIELD_MASK(reg_name, field)) | (val) << REG_FIELD_SHIFT(reg_name, field), \
- 0, ip##_HWIP, idx)
+ adev->gfx.rlc.reg_funcs->rreg32(adev, reg, flag, hwip, inst)
+
+#define WREG32_FIELD15(ip, idx, reg_name, field, val) \
+do { \
+ u32 reg__ = adev->reg_offset[ip##_HWIP][idx][mm##reg_name##_BASE_IDX] + mm##reg_name; \
+ u32 val__ = __RREG32_SOC15_RLC__(reg__, 0, ip##_HWIP, idx); \
+\
+ val__ &= ~REG_FIELD_MASK(reg_name, field); \
+ val__ |= (val) << REG_FIELD_SHIFT(reg_name, field); \
+ __WREG32_SOC15_RLC__(reg__, val__, 0, ip##_HWIP, idx); \
+} while (0)
+
+#define WREG32_FIELD15_PREREG(ip, idx, reg_name, field, val) \
+do { \
+ u32 reg__ = adev->reg_offset[ip##_HWIP][idx][reg##reg_name##_BASE_IDX] + reg##reg_name; \
+ u32 val__ = __RREG32_SOC15_RLC__(reg__, 0, ip##_HWIP, idx); \
+\
+ val__ &= ~REG_FIELD_MASK(reg_name, field); \
+ val__ |= (val) << REG_FIELD_SHIFT(reg_name, field); \
+ __WREG32_SOC15_RLC__(reg__, val__, 0, ip##_HWIP, idx); \
+} while (0)
#define RREG32_SOC15(ip, inst, reg) \
__RREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg, \
@@ -181,12 +181,15 @@
WREG32_RLC_EX(prefix, target_reg, value, inst); \
} while (0)
-#define WREG32_FIELD15_RLC(ip, idx, reg, field, val) \
- __WREG32_SOC15_RLC__((adev->reg_offset[ip##_HWIP][idx][mm##reg##_BASE_IDX] + mm##reg), \
- (__RREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][idx][mm##reg##_BASE_IDX] + mm##reg, \
- AMDGPU_REGS_RLC, ip##_HWIP, idx) & \
- ~REG_FIELD_MASK(reg, field)) | (val) << REG_FIELD_SHIFT(reg, field), \
- AMDGPU_REGS_RLC, ip##_HWIP, idx)
+#define WREG32_FIELD15_RLC(ip, idx, reg_name, field, val) \
+do { \
+ u32 reg__ = adev->reg_offset[ip##_HWIP][idx][mm##reg_name##_BASE_IDX] + mm##reg_name; \
+ u32 val__ = __RREG32_SOC15_RLC__(reg__, AMDGPU_REGS_RLC, ip##_HWIP, idx); \
+\
+ val__ &= ~REG_FIELD_MASK(reg_name, field); \
+ val__ |= (val) << REG_FIELD_SHIFT(reg_name, field); \
+ __WREG32_SOC15_RLC__(reg__, val__, AMDGPU_REGS_RLC, ip##_HWIP, idx); \
+} while (0)
#define WREG32_SOC15_OFFSET_RLC(ip, inst, reg, offset, value) \
__WREG32_SOC15_RLC__((adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg) + offset, value, AMDGPU_REGS_RLC, ip##_HWIP, inst)
@@ -207,10 +210,4 @@
amdgpu_reg_get_smn_base64(adev, ip##_HWIP, inst), \
value)
-#define RREG64_MCA(smn_base, mca_base, idx) \
- RREG64_PCIE_EXT(smn_base + mca_base + (idx * 8))
-
-#define WREG64_MCA(smn_base, mca_base, idx, val) \
- WREG64_PCIE_EXT(smn_base + mca_base + (idx * 8), val)
-
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c
index 1677e88a4e36..09f28dbd60ee 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc21.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc21.c
@@ -461,17 +461,6 @@ const struct amdgpu_ip_block_version soc21_common_ip_block = {
.funcs = &soc21_common_ip_funcs,
};
-static bool soc21_need_full_reset(struct amdgpu_device *adev)
-{
- switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
- case IP_VERSION(11, 0, 0):
- case IP_VERSION(11, 0, 2):
- case IP_VERSION(11, 0, 3):
- default:
- return true;
- }
-}
-
static bool soc21_need_reset_on_init(struct amdgpu_device *adev)
{
u32 sol_reg;
@@ -550,7 +539,6 @@ static const struct amdgpu_asic_funcs soc21_asic_funcs = {
.set_vce_clocks = &soc21_set_vce_clocks,
.get_config_memsize = &soc21_get_config_memsize,
.init_doorbell_index = &soc21_init_doorbell_index,
- .need_full_reset = &soc21_need_full_reset,
.need_reset_on_init = &soc21_need_reset_on_init,
.get_pcie_replay_count = &amdgpu_nbio_get_pcie_replay_count,
.supports_baco = &amdgpu_dpm_is_baco_supported,
diff --git a/drivers/gpu/drm/amd/amdgpu/soc24.c b/drivers/gpu/drm/amd/amdgpu/soc24.c
index 9dce30d2bb8d..e5e3a460e486 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc24.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc24.c
@@ -238,16 +238,6 @@ const struct amdgpu_ip_block_version soc24_common_ip_block = {
.funcs = &soc24_common_ip_funcs,
};
-static bool soc24_need_full_reset(struct amdgpu_device *adev)
-{
- switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
- case IP_VERSION(12, 0, 0):
- case IP_VERSION(12, 0, 1):
- default:
- return true;
- }
-}
-
static bool soc24_need_reset_on_init(struct amdgpu_device *adev)
{
u32 sol_reg;
@@ -330,7 +320,6 @@ static const struct amdgpu_asic_funcs soc24_asic_funcs = {
.get_xclk = &soc24_get_xclk,
.get_config_memsize = &soc24_get_config_memsize,
.init_doorbell_index = &soc24_init_doorbell_index,
- .need_full_reset = &soc24_need_full_reset,
.need_reset_on_init = &soc24_need_reset_on_init,
.get_pcie_replay_count = &soc24_get_pcie_replay_count,
.supports_baco = &amdgpu_dpm_is_baco_supported,
diff --git a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
index 5f05c8e68297..a9039fb1a77b 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
@@ -223,15 +223,6 @@ static int soc_v1_0_read_register(struct amdgpu_device *adev,
return -EINVAL;
}
-static bool soc_v1_0_need_full_reset(struct amdgpu_device *adev)
-{
- switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
- case IP_VERSION(12, 1, 0):
- default:
- return true;
- }
-}
-
static bool soc_v1_0_need_reset_on_init(struct amdgpu_device *adev)
{
@@ -271,7 +262,6 @@ static const struct amdgpu_asic_funcs soc_v1_0_asic_funcs = {
.read_register = &soc_v1_0_read_register,
.get_config_memsize = &soc_v1_0_get_config_memsize,
.get_xclk = &soc_v1_0_get_xclk,
- .need_full_reset = &soc_v1_0_need_full_reset,
.init_doorbell_index = &soc_v1_0_doorbell_index_init,
.need_reset_on_init = &soc_v1_0_need_reset_on_init,
.encode_ext_smn_addressing = &soc_v1_0_encode_ext_smn_addressing,
@@ -600,8 +590,10 @@ static int soc_v1_0_get_xcp_res_info(struct amdgpu_xcp_mgr *xcp_mgr,
xcp_cfg->num_res = ARRAY_SIZE(max_res);
for (i = 0; i < xcp_cfg->num_res; i++) {
- res_lt_xcp = max_res[i] < num_xcp;
xcp_cfg->xcp_res[i].id = i;
+ if (!max_res[i])
+ continue;
+ res_lt_xcp = max_res[i] < num_xcp;
xcp_cfg->xcp_res[i].num_inst =
res_lt_xcp ? 1 : max_res[i] / num_xcp;
xcp_cfg->xcp_res[i].num_inst =
diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
index ee8038df17e3..a3e883f6f099 100644
--- a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
@@ -390,43 +390,6 @@ static int tonga_ih_wait_for_idle(struct amdgpu_ip_block *ip_block)
return -ETIMEDOUT;
}
-static bool tonga_ih_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 srbm_soft_reset = 0;
- u32 tmp = RREG32(mmSRBM_STATUS);
-
- if (tmp & SRBM_STATUS__IH_BUSY_MASK)
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
- SOFT_RESET_IH, 1);
-
- if (srbm_soft_reset) {
- adev->irq.srbm_soft_reset = srbm_soft_reset;
- return true;
- } else {
- adev->irq.srbm_soft_reset = 0;
- return false;
- }
-}
-
-static int tonga_ih_pre_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- if (!ip_block->adev->irq.srbm_soft_reset)
- return 0;
-
- return tonga_ih_hw_fini(ip_block);
-}
-
-static int tonga_ih_post_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
-
- if (!adev->irq.srbm_soft_reset)
- return 0;
-
- return tonga_ih_hw_init(ip_block);
-}
-
static int tonga_ih_soft_reset(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -481,10 +444,7 @@ static const struct amd_ip_funcs tonga_ih_ip_funcs = {
.resume = tonga_ih_resume,
.is_idle = tonga_ih_is_idle,
.wait_for_idle = tonga_ih_wait_for_idle,
- .check_soft_reset = tonga_ih_check_soft_reset,
- .pre_soft_reset = tonga_ih_pre_soft_reset,
.soft_reset = tonga_ih_soft_reset,
- .post_soft_reset = tonga_ih_post_soft_reset,
.set_clockgating_state = tonga_ih_set_clockgating_state,
.set_powergating_state = tonga_ih_set_powergating_state,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
index 14092150336a..67bdf7303e6b 100644
--- a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
@@ -28,48 +28,6 @@
#include "umc/umc_12_0_0_sh_mask.h"
#include "mp/mp_13_0_6_sh_mask.h"
-#define MAX_ECC_NUM_PER_RETIREMENT 32
-#define DELAYED_TIME_FOR_GPU_RESET 1000 //ms
-
-static inline uint64_t get_umc_v12_0_reg_offset(struct amdgpu_device *adev,
- uint32_t node_inst,
- uint32_t umc_inst,
- uint32_t ch_inst)
-{
- uint32_t index = umc_inst * adev->umc.channel_inst_num + ch_inst;
- uint64_t cross_node_offset = (node_inst == 0) ? 0 : UMC_V12_0_CROSS_NODE_OFFSET;
-
- umc_inst = index / 4;
- ch_inst = index % 4;
-
- return adev->umc.channel_offs * ch_inst + UMC_V12_0_INST_DIST * umc_inst +
- UMC_V12_0_NODE_DIST * node_inst + cross_node_offset;
-}
-
-static int umc_v12_0_reset_error_count_per_channel(struct amdgpu_device *adev,
- uint32_t node_inst, uint32_t umc_inst,
- uint32_t ch_inst, void *data)
-{
- uint64_t odecc_err_cnt_addr;
- uint64_t umc_reg_offset =
- get_umc_v12_0_reg_offset(adev, node_inst, umc_inst, ch_inst);
-
- odecc_err_cnt_addr =
- SOC15_REG_OFFSET(UMC, 0, regUMCCH0_OdEccErrCnt);
-
- /* clear error count */
- WREG32_PCIE_EXT((odecc_err_cnt_addr + umc_reg_offset) * 4,
- UMC_V12_0_CE_CNT_INIT);
-
- return 0;
-}
-
-static void umc_v12_0_reset_error_count(struct amdgpu_device *adev)
-{
- amdgpu_umc_loop_channels(adev,
- umc_v12_0_reset_error_count_per_channel, NULL);
-}
-
bool umc_v12_0_is_deferred_error(struct amdgpu_device *adev, uint64_t mc_umc_status)
{
dev_dbg(adev->dev,
@@ -115,65 +73,6 @@ bool umc_v12_0_is_correctable_error(struct amdgpu_device *adev, uint64_t mc_umc_
!(umc_v12_0_is_uncorrectable_error(adev, mc_umc_status)))));
}
-static void umc_v12_0_query_error_count_per_type(struct amdgpu_device *adev,
- uint64_t umc_reg_offset,
- unsigned long *error_count,
- check_error_type_func error_type_func)
-{
- uint64_t mc_umc_status;
- uint64_t mc_umc_status_addr;
-
- mc_umc_status_addr =
- SOC15_REG_OFFSET(UMC, 0, regMCA_UMC_UMC0_MCUMC_STATUST0);
-
- /* Check MCUMC_STATUS */
- mc_umc_status =
- RREG64_PCIE_EXT((mc_umc_status_addr + umc_reg_offset) * 4);
-
- if (error_type_func(adev, mc_umc_status))
- *error_count += 1;
-}
-
-static int umc_v12_0_query_error_count(struct amdgpu_device *adev,
- uint32_t node_inst, uint32_t umc_inst,
- uint32_t ch_inst, void *data)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)data;
- unsigned long ue_count = 0, ce_count = 0, de_count = 0;
-
- /* NOTE: node_inst is converted by adev->umc.active_mask and the range is [0-3],
- * which can be used as die ID directly */
- struct amdgpu_smuio_mcm_config_info mcm_info = {
- .socket_id = adev->smuio.funcs->get_socket_id(adev),
- .die_id = node_inst,
- };
-
- uint64_t umc_reg_offset =
- get_umc_v12_0_reg_offset(adev, node_inst, umc_inst, ch_inst);
-
- umc_v12_0_query_error_count_per_type(adev, umc_reg_offset,
- &ce_count, umc_v12_0_is_correctable_error);
- umc_v12_0_query_error_count_per_type(adev, umc_reg_offset,
- &ue_count, umc_v12_0_is_uncorrectable_error);
- umc_v12_0_query_error_count_per_type(adev, umc_reg_offset,
- &de_count, umc_v12_0_is_deferred_error);
-
- amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, ue_count);
- amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, ce_count);
- amdgpu_ras_error_statistic_de_count(err_data, &mcm_info, de_count);
-
- return 0;
-}
-
-static void umc_v12_0_query_ras_error_count(struct amdgpu_device *adev,
- void *ras_error_status)
-{
- amdgpu_umc_loop_channels(adev,
- umc_v12_0_query_error_count, ras_error_status);
-
- umc_v12_0_reset_error_count(adev);
-}
-
static void umc_v12_0_get_retire_flip_bits(struct amdgpu_device *adev)
{
enum amdgpu_memory_partition nps = AMDGPU_NPS1_PARTITION_MODE;
@@ -279,190 +178,6 @@ static void umc_v12_0_get_retire_flip_bits(struct amdgpu_device *adev)
adev->umc.retire_unit = 0x1 << flip_bits->bit_num;
}
-static int umc_v12_0_convert_error_address(struct amdgpu_device *adev,
- struct ras_err_data *err_data,
- struct ta_ras_query_address_input *addr_in,
- struct ta_ras_query_address_output *addr_out,
- bool dump_addr)
-{
- uint32_t row = 0, row_lower = 0, row_high = 0;
- uint32_t col = 0, col_lower = 0, bank = 0;
- uint32_t channel_index = 0, umc_inst = 0;
- uint32_t i, bit_num, retire_unit, *flip_bits;
- uint64_t soc_pa, column, err_addr;
- struct ta_ras_query_address_output addr_out_tmp;
- struct ta_ras_query_address_output *paddr_out;
- int ret = 0;
-
- if (!addr_out)
- paddr_out = &addr_out_tmp;
- else
- paddr_out = addr_out;
-
- err_addr = bank = 0;
- if (addr_in) {
- err_addr = addr_in->ma.err_addr;
- addr_in->addr_type = TA_RAS_MCA_TO_PA;
- ret = psp_ras_query_address(&adev->psp, addr_in, paddr_out);
- if (ret) {
- dev_warn(adev->dev, "Failed to query RAS physical address for 0x%llx",
- err_addr);
-
- goto out;
- }
-
- bank = paddr_out->pa.bank;
- /* no need to care about umc inst if addr_in is NULL */
- umc_inst = addr_in->ma.umc_inst;
- }
-
- flip_bits = adev->umc.flip_bits.flip_bits_in_pa;
- bit_num = adev->umc.flip_bits.bit_num;
- retire_unit = adev->umc.retire_unit;
-
- soc_pa = paddr_out->pa.pa;
- channel_index = paddr_out->pa.channel_idx;
- /* clear loop bits in soc physical address */
- for (i = 0; i < bit_num; i++)
- soc_pa &= ~BIT_ULL(flip_bits[i]);
-
- paddr_out->pa.pa = soc_pa;
- /* get column bit 0 and 1 in mca address */
- col_lower = (err_addr >> 1) & 0x3ULL;
- /* extra row bit will be handled later */
- row_lower = (err_addr >> UMC_V12_0_MA_R0_BIT) & 0x1fffULL;
- row_lower &= ~BIT_ULL(adev->umc.flip_bits.flip_row_bit);
-
- if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(9, 5, 0)) {
- row_high = (soc_pa >> adev->umc.flip_bits.r13_in_pa) & 0x3ULL;
- /* it's 2.25GB in each channel, from MCA address to PA
- * [R14 R13] is converted if the two bits value are 0x3,
- * get them from PA instead of MCA address.
- */
- row_lower |= (row_high << 13);
- }
-
- if (!err_data && !dump_addr)
- goto out;
-
- /* loop for all possibilities of retired bits */
- for (column = 0; column < retire_unit; column++) {
- soc_pa = paddr_out->pa.pa;
- for (i = 0; i < bit_num; i++)
- soc_pa |= (((column >> i) & 0x1ULL) << flip_bits[i]);
-
- col = ((column & 0x7) << 2) | col_lower;
- /* handle extra row bit */
- if (bit_num == RETIRE_FLIP_BITS_NUM)
- row = ((column >> 3) << adev->umc.flip_bits.flip_row_bit) |
- row_lower;
-
- if (dump_addr)
- dev_info(adev->dev,
- "Error Address(PA):0x%-10llx Row:0x%-4x Col:0x%-2x Bank:0x%x Channel:0x%x\n",
- soc_pa, row, col, bank, channel_index);
-
- if (err_data)
- amdgpu_umc_fill_error_record(err_data, err_addr,
- soc_pa, channel_index, umc_inst);
- }
-
-out:
- return ret;
-}
-
-static int umc_v12_0_query_error_address(struct amdgpu_device *adev,
- uint32_t node_inst, uint32_t umc_inst,
- uint32_t ch_inst, void *data)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)data;
- struct ta_ras_query_address_input addr_in;
- uint64_t mc_umc_status_addr;
- uint64_t mc_umc_status, err_addr;
- uint64_t mc_umc_addrt0;
- uint64_t umc_reg_offset =
- get_umc_v12_0_reg_offset(adev, node_inst, umc_inst, ch_inst);
-
- mc_umc_status_addr =
- SOC15_REG_OFFSET(UMC, 0, regMCA_UMC_UMC0_MCUMC_STATUST0);
-
- mc_umc_status = RREG64_PCIE_EXT((mc_umc_status_addr + umc_reg_offset) * 4);
-
- if (mc_umc_status == 0)
- return 0;
-
- if (!err_data->err_addr) {
- /* clear umc status */
- WREG64_PCIE_EXT((mc_umc_status_addr + umc_reg_offset) * 4, 0x0ULL);
-
- return 0;
- }
-
- /* calculate error address if ue error is detected */
- if (umc_v12_0_is_uncorrectable_error(adev, mc_umc_status) ||
- umc_v12_0_is_deferred_error(adev, mc_umc_status)) {
- mc_umc_addrt0 =
- SOC15_REG_OFFSET(UMC, 0, regMCA_UMC_UMC0_MCUMC_ADDRT0);
-
- err_addr = RREG64_PCIE_EXT((mc_umc_addrt0 + umc_reg_offset) * 4);
-
- err_addr = REG_GET_FIELD(err_addr, MCA_UMC_UMC0_MCUMC_ADDRT0, ErrorAddr);
-
- if (!adev->aid_mask &&
- adev->smuio.funcs &&
- adev->smuio.funcs->get_socket_id)
- addr_in.ma.socket_id = adev->smuio.funcs->get_socket_id(adev);
- else
- addr_in.ma.socket_id = 0;
-
- addr_in.ma.err_addr = err_addr;
- addr_in.ma.ch_inst = ch_inst;
- addr_in.ma.umc_inst = umc_inst;
- addr_in.ma.node_inst = node_inst;
-
- umc_v12_0_convert_error_address(adev, err_data, &addr_in, NULL, true);
- }
-
- /* clear umc status */
- WREG64_PCIE_EXT((mc_umc_status_addr + umc_reg_offset) * 4, 0x0ULL);
-
- return 0;
-}
-
-static void umc_v12_0_query_ras_error_address(struct amdgpu_device *adev,
- void *ras_error_status)
-{
- amdgpu_umc_loop_channels(adev,
- umc_v12_0_query_error_address, ras_error_status);
-}
-
-static int umc_v12_0_err_cnt_init_per_channel(struct amdgpu_device *adev,
- uint32_t node_inst, uint32_t umc_inst,
- uint32_t ch_inst, void *data)
-{
- uint32_t odecc_cnt_sel;
- uint64_t odecc_cnt_sel_addr, odecc_err_cnt_addr;
- uint64_t umc_reg_offset =
- get_umc_v12_0_reg_offset(adev, node_inst, umc_inst, ch_inst);
-
- odecc_cnt_sel_addr =
- SOC15_REG_OFFSET(UMC, 0, regUMCCH0_OdEccCntSel);
- odecc_err_cnt_addr =
- SOC15_REG_OFFSET(UMC, 0, regUMCCH0_OdEccErrCnt);
-
- odecc_cnt_sel = RREG32_PCIE_EXT((odecc_cnt_sel_addr + umc_reg_offset) * 4);
-
- /* set ce error interrupt type to APIC based interrupt */
- odecc_cnt_sel = REG_SET_FIELD(odecc_cnt_sel, UMCCH0_OdEccCntSel,
- OdEccErrInt, 0x1);
- WREG32_PCIE_EXT((odecc_cnt_sel_addr + umc_reg_offset) * 4, odecc_cnt_sel);
-
- /* set error count to initial value */
- WREG32_PCIE_EXT((odecc_err_cnt_addr + umc_reg_offset) * 4, UMC_V12_0_CE_CNT_INIT);
-
- return 0;
-}
-
static bool umc_v12_0_check_ecc_err_status(struct amdgpu_device *adev,
enum amdgpu_mca_error_type type, void *ras_error_status)
{
@@ -482,309 +197,11 @@ static bool umc_v12_0_check_ecc_err_status(struct amdgpu_device *adev,
return false;
}
-static void umc_v12_0_err_cnt_init(struct amdgpu_device *adev)
-{
- amdgpu_umc_loop_channels(adev,
- umc_v12_0_err_cnt_init_per_channel, NULL);
-}
-
-static bool umc_v12_0_query_ras_poison_mode(struct amdgpu_device *adev)
-{
- /*
- * Force return true, because regUMCCH0_EccCtrl
- * is not accessible from host side
- */
- return true;
-}
-
-const struct amdgpu_ras_block_hw_ops umc_v12_0_ras_hw_ops = {
- .query_ras_error_count = umc_v12_0_query_ras_error_count,
- .query_ras_error_address = umc_v12_0_query_ras_error_address,
-};
-
-static int umc_v12_0_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct amdgpu_device *adev = handle->adev;
- struct aca_bank_info info;
- enum aca_error_type err_type;
- u64 status, count;
- u32 ext_error_code;
- int ret;
-
- status = bank->regs[ACA_REG_IDX_STATUS];
- if (umc_v12_0_is_deferred_error(adev, status))
- err_type = ACA_ERROR_TYPE_DEFERRED;
- else if (umc_v12_0_is_uncorrectable_error(adev, status))
- err_type = ACA_ERROR_TYPE_UE;
- else if (umc_v12_0_is_correctable_error(adev, status))
- err_type = ACA_ERROR_TYPE_CE;
- else
- return 0;
- bank->aca_err_type = err_type;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- amdgpu_umc_update_ecc_status(adev,
- bank->regs[ACA_REG_IDX_STATUS],
- bank->regs[ACA_REG_IDX_IPID],
- bank->regs[ACA_REG_IDX_ADDR]);
-
- ext_error_code = ACA_REG__STATUS__ERRORCODEEXT(status);
- if (umc_v12_0_is_deferred_error(adev, status))
- count = ext_error_code == 0 ?
- adev->umc.err_addr_cnt / adev->umc.retire_unit : 1ULL;
- else
- count = ext_error_code == 0 ?
- ACA_REG__MISC0__ERRCNT(bank->regs[ACA_REG_IDX_MISC0]) : 1ULL;
-
- return aca_error_cache_log_bank_error(handle, &info, err_type, count);
-}
-
-static const struct aca_bank_ops umc_v12_0_aca_bank_ops = {
- .aca_bank_parser = umc_v12_0_aca_bank_parser,
-};
-
-const struct aca_info umc_v12_0_aca_info = {
- .hwip = ACA_HWIP_TYPE_UMC,
- .mask = ACA_ERROR_UE_MASK | ACA_ERROR_CE_MASK | ACA_ERROR_DEFERRED_MASK,
- .bank_ops = &umc_v12_0_aca_bank_ops,
-};
-
-static int umc_v12_0_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
-{
- int ret;
-
- ret = amdgpu_umc_ras_late_init(adev, ras_block);
- if (ret)
- return ret;
-
- ret = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__UMC,
- &umc_v12_0_aca_info, NULL);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int umc_v12_0_update_ecc_status(struct amdgpu_device *adev,
- uint64_t status, uint64_t ipid, uint64_t addr)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- uint16_t hwid, mcatype;
- uint64_t page_pfn[UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL];
- uint64_t err_addr, pa_addr = 0;
- struct ras_ecc_err *ecc_err;
- struct ta_ras_query_address_output addr_out;
- uint32_t shift_bit = adev->umc.flip_bits.flip_bits_in_pa[2];
- int count, ret, i;
-
- hwid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, HardwareID);
- mcatype = REG_GET_FIELD(ipid, MCMP1_IPIDT0, McaType);
-
- /* The IP block decode of consumption is SMU */
- if (hwid != MCA_UMC_HWID_V12_0 || mcatype != MCA_UMC_MCATYPE_V12_0) {
- con->umc_ecc_log.consumption_q_count++;
- return 0;
- }
-
- if (!status)
- return 0;
-
- if (!umc_v12_0_is_deferred_error(adev, status))
- return 0;
-
- err_addr = REG_GET_FIELD(addr,
- MCA_UMC_UMC0_MCUMC_ADDRT0, ErrorAddr);
-
- dev_dbg(adev->dev,
- "UMC:IPID:0x%llx, socket:%llu, aid:%llu, inst:%llu, ch:%llu, err_addr:0x%llx\n",
- ipid,
- MCA_IPID_2_SOCKET_ID(ipid),
- MCA_IPID_2_DIE_ID(ipid),
- MCA_IPID_2_UMC_INST(ipid),
- MCA_IPID_2_UMC_CH(ipid),
- err_addr);
-
- ret = amdgpu_umc_mca_to_addr(adev,
- err_addr, MCA_IPID_2_UMC_CH(ipid),
- MCA_IPID_2_UMC_INST(ipid), MCA_IPID_2_DIE_ID(ipid),
- MCA_IPID_2_SOCKET_ID(ipid), &addr_out, true);
- if (ret)
- return ret;
-
- ecc_err = kzalloc_obj(*ecc_err);
- if (!ecc_err)
- return -ENOMEM;
-
- pa_addr = addr_out.pa.pa;
- ecc_err->status = status;
- ecc_err->ipid = ipid;
- ecc_err->addr = addr;
- ecc_err->pa_pfn = pa_addr >> AMDGPU_GPU_PAGE_SHIFT;
- ecc_err->channel_idx = addr_out.pa.channel_idx;
-
- /* If converted pa_pfn is 0, use pa C4 pfn. */
- if (!ecc_err->pa_pfn)
- ecc_err->pa_pfn = BIT_ULL(shift_bit) >> AMDGPU_GPU_PAGE_SHIFT;
-
- ret = amdgpu_umc_logs_ecc_err(adev, &con->umc_ecc_log.de_page_tree, ecc_err);
- if (ret) {
- if (ret == -EEXIST)
- con->umc_ecc_log.de_queried_count++;
- else
- dev_err(adev->dev, "Fail to log ecc error! ret:%d\n", ret);
-
- kfree(ecc_err);
- return ret;
- }
-
- con->umc_ecc_log.de_queried_count++;
-
- memset(page_pfn, 0, sizeof(page_pfn));
- count = amdgpu_umc_lookup_bad_pages_in_a_row(adev,
- pa_addr,
- page_pfn, ARRAY_SIZE(page_pfn));
- if (count <= 0) {
- dev_warn(adev->dev, "Fail to convert error address! count:%d\n", count);
- return 0;
- }
-
- /* Reserve memory */
- for (i = 0; i < count; i++)
- amdgpu_ras_reserve_page(adev, page_pfn[i]);
-
- /* The problem case is as follows:
- * 1. GPU A triggers a gpu ras reset, and GPU A drives
- * GPU B to also perform a gpu ras reset.
- * 2. After gpu B ras reset started, gpu B queried a DE
- * data. Since the DE data was queried in the ras reset
- * thread instead of the page retirement thread, bad
- * page retirement work would not be triggered. Then
- * even if all gpu resets are completed, the bad pages
- * will be cached in RAM until GPU B's bad page retirement
- * work is triggered again and then saved to eeprom.
- * Trigger delayed work to save the bad pages to eeprom in time
- * after gpu ras reset is completed.
- */
- if (amdgpu_ras_in_recovery(adev))
- schedule_delayed_work(&con->page_retirement_dwork,
- msecs_to_jiffies(DELAYED_TIME_FOR_GPU_RESET));
-
- return 0;
-}
-
-static int umc_v12_0_fill_error_record(struct amdgpu_device *adev,
- struct ras_ecc_err *ecc_err, void *ras_error_status)
-{
- struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status;
- uint64_t page_pfn[UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL];
- int ret, i, count;
-
- if (!err_data || !ecc_err)
- return -EINVAL;
-
- memset(page_pfn, 0, sizeof(page_pfn));
- count = amdgpu_umc_lookup_bad_pages_in_a_row(adev,
- ecc_err->pa_pfn << AMDGPU_GPU_PAGE_SHIFT,
- page_pfn, ARRAY_SIZE(page_pfn));
-
- for (i = 0; i < count; i++) {
- ret = amdgpu_umc_fill_error_record(err_data,
- ecc_err->addr,
- page_pfn[i] << AMDGPU_GPU_PAGE_SHIFT,
- ecc_err->channel_idx,
- MCA_IPID_2_UMC_INST(ecc_err->ipid));
- if (ret)
- break;
- }
-
- err_data->de_count++;
-
- return ret;
-}
-
-static void umc_v12_0_query_ras_ecc_err_addr(struct amdgpu_device *adev,
- void *ras_error_status)
-{
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- struct ras_ecc_err *entries[MAX_ECC_NUM_PER_RETIREMENT];
- struct radix_tree_root *ecc_tree;
- int new_detected, ret, i;
-
- ecc_tree = &con->umc_ecc_log.de_page_tree;
-
- mutex_lock(&con->umc_ecc_log.lock);
- new_detected = radix_tree_gang_lookup_tag(ecc_tree, (void **)entries,
- 0, ARRAY_SIZE(entries), UMC_ECC_NEW_DETECTED_TAG);
- for (i = 0; i < new_detected; i++) {
- if (!entries[i])
- continue;
-
- ret = umc_v12_0_fill_error_record(adev, entries[i], ras_error_status);
- if (ret) {
- dev_err(adev->dev, "Fail to fill umc error record, ret:%d\n", ret);
- break;
- }
- radix_tree_tag_clear(ecc_tree,
- entries[i]->pa_pfn, UMC_ECC_NEW_DETECTED_TAG);
- }
- mutex_unlock(&con->umc_ecc_log.lock);
-}
-
-static uint32_t umc_v12_0_get_die_id(struct amdgpu_device *adev,
- uint64_t mca_addr, uint64_t retired_page)
-{
- uint32_t die = 0;
-
- /* we only calculate die id for nps1 mode right now */
- die += ((((retired_page >> 12) & 0x1ULL)^
- ((retired_page >> 20) & 0x1ULL) ^
- ((retired_page >> 27) & 0x1ULL) ^
- ((retired_page >> 34) & 0x1ULL) ^
- ((retired_page >> 41) & 0x1ULL)) << 0);
-
- /* the original PA_C4 and PA_R13 may be cleared in retired_page, so
- * get them from mca_addr.
- */
- die += ((((retired_page >> 13) & 0x1ULL) ^
- ((mca_addr >> 5) & 0x1ULL) ^
- ((retired_page >> 28) & 0x1ULL) ^
- ((mca_addr >> 23) & 0x1ULL) ^
- ((retired_page >> 42) & 0x1ULL)) << 1);
- die &= 3;
-
- return die;
-}
-
-static void umc_v12_0_mca_ipid_parse(struct amdgpu_device *adev, uint64_t ipid,
- uint32_t *did, uint32_t *ch, uint32_t *umc_inst, uint32_t *sid)
-{
- if (did)
- *did = MCA_IPID_2_DIE_ID(ipid);
- if (ch)
- *ch = MCA_IPID_2_UMC_CH(ipid);
- if (umc_inst)
- *umc_inst = MCA_IPID_2_UMC_INST(ipid);
- if (sid)
- *sid = MCA_IPID_2_SOCKET_ID(ipid);
-}
-
struct amdgpu_umc_ras umc_v12_0_ras = {
.ras_block = {
- .hw_ops = &umc_v12_0_ras_hw_ops,
- .ras_late_init = umc_v12_0_ras_late_init,
+ .hw_ops = NULL,
},
- .err_cnt_init = umc_v12_0_err_cnt_init,
- .query_ras_poison_mode = umc_v12_0_query_ras_poison_mode,
- .ecc_info_query_ras_error_address = umc_v12_0_query_ras_ecc_err_addr,
.check_ecc_err_status = umc_v12_0_check_ecc_err_status,
- .update_ecc_status = umc_v12_0_update_ecc_status,
- .convert_ras_err_addr = umc_v12_0_convert_error_address,
- .get_die_id_from_pa = umc_v12_0_get_die_id,
.get_retire_flip_bits = umc_v12_0_get_retire_flip_bits,
- .mca_ipid_parse = umc_v12_0_mca_ipid_parse,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.h b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.h
index 63b7e7254526..9d9e84d8d3bb 100644
--- a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.h
+++ b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.h
@@ -26,31 +26,6 @@
#include "soc15_common.h"
#include "amdgpu.h"
-#define UMC_V12_0_NODE_DIST 0x40000000
-#define UMC_V12_0_INST_DIST 0x40000
-
-/* UMC register per channel offset */
-#define UMC_V12_0_PER_CHANNEL_OFFSET 0x400
-
-/* UMC cross node offset */
-#define UMC_V12_0_CROSS_NODE_OFFSET 0x100000000
-
-/* OdEccErrCnt max value */
-#define UMC_V12_0_CE_CNT_MAX 0xffff
-/* umc ce interrupt threshold */
-#define UMC_V12_0_CE_INT_THRESHOLD 0xffff
-/* umc ce count initial value */
-#define UMC_V12_0_CE_CNT_INIT (UMC_V12_0_CE_CNT_MAX - UMC_V12_0_CE_INT_THRESHOLD)
-
-/* number of umc channel instance with memory map register access */
-#define UMC_V12_0_CHANNEL_INSTANCE_NUM 8
-/* number of umc instance with memory map register access */
-#define UMC_V12_0_UMC_INSTANCE_NUM 4
-
-/* Total channel instances for all available umc nodes */
-#define UMC_V12_0_TOTAL_CHANNEL_NUM(adev) \
- (UMC_V12_0_CHANNEL_INSTANCE_NUM * (adev)->gmc.num_umc)
-
/* one piece of normalized address is mapped to 8 pieces of physical address */
#define UMC_V12_0_NA_MAP_PA_NUM 8
/* R13 bit shift should be considered, double the number */
@@ -75,9 +50,6 @@
/* row bits in MCA address */
#define UMC_V12_0_MA_R0_BIT 10
-#define MCA_UMC_HWID_V12_0 0x96
-#define MCA_UMC_MCATYPE_V12_0 0x0
-
#define MCA_IPID_LO_2_UMC_CH(_ipid_lo) (((((_ipid_lo) >> 20) & 0x1) * 4) + \
(((_ipid_lo) >> 12) & 0xF))
#define MCA_IPID_LO_2_UMC_INST(_ipid_lo) (((_ipid_lo) >> 21) & 0x7)
diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
index ecd7ead7a60b..8bb9592b0981 100644
--- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
@@ -1165,36 +1165,6 @@ static int uvd_v6_0_wait_for_idle(struct amdgpu_ip_block *ip_block)
}
#define AMDGPU_UVD_STATUS_BUSY_MASK 0xfd
-static bool uvd_v6_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 srbm_soft_reset = 0;
- u32 tmp = RREG32(mmSRBM_STATUS);
-
- if (REG_GET_FIELD(tmp, SRBM_STATUS, UVD_RQ_PENDING) ||
- REG_GET_FIELD(tmp, SRBM_STATUS, UVD_BUSY) ||
- (RREG32(mmUVD_STATUS) & AMDGPU_UVD_STATUS_BUSY_MASK))
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_UVD, 1);
-
- if (srbm_soft_reset) {
- adev->uvd.inst->srbm_soft_reset = srbm_soft_reset;
- return true;
- } else {
- adev->uvd.inst->srbm_soft_reset = 0;
- return false;
- }
-}
-
-static int uvd_v6_0_pre_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
-
- if (!adev->uvd.inst->srbm_soft_reset)
- return 0;
-
- uvd_v6_0_stop(adev);
- return 0;
-}
static int uvd_v6_0_soft_reset(struct amdgpu_ip_block *ip_block)
{
@@ -1227,18 +1197,6 @@ static int uvd_v6_0_soft_reset(struct amdgpu_ip_block *ip_block)
return 0;
}
-static int uvd_v6_0_post_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
-
- if (!adev->uvd.inst->srbm_soft_reset)
- return 0;
-
- mdelay(5);
-
- return uvd_v6_0_start(adev);
-}
-
static int uvd_v6_0_set_interrupt_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
unsigned type,
@@ -1538,10 +1496,7 @@ static const struct amd_ip_funcs uvd_v6_0_ip_funcs = {
.resume = uvd_v6_0_resume,
.is_idle = uvd_v6_0_is_idle,
.wait_for_idle = uvd_v6_0_wait_for_idle,
- .check_soft_reset = uvd_v6_0_check_soft_reset,
- .pre_soft_reset = uvd_v6_0_pre_soft_reset,
.soft_reset = uvd_v6_0_soft_reset,
- .post_soft_reset = uvd_v6_0_post_soft_reset,
.set_clockgating_state = uvd_v6_0_set_clockgating_state,
.set_powergating_state = uvd_v6_0_set_powergating_state,
.get_clockgating_state = uvd_v6_0_get_clockgating_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
index c69f7d82060f..9f4e88440c0a 100644
--- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
@@ -631,47 +631,6 @@ static int vce_v3_0_wait_for_idle(struct amdgpu_ip_block *ip_block)
#define AMDGPU_VCE_STATUS_BUSY_MASK (VCE_STATUS_VCPU_REPORT_AUTO_BUSY_MASK | \
VCE_STATUS_VCPU_REPORT_RB0_BUSY_MASK)
-static bool vce_v3_0_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
- u32 srbm_soft_reset = 0;
-
- /* According to VCE team , we should use VCE_STATUS instead
- * SRBM_STATUS.VCE_BUSY bit for busy status checking.
- * GRBM_GFX_INDEX.INSTANCE_INDEX is used to specify which VCE
- * instance's registers are accessed
- * (0 for 1st instance, 10 for 2nd instance).
- *
- *VCE_STATUS
- *|UENC|ACPI|AUTO ACTIVE|RB1 |RB0 |RB2 | |FW_LOADED|JOB |
- *|----+----+-----------+----+----+----+----------+---------+----|
- *|bit8|bit7| bit6 |bit5|bit4|bit3| bit2 | bit1 |bit0|
- *
- * VCE team suggest use bit 3--bit 6 for busy status check
- */
- mutex_lock(&adev->grbm_idx_mutex);
- WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(0));
- if (RREG32(mmVCE_STATUS) & AMDGPU_VCE_STATUS_BUSY_MASK) {
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE0, 1);
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1);
- }
- WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(1));
- if (RREG32(mmVCE_STATUS) & AMDGPU_VCE_STATUS_BUSY_MASK) {
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE0, 1);
- srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1);
- }
- WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(0));
- mutex_unlock(&adev->grbm_idx_mutex);
-
- if (srbm_soft_reset) {
- adev->vce.srbm_soft_reset = srbm_soft_reset;
- return true;
- } else {
- adev->vce.srbm_soft_reset = 0;
- return false;
- }
-}
-
static int vce_v3_0_soft_reset(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -703,31 +662,6 @@ static int vce_v3_0_soft_reset(struct amdgpu_ip_block *ip_block)
return 0;
}
-static int vce_v3_0_pre_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
-
- if (!adev->vce.srbm_soft_reset)
- return 0;
-
- mdelay(5);
-
- return vce_v3_0_suspend(ip_block);
-}
-
-
-static int vce_v3_0_post_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- struct amdgpu_device *adev = ip_block->adev;
-
- if (!adev->vce.srbm_soft_reset)
- return 0;
-
- mdelay(5);
-
- return vce_v3_0_resume(ip_block);
-}
-
static int vce_v3_0_set_interrupt_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
unsigned type,
@@ -909,10 +843,7 @@ static const struct amd_ip_funcs vce_v3_0_ip_funcs = {
.resume = vce_v3_0_resume,
.is_idle = vce_v3_0_is_idle,
.wait_for_idle = vce_v3_0_wait_for_idle,
- .check_soft_reset = vce_v3_0_check_soft_reset,
- .pre_soft_reset = vce_v3_0_pre_soft_reset,
.soft_reset = vce_v3_0_soft_reset,
- .post_soft_reset = vce_v3_0_post_soft_reset,
.set_clockgating_state = vce_v3_0_set_clockgating_state,
.set_powergating_state = vce_v3_0_set_powergating_state,
.get_clockgating_state = vce_v3_0_get_clockgating_state,
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c
index 8b8184fe6764..0d8a3cea63ee 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c
@@ -159,9 +159,8 @@ static void vcn_v2_5_ring_begin_use(struct amdgpu_ring *ring)
struct amdgpu_device *adev = ring->adev;
struct amdgpu_vcn_inst *v = &adev->vcn.inst[ring->me];
- atomic_inc(&adev->vcn.inst[0].total_submission_cnt);
-
- cancel_delayed_work_sync(&adev->vcn.inst[0].idle_work);
+ if (!atomic_fetch_inc(&adev->vcn.inst[0].total_submission_cnt))
+ cancel_delayed_work_sync(&adev->vcn.inst[0].idle_work);
/* We can safely return early here because we've cancelled the
* the delayed work so there is no one else to set it to false
@@ -207,10 +206,9 @@ static void vcn_v2_5_ring_end_use(struct amdgpu_ring *ring)
!adev->vcn.inst[ring->me].using_unified_queue)
atomic_dec(&adev->vcn.inst[ring->me].dpg_enc_submission_cnt);
- atomic_dec(&adev->vcn.inst[0].total_submission_cnt);
-
- schedule_delayed_work(&adev->vcn.inst[0].idle_work,
- VCN_IDLE_TIMEOUT);
+ if (atomic_dec_and_test(&adev->vcn.inst[0].total_submission_cnt))
+ schedule_delayed_work(&adev->vcn.inst[0].idle_work,
+ VCN_IDLE_TIMEOUT);
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
index 894780669f9c..0cce78b205a8 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c
@@ -1995,7 +1995,7 @@ static int vcn_v4_0_ring_reset(struct amdgpu_ring *ring,
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}
-static struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs = {
+static const struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs = {
.type = AMDGPU_RING_TYPE_VCN_ENC,
.align_mask = 0x3f,
.nop = VCN_ENC_CMD_NO_OP,
@@ -2028,6 +2028,40 @@ static struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs = {
.reset = vcn_v4_0_ring_reset,
};
+static const struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs_secure = {
+ .type = AMDGPU_RING_TYPE_VCN_ENC,
+ .align_mask = 0x3f,
+ .nop = VCN_ENC_CMD_NO_OP,
+ .secure_submission_supported = true,
+ .no_user_fence = true,
+ .extra_bytes = sizeof(struct amdgpu_vcn_rb_metadata),
+ .get_rptr = vcn_v4_0_unified_ring_get_rptr,
+ .get_wptr = vcn_v4_0_unified_ring_get_wptr,
+ .set_wptr = vcn_v4_0_unified_ring_set_wptr,
+ .patch_cs_in_place = vcn_v4_0_ring_patch_cs_in_place,
+ .emit_frame_size =
+ SOC15_FLUSH_GPU_TLB_NUM_WREG * 3 +
+ SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 4 +
+ 4 + /* vcn_v2_0_enc_ring_emit_vm_flush */
+ 5 + 5 + /* vcn_v2_0_enc_ring_emit_fence x2 vm fence */
+ 1, /* vcn_v2_0_enc_ring_insert_end */
+ .emit_ib_size = 5, /* vcn_v2_0_enc_ring_emit_ib */
+ .emit_ib = vcn_v2_0_enc_ring_emit_ib,
+ .emit_fence = vcn_v2_0_enc_ring_emit_fence,
+ .emit_vm_flush = vcn_v2_0_enc_ring_emit_vm_flush,
+ .test_ring = amdgpu_vcn_enc_ring_test_ring,
+ .test_ib = amdgpu_vcn_unified_ring_test_ib,
+ .insert_nop = amdgpu_ring_insert_nop,
+ .insert_end = vcn_v2_0_enc_ring_insert_end,
+ .pad_ib = amdgpu_ring_generic_pad_ib,
+ .begin_use = amdgpu_vcn_ring_begin_use,
+ .end_use = amdgpu_vcn_ring_end_use,
+ .emit_wreg = vcn_v2_0_enc_ring_emit_wreg,
+ .emit_reg_wait = vcn_v2_0_enc_ring_emit_reg_wait,
+ .emit_reg_write_reg_wait = amdgpu_ring_emit_reg_write_reg_wait_helper,
+ .reset = vcn_v4_0_ring_reset,
+};
+
/**
* vcn_v4_0_set_unified_ring_funcs - set unified ring functions
*
@@ -2044,10 +2078,11 @@ static void vcn_v4_0_set_unified_ring_funcs(struct amdgpu_device *adev)
continue;
if (amdgpu_ip_version(adev, VCN_HWIP, 0) == IP_VERSION(4, 0, 2))
- vcn_v4_0_unified_ring_vm_funcs.secure_submission_supported = true;
-
- adev->vcn.inst[i].ring_enc[0].funcs =
- (const struct amdgpu_ring_funcs *)&vcn_v4_0_unified_ring_vm_funcs;
+ adev->vcn.inst[i].ring_enc[0].funcs =
+ &vcn_v4_0_unified_ring_vm_funcs_secure;
+ else
+ adev->vcn.inst[i].ring_enc[0].funcs =
+ &vcn_v4_0_unified_ring_vm_funcs;
adev->vcn.inst[i].ring_enc[0].me = i;
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
index 7f001c32e911..179b892fb410 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
@@ -115,6 +115,19 @@ static int vcn_v4_0_3_early_init(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int i, r;
+ switch (amdgpu_user_queue) {
+ case -1:
+ case 0:
+ default:
+ adev->vcn.disable_kq = false;
+ adev->vcn.disable_uq = true;
+ break;
+ case 2:
+ adev->vcn.disable_kq = true;
+ adev->vcn.disable_uq = true;
+ break;
+ }
+
for (i = 0; i < adev->vcn.num_vcn_inst; ++i)
/* re-use enc ring as unified ring */
adev->vcn.inst[i].num_enc_rings = 1;
@@ -217,6 +230,10 @@ static int vcn_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block)
ring = &adev->vcn.inst[i].ring_enc[0];
ring->use_doorbell = true;
+ if (adev->vcn.disable_kq) {
+ ring->no_scheduler = true;
+ ring->no_user_submission = true;
+ }
if (!amdgpu_sriov_vf(adev))
ring->doorbell_index =
@@ -2146,71 +2163,6 @@ static const struct amdgpu_ras_block_hw_ops vcn_v4_0_3_ras_hw_ops = {
.query_poison_status = vcn_v4_0_3_query_poison_status,
};
-static int vcn_v4_0_3_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct aca_bank_info info;
- u64 misc0;
- int ret;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- misc0 = bank->regs[ACA_REG_IDX_MISC0];
- switch (type) {
- case ACA_SMU_TYPE_UE:
- bank->aca_err_type = ACA_ERROR_TYPE_UE;
- ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE,
- 1ULL);
- break;
- case ACA_SMU_TYPE_CE:
- bank->aca_err_type = ACA_ERROR_TYPE_CE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type,
- ACA_REG__MISC0__ERRCNT(misc0));
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
-/* reference to smu driver if header file */
-static int vcn_v4_0_3_err_codes[] = {
- 14, 15, /* VCN */
-};
-
-static bool vcn_v4_0_3_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- u32 instlo;
-
- instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]);
- instlo &= GENMASK(31, 1);
-
- if (instlo != mmSMNAID_AID0_MCA_SMU)
- return false;
-
- if (aca_bank_check_error_codes(handle->adev, bank,
- vcn_v4_0_3_err_codes,
- ARRAY_SIZE(vcn_v4_0_3_err_codes)))
- return false;
-
- return true;
-}
-
-static const struct aca_bank_ops vcn_v4_0_3_aca_bank_ops = {
- .aca_bank_parser = vcn_v4_0_3_aca_bank_parser,
- .aca_bank_is_valid = vcn_v4_0_3_aca_bank_is_valid,
-};
-
-static const struct aca_info vcn_v4_0_3_aca_info = {
- .hwip = ACA_HWIP_TYPE_SMU,
- .mask = ACA_ERROR_UE_MASK,
- .bank_ops = &vcn_v4_0_3_aca_bank_ops,
-};
-
static int vcn_v4_0_3_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
{
int r;
@@ -2226,11 +2178,6 @@ static int vcn_v4_0_3_ras_late_init(struct amdgpu_device *adev, struct ras_commo
goto late_fini;
}
- r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__VCN,
- &vcn_v4_0_3_aca_info, NULL);
- if (r)
- goto late_fini;
-
return 0;
late_fini:
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
index 1571cc5a148c..c8879a6e5297 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c
@@ -1479,10 +1479,11 @@ static int vcn_v4_0_5_ring_reset(struct amdgpu_ring *ring,
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}
-static struct amdgpu_ring_funcs vcn_v4_0_5_unified_ring_vm_funcs = {
+static const struct amdgpu_ring_funcs vcn_v4_0_5_unified_ring_vm_funcs = {
.type = AMDGPU_RING_TYPE_VCN_ENC,
.align_mask = 0x3f,
.nop = VCN_ENC_CMD_NO_OP,
+ .secure_submission_supported = true,
.no_user_fence = true,
.get_rptr = vcn_v4_0_5_unified_ring_get_rptr,
.get_wptr = vcn_v4_0_5_unified_ring_get_wptr,
@@ -1525,9 +1526,6 @@ static void vcn_v4_0_5_set_unified_ring_funcs(struct amdgpu_device *adev)
if (adev->vcn.harvest_config & (1 << i))
continue;
- if (amdgpu_ip_version(adev, VCN_HWIP, 0) == IP_VERSION(4, 0, 5))
- vcn_v4_0_5_unified_ring_vm_funcs.secure_submission_supported = true;
-
adev->vcn.inst[i].ring_enc[0].funcs = &vcn_v4_0_5_unified_ring_vm_funcs;
adev->vcn.inst[i].ring_enc[0].me = i;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
index d3db0494341e..1a07c3bf4425 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
@@ -94,6 +94,19 @@ static int vcn_v5_0_1_early_init(struct amdgpu_ip_block *ip_block)
struct amdgpu_device *adev = ip_block->adev;
int i, r;
+ switch (amdgpu_user_queue) {
+ case -1:
+ case 0:
+ default:
+ adev->vcn.disable_kq = false;
+ adev->vcn.disable_uq = true;
+ break;
+ case 2:
+ adev->vcn.disable_kq = true;
+ adev->vcn.disable_uq = true;
+ break;
+ }
+
for (i = 0; i < adev->vcn.num_vcn_inst; ++i)
/* re-use enc ring as unified ring */
adev->vcn.inst[i].num_enc_rings = 1;
@@ -188,6 +201,10 @@ static int vcn_v5_0_1_sw_init(struct amdgpu_ip_block *ip_block)
ring = &adev->vcn.inst[i].ring_enc[0];
ring->use_doorbell = true;
+ if (adev->vcn.disable_kq) {
+ ring->no_scheduler = true;
+ ring->no_user_submission = true;
+ }
if (!amdgpu_sriov_vf(adev))
ring->doorbell_index =
(adev->doorbell_index.vcn.vcn_ring0_1 << 1) +
@@ -1657,10 +1674,7 @@ static const struct amd_ip_funcs vcn_v5_0_1_ip_funcs = {
.resume = vcn_v5_0_1_resume,
.is_idle = vcn_v5_0_1_is_idle,
.wait_for_idle = vcn_v5_0_1_wait_for_idle,
- .check_soft_reset = NULL,
- .pre_soft_reset = NULL,
.soft_reset = NULL,
- .post_soft_reset = NULL,
.set_clockgating_state = vcn_v5_0_1_set_clockgating_state,
.set_powergating_state = vcn_set_powergating_state,
.dump_ip_state = amdgpu_vcn_dump_ip_state,
@@ -1713,71 +1727,6 @@ static const struct amdgpu_ras_block_hw_ops vcn_v5_0_1_ras_hw_ops = {
.query_poison_status = vcn_v5_0_1_query_poison_status,
};
-static int vcn_v5_0_1_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- struct aca_bank_info info;
- u64 misc0;
- int ret;
-
- ret = aca_bank_info_decode(bank, &info);
- if (ret)
- return ret;
-
- misc0 = bank->regs[ACA_REG_IDX_MISC0];
- switch (type) {
- case ACA_SMU_TYPE_UE:
- bank->aca_err_type = ACA_ERROR_TYPE_UE;
- ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE,
- 1ULL);
- break;
- case ACA_SMU_TYPE_CE:
- bank->aca_err_type = ACA_ERROR_TYPE_CE;
- ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type,
- ACA_REG__MISC0__ERRCNT(misc0));
- break;
- default:
- return -EINVAL;
- }
-
- return ret;
-}
-
-/* reference to smu driver if header file */
-static int vcn_v5_0_1_err_codes[] = {
- 14, 15, 47, /* VCN [D|V|S] */
-};
-
-static bool vcn_v5_0_1_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank,
- enum aca_smu_type type, void *data)
-{
- u32 instlo;
-
- instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]);
- instlo &= GENMASK(31, 1);
-
- if (instlo != mmSMNAID_AID0_MCA_SMU)
- return false;
-
- if (aca_bank_check_error_codes(handle->adev, bank,
- vcn_v5_0_1_err_codes,
- ARRAY_SIZE(vcn_v5_0_1_err_codes)))
- return false;
-
- return true;
-}
-
-static const struct aca_bank_ops vcn_v5_0_1_aca_bank_ops = {
- .aca_bank_parser = vcn_v5_0_1_aca_bank_parser,
- .aca_bank_is_valid = vcn_v5_0_1_aca_bank_is_valid,
-};
-
-static const struct aca_info vcn_v5_0_1_aca_info = {
- .hwip = ACA_HWIP_TYPE_SMU,
- .mask = ACA_ERROR_UE_MASK,
- .bank_ops = &vcn_v5_0_1_aca_bank_ops,
-};
-
static int vcn_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
{
int r;
@@ -1786,11 +1735,6 @@ static int vcn_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_commo
if (r)
return r;
- r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__VCN,
- &vcn_v5_0_1_aca_info, NULL);
- if (r)
- goto late_fini;
-
if (amdgpu_ras_is_supported(adev, ras_block->block) &&
adev->vcn.inst->ras_poison_irq.funcs) {
r = amdgpu_irq_get(adev, &adev->vcn.inst->ras_poison_irq, 0);
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
index bbc172db91a1..b9f6ae75ea72 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c
@@ -1203,10 +1203,7 @@ static const struct amd_ip_funcs vcn_v5_0_2_ip_funcs = {
.resume = vcn_v5_0_2_resume,
.is_idle = vcn_v5_0_2_is_idle,
.wait_for_idle = vcn_v5_0_2_wait_for_idle,
- .check_soft_reset = NULL,
- .pre_soft_reset = NULL,
.soft_reset = NULL,
- .post_soft_reset = NULL,
.set_clockgating_state = vcn_v5_0_2_set_clockgating_state,
.set_powergating_state = vcn_set_powergating_state,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c
index a256320b92f3..5715b6b596af 100644
--- a/drivers/gpu/drm/amd/amdgpu/vi.c
+++ b/drivers/gpu/drm/amd/amdgpu/vi.c
@@ -1328,27 +1328,6 @@ static void vi_invalidate_hdp(struct amdgpu_device *adev,
}
}
-static bool vi_need_full_reset(struct amdgpu_device *adev)
-{
- switch (adev->asic_type) {
- case CHIP_CARRIZO:
- case CHIP_STONEY:
- /* CZ has hang issues with full reset at the moment */
- return false;
- case CHIP_FIJI:
- case CHIP_TONGA:
- /* XXX: soft reset should work on fiji and tonga */
- return true;
- case CHIP_POLARIS10:
- case CHIP_POLARIS11:
- case CHIP_POLARIS12:
- case CHIP_TOPAZ:
- default:
- /* change this when we support soft reset */
- return true;
- }
-}
-
static void vi_get_pcie_usage(struct amdgpu_device *adev, uint64_t *count0,
uint64_t *count1)
{
@@ -1437,7 +1416,6 @@ static const struct amdgpu_asic_funcs vi_asic_funcs =
.get_config_memsize = &vi_get_config_memsize,
.flush_hdp = &vi_flush_hdp,
.invalidate_hdp = &vi_invalidate_hdp,
- .need_full_reset = &vi_need_full_reset,
.init_doorbell_index = &legacy_doorbell_index_init,
.get_pcie_usage = &vi_get_pcie_usage,
.need_reset_on_init = &vi_need_reset_on_init,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index c7edebd2fd8a..411ee894f623 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -1783,9 +1783,6 @@ static int kfd_ptl_control(struct kfd_process_device *pdd, bool enable)
uint32_t ptl_state = enable ? 1 : 0;
int ret;
- if (!ptl->hw_supported)
- return -EOPNOTSUPP;
-
if (!pdd->dev->kfd2kgd || !pdd->dev->kfd2kgd->ptl_ctrl)
return -EOPNOTSUPP;
@@ -1804,6 +1801,9 @@ int kfd_ptl_disable_request(struct kfd_process_device *pdd,
struct amdgpu_ptl *ptl = &adev->psp.ptl;
int ret = 0;
+ if (!ptl->hw_supported)
+ return -EOPNOTSUPP;
+
mutex_lock(&ptl->mutex);
if (pdd->ptl_disable_req)
@@ -1833,6 +1833,9 @@ int kfd_ptl_disable_release(struct kfd_process_device *pdd,
struct amdgpu_ptl *ptl = &adev->psp.ptl;
int ret = 0;
+ if (!ptl->hw_supported)
+ return -EOPNOTSUPP;
+
mutex_lock(&ptl->mutex);
if (!pdd->ptl_disable_req)
@@ -3734,7 +3737,8 @@ static int kfd_mmap(struct file *filep, struct vm_area_struct *vma)
return kfd_doorbell_mmap(dev, process, vma);
case KFD_MMAP_TYPE_EVENTS:
- return kfd_event_mmap(process, vma);
+ pr_warn("KFD_MMAP_TYPE_EVENTS is no longer supported\n");
+ return -EINVAL;
case KFD_MMAP_TYPE_RESERVED_MEM:
pr_warn("KFD_MMAP_TYPE_RESERVED_MEM is no longer supported\n");
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
index 008a0719fe1f..586e640f13dc 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
@@ -1806,6 +1806,30 @@ void kgd2kfd_teardown_processes(struct amdgpu_device *adev)
cond_resched();
}
+int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id,
+ int queue_type, int pipe, int queue,
+ unsigned int db)
+{
+ struct kfd_node *node;
+ int ret;
+
+ if (!kfd->init_complete)
+ return 0;
+
+ if (node_id >= kfd->num_nodes) {
+ dev_warn(kfd->adev->dev, "Invalid node ID: %u exceeds %u\n",
+ node_id, kfd->num_nodes - 1);
+ return -EINVAL;
+ }
+ node = kfd->nodes[node_id];
+
+ ret = kfd_reset_queue_mes(node->dqm, queue_type, pipe, queue, db);
+ if (ret)
+ dev_err(kfd_device, "Error resetting queue\n");
+
+ return ret;
+}
+
#if defined(CONFIG_DEBUG_FS)
/* This function will send a package to HIQ to hang the HWS
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 2e010c1f8828..97402e6c8f83 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -37,7 +37,8 @@
#include "amdgpu_amdkfd.h"
#include "amdgpu_reset.h"
#include "amdgpu_sdma.h"
-#include "mes_v11_api_def.h"
+#include "amdgpu_ring.h"
+#include "amdgpu_mes.h"
#include "kfd_debug.h"
/* Size of the per-pipe EOP queue */
@@ -71,12 +72,11 @@ static int allocate_sdma_queue(struct device_queue_manager *dqm,
struct queue *q, const uint32_t *restore_sdma_id);
static int reset_queues_on_hws_hang(struct device_queue_manager *dqm, bool is_sdma);
-static int resume_all_queues_mes(struct device_queue_manager *dqm);
-static int suspend_all_queues_mes(struct device_queue_manager *dqm);
static struct queue *find_queue_by_doorbell_offset(struct device_queue_manager *dqm,
u32 doorbell_offset);
static void set_queue_as_reset(struct device_queue_manager *dqm, struct queue *q,
struct qcm_process_device *qpd);
+static int reset_queues_mes(struct device_queue_manager *dqm, struct queue *q);
static inline
enum KFD_MQD_TYPE get_mqd_type_from_queue_type(enum kfd_queue_type type)
@@ -184,24 +184,24 @@ static void kfd_hws_hang(struct device_queue_manager *dqm)
amdgpu_amdkfd_gpu_reset(dqm->dev->adev);
}
-static int convert_to_mes_queue_type(int queue_type)
+static int convert_to_amdgpu_ring_type(int queue_type)
{
- int mes_queue_type;
+ int amdgpu_ring_type;
switch (queue_type) {
case KFD_QUEUE_TYPE_COMPUTE:
- mes_queue_type = MES_QUEUE_TYPE_COMPUTE;
+ amdgpu_ring_type = AMDGPU_RING_TYPE_COMPUTE;
break;
case KFD_QUEUE_TYPE_SDMA:
- mes_queue_type = MES_QUEUE_TYPE_SDMA;
+ amdgpu_ring_type = AMDGPU_RING_TYPE_SDMA;
break;
default:
WARN(1, "Invalid queue type %d", queue_type);
- mes_queue_type = -EINVAL;
+ amdgpu_ring_type = -EINVAL;
break;
}
- return mes_queue_type;
+ return amdgpu_ring_type;
}
static int add_queue_mes(struct device_queue_manager *dqm, struct queue *q,
@@ -251,7 +251,7 @@ static int add_queue_mes(struct device_queue_manager *dqm, struct queue *q,
(qpd->pqm->process->debug_trap_enabled ||
kfd_dbg_has_ttmps_always_setup(q->device));
- queue_type = convert_to_mes_queue_type(q->properties.type);
+ queue_type = convert_to_amdgpu_ring_type(q->properties.type);
if (queue_type < 0) {
dev_err(adev->dev, "Queue type not supported with MES, queue:%d\n",
q->properties.type);
@@ -300,6 +300,7 @@ static int remove_queue_mes_on_reset_option(struct device_queue_manager *dqm, st
memset(&queue_input, 0x0, sizeof(struct mes_remove_queue_input));
queue_input.doorbell_offset = q->properties.doorbell_off;
queue_input.gang_context_addr = q->gang_ctx_gpu_addr;
+ queue_input.queue_type = convert_to_amdgpu_ring_type(q->properties.type);
queue_input.remove_queue_after_reset = flush_mes_queue;
queue_input.xcc_id = ffs(dqm->dev->xcc_mask) - 1;
@@ -308,14 +309,14 @@ static int remove_queue_mes_on_reset_option(struct device_queue_manager *dqm, st
amdgpu_mes_unlock(&adev->mes);
up_read(&adev->reset_domain->sem);
- if (is_for_reset)
+ /* If is_for_reset set, it is a mes internal cleanup */
+ if (!r || is_for_reset)
return r;
- if (r) {
- if (!suspend_all_queues_mes(dqm))
- return resume_all_queues_mes(dqm);
-
- dev_err(adev->dev, "failed to remove hardware queue from MES, doorbell=0x%x\n",
+ /* remove_hw_queue failure indicates a queue hang. reset the queue */
+ r = reset_queues_mes(dqm, q);
+ if (r && amdgpu_gpu_recovery) {
+ dev_err(adev->dev, "failed to remove queue from MES, doorbell=0x%x\n",
q->properties.doorbell_off);
dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n");
kfd_hws_hang(dqm);
@@ -407,16 +408,50 @@ static int add_all_kfd_queues_mes(struct device_queue_manager *dqm)
return retval;
}
-static int reset_queues_mes(struct device_queue_manager *dqm)
+static int reset_queue_mes(struct device_queue_manager *dqm, struct queue *q,
+ int queue_type, int pipe, int queue, unsigned int db)
{
struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev;
- int hqd_info_size = adev->mes.hung_queue_hqd_info_offset;
- int num_hung = 0, r = 0, i, pipe, queue, queue_type;
- u32 *hung_array = dqm->hung_db_array;
- struct amdgpu_mes_hung_queue_hqd_info *hqd_info = dqm->hqd_info;
struct kfd_process_device *pdd;
+ bool use_mmio = adev->gfx.mec.use_mmio_for_reset;
+ int r;
+
+ pdd = kfd_get_process_device_data(q->device, q->process);
+ if (!pdd)
+ return -ENODEV;
+
+ if (use_mmio)
+ r = amdgpu_mes_reset_queue_mmio(adev, queue_type, 0, 1, pipe, queue,
+ ffs(dqm->dev->xcc_mask) - 1);
+ else
+ r = amdgpu_mes_reset_user_queue(adev, queue_type, db,
+ ffs(dqm->dev->xcc_mask) - 1);
+ if (r)
+ return r;
+ /* Proceed remove_queue with reset=true */
+ remove_queue_mes_on_reset_option(dqm, q, &pdd->qpd, true, true);
+ set_queue_as_reset(dqm, q, &pdd->qpd);
+ return 0;
+}
+
+int kfd_reset_queue_mes(struct device_queue_manager *dqm, int queue_type,
+ int pipe, int queue, unsigned int db)
+{
struct queue *q;
+ q = find_queue_by_doorbell_offset(dqm, db);
+ if (!q)
+ return 0;
+ return reset_queue_mes(dqm, q, queue_type, pipe, queue, db);
+}
+
+static int reset_queues_mes(struct device_queue_manager *dqm, struct queue *q)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev;
+ unsigned int num_hung = 0;
+ int r = 0;
+ struct mes_remove_queue_input queue_input;
+
if (!amdgpu_mes_queue_reset_by_mes_supported(adev)) {
r = -ENOTRECOVERABLE;
goto fail;
@@ -431,111 +466,29 @@ static int reset_queues_mes(struct device_queue_manager *dqm)
goto fail;
}
- if (!hung_array || !hqd_info) {
- r = -ENOMEM;
- goto fail;
- }
-
- memset(hqd_info, 0, hqd_info_size * sizeof(struct amdgpu_mes_hung_queue_hqd_info));
-
- /*
- * AMDGPU_RING_TYPE_COMPUTE parameter does not matter if called
- * post suspend_all as reset & detect will return all hung queue types.
- *
- * Passed parameter is for targeting queues not scheduled by MES add_queue.
- */
- r = amdgpu_mes_detect_and_reset_hung_queues(adev, AMDGPU_RING_TYPE_COMPUTE,
- false, &num_hung, hung_array, ffs(dqm->dev->xcc_mask) - 1);
-
- if (!num_hung || r) {
- r = -ENOTRECOVERABLE;
+ memset(&queue_input, 0x0, sizeof(struct mes_remove_queue_input));
+ queue_input.doorbell_offset = q->properties.doorbell_off;
+ queue_input.gang_context_addr = q->gang_ctx_gpu_addr;
+ queue_input.queue_type = convert_to_amdgpu_ring_type(q->properties.type);
+ queue_input.remove_queue_after_reset = false;
+ queue_input.xcc_id = ffs(dqm->dev->xcc_mask) - 1;
+ /* pass the known bad queue info to the reset function */
+ r = amdgpu_gfx_reset_mes_compute(adev, NULL, NULL, NULL, &num_hung, &queue_input);
+ if (r)
goto fail;
- }
-
- /* MES resets queue/pipe and cleans up internally */
- for (i = 0; i < num_hung; i++) {
- hqd_info[i].bit0_31 = hung_array[i + hqd_info_size];
- pipe = hqd_info[i].pipe_index;
- queue = hqd_info[i].queue_index;
- queue_type = hqd_info[i].queue_type;
-
- if (queue_type != MES_QUEUE_TYPE_COMPUTE &&
- queue_type != MES_QUEUE_TYPE_SDMA) {
- pr_warn("Unsupported hung queue reset type: %d\n", queue_type);
- hung_array[i] = AMDGPU_MES_INVALID_DB_OFFSET;
- continue;
- }
-
- q = find_queue_by_doorbell_offset(dqm, hung_array[i]);
- if (!q) {
- r = -ENOTRECOVERABLE;
- goto fail;
- }
-
- pdd = kfd_get_process_device_data(q->device, q->process);
- if (!pdd) {
- r = -ENODEV;
- goto fail;
- }
-
- pr_warn("Hang detected doorbell %x pipe %d queue %d type %d\n",
- hung_array[i], pipe, queue, queue_type);
- /* Proceed remove_queue with reset=true */
- remove_queue_mes_on_reset_option(dqm, q, &pdd->qpd, true, false);
- set_queue_as_reset(dqm, q, &pdd->qpd);
- }
dqm->detect_hang_count = num_hung;
- kfd_signal_reset_event(dqm->dev);
+ /* When MES doesn't detect any queue hang, no reset happens. Don't signal reset
+ * event.
+ */
+ if (dqm->detect_hang_count)
+ kfd_signal_reset_event(dqm->dev);
fail:
dqm->detect_hang_count = 0;
return r;
}
-static int suspend_all_queues_mes(struct device_queue_manager *dqm)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev;
- int r = 0;
-
- if (!down_read_trylock(&adev->reset_domain->sem))
- return -EIO;
-
- r = amdgpu_mes_suspend(adev, ffs(dqm->dev->xcc_mask) - 1);
- up_read(&adev->reset_domain->sem);
-
- if (r) {
- if (!reset_queues_mes(dqm))
- return 0;
-
- dev_err(adev->dev, "failed to suspend gangs from MES\n");
- dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n");
- kfd_hws_hang(dqm);
- }
-
- return r;
-}
-
-static int resume_all_queues_mes(struct device_queue_manager *dqm)
-{
- struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev;
- int r = 0;
-
- if (!down_read_trylock(&adev->reset_domain->sem))
- return -EIO;
-
- r = amdgpu_mes_resume(adev, ffs(dqm->dev->xcc_mask) - 1);
- up_read(&adev->reset_domain->sem);
-
- if (r) {
- dev_err(adev->dev, "failed to resume gangs from MES\n");
- dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n");
- kfd_hws_hang(dqm);
- }
-
- return r;
-}
-
static void increment_queue_count(struct device_queue_manager *dqm,
struct qcm_process_device *qpd,
struct queue *q)
@@ -1064,8 +1017,15 @@ static int destroy_queue_nocpsch(struct device_queue_manager *dqm,
/* Get the SDMA queue stats */
if ((q->properties.type == KFD_QUEUE_TYPE_SDMA) ||
(q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI)) {
- retval = read_sdma_queue_counter((uint64_t __user *)q->properties.read_ptr,
- &sdma_val);
+ if (dqm->dev->kfd2kgd->hqd_sdma_get_counter)
+ retval = dqm->dev->kfd2kgd->hqd_sdma_get_counter(
+ dqm->dev->adev, q->mqd,
+ dqm->dev->kfd->device_info.num_sdma_queues_per_engine,
+ &sdma_val);
+ else
+ retval = read_sdma_queue_counter(
+ (uint64_t __user *)q->properties.read_ptr,
+ &sdma_val);
if (retval)
dev_err(dev, "Failed to read SDMA queue counter for queue: %d\n",
q->properties.queue_id);
@@ -2703,8 +2663,16 @@ static int destroy_queue_cpsch(struct device_queue_manager *dqm,
/* Get the SDMA queue stats */
if ((q->properties.type == KFD_QUEUE_TYPE_SDMA) ||
(q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI)) {
- retval = read_sdma_queue_counter((uint64_t __user *)q->properties.read_ptr,
- &sdma_val);
+ if (dqm->dev->kfd2kgd->hqd_sdma_get_counter)
+ retval = dqm->dev->kfd2kgd->hqd_sdma_get_counter(
+ dqm->dev->adev, q->mqd,
+ dqm->dev->kfd->device_info.num_sdma_queues_per_engine,
+ &sdma_val);
+ else
+ retval = read_sdma_queue_counter(
+ (uint64_t __user *)q->properties.read_ptr,
+ &sdma_val);
+
if (retval)
dev_err(dev, "Failed to read SDMA queue counter for queue: %d\n",
q->properties.queue_id);
@@ -3244,12 +3212,12 @@ void device_queue_manager_uninit(struct device_queue_manager *dqm)
kfree(dqm);
}
+/* bad queue notified by interrupt from CP */
int kfd_dqm_suspend_bad_queue_mes(struct kfd_node *knode, u32 pasid, u32 doorbell_id)
{
struct kfd_process_device *pdd = NULL;
struct kfd_process *p = kfd_lookup_process_by_pasid(pasid, &pdd);
struct device_queue_manager *dqm = knode->dqm;
- struct device *dev = dqm->dev->adev->dev;
struct qcm_process_device *qpd;
struct queue *q = NULL;
int ret = 0;
@@ -3264,19 +3232,10 @@ int kfd_dqm_suspend_bad_queue_mes(struct kfd_node *knode, u32 pasid, u32 doorbel
list_for_each_entry(q, &qpd->queues_list, list) {
if (q->doorbell_id == doorbell_id && q->properties.is_active) {
- /* suspend all queues will save any good queues and mark the rest as bad */
- suspend_all_queues_mes(dqm);
-
+ reset_queues_mes(dqm, q);
q->properties.is_evicted = true;
q->properties.is_active = false;
decrement_queue_count(dqm, qpd, q);
-
- /* this will remove the bad queue and sched a GPU reset if needed */
- ret = remove_queue_mes(dqm, q, qpd);
- if (ret)
- dev_err(dev, "Removing bad queue failed");
- /* resume the good queues */
- resume_all_queues_mes(dqm);
break;
}
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
index e0b6a47e7722..2229f8b2f446 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
@@ -333,6 +333,8 @@ int debug_refresh_runlist(struct device_queue_manager *dqm);
bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm,
struct qcm_process_device *qpd,
int doorbell_off, u32 *queue_format);
+int kfd_reset_queue_mes(struct device_queue_manager *dqm, int queue_type,
+ int pipe, int queue, unsigned int db);
static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd)
{
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index 81900b49d9d5..dae01e2bb464 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -29,10 +29,12 @@
#include <linux/uaccess.h>
#include <linux/mman.h>
#include <linux/memory.h>
+#include <linux/workqueue.h>
#include "kfd_priv.h"
#include "kfd_events.h"
#include "kfd_device_queue_manager.h"
#include <linux/device.h>
+#include <drm/amdgpu_drm.h>
/*
* Wrapper around wait_queue_entry_t
@@ -44,67 +46,19 @@ struct kfd_event_waiter {
bool event_age_enabled; /* set to true when last_event_age is non-zero */
};
-/*
- * Each signal event needs a 64-bit signal slot where the signaler will write
- * a 1 before sending an interrupt. (This is needed because some interrupts
- * do not contain enough spare data bits to identify an event.)
- * We get whole pages and map them to the process VA.
- * Individual signal events use their event_id as slot index.
- */
-struct kfd_signal_page {
- uint64_t *kernel_address;
- uint64_t __user *user_address;
- bool need_to_free_pages;
-};
-
-static uint64_t *page_slots(struct kfd_signal_page *page)
-{
- return page->kernel_address;
-}
-
-static struct kfd_signal_page *allocate_signal_page(struct kfd_process *p)
-{
- void *backing_store;
- struct kfd_signal_page *page;
-
- page = kzalloc_obj(*page);
- if (!page)
- return NULL;
-
- backing_store = (void *) __get_free_pages(GFP_KERNEL,
- get_order(KFD_SIGNAL_EVENT_LIMIT * 8));
- if (!backing_store)
- goto fail_alloc_signal_store;
-
- /* Initialize all events to unsignaled */
- memset(backing_store, (uint8_t) UNSIGNALED_EVENT_SLOT,
- KFD_SIGNAL_EVENT_LIMIT * 8);
-
- page->kernel_address = backing_store;
- page->need_to_free_pages = true;
- pr_debug("Allocated new event signal page at %p, for process %p\n",
- page, p);
-
- return page;
-
-fail_alloc_signal_store:
- kfree(page);
- return NULL;
-}
-
static int allocate_event_notification_slot(struct kfd_process *p,
struct kfd_event *ev,
const int *restore_id)
{
int id;
- if (!p->signal_page) {
- p->signal_page = allocate_signal_page(p);
- if (!p->signal_page)
- return -ENOMEM;
- /* Oldest user mode expects 256 event slots */
- p->signal_mapped_size = 256*8;
- }
+ /*
+ * The signal page is allocated in user mode and mapped to the kernel
+ * via the event_page_offset of the create event IOCTL. Without it no
+ * signal events can be created.
+ */
+ if (!p->signal_page)
+ return -ENOMEM;
if (restore_id) {
id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1,
@@ -123,7 +77,7 @@ static int allocate_event_notification_slot(struct kfd_process *p,
return id;
ev->event_id = id;
- page_slots(p->signal_page)[id] = UNSIGNALED_EVENT_SLOT;
+ p->signal_page[id] = UNSIGNALED_EVENT_SLOT;
return 0;
}
@@ -169,7 +123,7 @@ static struct kfd_event *lookup_signaled_event_by_partial_id(
*/
if (bits > 31 || (1U << bits) >= KFD_SIGNAL_EVENT_LIMIT) {
if (signal_mailbox_updated &&
- page_slots(p->signal_page)[id] == UNSIGNALED_EVENT_SLOT)
+ p->signal_page[id] == UNSIGNALED_EVENT_SLOT)
return NULL;
return idr_find(&p->event_idr, id);
@@ -179,7 +133,7 @@ static struct kfd_event *lookup_signaled_event_by_partial_id(
* and find the first one that has signaled.
*/
for (ev = NULL; id < KFD_SIGNAL_EVENT_LIMIT && !ev; id += 1U << bits) {
- if (page_slots(p->signal_page)[id] == UNSIGNALED_EVENT_SLOT)
+ if (p->signal_page[id] == UNSIGNALED_EVENT_SLOT)
continue;
ev = idr_find(&p->event_idr, id);
@@ -210,10 +164,8 @@ static int create_signal_event(struct file *devkfd, struct kfd_process *p,
p->signal_event_count++;
- ev->user_signal_address = &p->signal_page->user_address[ev->event_id];
- pr_debug("Signal event number %zu created with id %d, address %p\n",
- p->signal_event_count, ev->event_id,
- ev->user_signal_address);
+ pr_debug("Signal event number %zu created with id %d\n",
+ p->signal_event_count, ev->event_id);
return 0;
}
@@ -293,26 +245,9 @@ static void destroy_events(struct kfd_process *p)
mutex_destroy(&p->event_mutex);
}
-/*
- * We assume that the process is being destroyed and there is no need to
- * unmap the pages or keep bookkeeping data in order.
- */
-static void shutdown_signal_page(struct kfd_process *p)
-{
- struct kfd_signal_page *page = p->signal_page;
-
- if (page) {
- if (page->need_to_free_pages)
- free_pages((unsigned long)page->kernel_address,
- get_order(KFD_SIGNAL_EVENT_LIMIT * 8));
- kfree(page);
- }
-}
-
void kfd_event_free_process(struct kfd_process *p)
{
destroy_events(p);
- shutdown_signal_page(p);
}
static bool event_can_be_gpu_signaled(const struct kfd_event *ev)
@@ -329,8 +264,6 @@ static bool event_can_be_cpu_signaled(const struct kfd_event *ev)
static int kfd_event_page_set(struct kfd_process *p, void *kernel_address,
uint64_t size, uint64_t user_handle)
{
- struct kfd_signal_page *page;
-
if (p->signal_page)
return -EBUSY;
@@ -340,17 +273,11 @@ static int kfd_event_page_set(struct kfd_process *p, void *kernel_address,
return -EINVAL;
}
- page = kzalloc_obj(*page);
- if (!page)
- return -ENOMEM;
-
/* Initialize all events to unsignaled */
memset(kernel_address, (uint8_t) UNSIGNALED_EVENT_SLOT,
KFD_SIGNAL_EVENT_LIMIT * 8);
- page->kernel_address = kernel_address;
-
- p->signal_page = page;
+ p->signal_page = kernel_address;
p->signal_mapped_size = size;
p->signal_handle = user_handle;
return 0;
@@ -387,7 +314,8 @@ int kfd_kmap_event_page(struct kfd_process *p, uint64_t event_page_offset)
return -EINVAL;
}
- err = amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(mem, &kern_addr, &size);
+ err = amdgpu_amdkfd_gpuvm_map_bo_to_kernel(mem, &kern_addr, &size,
+ AMDGPU_GEM_DOMAIN_GTT);
if (err) {
pr_err("Failed to map event page to kernel\n");
return err;
@@ -396,7 +324,7 @@ int kfd_kmap_event_page(struct kfd_process *p, uint64_t event_page_offset)
err = kfd_event_page_set(p, kern_addr, size, event_page_offset);
if (err) {
pr_err("Failed to set event page\n");
- amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(mem);
+ amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(mem);
return err;
}
return err;
@@ -717,7 +645,7 @@ unlock_rcu:
static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev)
{
- WRITE_ONCE(page_slots(p->signal_page)[ev->event_id], UNSIGNALED_EVENT_SLOT);
+ WRITE_ONCE(p->signal_page[ev->event_id], UNSIGNALED_EVENT_SLOT);
}
static void set_event_from_interrupt(struct kfd_process *p,
@@ -760,7 +688,7 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
* in the interrupt payload was invalid and do an
* exhaustive search of signaled events.
*/
- uint64_t *slots = page_slots(p->signal_page);
+ uint64_t *slots = p->signal_page;
uint32_t id;
if (valid_id_bits)
@@ -1068,51 +996,6 @@ out:
return ret;
}
-int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma)
-{
- unsigned long pfn;
- struct kfd_signal_page *page;
- int ret;
-
- /* check required size doesn't exceed the allocated size */
- if (get_order(KFD_SIGNAL_EVENT_LIMIT * 8) <
- get_order(vma->vm_end - vma->vm_start)) {
- pr_err("Event page mmap requested illegal size\n");
- return -EINVAL;
- }
-
- page = p->signal_page;
- if (!page) {
- /* Probably KFD bug, but mmap is user-accessible. */
- pr_debug("Signal page could not be found\n");
- return -EINVAL;
- }
-
- pfn = __pa(page->kernel_address);
- pfn >>= PAGE_SHIFT;
-
- vm_flags_set(vma, VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE
- | VM_DONTDUMP | VM_PFNMAP);
-
- pr_debug("Mapping signal page\n");
- pr_debug(" start user address == 0x%08lx\n", vma->vm_start);
- pr_debug(" end user address == 0x%08lx\n", vma->vm_end);
- pr_debug(" pfn == 0x%016lX\n", pfn);
- pr_debug(" vm_flags == 0x%08lX\n", vma->vm_flags);
- pr_debug(" size == 0x%08lX\n",
- vma->vm_end - vma->vm_start);
-
- page->user_address = (uint64_t __user *)vma->vm_start;
-
- /* mapping the page to user process */
- ret = remap_pfn_range(vma, vma->vm_start, pfn,
- vma->vm_end - vma->vm_start, vma->vm_page_prot);
- if (!ret)
- p->signal_mapped_size = vma->vm_end - vma->vm_start;
-
- return ret;
-}
-
/*
* Assumes that p is not going away.
*/
@@ -1338,6 +1221,71 @@ void kfd_signal_reset_event(struct kfd_node *dev)
srcu_read_unlock(&kfd_processes_srcu, idx);
}
+/*
+ * Per-process opt-in for poison-consumption SIGBUS handling.
+ *
+ * Default: kernel sends SIGBUS to the process immediately when poison is
+ * consumed, in addition to delivering the KFD HW/MEMORY exception events.
+ *
+ * Userspace (ROCr) can opt-in per-process via the
+ * DRM_IOCTL_AMDGPU_PROC_OPTIONS / AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY
+ * option. This lets the app's registered system-event callback handle the
+ * RAS error first, instead of being killed by SIGBUS.
+ *
+ * Encoded value (stored on the kfd_process):
+ * 0 - default: SIGBUS immediately (no opt-in)
+ * 0xFFFFFFFF - opt-in, never escalate to SIGBUS
+ * N (other) - opt-in, escalate to SIGBUS after N ms if app does not
+ * handle the error in time (safety timeout)
+ */
+
+void kfd_signal_sigbus_delayed_fn(struct work_struct *work)
+{
+ struct kfd_process *p = container_of(to_delayed_work(work),
+ struct kfd_process, signal_work);
+
+ if (p->lead_thread)
+ send_sig(SIGBUS, p->lead_thread, 0);
+
+ kfd_unref_process(p);
+}
+
+static void kfd_signal_sigbus_with_delay(struct kfd_node *dev,
+ struct kfd_process *p)
+{
+ u32 delay_ms = atomic_read(&p->kfd_sigbus_delay_ms);
+
+ if (delay_ms == AMDGPU_PROC_OPTIONS_KFD_SIGBUS_DELAY_DISABLED) {
+ dev_info(dev->adev->dev,
+ "SIGBUS suppressed for process %s(pid:%d): app opted in to handle RAS error\n",
+ p->lead_thread->comm, p->lead_thread->pid);
+ return;
+ }
+
+ if (delay_ms == 0)
+ goto send_now;
+
+ /*
+ * Take an extra reference for the delayed worker. If the work is
+ * already pending (e.g. another device of this process consumed poison
+ * just before), drop the reference and skip rescheduling - the process
+ * only needs to be notified once.
+ */
+ kref_get(&p->ref);
+ if (!schedule_delayed_work(&p->signal_work, msecs_to_jiffies(delay_ms))) {
+ kfd_unref_process(p);
+ return;
+ }
+
+ dev_info(dev->adev->dev,
+ "Deferring SIGBUS to process %s(pid:%d) by %u ms (RAS error opt-in safety timeout)\n",
+ p->lead_thread->comm, p->lead_thread->pid, delay_ms);
+ return;
+
+send_now:
+ send_sig(SIGBUS, p->lead_thread, 0);
+}
+
void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid)
{
struct kfd_process *p = kfd_lookup_process_by_pasid(pasid, NULL);
@@ -1392,7 +1340,7 @@ void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid)
rcu_read_unlock();
/* user application will handle SIGBUS signal */
- send_sig(SIGBUS, p->lead_thread, 0);
+ kfd_signal_sigbus_with_delay(dev, p);
kfd_unref_process(p);
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.h b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
index 1dc21c13833b..827a2c7d7721 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.h
@@ -49,7 +49,6 @@
#define UNSIGNALED_EVENT_SLOT ((uint64_t)-1)
struct kfd_event_waiter;
-struct signal_page;
struct kfd_event {
u32 event_id;
@@ -63,9 +62,6 @@ struct kfd_event {
spinlock_t lock;
wait_queue_head_t wq; /* List of event waiters. */
- /* Only for signal events. */
- uint64_t __user *user_signal_address;
-
/* type specific data */
union {
struct kfd_hsa_memory_exception_data memory_exception_data;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c
index 723b725d20b8..9b7859a77950 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c
@@ -315,3 +315,19 @@ bool kfd_check_hiq_mqd_doorbell_id(struct kfd_node *node, uint32_t doorbell_id,
return false;
}
+
+bool mqd_on_vram(struct amdgpu_device *adev)
+{
+ if (adev->apu_prefer_gtt)
+ return false;
+
+ switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
+ case IP_VERSION(9, 4, 2):
+ case IP_VERSION(9, 4, 3):
+ case IP_VERSION(9, 4, 4):
+ case IP_VERSION(9, 5, 0):
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
index 63ea70e5c0e6..59eff3389d39 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
@@ -202,4 +202,6 @@ uint64_t kfd_mqd_stride(struct mqd_manager *mm,
struct queue_properties *q);
bool kfd_check_hiq_mqd_doorbell_id(struct kfd_node *node, uint32_t doorbell_id,
uint32_t inst);
+bool mqd_on_vram(struct amdgpu_device *adev);
+
#endif /* KFD_MQD_MANAGER_H_ */
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
index be99f0d53b18..75e5a9f67d50 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
@@ -116,20 +116,6 @@ static void set_priority(struct v9_mqd *m, struct queue_properties *q)
m->cp_hqd_pipe_priority = pipe_priority_map[q->priority];
}
-static bool mqd_on_vram(struct amdgpu_device *adev)
-{
- if (adev->apu_prefer_gtt)
- return false;
-
- switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
- case IP_VERSION(9, 4, 3):
- case IP_VERSION(9, 5, 0):
- return true;
- default:
- return false;
- }
-}
-
static struct kfd_mem_obj *allocate_mqd(struct mqd_manager *mm,
struct queue_properties *q)
{
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index acd0e41e744c..90f010cbe54e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -952,11 +952,32 @@ struct kfd_process {
struct idr event_idr;
/* Event page */
u64 signal_handle;
- struct kfd_signal_page *signal_page;
+ /*
+ * Each signal event needs a 64-bit signal slot where the signaler will
+ * write a 1 before sending an interrupt. (This is needed because some
+ * interrupts do not contain enough spare data bits to identify an
+ * event.) The signal page is allocated in user mode and mapped to the
+ * kernel; individual signal events use their event_id as slot index.
+ */
+ uint64_t *signal_page;
size_t signal_mapped_size;
size_t signal_event_count;
bool signal_event_limit_reached;
+ /**
+ * @kfd_sigbus_delay_ms: Per-process KFD SIGBUS delivery option for
+ * poison/RAS events (set via DRM_IOCTL_AMDGPU_PROC_OPTIONS /
+ * AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY).
+ *
+ * 0 - send SIGBUS immediately (default)
+ * 0xFFFFFFFF - suppress SIGBUS delivery
+ * other - delay SIGBUS delivery by this many milliseconds
+ */
+ atomic_t kfd_sigbus_delay_ms;
+
+ /* Delayed signal delivery to user */
+ struct delayed_work signal_work;
+
/* Information used for memory eviction */
void *kgd_process_info;
/* Eviction fence that is attached to all the BOs of this process. The
@@ -1525,7 +1546,6 @@ extern const struct kfd_device_global_init_class device_global_init_class_cik;
int kfd_event_init_process(struct kfd_process *p);
void kfd_event_free_process(struct kfd_process *p);
-int kfd_event_mmap(struct kfd_process *process, struct vm_area_struct *vma);
int kfd_wait_on_events(struct kfd_process *p,
uint32_t num_events, void __user *data,
bool all, uint32_t *user_timeout_ms,
@@ -1554,6 +1574,7 @@ void kfd_signal_vm_fault_event(struct kfd_process_device *pdd,
void kfd_signal_reset_event(struct kfd_node *dev);
void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid);
+void kfd_signal_sigbus_delayed_fn(struct work_struct *work);
void kfd_signal_process_terminate_event(struct kfd_process *p);
static inline void kfd_flush_tlb(struct kfd_process_device *pdd)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index ca71fa726e32..767c2cc8e29e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -153,6 +153,21 @@ static void kfd_sdma_activity_worker(struct work_struct *work)
(q->properties.type != KFD_QUEUE_TYPE_SDMA_XGMI))
continue;
+ if (dqm->dev->kfd2kgd->hqd_sdma_get_counter) {
+ val = 0;
+ ret = dqm->dev->kfd2kgd->hqd_sdma_get_counter(
+ dqm->dev->adev, q->mqd,
+ dqm->dev->kfd->device_info.num_sdma_queues_per_engine,
+ &val);
+
+ if (ret)
+ pr_debug("Failed to read SDMA queue active counter %i\n", ret);
+ else
+ workarea->sdma_activity_counter += val;
+
+ continue;
+ }
+
sdma_q = kzalloc_obj(struct temp_sdma_queue_list);
if (!sdma_q) {
dqm_unlock(dqm);
@@ -171,7 +186,7 @@ static void kfd_sdma_activity_worker(struct work_struct *work)
* count
*/
if (list_empty(&sdma_q_list.list)) {
- workarea->sdma_activity_counter = pdd->sdma_past_activity_counter;
+ workarea->sdma_activity_counter += pdd->sdma_past_activity_counter;
dqm_unlock(dqm);
return;
}
@@ -721,7 +736,7 @@ static void kfd_process_free_gpuvm(struct kgd_mem *mem,
struct kfd_node *dev = pdd->dev;
if (kptr && *kptr) {
- amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(mem);
+ amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(mem);
*kptr = NULL;
}
@@ -761,10 +776,16 @@ static int kfd_process_alloc_gpuvm(struct kfd_process_device *pdd,
}
if (kptr) {
- err = amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(
- (struct kgd_mem *)*mem, kptr, NULL);
+ u32 domain;
+
+ if (flags & KFD_IOC_ALLOC_MEM_FLAGS_VRAM)
+ domain = AMDGPU_GEM_DOMAIN_VRAM;
+ else
+ domain = AMDGPU_GEM_DOMAIN_GTT;
+ err = amdgpu_amdkfd_gpuvm_map_bo_to_kernel((struct kgd_mem *)*mem,
+ kptr, NULL, domain);
if (err) {
- pr_debug("Map GTT BO to kernel failed\n");
+ pr_debug("Map BO to kernel failed err %d\n", err);
goto sync_memory_failed;
}
}
@@ -986,6 +1007,33 @@ out:
return process;
}
+/**
+ * amdgpu_amdkfd_set_sigbus_delay - Set per-process KFD SIGBUS delay
+ * @task: task in the target process
+ * @ms: encoded delay value (0 = immediate, 0xFFFFFFFF = suppress,
+ * otherwise delay in milliseconds)
+ *
+ * Stores the SIGBUS delivery option on the kfd_process associated with
+ * @task. If the calling process has not opened /dev/kfd yet (no
+ * kfd_process exists), this is a no-op - the option only applies to
+ * processes that actually use KFD.
+ */
+int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms)
+{
+ struct kfd_process *p;
+
+ if (!task->mm)
+ return -EINVAL;
+
+ p = kfd_lookup_process_by_mm(task->mm);
+ if (!p)
+ return 0;
+
+ atomic_set(&p->kfd_sigbus_delay_ms, ms);
+ kfd_unref_process(p);
+ return 0;
+}
+
static struct kfd_process *find_process_by_mm(const struct mm_struct *mm)
{
struct kfd_process *process;
@@ -1092,7 +1140,7 @@ static void kfd_process_kunmap_signal_bo(struct kfd_process *p)
if (!mem)
goto out;
- amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(mem);
+ amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(mem);
out:
mutex_unlock(&p->mutex);
@@ -1330,6 +1378,11 @@ void kfd_process_notifier_release_internal(struct kfd_process *p)
kfd_process_table_remove(p);
cancel_delayed_work_sync(&p->eviction_work);
cancel_delayed_work_sync(&p->restore_work);
+ /*
+ * If work pending, cancel it and drop the extra ref
+ */
+ if (cancel_delayed_work_sync(&p->signal_work))
+ kfd_unref_process(p);
/*
* Dequeue and destroy user queues, it is not safe for GPU to access
@@ -1436,8 +1489,7 @@ static int kfd_process_device_init_cwsr_dgpu(struct kfd_process_device *pdd)
{
struct kfd_node *dev = pdd->dev;
struct qcm_process_device *qpd = &pdd->qpd;
- uint32_t flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT
- | KFD_IOC_ALLOC_MEM_FLAGS_NO_SUBSTITUTE
+ u32 flags = KFD_IOC_ALLOC_MEM_FLAGS_NO_SUBSTITUTE
| KFD_IOC_ALLOC_MEM_FLAGS_EXECUTABLE;
struct kgd_mem *mem;
void *kaddr;
@@ -1446,7 +1498,12 @@ static int kfd_process_device_init_cwsr_dgpu(struct kfd_process_device *pdd)
if (!dev->kfd->cwsr_enabled || qpd->cwsr_kaddr || !qpd->cwsr_base)
return 0;
- /* cwsr_base is only set for dGPU */
+ if (KFD_GC_VERSION(dev) >= IP_VERSION(9, 4, 2) && !dev->adev->apu_prefer_gtt)
+ flags |= KFD_IOC_ALLOC_MEM_FLAGS_VRAM;
+ else
+ flags |= KFD_IOC_ALLOC_MEM_FLAGS_GTT;
+
+ /* Allocate CWSR TBA/TMA buffers */
ret = kfd_process_alloc_gpuvm(pdd, qpd->cwsr_base,
KFD_CWSR_TBA_TMA_SIZE, flags, &mem, &kaddr);
if (ret)
@@ -1586,6 +1643,7 @@ struct kfd_process *create_process(const struct task_struct *thread, bool primar
INIT_DELAYED_WORK(&process->eviction_work, evict_process_worker);
INIT_DELAYED_WORK(&process->restore_work, restore_process_worker);
+ INIT_DELAYED_WORK(&process->signal_work, kfd_signal_sigbus_delayed_fn);
process->last_restore_timestamp = get_jiffies_64();
err = kfd_event_init_process(process);
if (err)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index 0ac35789b239..acbdca91cde5 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -1040,7 +1040,13 @@ int kfd_criu_restore_queue(struct kfd_process *p,
ctl_stack = mqd + q_data->mqd_size;
memset(&qp, 0, sizeof(qp));
- set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->adev->gfx.xcc_mask));
+ set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->xcc_mask));
+
+ ret = kfd_queue_acquire_buffers(pdd, &qp);
+ if (ret) {
+ pr_debug("failed to acquire user queue buffers for CRIU\n");
+ goto exit;
+ }
ret = kfd_queue_acquire_buffers(pdd, &qp);
if (ret) {
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
index e659cd50eb0b..6a7b4d959541 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
@@ -224,10 +224,10 @@ static void kfd_smi_event_add(struct task_struct *task, struct kfd_node *dev,
pid = kfd_smi_task_to_pid(task);
- len = snprintf(fifo_in, sizeof(fifo_in), "%x ", event);
+ len = scnprintf(fifo_in, sizeof(fifo_in), "%x ", event);
va_start(args, fmt);
- len += vsnprintf(fifo_in + len, sizeof(fifo_in) - len, fmt, args);
+ len += vscnprintf(fifo_in + len, sizeof(fifo_in) - len, fmt, args);
va_end(args);
add_event_to_kfifo(pid, dev, event, fifo_in, len);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index 0900bb23349e..30ad10bbd47e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -3473,7 +3473,13 @@ svm_range_is_valid(struct kfd_process *p, uint64_t start, uint64_t size)
unsigned long start_unchg = start;
start <<= PAGE_SHIFT;
- end = start + (size << PAGE_SHIFT);
+
+ if (size == 0)
+ return -EINVAL;
+
+ if (check_add_overflow(start, size << PAGE_SHIFT, &end))
+ return -EOVERFLOW;
+
do {
vma = vma_lookup(p->mm, start);
if (!vma || (vma->vm_flags & device_vma))
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
index 00517c3d0e6a..35b3abe57b80 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
@@ -2013,12 +2013,21 @@ static void kfd_topology_set_capabilities(struct kfd_topology_device *dev)
dev->node_props.capability |=
HSA_CAP_TRAP_DEBUG_PRECISE_MEMORY_OPERATIONS_SUPPORTED;
- if (!amdgpu_sriov_vf(dev->gpu->adev))
+ if (KFD_GC_VERSION(dev->gpu) >= IP_VERSION(9, 4, 3) &&
+ !amdgpu_sriov_vf(dev->gpu->adev))
dev->node_props.capability |= HSA_CAP_PER_QUEUE_RESET_SUPPORTED;
} else {
dev->node_props.debug_prop |= HSA_DBG_WATCH_ADDR_MASK_LO_BIT_GFX10 |
HSA_DBG_WATCH_ADDR_MASK_HI_BIT;
+ /* gfx11 dGPU and gfx12.0 */
+ if ((KFD_GC_VERSION(dev->gpu) == IP_VERSION(11, 0, 0) ||
+ KFD_GC_VERSION(dev->gpu) == IP_VERSION(11, 0, 2) ||
+ KFD_GC_VERSION(dev->gpu) == IP_VERSION(11, 0, 3) ||
+ KFD_GC_VERSION(dev->gpu) == IP_VERSION(12, 0, 0) ||
+ KFD_GC_VERSION(dev->gpu) == IP_VERSION(12, 0, 1)) &&
+ !amdgpu_sriov_vf(dev->gpu->adev))
+ dev->node_props.capability |= HSA_CAP_PER_QUEUE_RESET_SUPPORTED;
if (KFD_GC_VERSION(dev->gpu) >= IP_VERSION(12, 0, 0))
dev->node_props.capability |=
diff --git a/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c b/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c
index 995cae6be144..84e13537a7ba 100644
--- a/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c
+++ b/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c
@@ -45,7 +45,7 @@ static const struct drm_driver amdgpu_xcp_driver = {
.minor = 0,
};
-static int8_t pdev_num;
+static u8 pdev_num;
static struct xcp_device *xcp_dev[MAX_XCP_PLATFORM_DEVICE];
static DEFINE_MUTEX(xcp_mutex);
@@ -56,6 +56,11 @@ int amdgpu_xcp_drm_dev_alloc(struct drm_device **ddev)
char *dev_name;
int ret, i;
+ if (!ddev)
+ return -EINVAL;
+
+ BUILD_BUG_ON(MAX_XCP_PLATFORM_DEVICE >= U8_MAX);
+
guard(mutex)(&xcp_mutex);
if (pdev_num >= MAX_XCP_PLATFORM_DEVICE)
@@ -105,7 +110,7 @@ out_unregister:
}
EXPORT_SYMBOL(amdgpu_xcp_drm_dev_alloc);
-static void free_xcp_dev(int8_t index)
+static void free_xcp_dev(uint8_t index)
{
if ((index < MAX_XCP_PLATFORM_DEVICE) && (xcp_dev[index])) {
struct platform_device *pdev = xcp_dev[index]->pdev;
@@ -114,17 +119,18 @@ static void free_xcp_dev(int8_t index)
platform_device_unregister(pdev);
xcp_dev[index] = NULL;
- pdev_num--;
+ if (pdev_num > 0)
+ pdev_num--;
}
}
void amdgpu_xcp_drm_dev_free(struct drm_device *ddev)
{
- int8_t i;
+ uint8_t i;
guard(mutex)(&xcp_mutex);
- for (i = 0; i < MAX_XCP_PLATFORM_DEVICE; i++) {
+ for (i = 0; pdev_num && i < MAX_XCP_PLATFORM_DEVICE; i++) {
if ((xcp_dev[i]) && (&xcp_dev[i]->drm == ddev)) {
free_xcp_dev(i);
break;
@@ -135,7 +141,7 @@ EXPORT_SYMBOL(amdgpu_xcp_drm_dev_free);
void amdgpu_xcp_drv_release(void)
{
- int8_t i;
+ uint8_t i;
guard(mutex)(&xcp_mutex);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
index 54a93e4255b3..d83878e35b61 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
@@ -41,7 +41,11 @@ AMDGPUDM = \
amdgpu_dm_quirks.o \
amdgpu_dm_wb.o \
amdgpu_dm_colorop.o \
- amdgpu_dm_ism.o
+ amdgpu_dm_ism.o \
+ amdgpu_dm_backlight.o \
+ amdgpu_dm_audio.o \
+ amdgpu_dm_dmub.o \
+ amdgpu_dm_connector.o
ifdef CONFIG_DRM_AMD_DC_FP
AMDGPUDM += dc_fpu.o
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 97ae3e32e4a4..d0e612371c8f 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -66,6 +66,11 @@
#endif
#include "amdgpu_dm_psr.h"
#include "amdgpu_dm_replay.h"
+#include "amdgpu_dm_backlight.h"
+#include "amdgpu_dm_audio.h"
+#include "amdgpu_dm_dmub.h"
+#include "amdgpu_dm_connector.h"
+#include "amdgpu_dm_kunit_helpers.h"
#include "ivsrcid/ivsrcid_vislands30.h"
@@ -94,7 +99,6 @@
#include <drm/drm_mode.h>
#include <drm/drm_utils.h>
#include <drm/drm_vblank.h>
-#include <drm/drm_audio_component.h>
#include <drm/drm_colorop.h>
#include <drm/drm_gem_atomic_helper.h>
@@ -107,60 +111,9 @@
#include "modules/inc/mod_power.h"
#include "modules/power/power_helpers.h"
-static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch");
-
-#define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB);
-#define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID_DMUB);
-#define FIRMWARE_NAVY_FLOUNDER_DMUB "amdgpu/navy_flounder_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER_DMUB);
-#define FIRMWARE_GREEN_SARDINE_DMUB "amdgpu/green_sardine_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE_DMUB);
-#define FIRMWARE_VANGOGH_DMUB "amdgpu/vangogh_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_VANGOGH_DMUB);
-#define FIRMWARE_DIMGREY_CAVEFISH_DMUB "amdgpu/dimgrey_cavefish_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH_DMUB);
-#define FIRMWARE_BEIGE_GOBY_DMUB "amdgpu/beige_goby_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY_DMUB);
-#define FIRMWARE_YELLOW_CARP_DMUB "amdgpu/yellow_carp_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP_DMUB);
-#define FIRMWARE_DCN_314_DMUB "amdgpu/dcn_3_1_4_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_314_DMUB);
-#define FIRMWARE_DCN_315_DMUB "amdgpu/dcn_3_1_5_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB);
-#define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB);
-
-#define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB);
-#define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB);
-
-#define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin"
MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU);
-
-#define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin"
MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
-#define FIRMWARE_DCN_35_DMUB "amdgpu/dcn_3_5_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_35_DMUB);
-
-#define FIRMWARE_DCN_351_DMUB "amdgpu/dcn_3_5_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_351_DMUB);
-
-#define FIRMWARE_DCN_36_DMUB "amdgpu/dcn_3_6_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB);
-
-#define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
-
-#define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB);
-
-#define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin"
-MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
-
/**
* DOC: overview
*
@@ -174,46 +127,7 @@ MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
/* basic init/fini API */
static int amdgpu_dm_init(struct amdgpu_device *adev);
static void amdgpu_dm_fini(struct amdgpu_device *adev);
-static bool is_freesync_video_mode(const struct drm_display_mode *mode, struct amdgpu_dm_connector *aconnector);
static void reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state);
-static struct amdgpu_i2c_adapter *
-create_i2c(struct ddc_service *ddc_service, bool oem);
-
-static enum drm_mode_subconnector get_subconnector_type(struct dc_link *link)
-{
- switch (link->dpcd_caps.dongle_type) {
- case DISPLAY_DONGLE_NONE:
- return DRM_MODE_SUBCONNECTOR_Native;
- case DISPLAY_DONGLE_DP_VGA_CONVERTER:
- return DRM_MODE_SUBCONNECTOR_VGA;
- case DISPLAY_DONGLE_DP_DVI_CONVERTER:
- case DISPLAY_DONGLE_DP_DVI_DONGLE:
- return DRM_MODE_SUBCONNECTOR_DVID;
- case DISPLAY_DONGLE_DP_HDMI_CONVERTER:
- case DISPLAY_DONGLE_DP_HDMI_DONGLE:
- return DRM_MODE_SUBCONNECTOR_HDMIA;
- case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE:
- default:
- return DRM_MODE_SUBCONNECTOR_Unknown;
- }
-}
-
-static void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
-{
- struct dc_link *link = aconnector->dc_link;
- struct drm_connector *connector = &aconnector->base;
- enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
- return;
-
- if (aconnector->dc_sink)
- subconnector = get_subconnector_type(link);
-
- drm_object_property_set_value(&connector->base,
- connector->dev->mode_config.dp_subconnector_property,
- subconnector);
-}
/*
* initializes drm_device display related structures, based on the information
@@ -226,32 +140,23 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev);
/* removes and deallocates the drm structures, created by the above function */
static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm);
-static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *amdgpu_dm_connector,
- u32 link_index,
- struct amdgpu_encoder *amdgpu_encoder);
-static int amdgpu_dm_encoder_init(struct drm_device *dev,
- struct amdgpu_encoder *aencoder,
- uint32_t link_index);
-
-static int amdgpu_dm_connector_get_modes(struct drm_connector *connector);
-
static int amdgpu_dm_atomic_setup_commit(struct drm_atomic_commit *state);
static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state);
+static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context);
static int amdgpu_dm_atomic_check(struct drm_device *dev,
struct drm_atomic_commit *state);
-static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector);
-static void handle_hpd_rx_irq(void *param);
-
-static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
- int bl_idx,
- u32 user_brightness);
-
-static bool
+STATIC_IFN_KUNIT bool
is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state);
+
+static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc)
+{
+ if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter)
+ dc_exit_ips_for_hw_access(dc);
+}
+
/*
* dm_vblank_get_counter
*
@@ -332,40 +237,14 @@ static int dm_wait_for_idle(struct amdgpu_ip_block *ip_block)
return 0;
}
-static bool dm_check_soft_reset(struct amdgpu_ip_block *ip_block)
-{
- return false;
-}
-
static int dm_soft_reset(struct amdgpu_ip_block *ip_block)
{
/* XXX todo */
return 0;
}
-static struct amdgpu_crtc *
-get_crtc_by_otg_inst(struct amdgpu_device *adev,
- int otg_inst)
-{
- struct drm_device *dev = adev_to_drm(adev);
- struct drm_crtc *crtc;
- struct amdgpu_crtc *amdgpu_crtc;
-
- if (WARN_ON(otg_inst == -1))
- return adev->mode_info.crtcs[0];
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- amdgpu_crtc = to_amdgpu_crtc(crtc);
-
- if (amdgpu_crtc->otg_inst == otg_inst)
- return amdgpu_crtc;
- }
-
- return NULL;
-}
-
-static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
- struct dm_crtc_state *new_state)
+STATIC_IFN_KUNIT bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
+ struct dm_crtc_state *new_state)
{
if (new_state->stream->adjust.timing_adjust_pending)
return true;
@@ -376,13 +255,14 @@ static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
else
return false;
}
+EXPORT_IF_KUNIT(is_dc_timing_adjust_needed);
/*
* DC will program planes with their z-order determined by their ordering
* in the dc_surface_updates array. This comparator is used to sort them
* by descending zpos.
*/
-static int dm_plane_layer_index_cmp(const void *a, const void *b)
+STATIC_IFN_KUNIT int dm_plane_layer_index_cmp(const void *a, const void *b)
{
const struct dc_surface_update *sa = (struct dc_surface_update *)a;
const struct dc_surface_update *sb = (struct dc_surface_update *)b;
@@ -390,6 +270,7 @@ static int dm_plane_layer_index_cmp(const void *a, const void *b)
/* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */
return sb->surface->layer_index - sa->surface->layer_index;
}
+EXPORT_IF_KUNIT(dm_plane_layer_index_cmp);
/**
* update_planes_and_stream_adapter() - Send planes to be updated in DC
@@ -430,633 +311,6 @@ static inline bool update_planes_and_stream_adapter(struct dc *dc,
stream_update);
}
-/**
- * dm_pflip_high_irq() - Handle pageflip interrupt
- * @interrupt_params: ignored
- *
- * Handles the pageflip interrupt by notifying all interested parties
- * that the pageflip has been completed.
- */
-static void dm_pflip_high_irq(void *interrupt_params)
-{
- struct amdgpu_crtc *amdgpu_crtc;
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct drm_device *dev = adev_to_drm(adev);
- unsigned long flags;
- struct drm_pending_vblank_event *e;
- u32 vpos, hpos, v_blank_start, v_blank_end;
- bool vrr_active;
-
- amdgpu_crtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP);
-
- /* IRQ could occur when in initial stage */
- /* TODO work and BO cleanup */
- if (amdgpu_crtc == NULL) {
- drm_dbg_state(dev, "CRTC is null, returning.\n");
- return;
- }
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
-
- if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED) {
- drm_dbg_state(dev,
- "amdgpu_crtc->pflip_status = %d != AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p]\n",
- amdgpu_crtc->pflip_status, AMDGPU_FLIP_SUBMITTED,
- amdgpu_crtc->crtc_id, amdgpu_crtc);
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- return;
- }
-
- /* page flip completed. */
- e = amdgpu_crtc->event;
- amdgpu_crtc->event = NULL;
-
- WARN_ON(!e);
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(amdgpu_crtc);
-
- /* Fixed refresh rate, or VRR scanout position outside front-porch? */
- if (!vrr_active ||
- !dc_stream_get_scanoutpos(amdgpu_crtc->dm_irq_params.stream, &v_blank_start,
- &v_blank_end, &hpos, &vpos) ||
- (vpos < v_blank_start)) {
- /* Update to correct count and vblank timestamp if racing with
- * vblank irq. This also updates to the correct vblank timestamp
- * even in VRR mode, as scanout is past the front-porch atm.
- */
- drm_crtc_accurate_vblank_count(&amdgpu_crtc->base);
-
- /* Wake up userspace by sending the pageflip event with proper
- * count and timestamp of vblank of flip completion.
- */
- if (e) {
- drm_crtc_send_vblank_event(&amdgpu_crtc->base, e);
-
- /* Event sent, so done with vblank for this flip */
- drm_crtc_vblank_put(&amdgpu_crtc->base);
- }
- } else if (e) {
- /* VRR active and inside front-porch: vblank count and
- * timestamp for pageflip event will only be up to date after
- * drm_crtc_handle_vblank() has been executed from late vblank
- * irq handler after start of back-porch (vline 0). We queue the
- * pageflip event for send-out by drm_crtc_handle_vblank() with
- * updated timestamp and count, once it runs after us.
- *
- * We need to open-code this instead of using the helper
- * drm_crtc_arm_vblank_event(), as that helper would
- * call drm_crtc_accurate_vblank_count(), which we must
- * not call in VRR mode while we are in front-porch!
- */
-
- /* sequence will be replaced by real count during send-out. */
- e->sequence = drm_crtc_vblank_count(&amdgpu_crtc->base);
- e->pipe = amdgpu_crtc->crtc_id;
-
- list_add_tail(&e->base.link, &adev_to_drm(adev)->vblank_event_list);
- e = NULL;
- }
-
- /* Keep track of vblank of this flip for flip throttling. We use the
- * cooked hw counter, as that one incremented at start of this vblank
- * of pageflip completion, so last_flip_vblank is the forbidden count
- * for queueing new pageflips if vsync + VRR is enabled.
- */
- amdgpu_crtc->dm_irq_params.last_flip_vblank =
- amdgpu_get_vblank_counter_kms(&amdgpu_crtc->base);
-
- amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-
- drm_dbg_state(dev,
- "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n",
- amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e);
-}
-
-static void dm_handle_vmin_vmax_update(struct work_struct *offload_work)
-{
- struct vupdate_offload_work *work = container_of(offload_work, struct vupdate_offload_work, work);
- struct amdgpu_device *adev = work->adev;
- struct dc_stream_state *stream = work->stream;
- struct dc_crtc_timing_adjust *adjust = work->adjust;
-
- mutex_lock(&adev->dm.dc_lock);
- dc_stream_adjust_vmin_vmax(adev->dm.dc, stream, adjust);
- mutex_unlock(&adev->dm.dc_lock);
-
- dc_stream_release(stream);
- kfree(work->adjust);
- kfree(work);
-}
-
-static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
- struct dc_stream_state *stream,
- struct dc_crtc_timing_adjust *adjust)
-{
- struct vupdate_offload_work *offload_work = kzalloc_obj(*offload_work,
- GFP_NOWAIT);
- if (!offload_work) {
- drm_dbg_driver(adev_to_drm(adev), "Failed to allocate vupdate_offload_work\n");
- return;
- }
-
- struct dc_crtc_timing_adjust *adjust_copy = kzalloc_obj(*adjust_copy,
- GFP_NOWAIT);
- if (!adjust_copy) {
- drm_dbg_driver(adev_to_drm(adev), "Failed to allocate adjust_copy\n");
- kfree(offload_work);
- return;
- }
-
- dc_stream_retain(stream);
- memcpy(adjust_copy, adjust, sizeof(*adjust_copy));
-
- INIT_WORK(&offload_work->work, dm_handle_vmin_vmax_update);
- offload_work->adev = adev;
- offload_work->stream = stream;
- offload_work->adjust = adjust_copy;
-
- queue_work(system_percpu_wq, &offload_work->work);
-}
-
-static void dm_vupdate_high_irq(void *interrupt_params)
-{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
- struct drm_device *drm_dev;
- struct drm_vblank_crtc *vblank;
- ktime_t frame_duration_ns, previous_timestamp;
- unsigned long flags;
- int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
-
- if (acrtc) {
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
- drm_dev = acrtc->base.dev;
- vblank = drm_crtc_vblank_crtc(&acrtc->base);
- previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
- frame_duration_ns = vblank->time - previous_timestamp;
-
- if (frame_duration_ns > 0) {
- trace_amdgpu_refresh_rate_track(acrtc->base.index,
- frame_duration_ns,
- ktime_divns(NSEC_PER_SEC, frame_duration_ns));
- atomic64_set(&irq_params->previous_timestamp, vblank->time);
- }
-
- drm_dbg_vbl(drm_dev,
- "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
- vrr_active);
-
- /* Core vblank handling is done here after end of front-porch in
- * vrr mode, as vblank timestamping will give valid results
- * while now done after front-porch. This will also deliver
- * page-flip completion events that have been queued to us
- * if a pageflip happened inside front-porch.
- */
- if (vrr_active && acrtc->dm_irq_params.stream) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
- == VRR_STATE_ACTIVE_VARIABLE;
-
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /* BTR processing for pre-DCE12 ASICs */
- if (adev->family < AMDGPU_FAMILY_AI) {
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- mod_freesync_handle_v_update(
- adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
- }
- }
-}
-
-/**
- * dm_crtc_high_irq() - Handles CRTC interrupt
- * @interrupt_params: used for determining the CRTC instance
- *
- * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK
- * event handler.
- */
-static void dm_crtc_high_irq(void *interrupt_params)
-{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct drm_writeback_job *job;
- struct amdgpu_crtc *acrtc;
- unsigned long flags;
- int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
- if (!acrtc)
- return;
-
- if (acrtc->wb_conn) {
- spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
-
- if (acrtc->wb_pending) {
- job = list_first_entry_or_null(&acrtc->wb_conn->job_queue,
- struct drm_writeback_job,
- list_entry);
- acrtc->wb_pending = false;
- spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
-
- if (job) {
- unsigned int v_total, refresh_hz;
- struct dc_stream_state *stream = acrtc->dm_irq_params.stream;
-
- v_total = stream->adjust.v_total_max ?
- stream->adjust.v_total_max : stream->timing.v_total;
- refresh_hz = div_u64((uint64_t) stream->timing.pix_clk_100hz *
- 100LL, (v_total * stream->timing.h_total));
- mdelay(1000 / refresh_hz);
-
- drm_writeback_signal_completion(acrtc->wb_conn, 0);
- dc_stream_fc_disable_writeback(adev->dm.dc,
- acrtc->dm_irq_params.stream, 0);
- }
- } else
- spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
- }
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
-
- drm_dbg_vbl(adev_to_drm(adev),
- "crtc:%d, vupdate-vrr:%d, planes:%d\n", acrtc->crtc_id,
- vrr_active, acrtc->dm_irq_params.active_planes);
-
- /**
- * Core vblank handling at start of front-porch is only possible
- * in non-vrr mode, as only there vblank timestamping will give
- * valid results while done in front-porch. Otherwise defer it
- * to dm_vupdate_high_irq after end of front-porch.
- */
- if (!vrr_active)
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /**
- * Following stuff must happen at start of vblank, for crc
- * computation and below-the-range btr support in vrr mode.
- */
- amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
-
- /* BTR updates need to happen before VUPDATE on Vega and above. */
- if (adev->family < AMDGPU_FAMILY_AI)
- return;
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
-
- if (acrtc->dm_irq_params.stream &&
- acrtc->dm_irq_params.vrr_params.supported) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state == VRR_STATE_ACTIVE_VARIABLE;
-
- mod_freesync_handle_v_update(adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- /* update vmin_vmax only if freesync is enabled, or only if PSR and REPLAY are disabled */
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev, acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- }
-
- /*
- * If there aren't any active_planes then DCH HUBP may be clock-gated.
- * In that case, pageflip completion interrupts won't fire and pageflip
- * completion events won't get delivered. Prevent this by sending
- * pending pageflip events from here if a flip is still pending.
- *
- * If any planes are enabled, use dm_pflip_high_irq() instead, to
- * avoid race conditions between flip programming and completion,
- * which could cause too early flip completion events.
- */
- if (adev->family >= AMDGPU_FAMILY_RV &&
- acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
- acrtc->dm_irq_params.active_planes == 0) {
- if (acrtc->event) {
- drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
- acrtc->event = NULL;
- drm_crtc_vblank_put(&acrtc->base);
- }
- acrtc->pflip_status = AMDGPU_FLIP_NONE;
- }
-
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-}
-
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
-/**
- * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
- * DCN generation ASICs
- * @interrupt_params: interrupt parameters
- *
- * Used to set crc window/read out crc value at vertical line 0 position
- */
-static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params)
-{
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VLINE0);
-
- if (!acrtc)
- return;
-
- amdgpu_dm_crtc_handle_crc_window_irq(&acrtc->base);
-}
-#endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */
-
-/**
- * dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * Dmub AUX or SET_CONFIG command completion processing callback
- * Copies dmub notification to DM which is to be read by AUX command.
- * issuing thread and also signals the event to wake up the thread.
- */
-static void dmub_aux_setconfig_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
-{
- if (adev->dm.dmub_notify)
- memcpy(adev->dm.dmub_notify, notify, sizeof(struct dmub_notification));
- if (notify->type == DMUB_NOTIFICATION_AUX_REPLY)
- complete(&adev->dm.dmub_aux_transfer_done);
-}
-
-static void dmub_aux_fused_io_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
-{
- if (!adev || !notify) {
- ASSERT(false);
- return;
- }
-
- const struct dmub_cmd_fused_request *req = &notify->fused_request;
- const uint8_t ddc_line = req->u.aux.ddc_line;
-
- if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) {
- ASSERT(false);
- return;
- }
-
- struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line];
-
- static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch");
- memcpy(sync->reply_data, req, sizeof(*req));
- complete(&sync->replied);
-}
-
-/**
- * dmub_hpd_callback - DMUB HPD interrupt processing callback.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * Dmub Hpd interrupt processing callback. Gets displayindex through the
- * ink index and calls helper to do the processing.
- */
-static void dmub_hpd_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
-{
- struct amdgpu_dm_connector *aconnector;
- struct amdgpu_dm_connector *hpd_aconnector = NULL;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- struct dc_link *link;
- u8 link_index = 0;
- struct drm_device *dev;
-
- if (adev == NULL)
- return;
-
- if (notify == NULL) {
- drm_err(adev_to_drm(adev), "DMUB HPD callback notification was NULL");
- return;
- }
-
- if (notify->link_index > adev->dm.dc->link_count) {
- drm_err(adev_to_drm(adev), "DMUB HPD index (%u)is abnormal", notify->link_index);
- return;
- }
-
- /* Skip DMUB HPD IRQ in suspend/resume. We will probe them later. */
- if (notify->type == DMUB_NOTIFICATION_HPD && adev->in_suspend) {
- drm_info(adev_to_drm(adev), "Skip DMUB HPD IRQ callback in suspend/resume\n");
- return;
- }
-
- link_index = notify->link_index;
- link = adev->dm.dc->links[link_index];
- dev = adev->dm.ddev;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (link && aconnector->dc_link == link) {
- if (notify->type == DMUB_NOTIFICATION_HPD)
- drm_info(adev_to_drm(adev), "DMUB HPD IRQ callback: link_index=%u\n", link_index);
- else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ)
- drm_info(adev_to_drm(adev), "DMUB HPD RX IRQ callback: link_index=%u\n", link_index);
- else
- drm_warn(adev_to_drm(adev), "DMUB Unknown HPD callback type %d, link_index=%u\n",
- notify->type, link_index);
-
- hpd_aconnector = aconnector;
- break;
- }
- }
- drm_connector_list_iter_end(&iter);
-
- if (hpd_aconnector) {
- if (notify->type == DMUB_NOTIFICATION_HPD) {
- if (hpd_aconnector->dc_link->hpd_status == (notify->hpd_status == DP_HPD_PLUG))
- drm_warn(adev_to_drm(adev), "DMUB reported hpd status unchanged. link_index=%u\n", link_index);
- handle_hpd_irq_helper(hpd_aconnector);
- } else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ) {
- handle_hpd_rx_irq(hpd_aconnector);
- }
- }
-}
-
-/**
- * dmub_hpd_sense_callback - DMUB HPD sense processing callback.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * HPD sense changes can occur during low power states and need to be
- * notified from firmware to driver.
- */
-static void dmub_hpd_sense_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
-{
- drm_dbg_driver(adev_to_drm(adev), "DMUB HPD SENSE callback.\n");
-}
-
-/**
- * register_dmub_notify_callback - Sets callback for DMUB notify
- * @adev: amdgpu_device pointer
- * @type: Type of dmub notification
- * @callback: Dmub interrupt callback function
- * @dmub_int_thread_offload: offload indicator
- *
- * API to register a dmub callback handler for a dmub notification
- * Also sets indicator whether callback processing to be offloaded.
- * to dmub interrupt handling thread
- * Return: true if successfully registered, false if there is existing registration
- */
-static bool register_dmub_notify_callback(struct amdgpu_device *adev,
- enum dmub_notification_type type,
- dmub_notify_interrupt_callback_t callback,
- bool dmub_int_thread_offload)
-{
- if (callback != NULL && type < ARRAY_SIZE(adev->dm.dmub_thread_offload)) {
- adev->dm.dmub_callback[type] = callback;
- adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload;
- } else
- return false;
-
- return true;
-}
-
-static void dm_handle_hpd_work(struct work_struct *work)
-{
- struct dmub_hpd_work *dmub_hpd_wrk;
-
- dmub_hpd_wrk = container_of(work, struct dmub_hpd_work, handle_hpd_work);
-
- if (!dmub_hpd_wrk->dmub_notify) {
- drm_err(adev_to_drm(dmub_hpd_wrk->adev), "dmub_hpd_wrk dmub_notify is NULL");
- return;
- }
-
- if (dmub_hpd_wrk->dmub_notify->type < ARRAY_SIZE(dmub_hpd_wrk->adev->dm.dmub_callback)) {
- dmub_hpd_wrk->adev->dm.dmub_callback[dmub_hpd_wrk->dmub_notify->type](dmub_hpd_wrk->adev,
- dmub_hpd_wrk->dmub_notify);
- }
-
- kfree(dmub_hpd_wrk->dmub_notify);
- kfree(dmub_hpd_wrk);
-
-}
-
-static const char *dmub_notification_type_str(enum dmub_notification_type e)
-{
- switch (e) {
- case DMUB_NOTIFICATION_NO_DATA:
- return "NO_DATA";
- case DMUB_NOTIFICATION_AUX_REPLY:
- return "AUX_REPLY";
- case DMUB_NOTIFICATION_HPD:
- return "HPD";
- case DMUB_NOTIFICATION_HPD_IRQ:
- return "HPD_IRQ";
- case DMUB_NOTIFICATION_SET_CONFIG_REPLY:
- return "SET_CONFIG_REPLY";
- case DMUB_NOTIFICATION_DPIA_NOTIFICATION:
- return "DPIA_NOTIFICATION";
- case DMUB_NOTIFICATION_HPD_SENSE_NOTIFY:
- return "HPD_SENSE_NOTIFY";
- case DMUB_NOTIFICATION_FUSED_IO:
- return "FUSED_IO";
- default:
- return "<unknown>";
- }
-}
-
-#define DMUB_TRACE_MAX_READ 64
-/**
- * dm_dmub_outbox1_low_irq() - Handles Outbox interrupt
- * @interrupt_params: used for determining the Outbox instance
- *
- * Handles the Outbox Interrupt
- * event handler.
- */
-static void dm_dmub_outbox1_low_irq(void *interrupt_params)
-{
- struct dmub_notification notify = {0};
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_display_manager *dm = &adev->dm;
- struct dmcub_trace_buf_entry entry = { 0 };
- u32 count = 0;
- struct dmub_hpd_work *dmub_hpd_wrk;
-
- do {
- if (dc_dmub_srv_get_dmub_outbox0_msg(dm->dc, &entry)) {
- trace_amdgpu_dmub_trace_high_irq(entry.trace_code, entry.tick_count,
- entry.param0, entry.param1);
-
- drm_dbg_driver(adev_to_drm(adev), "trace_code:%u, tick_count:%u, param0:%u, param1:%u\n",
- entry.trace_code, entry.tick_count, entry.param0, entry.param1);
- } else
- break;
-
- count++;
-
- } while (count <= DMUB_TRACE_MAX_READ);
-
- if (count > DMUB_TRACE_MAX_READ)
- drm_dbg_driver(adev_to_drm(adev), "Warning : count > DMUB_TRACE_MAX_READ");
-
- if (dc_enable_dmub_notifications(adev->dm.dc) &&
- irq_params->irq_src == DC_IRQ_SOURCE_DMCUB_OUTBOX) {
-
- do {
- dc_stat_get_dmub_notification(adev->dm.dc, &notify);
- if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) {
- drm_err(adev_to_drm(adev), "DM: notify type %d invalid!", notify.type);
- continue;
- }
- if (!dm->dmub_callback[notify.type]) {
- drm_warn(adev_to_drm(adev), "DMUB notification skipped due to no handler: type=%s\n",
- dmub_notification_type_str(notify.type));
- continue;
- }
- if (dm->dmub_thread_offload[notify.type] == true) {
- dmub_hpd_wrk = kzalloc_obj(*dmub_hpd_wrk,
- GFP_ATOMIC);
- if (!dmub_hpd_wrk) {
- drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk");
- return;
- }
- dmub_hpd_wrk->dmub_notify = kmemdup(&notify, sizeof(struct dmub_notification),
- GFP_ATOMIC);
- if (!dmub_hpd_wrk->dmub_notify) {
- kfree(dmub_hpd_wrk);
- drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk->dmub_notify");
- return;
- }
- INIT_WORK(&dmub_hpd_wrk->handle_hpd_work, dm_handle_hpd_work);
- dmub_hpd_wrk->adev = adev;
- queue_work(adev->dm.delayed_hpd_wq, &dmub_hpd_wrk->handle_hpd_work);
- } else {
- dm->dmub_callback[notify.type](adev, &notify);
- }
- } while (notify.pending_notification);
- }
-}
-
static int dm_set_clockgating_state(struct amdgpu_ip_block *ip_block,
enum amd_clockgating_state state)
{
@@ -1073,401 +327,6 @@ static int dm_set_powergating_state(struct amdgpu_ip_block *ip_block,
static int dm_early_init(struct amdgpu_ip_block *ip_block);
/* Allocate memory for FBC compressed data */
-static void amdgpu_dm_fbc_init(struct drm_connector *connector)
-{
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct dm_compressor_info *compressor = &adev->dm.compressor;
- struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(connector);
- struct drm_display_mode *mode;
- unsigned long max_size = 0;
-
- if (adev->dm.dc->fbc_compressor == NULL)
- return;
-
- if (aconn->dc_link->connector_signal != SIGNAL_TYPE_EDP)
- return;
-
- if (compressor->bo_ptr)
- return;
-
-
- list_for_each_entry(mode, &connector->modes, head) {
- if (max_size < (unsigned long) mode->htotal * mode->vtotal)
- max_size = (unsigned long) mode->htotal * mode->vtotal;
- }
-
- if (max_size) {
- int r = amdgpu_bo_create_kernel(adev, max_size * 4, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_GTT, &compressor->bo_ptr,
- &compressor->gpu_addr, &compressor->cpu_addr);
-
- if (r)
- drm_err(adev_to_drm(adev), "DM: Failed to initialize FBC\n");
- else {
- adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr;
- drm_info(adev_to_drm(adev), "DM: FBC alloc %lu\n", max_size*4);
- }
-
- }
-
-}
-
-static int amdgpu_dm_audio_component_get_eld(struct device *kdev, int port,
- int pipe, bool *enabled,
- unsigned char *buf, int max_bytes)
-{
- struct drm_device *dev = dev_get_drvdata(kdev);
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct drm_connector *connector;
- struct drm_connector_list_iter conn_iter;
- struct amdgpu_dm_connector *aconnector;
- int ret = 0;
-
- *enabled = false;
-
- mutex_lock(&adev->dm.audio_lock);
-
- drm_connector_list_iter_begin(dev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (aconnector->audio_inst != port)
- continue;
-
- *enabled = true;
- mutex_lock(&connector->eld_mutex);
- ret = drm_eld_size(connector->eld);
- memcpy(buf, connector->eld, min(max_bytes, ret));
- mutex_unlock(&connector->eld_mutex);
-
- break;
- }
- drm_connector_list_iter_end(&conn_iter);
-
- mutex_unlock(&adev->dm.audio_lock);
-
- drm_dbg_kms(adev_to_drm(adev), "Get ELD : idx=%d ret=%d en=%d\n", port, ret, *enabled);
-
- return ret;
-}
-
-static const struct drm_audio_component_ops amdgpu_dm_audio_component_ops = {
- .get_eld = amdgpu_dm_audio_component_get_eld,
-};
-
-static int amdgpu_dm_audio_component_bind(struct device *kdev,
- struct device *hda_kdev, void *data)
-{
- struct drm_device *dev = dev_get_drvdata(kdev);
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct drm_audio_component *acomp = data;
-
- acomp->ops = &amdgpu_dm_audio_component_ops;
- acomp->dev = kdev;
- adev->dm.audio_component = acomp;
-
- return 0;
-}
-
-static void amdgpu_dm_audio_component_unbind(struct device *kdev,
- struct device *hda_kdev, void *data)
-{
- struct amdgpu_device *adev = drm_to_adev(dev_get_drvdata(kdev));
- struct drm_audio_component *acomp = data;
-
- acomp->ops = NULL;
- acomp->dev = NULL;
- adev->dm.audio_component = NULL;
-}
-
-static const struct component_ops amdgpu_dm_audio_component_bind_ops = {
- .bind = amdgpu_dm_audio_component_bind,
- .unbind = amdgpu_dm_audio_component_unbind,
-};
-
-static int amdgpu_dm_audio_init(struct amdgpu_device *adev)
-{
- int i, ret;
-
- if (!amdgpu_audio)
- return 0;
-
- adev->mode_info.audio.enabled = true;
-
- adev->mode_info.audio.num_pins = adev->dm.dc->res_pool->audio_count;
-
- for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
- adev->mode_info.audio.pin[i].channels = -1;
- adev->mode_info.audio.pin[i].rate = -1;
- adev->mode_info.audio.pin[i].bits_per_sample = -1;
- adev->mode_info.audio.pin[i].status_bits = 0;
- adev->mode_info.audio.pin[i].category_code = 0;
- adev->mode_info.audio.pin[i].connected = false;
- adev->mode_info.audio.pin[i].id =
- adev->dm.dc->res_pool->audios[i]->inst;
- adev->mode_info.audio.pin[i].offset = 0;
- }
-
- ret = component_add(adev->dev, &amdgpu_dm_audio_component_bind_ops);
- if (ret < 0)
- return ret;
-
- adev->dm.audio_registered = true;
-
- return 0;
-}
-
-static void amdgpu_dm_audio_fini(struct amdgpu_device *adev)
-{
- if (!amdgpu_audio)
- return;
-
- if (!adev->mode_info.audio.enabled)
- return;
-
- if (adev->dm.audio_registered) {
- component_del(adev->dev, &amdgpu_dm_audio_component_bind_ops);
- adev->dm.audio_registered = false;
- }
-
- /* TODO: Disable audio? */
-
- adev->mode_info.audio.enabled = false;
-}
-
-static void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin)
-{
- struct drm_audio_component *acomp = adev->dm.audio_component;
-
- if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) {
- drm_dbg_kms(adev_to_drm(adev), "Notify ELD: %d\n", pin);
-
- acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
- pin, -1);
- }
-}
-
-static int dm_dmub_hw_init(struct amdgpu_device *adev)
-{
- const struct dmcub_firmware_header_v1_0 *hdr;
- struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
- struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
- const struct firmware *dmub_fw = adev->dm.dmub_fw;
- struct dc *dc = adev->dm.dc;
- struct dmcu *dmcu = adev->dm.dc->res_pool->dmcu;
- struct abm *abm = adev->dm.dc->res_pool->abm;
- struct dc_context *ctx = adev->dm.dc->ctx;
- struct dmub_srv_hw_params hw_params;
- enum dmub_status status;
- const unsigned char *fw_inst_const, *fw_bss_data;
- u32 i, fw_inst_const_size, fw_bss_data_size;
- bool has_hw_support;
-
- if (!dmub_srv)
- /* DMUB isn't supported on the ASIC. */
- return 0;
-
- if (!fb_info) {
- drm_err(adev_to_drm(adev), "No framebuffer info for DMUB service.\n");
- return -EINVAL;
- }
-
- if (!dmub_fw) {
- /* Firmware required for DMUB support. */
- drm_err(adev_to_drm(adev), "No firmware provided for DMUB.\n");
- return -EINVAL;
- }
-
- /* initialize register offsets for ASICs with runtime initialization available */
- if (dmub_srv->hw_funcs.init_reg_offsets)
- dmub_srv->hw_funcs.init_reg_offsets(dmub_srv, ctx);
-
- status = dmub_srv_has_hw_support(dmub_srv, &has_hw_support);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error checking HW support for DMUB: %d\n", status);
- return -EINVAL;
- }
-
- if (!has_hw_support) {
- drm_info(adev_to_drm(adev), "DMUB unsupported on ASIC\n");
- return 0;
- }
-
- /* Reset DMCUB if it was previously running - before we overwrite its memory. */
- status = dmub_srv_hw_reset(dmub_srv);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Error resetting DMUB HW: %d\n", status);
-
- hdr = (const struct dmcub_firmware_header_v1_0 *)dmub_fw->data;
-
- fw_inst_const = dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES_256;
-
- fw_bss_data = dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- le32_to_cpu(hdr->inst_const_bytes);
-
- /* Copy firmware and bios info into FB memory. */
- fw_inst_const_size = adev->dm.fw_inst_size;
-
- fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
-
- /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP,
- * amdgpu_ucode_init_single_fw will load dmub firmware
- * fw_inst_const part to cw0; otherwise, the firmware back door load
- * will be done by dm_dmub_hw_init
- */
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
- memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const,
- fw_inst_const_size);
- }
-
- if (fw_bss_data_size)
- memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr,
- fw_bss_data, fw_bss_data_size);
-
- /* Copy firmware bios info into FB memory. */
- memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios,
- adev->bios_size);
-
- /* Reset regions that need to be reset. */
- memset(fb_info->fb[DMUB_WINDOW_4_MAILBOX].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_4_MAILBOX].size);
-
- memset(fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size);
-
- memset(fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_6_FW_STATE].size);
-
- memset(fb_info->fb[DMUB_WINDOW_SHARED_STATE].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_SHARED_STATE].size);
-
- /* Initialize hardware. */
- memset(&hw_params, 0, sizeof(hw_params));
- hw_params.soc_fb_info.fb_base = adev->gmc.fb_start;
- hw_params.soc_fb_info.fb_offset = adev->vm_manager.vram_base_offset;
-
- /* backdoor load firmware and trigger dmub running */
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
- hw_params.load_inst_const = true;
-
- if (dmcu)
- hw_params.psp_version = dmcu->psp_version;
-
- for (i = 0; i < fb_info->num_fb; ++i)
- hw_params.fb[i] = &fb_info->fb[i];
-
- /* Enable usb4 dpia in the FW APU */
- if (dc->caps.is_apu &&
- dc->res_pool->usb4_dpia_count != 0 &&
- !dc->debug.dpia_debug.bits.disable_dpia) {
- hw_params.dpia_supported = true;
- hw_params.disable_dpia = dc->debug.dpia_debug.bits.disable_dpia;
- hw_params.dpia_hpd_int_enable_supported = false;
- hw_params.enable_non_transparent_setconfig = dc->config.consolidated_dpia_dp_lt;
- hw_params.disable_dpia_bw_allocation = !dc->config.usb4_bw_alloc_support;
- }
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(3, 6, 0):
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- hw_params.ips_sequential_ono = adev->external_rev_id > 0x10;
- hw_params.lower_hbr3_phy_ssc = true;
- break;
- default:
- break;
- }
-
- status = dmub_srv_hw_init(dmub_srv, &hw_params);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error initializing DMUB HW: %d\n", status);
- return -EINVAL;
- }
-
- /* Wait for firmware load to finish. */
- status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
-
- /* Init DMCU and ABM if available. */
- if (dmcu && abm) {
- dmcu->funcs->dmcu_init(dmcu);
- abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu);
- }
-
- if (!adev->dm.dc->ctx->dmub_srv)
- adev->dm.dc->ctx->dmub_srv = dc_dmub_srv_create(adev->dm.dc, dmub_srv);
- if (!adev->dm.dc->ctx->dmub_srv) {
- drm_err(adev_to_drm(adev), "Couldn't allocate DC DMUB server!\n");
- return -ENOMEM;
- }
-
- drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
-
- /* Keeping sanity checks off if
- * DCN31 >= 4.0.59.0
- * DCN314 >= 8.0.16.0
- * Otherwise, turn on sanity checks
- */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- if (adev->dm.dmcub_fw_version &&
- adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
- adev->dm.dmcub_fw_version < DMUB_FW_VERSION(4, 0, 59))
- adev->dm.dc->debug.sanity_checks = true;
- break;
- case IP_VERSION(3, 1, 4):
- if (adev->dm.dmcub_fw_version &&
- adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
- adev->dm.dmcub_fw_version < DMUB_FW_VERSION(8, 0, 16))
- adev->dm.dc->debug.sanity_checks = true;
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static void dm_dmub_hw_resume(struct amdgpu_device *adev)
-{
- struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
- enum dmub_status status;
- bool init;
- int r;
-
- if (!dmub_srv) {
- /* DMUB isn't supported on the ASIC. */
- return;
- }
-
- status = dmub_srv_is_hw_init(dmub_srv, &init);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "DMUB hardware init check failed: %d\n", status);
-
- if (status == DMUB_STATUS_OK && init) {
- /* Wait for firmware load to finish. */
- status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
- } else {
- /* Perform the full hardware initialization. */
- r = dm_dmub_hw_init(adev);
- if (r)
- drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r);
- }
-}
-
static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_addr_space_config *pa_config)
{
u64 pt_base;
@@ -1545,151 +404,6 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_
}
-static void force_connector_state(
- struct amdgpu_dm_connector *aconnector,
- enum drm_connector_force force_state)
-{
- struct drm_connector *connector = &aconnector->base;
-
- mutex_lock(&connector->dev->mode_config.mutex);
- aconnector->base.force = force_state;
- mutex_unlock(&connector->dev->mode_config.mutex);
-
- mutex_lock(&aconnector->hpd_lock);
- drm_kms_helper_connector_hotplug_event(connector);
- mutex_unlock(&aconnector->hpd_lock);
-}
-
-static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
-{
- struct hpd_rx_irq_offload_work *offload_work;
- struct amdgpu_dm_connector *aconnector;
- struct dc_link *dc_link;
- struct amdgpu_device *adev;
- enum dc_connection_type new_connection_type = dc_connection_none;
- unsigned long flags;
- union test_response test_response;
-
- memset(&test_response, 0, sizeof(test_response));
-
- offload_work = container_of(work, struct hpd_rx_irq_offload_work, work);
- aconnector = offload_work->offload_wq->aconnector;
- adev = offload_work->adev;
-
- if (!aconnector) {
- drm_err(adev_to_drm(adev), "Can't retrieve aconnector in hpd_rx_irq_offload_work");
- goto skip;
- }
-
- dc_link = aconnector->dc_link;
-
- mutex_lock(&aconnector->hpd_lock);
- if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
- mutex_unlock(&aconnector->hpd_lock);
-
- if (new_connection_type == dc_connection_none)
- goto skip;
-
- if (amdgpu_in_reset(adev))
- goto skip;
-
- if (offload_work->data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY ||
- offload_work->data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
- dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr, DOWN_OR_UP_MSG_RDY_EVENT);
- spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
- offload_work->offload_wq->is_handling_mst_msg_rdy_event = false;
- spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
- goto skip;
- }
-
- mutex_lock(&adev->dm.dc_lock);
- if (offload_work->data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
- dc_link_dp_handle_automated_test(dc_link);
-
- if (aconnector->timing_changed) {
- /* force connector disconnect and reconnect */
- force_connector_state(aconnector, DRM_FORCE_OFF);
- msleep(100);
- force_connector_state(aconnector, DRM_FORCE_UNSPECIFIED);
- }
-
- test_response.bits.ACK = 1;
-
- core_link_write_dpcd(
- dc_link,
- DP_TEST_RESPONSE,
- &test_response.raw,
- sizeof(test_response));
- } else if ((dc_link->connector_signal != SIGNAL_TYPE_EDP) &&
- dc_link_check_link_loss_status(dc_link, &offload_work->data) &&
- dc_link_dp_allow_hpd_rx_irq(dc_link)) {
- /* offload_work->data is from handle_hpd_rx_irq->
- * schedule_hpd_rx_offload_work.this is defer handle
- * for hpd short pulse. upon here, link status may be
- * changed, need get latest link status from dpcd
- * registers. if link status is good, skip run link
- * training again.
- */
- union hpd_irq_data irq_data;
-
- memset(&irq_data, 0, sizeof(irq_data));
-
- /* before dc_link_dp_handle_link_loss, allow new link lost handle
- * request be added to work queue if link lost at end of dc_link_
- * dp_handle_link_loss
- */
- spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
- offload_work->offload_wq->is_handling_link_loss = false;
- spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
-
- if ((dc_link_dp_read_hpd_rx_irq_data(dc_link, &irq_data) == DC_OK) &&
- dc_link_check_link_loss_status(dc_link, &irq_data))
- dc_link_dp_handle_link_loss(dc_link);
- }
- mutex_unlock(&adev->dm.dc_lock);
-
-skip:
- kfree(offload_work);
-
-}
-
-static struct hpd_rx_irq_offload_work_queue *hpd_rx_irq_create_workqueue(struct amdgpu_device *adev)
-{
- struct dc *dc = adev->dm.dc;
- int max_caps = dc->caps.max_links;
- int i = 0;
- struct hpd_rx_irq_offload_work_queue *hpd_rx_offload_wq = NULL;
-
- hpd_rx_offload_wq = kzalloc_objs(*hpd_rx_offload_wq, max_caps);
-
- if (!hpd_rx_offload_wq)
- return NULL;
-
-
- for (i = 0; i < max_caps; i++) {
- hpd_rx_offload_wq[i].wq =
- create_singlethread_workqueue("amdgpu_dm_hpd_rx_offload_wq");
-
- if (hpd_rx_offload_wq[i].wq == NULL) {
- drm_err(adev_to_drm(adev), "create amdgpu_dm_hpd_rx_offload_wq fail!");
- goto out_err;
- }
-
- spin_lock_init(&hpd_rx_offload_wq[i].offload_lock);
- }
-
- return hpd_rx_offload_wq;
-
-out_err:
- for (i = 0; i < max_caps; i++) {
- if (hpd_rx_offload_wq[i].wq)
- destroy_workqueue(hpd_rx_offload_wq[i].wq);
- }
- kfree(hpd_rx_offload_wq);
- return NULL;
-}
-
struct amdgpu_stutter_quirk {
u16 chip_vendor;
u16 chip_device;
@@ -1775,119 +489,6 @@ dm_free_gpu_mem(
}
-static enum dmub_status
-dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev,
- enum dmub_gpint_command command_code,
- uint16_t param,
- uint32_t timeout_us)
-{
- union dmub_gpint_data_register reg, test;
- uint32_t i;
-
- /* Assume that VBIOS DMUB is ready to take commands */
-
- reg.bits.status = 1;
- reg.bits.command_code = command_code;
- reg.bits.param = param;
-
- cgs_write_register(adev->dm.cgs_device, 0x34c0 + 0x01f8, reg.all);
-
- for (i = 0; i < timeout_us; ++i) {
- udelay(1);
-
- /* Check if our GPINT got acked */
- reg.bits.status = 0;
- test = (union dmub_gpint_data_register)
- cgs_read_register(adev->dm.cgs_device, 0x34c0 + 0x01f8);
-
- if (test.all == reg.all)
- return DMUB_STATUS_OK;
- }
-
- return DMUB_STATUS_TIMEOUT;
-}
-
-static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev)
-{
- void *bb;
- long long addr;
- unsigned int bb_size;
- int i = 0;
- uint16_t chunk;
- enum dmub_gpint_command send_addrs[] = {
- DMUB_GPINT__SET_BB_ADDR_WORD0,
- DMUB_GPINT__SET_BB_ADDR_WORD1,
- DMUB_GPINT__SET_BB_ADDR_WORD2,
- DMUB_GPINT__SET_BB_ADDR_WORD3,
- };
- enum dmub_status ret;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(4, 0, 1):
- bb_size = sizeof(struct dml2_soc_bb);
- break;
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- bb_size = sizeof(struct dml2_soc_bb);
- break;
- default:
- return NULL;
- }
-
- bb = dm_allocate_gpu_mem(adev,
- DC_MEM_ALLOC_TYPE_GART,
- bb_size,
- &addr);
- if (!bb)
- return NULL;
-
- for (i = 0; i < 4; i++) {
- /* Extract 16-bit chunk */
- chunk = ((uint64_t) addr >> (i * 16)) & 0xFFFF;
- /* Send the chunk */
- ret = dm_dmub_send_vbios_gpint_command(adev, send_addrs[i], chunk, 30000);
- if (ret != DMUB_STATUS_OK)
- goto free_bb;
- }
-
- /* Now ask DMUB to copy the bb */
- ret = dm_dmub_send_vbios_gpint_command(adev, DMUB_GPINT__BB_COPY, 1, 200000);
- if (ret != DMUB_STATUS_OK)
- goto free_bb;
-
- return bb;
-
-free_bb:
- dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, (void *) bb);
- return NULL;
-
-}
-
-static enum dmub_ips_disable_type dm_get_default_ips_mode(
- struct amdgpu_device *adev)
-{
- enum dmub_ips_disable_type ret = DMUB_IPS_ENABLE;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 6, 0):
- case IP_VERSION(3, 5, 1):
- ret = DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF;
- break;
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- ret = DMUB_IPS_ENABLE;
- break;
- default:
- /* ASICs older than DCN35 do not have IPSs */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0))
- ret = DMUB_IPS_DISABLE_ALL;
- break;
- }
-
- return ret;
-}
-
static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm)
{
struct mod_power_init_params init_data[MAX_NUM_EDP];
@@ -1952,40 +553,6 @@ static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm)
return 0;
}
-static void hdmi_frl_status_polling_work(struct work_struct *work)
-{
- struct amdgpu_display_manager *dm =
- container_of(to_delayed_work(work), struct amdgpu_display_manager,
- hdmi_frl_status_polling_work);
- struct dc *dc = dm->dc;
- struct dc_link *dc_link;
- bool link_update = false;
-
- for (int i = 0; i < MAX_LINKS; i++) {
- dc_link = dc->links[i];
-
- if (!dc_link || !dc_link->local_sink)
- continue;
-
- if (!dc_is_hdmi_signal(dc_link->connector_signal))
- continue;
-
- if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL)
- continue;
-
- link_update = dc_link_frl_poll_status_flag(dc_link);
- if (link_update) {
- mutex_lock(&dm->dc_lock);
- dc_link_detect(dc_link, DETECT_REASON_RETRAIN);
- mutex_unlock(&dm->dc_lock);
- }
- }
-
- queue_delayed_work(dm->hdmi_frl_status_polling_wq,
- &dm->hdmi_frl_status_polling_work,
- msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms));
-}
-
static int amdgpu_dm_init(struct amdgpu_device *adev)
{
struct dc_init_data init_data;
@@ -2212,7 +779,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
dc_hardware_init(adev->dm.dc);
- adev->dm.hpd_rx_offload_wq = hpd_rx_irq_create_workqueue(adev);
+ adev->dm.hpd_rx_offload_wq = amdgpu_dm_hpd_rx_irq_create_workqueue(adev);
if (!adev->dm.hpd_rx_offload_wq) {
drm_err(adev_to_drm(adev), "failed to create hpd rx offload workqueue.\n");
goto error;
@@ -2265,8 +832,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
create_singlethread_workqueue("hdmi_frl_status_polling_workqueue");
if (!adev->dm.hdmi_frl_status_polling_wq)
drm_err(adev_to_drm(adev), "failed to initialize hdmi_frl_status_polling_workqueue\n");
- adev->dm.hdmi_frl_status_polling_delay_ms = 200;
- INIT_DELAYED_WORK(&adev->dm.hdmi_frl_status_polling_work, hdmi_frl_status_polling_work);
}
if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
init_completion(&adev->dm.dmub_aux_transfer_done);
@@ -2283,8 +848,8 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
}
amdgpu_dm_outbox_init(adev);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
- dmub_aux_setconfig_callback, false)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
+ dm_dmub_aux_setconfig_callback, false)) {
drm_err(adev_to_drm(adev), "fail to register dmub aux callback");
goto error;
}
@@ -2292,16 +857,17 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
for (size_t i = 0; i < ARRAY_SIZE(adev->dm.fused_io); i++)
init_completion(&adev->dm.fused_io[i].replied);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
- dmub_aux_fused_io_callback, false)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
+ dm_dmub_aux_fused_io_callback, false)) {
drm_err(adev_to_drm(adev), "fail to register dmub fused io callback");
goto error;
}
/* Enable outbox notification only after IRQ handlers are registered and DMUB is alive.
* It is expected that DMUB will resend any pending notifications at this point. Note
- * that hpd and hpd_irq handler registration are deferred to register_hpd_handlers() to
- * align legacy interface initialization sequence. Connection status will be proactivly
- * detected once in the amdgpu_dm_initialize_drm_device.
+ * that hpd and hpd_irq handler registration are deferred to
+ * amdgpu_dm_register_hpd_handlers() to align legacy interface initialization
+ * sequence. Connection status will be proactivly detected once in the
+ * amdgpu_dm_initialize_drm_device.
*/
dc_enable_dmub_outbox(adev->dm.dc);
@@ -2581,224 +1147,6 @@ static int load_dmcu_fw(struct amdgpu_device *adev)
return 0;
}
-static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address)
-{
- struct amdgpu_device *adev = ctx;
-
- return dm_read_reg(adev->dm.dc->ctx, address);
-}
-
-static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
- uint32_t value)
-{
- struct amdgpu_device *adev = ctx;
-
- return dm_write_reg(adev->dm.dc->ctx, address, value);
-}
-
-static int dm_dmub_sw_init(struct amdgpu_device *adev)
-{
- struct dmub_srv_create_params create_params;
- struct dmub_srv_fw_meta_info_params fw_meta_info_params;
- struct dmub_srv_region_params region_params;
- struct dmub_srv_region_info region_info;
- struct dmub_srv_memory_params memory_params;
- struct dmub_fw_meta_info fw_info;
- struct dmub_srv_fb_info *fb_info;
- struct dmub_srv *dmub_srv;
- const struct dmcub_firmware_header_v1_0 *hdr;
- enum dmub_asic dmub_asic;
- enum dmub_status status;
- static enum dmub_window_memory_type window_memory_type[DMUB_WINDOW_TOTAL] = {
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_0_INST_CONST
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_1_STACK
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_2_BSS_DATA
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_3_VBIOS
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_4_MAILBOX
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_5_TRACEBUFF
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_6_FW_STATE
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_7_SCRATCH_MEM
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_IB_MEM
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_SHARED_STATE
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_LSDMA_BUFFER
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_CURSOR_OFFLOAD
- };
- int r;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- dmub_asic = DMUB_ASIC_DCN21;
- break;
- case IP_VERSION(3, 0, 0):
- dmub_asic = DMUB_ASIC_DCN30;
- break;
- case IP_VERSION(3, 0, 1):
- dmub_asic = DMUB_ASIC_DCN301;
- break;
- case IP_VERSION(3, 0, 2):
- dmub_asic = DMUB_ASIC_DCN302;
- break;
- case IP_VERSION(3, 0, 3):
- dmub_asic = DMUB_ASIC_DCN303;
- break;
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- dmub_asic = (adev->external_rev_id == YELLOW_CARP_B0) ? DMUB_ASIC_DCN31B : DMUB_ASIC_DCN31;
- break;
- case IP_VERSION(3, 1, 4):
- dmub_asic = DMUB_ASIC_DCN314;
- break;
- case IP_VERSION(3, 1, 5):
- dmub_asic = DMUB_ASIC_DCN315;
- break;
- case IP_VERSION(3, 1, 6):
- dmub_asic = DMUB_ASIC_DCN316;
- break;
- case IP_VERSION(3, 2, 0):
- dmub_asic = DMUB_ASIC_DCN32;
- break;
- case IP_VERSION(3, 2, 1):
- dmub_asic = DMUB_ASIC_DCN321;
- break;
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- dmub_asic = DMUB_ASIC_DCN35;
- break;
- case IP_VERSION(3, 6, 0):
- dmub_asic = DMUB_ASIC_DCN36;
- break;
- case IP_VERSION(4, 0, 1):
- dmub_asic = DMUB_ASIC_DCN401;
- break;
- case IP_VERSION(4, 2, 0):
- dmub_asic = DMUB_ASIC_DCN42;
- break;
- case IP_VERSION(4, 2, 1):
- dmub_asic = DMUB_ASIC_DCN42B;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
-
- hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data;
- adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id =
- AMDGPU_UCODE_ID_DMCUB;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw =
- adev->dm.dmub_fw;
- adev->firmware.fw_size +=
- ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
-
- drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
- }
-
-
- adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv);
- dmub_srv = adev->dm.dmub_srv;
-
- if (!dmub_srv) {
- drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n");
- return -ENOMEM;
- }
-
- memset(&create_params, 0, sizeof(create_params));
- create_params.user_ctx = adev;
- create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read;
- create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write;
- create_params.asic = dmub_asic;
-
- /* Create the DMUB service. */
- status = dmub_srv_create(dmub_srv, &create_params);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status);
- return -EINVAL;
- }
-
- /* Extract the FW meta info. */
- memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params));
-
- fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
- PSP_HEADER_BYTES_256;
- fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
- fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES_256;
- fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- le32_to_cpu(hdr->inst_const_bytes) : NULL;
- fw_meta_info_params.custom_psp_footer_size = 0;
-
- status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info);
- if (status != DMUB_STATUS_OK) {
- /* Skip returning early, just log the error. */
- drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status);
- // return -EINVAL;
- }
-
- /* Calculate the size of all the regions for the DMUB service. */
- memset(&region_params, 0, sizeof(region_params));
-
- region_params.inst_const_size = fw_meta_info_params.inst_const_size;
- region_params.bss_data_size = fw_meta_info_params.bss_data_size;
- region_params.vbios_size = adev->bios_size;
- region_params.fw_bss_data = fw_meta_info_params.fw_bss_data;
- region_params.fw_inst_const = fw_meta_info_params.fw_inst_const;
- region_params.window_memory_type = window_memory_type;
- region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL;
-
- status = dmub_srv_calc_region_info(dmub_srv, &region_params,
- &region_info);
-
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error calculating DMUB region info: %d\n", status);
- return -EINVAL;
- }
-
- /*
- * Allocate a framebuffer based on the total size of all the regions.
- * TODO: Move this into GART.
- */
- r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM |
- AMDGPU_GEM_DOMAIN_GTT,
- &adev->dm.dmub_bo,
- &adev->dm.dmub_bo_gpu_addr,
- &adev->dm.dmub_bo_cpu_addr);
- if (r)
- return r;
-
- /* Rebase the regions on the framebuffer address. */
- memset(&memory_params, 0, sizeof(memory_params));
- memory_params.cpu_fb_addr = adev->dm.dmub_bo_cpu_addr;
- memory_params.gpu_fb_addr = adev->dm.dmub_bo_gpu_addr;
- memory_params.region_info = &region_info;
- memory_params.window_memory_type = window_memory_type;
-
- adev->dm.dmub_fb_info = kzalloc_obj(*adev->dm.dmub_fb_info);
- fb_info = adev->dm.dmub_fb_info;
-
- if (!fb_info) {
- drm_err(adev_to_drm(adev),
- "Failed to allocate framebuffer info for DMUB service!\n");
- return -ENOMEM;
- }
-
- status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status);
- return -EINVAL;
- }
-
- adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
- adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size;
-
- return 0;
-}
-
static int dm_sw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -2852,41 +1200,6 @@ static int dm_sw_fini(struct amdgpu_ip_block *ip_block)
return 0;
}
-static int detect_mst_link_for_all_connectors(struct drm_device *dev)
-{
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- int ret = 0;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (aconnector->dc_link->type == dc_connection_mst_branch &&
- aconnector->mst_mgr.aux) {
- drm_dbg_kms(dev, "DM_MST: starting TM on aconnector: %p [id: %d]\n",
- aconnector,
- aconnector->base.base.id);
-
- ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
- if (ret < 0) {
- drm_err(dev, "DM_MST: Failed to start MST\n");
- aconnector->dc_link->type =
- dc_connection_single;
- ret = dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx,
- aconnector->dc_link);
- break;
- }
- }
- }
- drm_connector_list_iter_end(&iter);
-
- return ret;
-}
static void amdgpu_dm_boot_time_crc_init(struct amdgpu_device *adev)
{
@@ -2984,7 +1297,7 @@ static int dm_late_init(struct amdgpu_ip_block *ip_block)
}
}
- return detect_mst_link_for_all_connectors(adev_to_drm(adev));
+ return amdgpu_dm_detect_mst_link_for_all_connectors(adev_to_drm(adev));
}
static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
@@ -3038,48 +1351,6 @@ out_fail:
mutex_unlock(&mgr->lock);
}
-void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector)
-{
- struct cec_notifier *n = aconnector->notifier;
-
- if (!n)
- return;
-
- cec_notifier_phys_addr_invalidate(n);
-}
-
-void hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector)
-{
- struct drm_connector *connector = &aconnector->base;
- struct cec_notifier *n = aconnector->notifier;
-
- if (!n)
- return;
-
- cec_notifier_set_phys_addr(n,
- connector->display_info.source_physical_address);
-}
-
-static void s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend)
-{
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter conn_iter;
-
- drm_connector_list_iter_begin(ddev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter) {
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (suspend)
- hdmi_cec_unset_edid(aconnector);
- else
- hdmi_cec_set_edid(aconnector);
- }
- drm_connector_list_iter_end(&conn_iter);
-}
-
static void s3_handle_mst(struct drm_device *dev, bool suspend)
{
struct amdgpu_dm_connector *aconnector;
@@ -3182,7 +1453,7 @@ static int dm_oem_i2c_hw_init(struct amdgpu_device *adev)
oem_ddc_service = dc_get_oem_i2c_device(adev->dm.dc);
if (oem_ddc_service) {
- oem_i2c = create_i2c(oem_ddc_service, true);
+ oem_i2c = amdgpu_dm_create_i2c(oem_ddc_service, true);
if (!oem_i2c) {
drm_info(adev_to_drm(adev), "Failed to create oem i2c adapter data\n");
return -ENOMEM;
@@ -3267,7 +1538,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
int i = 0;
for (i = 0; i < state->stream_count; i++) {
- acrtc = get_crtc_by_otg_inst(
+ acrtc = amdgpu_dm_get_crtc_by_otg_inst(
adev, state->stream_status[i].primary_otg_inst);
if (acrtc && state->stream_status[i].plane_count != 0) {
@@ -3344,16 +1615,6 @@ static enum dc_status amdgpu_dm_commit_zero_streams(struct dc *dc)
return dc_commit_streams(dc, &params);
}
-static void hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm)
-{
- int i;
-
- if (dm->hpd_rx_offload_wq) {
- for (i = 0; i < dm->dc->caps.max_links; i++)
- flush_workqueue(dm->hpd_rx_offload_wq[i].wq);
- }
-}
-
static int dm_cache_state(struct amdgpu_device *adev)
{
int r;
@@ -3449,7 +1710,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
amdgpu_dm_irq_suspend(adev);
- hpd_rx_irq_work_suspend(dm);
+ amdgpu_dm_hpd_rx_irq_work_suspend(dm);
return 0;
}
@@ -3461,7 +1722,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
return r;
}
- s3_handle_hdmi_cec(adev_to_drm(adev), true);
+ amdgpu_dm_s3_handle_hdmi_cec(adev_to_drm(adev), true);
s3_handle_mst(adev_to_drm(adev), true);
@@ -3475,7 +1736,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
scoped_guard(mutex, &dm->dc_lock)
amdgpu_dm_ism_force_full_power(dm);
- hpd_rx_irq_work_suspend(dm);
+ amdgpu_dm_hpd_rx_irq_work_suspend(dm);
dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3);
@@ -3487,26 +1748,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
return 0;
}
-struct drm_connector *
-amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
- struct drm_crtc *crtc)
-{
- u32 i;
- struct drm_connector_state *new_con_state;
- struct drm_connector *connector;
- struct drm_crtc *crtc_from_state;
-
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
- crtc_from_state = new_con_state->crtc;
-
- if (crtc_from_state == crtc)
- return connector;
- }
-
- return NULL;
-}
-
-static void emulated_link_detect(struct dc_link *link)
+void amdgpu_dm_emulated_link_detect(struct dc_link *link)
{
struct dc_sink_init_data sink_init_data = { 0 };
struct display_sink_capability sink_caps = { 0 };
@@ -3627,8 +1869,8 @@ static void dm_gpureset_commit_state(struct dc_state *dc_state,
}
}
-static void apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
- struct dc_sink *sink)
+void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
+ struct dc_sink *sink)
{
struct dc_panel_patch *ppatch = NULL;
@@ -3771,8 +2013,7 @@ static int dm_resume(struct amdgpu_ip_block *ip_block)
for (i = 0; i < dc_state->stream_count; i++) {
dc_state->streams[i]->mode_changed = true;
for (j = 0; j < dc_state->stream_status[i].plane_count; j++) {
- dc_state->stream_status[i].plane_states[j]->update_flags.raw
- = 0xffffffff;
+ dc_pipe_update_bits_set_full(&dc_state->stream_status[i].plane_states[j]->update_bits);
}
}
@@ -3835,7 +2076,7 @@ static int dm_resume(struct amdgpu_ip_block *ip_block)
*/
amdgpu_dm_irq_resume_early(adev);
- s3_handle_hdmi_cec(ddev, false);
+ amdgpu_dm_s3_handle_hdmi_cec(ddev, false);
/* On resume we need to rewrite the MSTM control bits to enable MST*/
s3_handle_mst(ddev, false);
@@ -3870,14 +2111,14 @@ static int dm_resume(struct amdgpu_ip_block *ip_block)
drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(aconnector->dc_link);
+ amdgpu_dm_emulated_link_detect(aconnector->dc_link);
} else {
guard(mutex)(&dm->dc_lock);
dc_exit_ips_for_hw_access(dm->dc);
ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_RESUMEFROMS3S4);
if (ret) {
/* w/a delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
+ amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
}
}
@@ -3955,7 +2196,6 @@ static const struct amd_ip_funcs amdgpu_dm_funcs = {
.resume = dm_resume,
.is_idle = dm_is_idle,
.wait_for_idle = dm_wait_for_idle,
- .check_soft_reset = dm_check_soft_reset,
.soft_reset = dm_soft_reset,
.set_clockgating_state = dm_set_clockgating_state,
.set_powergating_state = dm_set_powergating_state,
@@ -3988,1070 +2228,6 @@ static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
.atomic_commit_setup = amdgpu_dm_atomic_setup_commit,
};
-#define DDC_MANUFACTURERNAME_SAMSUNG 0x2D4C
-
-static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
-{
- struct drm_connector *connector = &aconnector->base;
- struct drm_display_info *display_info = &connector->display_info;
- struct dc_link *link = aconnector->dc_link;
- struct amdgpu_device *adev;
-
- adev = drm_to_adev(connector->dev);
-
- link->panel_type = PANEL_TYPE_NONE;
-
- switch (display_info->amd_vsdb.panel_type) {
- case AMD_VSDB_PANEL_TYPE_OLED:
- link->panel_type = PANEL_TYPE_OLED;
- break;
- case AMD_VSDB_PANEL_TYPE_MINILED:
- link->panel_type = PANEL_TYPE_MINILED;
- break;
- }
-
- /* If VSDB didn't determine panel type, check DPCD ext caps */
- if (link->panel_type == PANEL_TYPE_NONE) {
- if (link->dpcd_sink_ext_caps.bits.miniled == 1)
- link->panel_type = PANEL_TYPE_MINILED;
- if (link->dpcd_sink_ext_caps.bits.oled == 1)
- link->panel_type = PANEL_TYPE_OLED;
- }
-
- /* If VSDB and DPCD didn't determine panel type, check DID */
- if (link->panel_type == PANEL_TYPE_NONE) {
- if (display_info->panel_type == DRM_MODE_PANEL_TYPE_LCD)
- link->panel_type = PANEL_TYPE_LCD;
- else if (display_info->panel_type == DRM_MODE_PANEL_TYPE_OLED)
- link->panel_type = PANEL_TYPE_OLED;
- }
-
- if (link->panel_type == PANEL_TYPE_NONE) {
- struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb;
- u32 lum1_max = vsdb->luminance_range1.max_luminance;
- u32 lum2_max = vsdb->luminance_range2.max_luminance;
-
- if (vsdb->version && link->local_sink &&
- link->local_sink->edid_caps.manufacturer_id ==
- DDC_MANUFACTURERNAME_SAMSUNG &&
- lum1_max >= ((lum2_max * 3) / 2))
- link->panel_type = PANEL_TYPE_MINILED;
- }
-
- if (link->panel_type == PANEL_TYPE_OLED)
- drm_object_property_set_value(&connector->base,
- adev_to_drm(adev)->mode_config.panel_type_property,
- DRM_MODE_PANEL_TYPE_OLED);
- else if (link->panel_type == PANEL_TYPE_LCD)
- drm_object_property_set_value(&connector->base,
- adev_to_drm(adev)->mode_config.panel_type_property,
- DRM_MODE_PANEL_TYPE_LCD);
- else
- drm_object_property_set_value(&connector->base,
- adev_to_drm(adev)->mode_config.panel_type_property,
- DRM_MODE_PANEL_TYPE_UNKNOWN);
-
- drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type);
-}
-
-static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
-{
- const struct drm_panel_backlight_quirk *panel_backlight_quirk;
- struct amdgpu_dm_backlight_caps *caps;
- struct drm_connector *conn_base;
- struct amdgpu_device *adev;
- struct drm_luminance_range_info *luminance_range;
- struct drm_device *drm;
-
- if (aconnector->bl_idx == -1 ||
- aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP)
- return;
-
- conn_base = &aconnector->base;
- drm = conn_base->dev;
- adev = drm_to_adev(drm);
-
- caps = &adev->dm.backlight_caps[aconnector->bl_idx];
- caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
- caps->aux_support = false;
-
- if (caps->ext_caps->bits.oled == 1
- /*
- * ||
- * caps->ext_caps->bits.sdr_aux_backlight_control == 1 ||
- * caps->ext_caps->bits.hdr_aux_backlight_control == 1
- */)
- caps->aux_support = true;
-
- if (amdgpu_backlight == 0)
- caps->aux_support = false;
- else if (amdgpu_backlight == 1)
- caps->aux_support = true;
- if (caps->aux_support)
- aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
-
- luminance_range = &conn_base->display_info.luminance_range;
-
- if (luminance_range->max_luminance)
- caps->aux_max_input_signal = luminance_range->max_luminance;
- else
- caps->aux_max_input_signal = 512;
-
- if (luminance_range->min_luminance)
- caps->aux_min_input_signal = luminance_range->min_luminance;
- else
- caps->aux_min_input_signal = 1;
-
- panel_backlight_quirk =
- drm_get_panel_backlight_quirk(aconnector->drm_edid);
- if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
- if (panel_backlight_quirk->min_brightness) {
- caps->min_input_signal =
- panel_backlight_quirk->min_brightness - 1;
- drm_info(drm,
- "Applying panel backlight quirk, min_brightness: %d\n",
- caps->min_input_signal);
- }
- if (panel_backlight_quirk->brightness_mask) {
- drm_info(drm,
- "Applying panel backlight quirk, brightness_mask: 0x%X\n",
- panel_backlight_quirk->brightness_mask);
- caps->brightness_mask =
- panel_backlight_quirk->brightness_mask;
- }
- }
-}
-
-DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T))
-
-void amdgpu_dm_update_connector_after_detect(
- struct amdgpu_dm_connector *aconnector)
-{
- struct drm_connector *connector = &aconnector->base;
- struct dc_sink *sink __free(sink_release) = NULL;
- struct drm_device *dev = connector->dev;
-
- /* MST handled by drm_mst framework */
- if (aconnector->mst_mgr.mst_state == true)
- return;
-
- sink = aconnector->dc_link->local_sink;
- if (sink)
- dc_sink_retain(sink);
-
- /*
- * Edid mgmt connector gets first update only in mode_valid hook and then
- * the connector sink is set to either fake or physical sink depends on link status.
- * Skip if already done during boot.
- */
- if (aconnector->base.force != DRM_FORCE_UNSPECIFIED
- && aconnector->dc_em_sink) {
-
- /*
- * For S3 resume with headless use eml_sink to fake stream
- * because on resume connector->sink is set to NULL
- */
- guard(mutex)(&dev->mode_config.mutex);
-
- if (sink) {
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- /*
- * retain and release below are used to
- * bump up refcount for sink because the link doesn't point
- * to it anymore after disconnect, so on next crtc to connector
- * reshuffle by UMD we will get into unwanted dc_sink release
- */
- dc_sink_release(aconnector->dc_sink);
- }
- aconnector->dc_sink = sink;
- dc_sink_retain(aconnector->dc_sink);
- amdgpu_dm_update_freesync_caps(connector,
- aconnector->drm_edid, true);
- } else {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- if (!aconnector->dc_sink) {
- aconnector->dc_sink = aconnector->dc_em_sink;
- dc_sink_retain(aconnector->dc_sink);
- }
- }
-
- return;
- }
-
- /*
- * TODO: temporary guard to look for proper fix
- * if this sink is MST sink, we should not do anything
- */
- if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
- return;
-
- if (aconnector->dc_sink == sink) {
- /*
- * We got a DP short pulse (Link Loss, DP CTS, etc...).
- * Do nothing!!
- */
- drm_dbg_kms(dev, "DCHPD: connector_id=%d: dc_sink didn't change.\n",
- aconnector->connector_id);
- return;
- }
-
- drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n",
- aconnector->connector_id, aconnector->dc_sink, sink);
-
- /* When polling, DRM has already locked the mutex for us. */
- if (!drm_kms_helper_is_poll_worker())
- mutex_lock(&dev->mode_config.mutex);
-
- /*
- * 1. Update status of the drm connector
- * 2. Send an event and let userspace tell us what to do
- */
- if (sink) {
- /*
- * TODO: check if we still need the S3 mode update workaround.
- * If yes, put it here.
- */
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- dc_sink_release(aconnector->dc_sink);
- }
-
- aconnector->dc_sink = sink;
- dc_sink_retain(aconnector->dc_sink);
- drm_edid_free(aconnector->drm_edid);
- aconnector->drm_edid = NULL;
- if (sink->dc_edid.length == 0) {
- hdmi_cec_unset_edid(aconnector);
- if (aconnector->dc_link->aux_mode) {
- drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
- }
- } else {
- const struct edid *edid = (const struct edid *)sink->dc_edid.raw_edid;
-
- aconnector->drm_edid = drm_edid_alloc(edid, sink->dc_edid.length);
- drm_edid_connector_update(connector, aconnector->drm_edid);
-
- hdmi_cec_set_edid(aconnector);
- if (aconnector->dc_link->aux_mode)
- drm_dp_cec_attach(&aconnector->dm_dp_aux.aux,
- connector->display_info.source_physical_address);
- }
-
- if (!aconnector->timing_requested) {
- aconnector->timing_requested =
- kzalloc_obj(struct dc_crtc_timing);
- if (!aconnector->timing_requested)
- drm_err(dev,
- "failed to create aconnector->requested_timing\n");
- }
-
- amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid, true);
- update_connector_ext_caps(aconnector);
- dm_set_panel_type(aconnector);
- } else {
- hdmi_cec_unset_edid(aconnector);
- drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- aconnector->num_modes = 0;
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
- drm_edid_free(aconnector->drm_edid);
- aconnector->drm_edid = NULL;
- kfree(aconnector->timing_requested);
- aconnector->timing_requested = NULL;
- /* Set CP to DESIRED if it was ENABLED, so we can re-enable it again on hotplug */
- if (connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
- connector->state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- }
-
- update_subconnector_property(aconnector);
-
- /* When polling, the mutex will be unlocked for us by DRM. */
- if (!drm_kms_helper_is_poll_worker())
- mutex_unlock(&dev->mode_config.mutex);
-}
-
-static bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2)
-{
- if (!sink1 || !sink2)
- return false;
- if (sink1->sink_signal != sink2->sink_signal)
- return false;
-
- if (sink1->dc_edid.length != sink2->dc_edid.length)
- return false;
-
- if (memcmp(sink1->dc_edid.raw_edid, sink2->dc_edid.raw_edid,
- sink1->dc_edid.length) != 0)
- return false;
- return true;
-}
-
-
-/**
- * DOC: hdmi_hpd_debounce_work
- *
- * HDMI HPD debounce delay in milliseconds. When an HDMI display toggles HPD
- * (such as during power save transitions), this delay determines how long to
- * wait before processing the HPD event. This allows distinguishing between a
- * physical unplug (>hdmi_hpd_debounce_delay)
- * and a spontaneous RX HPD toggle (<hdmi_hpd_debounce_delay).
- *
- * If the toggle is less than this delay, the driver compares sink capabilities
- * and permits a hotplug event if they changed.
- *
- * The default value of 1500ms was chosen based on experimental testing with
- * various monitors that exhibit spontaneous HPD toggling behavior.
- */
-static void hdmi_hpd_debounce_work(struct work_struct *work)
-{
- struct amdgpu_dm_connector *aconnector =
- container_of(to_delayed_work(work), struct amdgpu_dm_connector,
- hdmi_hpd_debounce_work);
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dc *dc = aconnector->dc_link->ctx->dc;
- bool fake_reconnect = false;
- bool reallow_idle = false;
- bool ret = false;
- guard(mutex)(&aconnector->hpd_lock);
-
- /* Re-detect the display */
- scoped_guard(mutex, &adev->dm.dc_lock) {
- if (dc->caps.ips_support && dc->ctx->dmub_srv->idle_allowed) {
- dc_allow_idle_optimizations(dc, false);
- reallow_idle = true;
- }
- ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
- }
-
- if (ret) {
- /* Apply workaround delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
- /* Compare sinks to determine if this was a spontaneous HPD toggle */
- if (are_sinks_equal(aconnector->dc_link->local_sink, aconnector->hdmi_prev_sink)) {
- /*
- * Sinks match - this was a spontaneous HDMI HPD toggle.
- */
- drm_dbg_kms(dev, "HDMI HPD: Sink unchanged after debounce, internal re-enable\n");
- fake_reconnect = true;
- }
-
- /* Update connector state */
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- /* Only notify OS if sink actually changed */
- if (!fake_reconnect && aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_hotplug_event(dev);
- }
-
- /* Release the cached sink reference */
- if (aconnector->hdmi_prev_sink) {
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = NULL;
- }
-
- scoped_guard(mutex, &adev->dm.dc_lock) {
- if (reallow_idle && dc->caps.ips_support)
- dc_allow_idle_optimizations(dc, true);
- }
-}
-
-static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
-{
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- enum dc_connection_type new_connection_type = dc_connection_none;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state);
- struct dc *dc = aconnector->dc_link->ctx->dc;
- bool ret = false;
- bool debounce_required = false;
-
- if (adev->dm.disable_hpd_irq)
- return;
-
- /*
- * In case of failure or MST no need to update connector status or notify the OS
- * since (for MST case) MST does this in its own context.
- */
- guard(mutex)(&aconnector->hpd_lock);
-
- if (adev->dm.hdcp_workqueue) {
- hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index);
- dm_con_state->update_hdcp = true;
- }
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- aconnector->timing_changed = false;
-
- if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
-
- /*
- * Check for HDMI disconnect with debounce enabled.
- */
- debounce_required = (aconnector->hdmi_hpd_debounce_delay_ms > 0 &&
- dc_is_hdmi_signal(aconnector->dc_link->connector_signal) &&
- new_connection_type == dc_connection_none &&
- aconnector->dc_link->local_sink != NULL);
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(aconnector->dc_link);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_connector_hotplug_event(connector);
- } else if (debounce_required) {
- /*
- * HDMI disconnect detected - schedule delayed work instead of
- * processing immediately. This allows us to coalesce spurious
- * HDMI signals from physical unplugs.
- */
- drm_dbg_kms(dev, "HDMI HPD: Disconnect detected, scheduling debounce work (%u ms)\n",
- aconnector->hdmi_hpd_debounce_delay_ms);
-
- /* Cache the current sink for later comparison */
- if (aconnector->hdmi_prev_sink)
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = aconnector->dc_link->local_sink;
- if (aconnector->hdmi_prev_sink)
- dc_sink_retain(aconnector->hdmi_prev_sink);
-
- /* Schedule delayed detection. */
- if (mod_delayed_work(system_percpu_wq,
- &aconnector->hdmi_hpd_debounce_work,
- msecs_to_jiffies(aconnector->hdmi_hpd_debounce_delay_ms)))
- drm_dbg_kms(dev, "HDMI HPD: Re-scheduled debounce work\n");
-
- } else {
-
- /* If the aconnector->hdmi_hpd_debounce_work is scheduled, exit early */
- if (delayed_work_pending(&aconnector->hdmi_hpd_debounce_work))
- return;
-
- scoped_guard(mutex, &adev->dm.dc_lock) {
- dc_exit_ips_for_hw_access(dc);
- ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
- }
- if (ret) {
- /* w/a delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_connector_hotplug_event(connector);
- }
- }
-}
-
-static void handle_hpd_irq(void *param)
-{
- struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
-
- handle_hpd_irq_helper(aconnector);
-
-}
-
-static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq,
- union hpd_irq_data hpd_irq_data)
-{
- struct hpd_rx_irq_offload_work *offload_work = kzalloc_obj(*offload_work);
-
- if (!offload_work) {
- drm_err(adev_to_drm(adev), "Failed to allocate hpd_rx_irq_offload_work.\n");
- return;
- }
-
- INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work);
- offload_work->data = hpd_irq_data;
- offload_work->offload_wq = offload_wq;
- offload_work->adev = adev;
-
- queue_work(offload_wq->wq, &offload_work->work);
- drm_dbg_kms(adev_to_drm(adev), "queue work to handle hpd_rx offload work");
-}
-
-static void handle_hpd_rx_irq(void *param)
-{
- struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct dc_link *dc_link = aconnector->dc_link;
- bool is_mst_root_connector = aconnector->mst_mgr.mst_state;
- bool result = false;
- enum dc_connection_type new_connection_type = dc_connection_none;
- struct amdgpu_device *adev = drm_to_adev(dev);
- union hpd_irq_data hpd_irq_data;
- bool link_loss = false;
- bool has_left_work = false;
- int idx = dc_link->link_index;
- struct hpd_rx_irq_offload_work_queue *offload_wq = &adev->dm.hpd_rx_offload_wq[idx];
- struct dc *dc = aconnector->dc_link->ctx->dc;
-
- memset(&hpd_irq_data, 0, sizeof(hpd_irq_data));
-
- if (adev->dm.disable_hpd_irq)
- return;
-
- /*
- * TODO:Temporary add mutex to protect hpd interrupt not have a gpio
- * conflict, after implement i2c helper, this mutex should be
- * retired.
- */
- mutex_lock(&aconnector->hpd_lock);
-
- result = dc_link_handle_hpd_rx_irq(dc_link, &hpd_irq_data,
- &link_loss, true, &has_left_work);
-
- if (!has_left_work)
- goto out;
-
- if (hpd_irq_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
- goto out;
- }
-
- if (dc_link_dp_allow_hpd_rx_irq(dc_link)) {
- if (hpd_irq_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY ||
- hpd_irq_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
- bool skip = false;
-
- /*
- * DOWN_REP_MSG_RDY is also handled by polling method
- * mgr->cbs->poll_hpd_irq()
- */
- spin_lock(&offload_wq->offload_lock);
- skip = offload_wq->is_handling_mst_msg_rdy_event;
-
- if (!skip)
- offload_wq->is_handling_mst_msg_rdy_event = true;
-
- spin_unlock(&offload_wq->offload_lock);
-
- if (!skip)
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
-
- goto out;
- }
-
- if (link_loss) {
- bool skip = false;
-
- spin_lock(&offload_wq->offload_lock);
- skip = offload_wq->is_handling_link_loss;
-
- if (!skip)
- offload_wq->is_handling_link_loss = true;
-
- spin_unlock(&offload_wq->offload_lock);
-
- if (!skip)
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
-
- goto out;
- }
- }
-
-out:
- if (result && !is_mst_root_connector) {
- /* Downstream Port status changed. */
- if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(dc_link);
-
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- amdgpu_dm_update_connector_after_detect(aconnector);
-
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- drm_kms_helper_connector_hotplug_event(connector);
- } else {
- bool ret = false;
-
- mutex_lock(&adev->dm.dc_lock);
- dc_exit_ips_for_hw_access(dc);
- ret = dc_link_detect(dc_link, DETECT_REASON_HPDRX);
- mutex_unlock(&adev->dm.dc_lock);
-
- if (ret) {
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- drm_kms_helper_connector_hotplug_event(connector);
- }
- }
- }
- if (hpd_irq_data.bytes.device_service_irq.bits.CP_IRQ) {
- if (adev->dm.hdcp_workqueue)
- hdcp_handle_cpirq(adev->dm.hdcp_workqueue, aconnector->base.index);
- }
-
- if (dc_link->type != dc_connection_mst_branch)
- drm_dp_cec_irq(&aconnector->dm_dp_aux.aux);
-
- mutex_unlock(&aconnector->hpd_lock);
-}
-
-static int register_hpd_handlers(struct amdgpu_device *adev)
-{
- struct drm_device *dev = adev_to_drm(adev);
- struct drm_connector *connector;
- struct amdgpu_dm_connector *aconnector;
- const struct dc_link *dc_link;
- struct dc_interrupt_params int_params = {0};
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD,
- dmub_hpd_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
- return -EINVAL;
- }
-
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ,
- dmub_hpd_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
- return -EINVAL;
- }
-
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_SENSE_NOTIFY,
- dmub_hpd_sense_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd sense callback");
- return -EINVAL;
- }
- }
-
- list_for_each_entry(connector,
- &dev->mode_config.connector_list, head) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- dc_link = aconnector->dc_link;
-
- if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) {
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source = dc_link->irq_source_hpd;
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_HPD1 ||
- int_params.irq_source > DC_IRQ_SOURCE_HPD6) {
- drm_err(adev_to_drm(adev), "Failed to register hpd irq!\n");
- return -EINVAL;
- }
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- handle_hpd_irq, (void *) aconnector))
- return -ENOMEM;
- }
-
- if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) {
-
- /* Also register for DP short pulse (hpd_rx). */
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source = dc_link->irq_source_hpd_rx;
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_HPD1RX ||
- int_params.irq_source > DC_IRQ_SOURCE_HPD6RX) {
- drm_err(adev_to_drm(adev), "Failed to register hpd rx irq!\n");
- return -EINVAL;
- }
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- handle_hpd_rx_irq, (void *) aconnector))
- return -ENOMEM;
- }
- }
- return 0;
-}
-
-/* Register IRQ sources and initialize IRQ callbacks */
-static int dce110_register_irq_handlers(struct amdgpu_device *adev)
-{
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r;
- int i;
- unsigned int src_id;
- unsigned int client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
- /* Use different interrupts for VBLANK on DCE 6 vs. newer. */
- const unsigned int vblank_d1 =
- adev->dm.dc->ctx->dce_version >= DCE_VERSION_8_0
- ? VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 : 1;
-
- if (adev->family >= AMDGPU_FAMILY_AI)
- client_id = SOC15_IH_CLIENTID_DCE;
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- /*
- * Actions of amdgpu_irq_add_id():
- * 1. Register a set() function with base driver.
- * Base driver will call set() function to enable/disable an
- * interrupt in DC hardware.
- * 2. Register amdgpu_dm_irq_handler().
- * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
- * coming from DC hardware.
- * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
- * for acknowledging and handling.
- */
-
- /* Use VBLANK interrupt */
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- src_id = vblank_d1 + i;
- r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->crtc_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, src_id, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
- drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_crtc_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- if (dc_supports_vrr(adev->dm.dc->ctx->dce_version)) {
- /* Use VUPDATE interrupt */
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + i * 2;
- r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->vupdate_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, src_id, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) {
- drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vupdate_params[
- int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_vupdate_high_irq, c_irq_params))
- return -ENOMEM;
- }
- }
-
- /* Use GRPH_PFLIP interrupt */
- for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP;
- i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) {
- r = amdgpu_irq_add_id(adev, client_id, i, &adev->pageflip_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
- int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
- drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_pflip_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- /* HPD */
- r = amdgpu_irq_add_id(adev, client_id,
- VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A, &adev->hpd_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n");
- return r;
- }
-
- r = register_hpd_handlers(adev);
-
- return r;
-}
-
-/* Register IRQ sources and initialize IRQ callbacks */
-static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
-{
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r;
- int i;
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- static const unsigned int vrtl_int_srcid[] = {
- DCN_1_0__SRCID__OTG1_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG2_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG3_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG4_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG5_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG6_VERTICAL_INTERRUPT0_CONTROL
- };
-#endif
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- /*
- * Actions of amdgpu_irq_add_id():
- * 1. Register a set() function with base driver.
- * Base driver will call set() function to enable/disable an
- * interrupt in DC hardware.
- * 2. Register amdgpu_dm_irq_handler().
- * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
- * coming from DC hardware.
- * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
- * for acknowledging and handling.
- */
-
- /* Use VSTARTUP interrupt */
- for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP;
- i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
- drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_crtc_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- /* Use otg vertical line interrupt */
-#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE,
- vrtl_int_srcid[i], &adev->vline0_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vline0 irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, vrtl_int_srcid[i], 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_DC1_VLINE0 ||
- int_params.irq_source > DC_IRQ_SOURCE_DC6_VLINE0) {
- drm_err(adev_to_drm(adev), "Failed to register vline0 irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vline0_params[int_params.irq_source
- - DC_IRQ_SOURCE_DC1_VLINE0];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_dcn_vertical_interrupt0_high_irq,
- c_irq_params))
- return -ENOMEM;
- }
-#endif
-
- /* Use VUPDATE_NO_LOCK interrupt on DCN, which seems to correspond to
- * the regular VUPDATE interrupt on DCE. We want DC_IRQ_SOURCE_VUPDATEx
- * to trigger at end of each vblank, regardless of state of the lock,
- * matching DCE behaviour.
- */
- for (i = DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT;
- i <= DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT + adev->mode_info.num_crtc - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->vupdate_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) {
- drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vupdate_params[int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_vupdate_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- /* Use GRPH_PFLIP interrupt */
- for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT;
- i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
- int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
- drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_pflip_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- /* HPD */
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
- &adev->hpd_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n");
- return r;
- }
-
- r = register_hpd_handlers(adev);
-
- return r;
-}
-/* Register Outbox IRQ sources and initialize IRQ callbacks */
-static int register_outbox_irq_handlers(struct amdgpu_device *adev)
-{
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r, i;
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT,
- &adev->dmub_outbox_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add outbox irq id!\n");
- return r;
- }
-
- if (dc->ctx->dmub_srv) {
- i = DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT;
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- c_irq_params = &adev->dm.dmub_outbox_params[0];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_dmub_outbox1_low_irq, c_irq_params))
- return -ENOMEM;
- }
-
- return 0;
-}
-
/*
* Acquires the lock for the atomic state object and returns
* the new atomic state.
@@ -5201,420 +2377,6 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
return 0;
}
-#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
-#define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
-#define AMDGPU_DM_MIN_SPREAD ((AMDGPU_DM_DEFAULT_MAX_BACKLIGHT - AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) / 2)
-#define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
-
-void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
- int bl_idx)
-{
- struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx];
-
- if (caps->caps_valid)
- return;
-
-#if defined(CONFIG_ACPI)
- amdgpu_acpi_get_backlight_caps(caps);
-
- /* validate the firmware value is sane */
- if (caps->caps_valid) {
- int spread = caps->max_input_signal - caps->min_input_signal;
-
- if (caps->max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
- caps->min_input_signal < 0 ||
- spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
- spread < AMDGPU_DM_MIN_SPREAD) {
- drm_dbg_kms(adev_to_drm(dm->adev), "DM: Invalid backlight caps: min=%d, max=%d\n",
- caps->min_input_signal, caps->max_input_signal);
- caps->caps_valid = false;
- }
- }
-
- if (!caps->caps_valid) {
- caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
- caps->caps_valid = true;
- }
-#else
- if (caps->aux_support)
- return;
-
- caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
- caps->caps_valid = true;
-#endif
-}
-
-static int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
- unsigned int *min, unsigned int *max)
-{
- if (!caps)
- return 0;
-
- if (caps->aux_support) {
- // Firmware limits are in nits, DC API wants millinits.
- *max = 1000 * caps->aux_max_input_signal;
- *min = 1000 * caps->aux_min_input_signal;
- } else {
- // Firmware limits are 8-bit, PWM control is 16-bit.
- *max = 0x101 * caps->max_input_signal;
- *min = 0x101 * caps->min_input_signal;
- }
- return 1;
-}
-
-/* Rescale from [min..max] to [0..AMDGPU_MAX_BL_LEVEL] */
-static inline u32 scale_input_to_fw(int min, int max, u64 input)
-{
- return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max - min);
-}
-
-/* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */
-static inline u32 scale_fw_to_input(int min, int max, u64 input)
-{
- return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);
-}
-
-static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
- unsigned int min, unsigned int max,
- uint32_t *user_brightness)
-{
- u32 brightness = scale_input_to_fw(min, max, *user_brightness);
- u8 lower_signal, upper_signal, upper_lum, lower_lum, lum;
- int left, right;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
- return;
-
- if (!caps->data_points)
- return;
-
- /*
- * Handle the case where brightness is below the first data point
- * Interpolate between (0,0) and (first_signal, first_lum)
- */
- if (brightness < caps->luminance_data[0].input_signal) {
- lum = DIV_ROUND_CLOSEST(caps->luminance_data[0].luminance * brightness,
- caps->luminance_data[0].input_signal);
- goto scale;
- }
-
- left = 0;
- right = caps->data_points - 1;
- while (left <= right) {
- int mid = left + (right - left) / 2;
- u8 signal = caps->luminance_data[mid].input_signal;
-
- /* Exact match found */
- if (signal == brightness) {
- lum = caps->luminance_data[mid].luminance;
- goto scale;
- }
-
- if (signal < brightness)
- left = mid + 1;
- else
- right = mid - 1;
- }
-
- /* verify bound */
- if (left >= caps->data_points)
- left = caps->data_points - 1;
-
- /* At this point, left > right */
- lower_signal = caps->luminance_data[right].input_signal;
- upper_signal = caps->luminance_data[left].input_signal;
- lower_lum = caps->luminance_data[right].luminance;
- upper_lum = caps->luminance_data[left].luminance;
-
- /* interpolate */
- if (right == left || !lower_lum)
- lum = upper_lum;
- else
- lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) *
- (brightness - lower_signal),
- upper_signal - lower_signal);
-scale:
- *user_brightness = scale_fw_to_input(min, max,
- DIV_ROUND_CLOSEST(lum * brightness, 101));
-}
-
-static u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps,
- uint32_t brightness)
-{
- unsigned int min, max;
-
- if (!get_brightness_range(caps, &min, &max))
- return brightness;
-
- convert_custom_brightness(caps, min, max, &brightness);
-
- // Rescale 0..max to min..max
- return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max);
-}
-
-static u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
- uint32_t brightness)
-{
- unsigned int min, max;
-
- if (!get_brightness_range(caps, &min, &max))
- return brightness;
-
- if (brightness < min)
- return 0;
- // Rescale min..max to 0..max
- return DIV_ROUND_CLOSEST_ULL((u64)max * (brightness - min),
- max - min);
-}
-
-static struct dc_stream_state *dm_find_stream_with_link(
- struct amdgpu_display_manager *dm,
- struct dc_link *link)
-{
- struct dc_state *cur_dc_state = dm->dc->current_state;
- struct dc_stream_state *stream = NULL;
- int i;
-
- for (i = 0; i < cur_dc_state->stream_count; i++) {
- stream = cur_dc_state->streams[i];
- if (stream->link == link)
- return stream;
- }
-
- return NULL;
-}
-
-static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
- int bl_idx,
- u32 user_brightness)
-{
- struct amdgpu_dm_backlight_caps *caps;
- struct dc_link *link;
- u32 brightness = 0;
- bool rc = false, reallow_idle = false;
- struct drm_connector *connector;
- struct dc_stream_state *stream;
- unsigned int min, max;
-
- list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- if (aconnector->bl_idx != bl_idx)
- continue;
-
- /* if connector is off, save the brightness for next time it's on */
- if (!aconnector->base.encoder) {
- dm->brightness[bl_idx] = user_brightness;
- dm->actual_brightness[bl_idx] = 0;
- return;
- }
- }
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- caps = &dm->backlight_caps[bl_idx];
-
- dm->brightness[bl_idx] = user_brightness;
- /* update scratch register */
- if (bl_idx == 0)
- amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]);
- brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
- link = (struct dc_link *)dm->backlight_link[bl_idx];
-
- /* Apply brightness quirk */
- if (caps->brightness_mask)
- brightness |= caps->brightness_mask;
-
- if (trace_amdgpu_dm_brightness_enabled()) {
- trace_amdgpu_dm_brightness(__builtin_return_address(0),
- user_brightness,
- brightness,
- caps->aux_support,
- power_supply_is_system_supplied() > 0);
- }
-
- stream = dm_find_stream_with_link(dm, link);
- if (!stream)
- return;
-
- mutex_lock(&dm->dc_lock);
- if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) {
- dc_allow_idle_optimizations(dm->dc, false);
- reallow_idle = true;
- }
-
- if (caps->aux_support) {
- rc = mod_power_set_backlight_nits(dm->power_module, stream, brightness,
- AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true);
- } else {
- /* power module uses millipercent */
- get_brightness_range(caps, &min, &max);
- brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000;
- rc = mod_power_set_backlight_percent(dm->power_module, stream,
- brightness, 0, false);
- }
-
- /*
- * Some kms clients create a ramped backlight transition effect
- * by rapidly changing the backlight. Yet we must wait on dmcub
- * fw to exit psr/replay before programming backlight. To
- * prevent lag, keep disable psr/replay and let the next atomic
- * flip clear the event.
- *
- * ToDo: use ISM to handle rapidly backlight change
- *
- * Rapidly backlight change is similar to rapidly cursor events,
- * which is now handled by ISM. ISM can delay the event until system
- * is really idle, so we may use ISM to handle backlight change as well.
- */
- amdgpu_dm_psr_set_event(dm, stream, true,
- psr_event_hw_programming, true);
- amdgpu_dm_replay_set_event(dm, stream, true,
- replay_event_hw_programming, true);
-
- if (dm->dc->caps.ips_support && reallow_idle)
- dc_allow_idle_optimizations(dm->dc, true);
-
- mutex_unlock(&dm->dc_lock);
-
- if (rc)
- dm->actual_brightness[bl_idx] = user_brightness;
-}
-
-static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
-{
- struct amdgpu_display_manager *dm = bl_get_data(bd);
- int i;
-
- for (i = 0; i < dm->num_of_edps; i++) {
- if (bd == dm->backlight_dev[i])
- break;
- }
- if (i >= AMDGPU_DM_MAX_NUM_EDP)
- i = 0;
- amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness);
-
- return 0;
-}
-
-static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm,
- int bl_idx)
-{
- int ret;
- struct amdgpu_dm_backlight_caps caps;
- struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx];
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- caps = dm->backlight_caps[bl_idx];
-
- if (caps.aux_support) {
- u32 avg, peak;
-
- if (!dc_link_get_backlight_level_nits(link, &avg, &peak))
- return dm->brightness[bl_idx];
- return convert_brightness_to_user(&caps, avg);
- }
-
- ret = dc_link_get_backlight_level(link);
-
- if (ret == DC_ERROR_UNEXPECTED)
- return dm->brightness[bl_idx];
-
- return convert_brightness_to_user(&caps, ret);
-}
-
-static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
-{
- struct amdgpu_display_manager *dm = bl_get_data(bd);
- int i;
-
- for (i = 0; i < dm->num_of_edps; i++) {
- if (bd == dm->backlight_dev[i])
- break;
- }
- if (i >= AMDGPU_DM_MAX_NUM_EDP)
- i = 0;
- return amdgpu_dm_backlight_get_level(dm, i);
-}
-
-static const struct backlight_ops amdgpu_dm_backlight_ops = {
- .options = BL_CORE_SUSPENDRESUME,
- .get_brightness = amdgpu_dm_backlight_get_brightness,
- .update_status = amdgpu_dm_backlight_update_status,
-};
-
-static void
-amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
-{
- struct drm_device *drm = aconnector->base.dev;
- struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
- struct backlight_properties props = { 0 };
- struct amdgpu_dm_backlight_caps *caps;
- char bl_name[16];
- int min, max;
- int real_brightness;
- int init_brightness;
-
- if (aconnector->bl_idx == -1)
- return;
-
- if (!acpi_video_backlight_use_native()) {
- drm_info(drm, "Skipping amdgpu DM backlight registration\n");
- /* Try registering an ACPI video backlight device instead. */
- acpi_video_register_backlight();
- return;
- }
-
- caps = &dm->backlight_caps[aconnector->bl_idx];
- if (get_brightness_range(caps, &min, &max)) {
- if (power_supply_is_system_supplied() > 0)
- props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100);
- else
- props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100);
- /* min is zero, so max needs to be adjusted */
- props.max_brightness = max - min;
- drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max,
- caps->ac_level, caps->dc_level);
- } else
- props.brightness = props.max_brightness = MAX_BACKLIGHT_LEVEL;
-
- init_brightness = props.brightness;
-
- if (caps->data_points && !(amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)) {
- drm_info(drm, "Using custom brightness curve\n");
- props.scale = BACKLIGHT_SCALE_NON_LINEAR;
- } else
- props.scale = BACKLIGHT_SCALE_LINEAR;
- props.type = BACKLIGHT_RAW;
-
- snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d",
- drm->primary->index + aconnector->bl_idx);
-
- dm->backlight_dev[aconnector->bl_idx] =
- backlight_device_register(bl_name, aconnector->base.kdev, dm,
- &amdgpu_dm_backlight_ops, &props);
- dm->brightness[aconnector->bl_idx] = props.brightness;
-
- if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) {
- drm_err(drm, "DM: Backlight registration failed!\n");
- dm->backlight_dev[aconnector->bl_idx] = NULL;
- } else {
- /*
- * dm->brightness[x] can be inconsistent just after startup until
- * ops.get_brightness is called.
- */
- real_brightness =
- amdgpu_dm_backlight_ops.get_brightness(dm->backlight_dev[aconnector->bl_idx]);
-
- if (real_brightness != init_brightness) {
- dm->actual_brightness[aconnector->bl_idx] = real_brightness;
- dm->brightness[aconnector->bl_idx] = real_brightness;
- }
- drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name);
- }
-}
-
static int initialize_plane(struct amdgpu_display_manager *dm,
struct amdgpu_mode_info *mode_info, int plane_id,
enum drm_plane_type plane_type,
@@ -5656,42 +2418,6 @@ static int initialize_plane(struct amdgpu_display_manager *dm,
}
-static void setup_backlight_device(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector)
-{
- struct amdgpu_dm_backlight_caps *caps;
- struct dc_link *link = aconnector->dc_link;
- int bl_idx = dm->num_of_edps;
-
- if (!(link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) ||
- link->type == dc_connection_none)
- return;
-
- if (dm->num_of_edps >= AMDGPU_DM_MAX_NUM_EDP) {
- drm_warn(adev_to_drm(dm->adev), "Too much eDP connections, skipping backlight setup for additional eDPs\n");
- return;
- }
-
- aconnector->bl_idx = bl_idx;
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- dm->backlight_link[bl_idx] = link;
- dm->num_of_edps++;
-
- update_connector_ext_caps(aconnector);
- caps = &dm->backlight_caps[aconnector->bl_idx];
-
- /* Only offer ABM property when non-OLED and user didn't turn off by module parameter */
- if (caps->ext_caps && !caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
- drm_object_attach_property(&aconnector->base.base,
- dm->adev->mode_info.abm_level_property,
- ABM_SYSFS_CONTROL);
-}
-
-static void amdgpu_set_panel_orientation(struct drm_connector *connector);
-
-
-
/*
* In this architecture, the association
* connector -> encoder -> crtc
@@ -5803,7 +2529,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
- if (register_outbox_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_register_outbox_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@ -5916,7 +2642,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(link);
+ amdgpu_dm_emulated_link_detect(link);
amdgpu_dm_update_connector_after_detect(aconnector);
} else {
bool ret = false;
@@ -5928,7 +2654,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
if (ret) {
amdgpu_dm_update_connector_after_detect(aconnector);
- setup_backlight_device(dm, aconnector);
+ amdgpu_dm_setup_backlight_device(dm, aconnector);
/* Disable PSR if Replay can be enabled */
if (replay_feature_enabled)
@@ -5980,7 +2706,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
case CHIP_VEGA10:
case CHIP_VEGA12:
case CHIP_VEGA20:
- if (dce110_register_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_dce110_register_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@ -6010,7 +2736,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
- if (dcn10_register_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_dcn10_register_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@ -6101,78 +2827,6 @@ DEVICE_ATTR_WO(s3_debug);
#endif
-static int dm_init_microcode(struct amdgpu_device *adev)
-{
- char *fw_name_dmub;
- int r;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- fw_name_dmub = FIRMWARE_RENOIR_DMUB;
- if (ASICREV_IS_GREEN_SARDINE(adev->external_rev_id))
- fw_name_dmub = FIRMWARE_GREEN_SARDINE_DMUB;
- break;
- case IP_VERSION(3, 0, 0):
- if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(10, 3, 0))
- fw_name_dmub = FIRMWARE_SIENNA_CICHLID_DMUB;
- else
- fw_name_dmub = FIRMWARE_NAVY_FLOUNDER_DMUB;
- break;
- case IP_VERSION(3, 0, 1):
- fw_name_dmub = FIRMWARE_VANGOGH_DMUB;
- break;
- case IP_VERSION(3, 0, 2):
- fw_name_dmub = FIRMWARE_DIMGREY_CAVEFISH_DMUB;
- break;
- case IP_VERSION(3, 0, 3):
- fw_name_dmub = FIRMWARE_BEIGE_GOBY_DMUB;
- break;
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- fw_name_dmub = FIRMWARE_YELLOW_CARP_DMUB;
- break;
- case IP_VERSION(3, 1, 4):
- fw_name_dmub = FIRMWARE_DCN_314_DMUB;
- break;
- case IP_VERSION(3, 1, 5):
- fw_name_dmub = FIRMWARE_DCN_315_DMUB;
- break;
- case IP_VERSION(3, 1, 6):
- fw_name_dmub = FIRMWARE_DCN316_DMUB;
- break;
- case IP_VERSION(3, 2, 0):
- fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB;
- break;
- case IP_VERSION(3, 2, 1):
- fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB;
- break;
- case IP_VERSION(3, 5, 0):
- fw_name_dmub = FIRMWARE_DCN_35_DMUB;
- break;
- case IP_VERSION(3, 5, 1):
- fw_name_dmub = FIRMWARE_DCN_351_DMUB;
- break;
- case IP_VERSION(3, 6, 0):
- fw_name_dmub = FIRMWARE_DCN_36_DMUB;
- break;
- case IP_VERSION(4, 0, 1):
- fw_name_dmub = FIRMWARE_DCN_401_DMUB;
- break;
- case IP_VERSION(4, 2, 0):
- fw_name_dmub = FIRMWARE_DCN_42_DMUB;
- break;
- case IP_VERSION(4, 2, 1):
- fw_name_dmub = FIRMWARE_DCN_42B_DMUB;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
- r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED,
- "%s", fw_name_dmub);
- return r;
-}
-
static int dm_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -6323,22 +2977,13 @@ static int dm_early_init(struct amdgpu_ip_block *ip_block)
return dm_init_microcode(adev);
}
-static bool modereset_required(struct drm_crtc_state *crtc_state)
+STATIC_IFN_KUNIT bool modereset_required(struct drm_crtc_state *crtc_state)
{
return !crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
}
+EXPORT_IF_KUNIT(modereset_required);
-static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder)
-{
- drm_encoder_cleanup(encoder);
- kfree(encoder);
-}
-
-static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = {
- .destroy = amdgpu_dm_encoder_destroy,
-};
-
-static int
+STATIC_IFN_KUNIT int
fill_plane_color_attributes(const struct drm_plane_state *plane_state,
const enum surface_pixel_format format,
enum dc_color_space *color_space)
@@ -6385,6 +3030,7 @@ fill_plane_color_attributes(const struct drm_plane_state *plane_state,
return 0;
}
+EXPORT_IF_KUNIT(fill_plane_color_attributes);
static int
fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
@@ -6723,7 +3369,7 @@ ffu:
&flip_addrs->dirty_rect_count, true);
}
-static void update_stream_scaling_settings(struct drm_device *dev,
+void amdgpu_dm_update_stream_scaling_settings(struct drm_device *dev,
const struct drm_display_mode *mode,
const struct dm_connector_state *dm_state,
struct dc_stream_state *stream)
@@ -6779,2111 +3425,6 @@ static void update_stream_scaling_settings(struct drm_device *dev,
}
-static enum dc_color_depth
-convert_color_depth_from_display_info(const struct drm_connector *connector,
- bool is_y420, int requested_bpc)
-{
- u8 bpc;
-
- if (is_y420) {
- bpc = 8;
-
- /* Cap display bpc based on HDMI 2.0 HF-VSDB */
- if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)
- bpc = 16;
- else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
- bpc = 12;
- else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
- bpc = 10;
- } else {
- bpc = (uint8_t)connector->display_info.bpc;
- /* Assume 8 bpc by default if no bpc is specified. */
- bpc = bpc ? bpc : 8;
- }
-
- if (requested_bpc > 0) {
- /*
- * Cap display bpc based on the user requested value.
- *
- * The value for state->max_bpc may not correctly updated
- * depending on when the connector gets added to the state
- * or if this was called outside of atomic check, so it
- * can't be used directly.
- */
- bpc = min_t(u8, bpc, requested_bpc);
-
- /* Round down to the nearest even number. */
- bpc = bpc - (bpc & 1);
- }
-
- switch (bpc) {
- case 0:
- /*
- * Temporary Work around, DRM doesn't parse color depth for
- * EDID revision before 1.4
- * TODO: Fix edid parsing
- */
- return COLOR_DEPTH_888;
- case 6:
- return COLOR_DEPTH_666;
- case 8:
- return COLOR_DEPTH_888;
- case 10:
- return COLOR_DEPTH_101010;
- case 12:
- return COLOR_DEPTH_121212;
- case 14:
- return COLOR_DEPTH_141414;
- case 16:
- return COLOR_DEPTH_161616;
- default:
- return COLOR_DEPTH_UNDEFINED;
- }
-}
-
-static enum dc_aspect_ratio
-get_aspect_ratio(const struct drm_display_mode *mode_in)
-{
- /* 1-1 mapping, since both enums follow the HDMI spec. */
- return (enum dc_aspect_ratio) mode_in->picture_aspect_ratio;
-}
-
-static enum dc_color_space
-get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
- const struct drm_connector_state *connector_state)
-{
- enum dc_color_space color_space = COLOR_SPACE_SRGB;
-
- switch (connector_state->colorspace) {
- case DRM_MODE_COLORIMETRY_BT601_YCC:
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space = COLOR_SPACE_YCBCR601_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR601;
- break;
- case DRM_MODE_COLORIMETRY_BT709_YCC:
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space = COLOR_SPACE_YCBCR709_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR709;
- break;
- case DRM_MODE_COLORIMETRY_OPRGB:
- color_space = COLOR_SPACE_ADOBERGB;
- break;
- case DRM_MODE_COLORIMETRY_BT2020_RGB:
- case DRM_MODE_COLORIMETRY_BT2020_YCC:
- if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB)
- color_space = COLOR_SPACE_2020_RGB_FULLRANGE;
- else
- color_space = COLOR_SPACE_2020_YCBCR_LIMITED;
- break;
- case DRM_MODE_COLORIMETRY_DEFAULT: // ITU601
- default:
- if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
- color_space = COLOR_SPACE_SRGB;
- if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
- color_space = COLOR_SPACE_SRGB_LIMITED;
- /*
- * 27030khz is the separation point between HDTV and SDTV
- * according to HDMI spec, we use YCbCr709 and YCbCr601
- * respectively
- */
- } else if (dc_crtc_timing->pix_clk_100hz > 270300) {
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space =
- COLOR_SPACE_YCBCR709_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR709;
- } else {
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space =
- COLOR_SPACE_YCBCR601_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR601;
- }
- break;
- }
-
- return color_space;
-}
-
-static enum display_content_type
-get_output_content_type(const struct drm_connector_state *connector_state)
-{
- switch (connector_state->content_type) {
- default:
- case DRM_MODE_CONTENT_TYPE_NO_DATA:
- return DISPLAY_CONTENT_TYPE_NO_DATA;
- case DRM_MODE_CONTENT_TYPE_GRAPHICS:
- return DISPLAY_CONTENT_TYPE_GRAPHICS;
- case DRM_MODE_CONTENT_TYPE_PHOTO:
- return DISPLAY_CONTENT_TYPE_PHOTO;
- case DRM_MODE_CONTENT_TYPE_CINEMA:
- return DISPLAY_CONTENT_TYPE_CINEMA;
- case DRM_MODE_CONTENT_TYPE_GAME:
- return DISPLAY_CONTENT_TYPE_GAME;
- }
-}
-
-static bool adjust_colour_depth_from_display_info(
- struct dc_crtc_timing *timing_out,
- const struct drm_display_info *info)
-{
- enum dc_color_depth depth = timing_out->display_color_depth;
- int normalized_clk;
-
- do {
- normalized_clk = timing_out->pix_clk_100hz / 10;
- /* YCbCr 4:2:0 requires additional adjustment of 1/2 */
- if (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420)
- normalized_clk /= 2;
- /* Adjusting pix clock following on HDMI spec based on colour depth */
- switch (depth) {
- case COLOR_DEPTH_888:
- break;
- case COLOR_DEPTH_101010:
- normalized_clk = (normalized_clk * 30) / 24;
- break;
- case COLOR_DEPTH_121212:
- normalized_clk = (normalized_clk * 36) / 24;
- break;
- case COLOR_DEPTH_161616:
- normalized_clk = (normalized_clk * 48) / 24;
- break;
- default:
- /* The above depths are the only ones valid for HDMI. */
- return false;
- }
- if (normalized_clk <= info->max_tmds_clock) {
- timing_out->display_color_depth = depth;
- return true;
- }
- } while (--depth > COLOR_DEPTH_666);
- return false;
-}
-
-static void fill_stream_properties_from_drm_display_mode(
- struct dc_stream_state *stream,
- const struct drm_display_mode *mode_in,
- const struct drm_connector *connector,
- const struct drm_connector_state *connector_state,
- const struct dc_stream_state *old_stream,
- int requested_bpc)
-{
- bool is_dp_or_hdmi = dc_is_hdmi_signal(stream->signal) || dc_is_dp_signal(stream->signal);
- struct dc_crtc_timing *timing_out = &stream->timing;
- const struct drm_display_info *info = &connector->display_info;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct hdmi_vendor_infoframe hv_frame;
- struct hdmi_avi_infoframe avi_frame;
- bool want_420;
- bool want_422;
- ssize_t err;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- aconnector = to_amdgpu_dm_connector(connector);
-
- memset(&hv_frame, 0, sizeof(hv_frame));
- memset(&avi_frame, 0, sizeof(avi_frame));
-
- timing_out->h_border_left = 0;
- timing_out->h_border_right = 0;
- timing_out->v_border_top = 0;
- timing_out->v_border_bottom = 0;
-
- want_420 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) ||
- (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420);
- want_422 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) ||
- (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422);
-
- if (drm_mode_is_420_only(info, mode_in) &&
- (want_420 || connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO)) {
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- } else if (drm_mode_is_420_also(info, mode_in) && want_420) {
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- } else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) &&
- want_422 && is_dp_or_hdmi) {
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422;
- } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 &&
- (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) &&
- is_dp_or_hdmi) {
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
- } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444 ||
- connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO) {
- timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
- } else {
- /*
- * If a format was explicitly requested but the requested format
- * can't be satisfied, set it to an invalid value so that an
- * error bubbles up to userspace. This way, userspace knows it
- * needs to make a better choice.
- */
- if (connector_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO)
- timing_out->pixel_encoding = PIXEL_ENCODING_UNDEFINED;
- else if (drm_mode_is_420_only(info, mode_in))
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else
- timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
- }
-
- timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
- timing_out->display_color_depth = convert_color_depth_from_display_info(
- connector,
- (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420),
- requested_bpc);
- timing_out->scan_type = SCANNING_TYPE_NODATA;
- timing_out->hdmi_vic = 0;
-
- if (old_stream) {
- timing_out->vic = old_stream->timing.vic;
- timing_out->flags.HSYNC_POSITIVE_POLARITY = old_stream->timing.flags.HSYNC_POSITIVE_POLARITY;
- timing_out->flags.VSYNC_POSITIVE_POLARITY = old_stream->timing.flags.VSYNC_POSITIVE_POLARITY;
- } else {
- timing_out->vic = drm_match_cea_mode(mode_in);
- if (mode_in->flags & DRM_MODE_FLAG_PHSYNC)
- timing_out->flags.HSYNC_POSITIVE_POLARITY = 1;
- if (mode_in->flags & DRM_MODE_FLAG_PVSYNC)
- timing_out->flags.VSYNC_POSITIVE_POLARITY = 1;
- }
-
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL) {
- err = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame,
- (struct drm_connector *)connector,
- mode_in);
- if (err < 0)
- drm_warn_once(connector->dev, "Failed to setup avi infoframe on connector %s: %zd\n",
- connector->name, err);
- timing_out->vic = avi_frame.video_code;
- err = drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame,
- (struct drm_connector *)connector,
- mode_in);
- if (err < 0)
- drm_warn_once(connector->dev, "Failed to setup vendor infoframe on connector %s: %zd\n",
- connector->name, err);
- timing_out->hdmi_vic = hv_frame.vic;
- }
-
- if (aconnector && is_freesync_video_mode(mode_in, aconnector)) {
- timing_out->h_addressable = mode_in->hdisplay;
- timing_out->h_total = mode_in->htotal;
- timing_out->h_sync_width = mode_in->hsync_end - mode_in->hsync_start;
- timing_out->h_front_porch = mode_in->hsync_start - mode_in->hdisplay;
- timing_out->v_total = mode_in->vtotal;
- timing_out->v_addressable = mode_in->vdisplay;
- timing_out->v_front_porch = mode_in->vsync_start - mode_in->vdisplay;
- timing_out->v_sync_width = mode_in->vsync_end - mode_in->vsync_start;
- timing_out->pix_clk_100hz = mode_in->clock * 10;
- } else {
- timing_out->h_addressable = mode_in->crtc_hdisplay;
- timing_out->h_total = mode_in->crtc_htotal;
- timing_out->h_sync_width = mode_in->crtc_hsync_end - mode_in->crtc_hsync_start;
- timing_out->h_front_porch = mode_in->crtc_hsync_start - mode_in->crtc_hdisplay;
- timing_out->v_total = mode_in->crtc_vtotal;
- timing_out->v_addressable = mode_in->crtc_vdisplay;
- timing_out->v_front_porch = mode_in->crtc_vsync_start - mode_in->crtc_vdisplay;
- timing_out->v_sync_width = mode_in->crtc_vsync_end - mode_in->crtc_vsync_start;
- timing_out->pix_clk_100hz = mode_in->crtc_clock * 10;
- }
-
- timing_out->aspect_ratio = get_aspect_ratio(mode_in);
-
- stream->out_transfer_func.type = TF_TYPE_PREDEFINED;
- stream->out_transfer_func.tf = TRANSFER_FUNCTION_SRGB;
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- if (!adjust_colour_depth_from_display_info(timing_out, info) &&
- drm_mode_is_420_also(info, mode_in) &&
- timing_out->pixel_encoding != PIXEL_ENCODING_YCBCR420) {
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- adjust_colour_depth_from_display_info(timing_out, info);
- }
- }
-
- stream->output_color_space = get_output_color_space(timing_out, connector_state);
- stream->content_type = get_output_content_type(connector_state);
-}
-
-static void fill_audio_info(struct audio_info *audio_info,
- const struct drm_connector *drm_connector,
- const struct dc_sink *dc_sink)
-{
- int i = 0;
- int cea_revision = 0;
- const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps;
-
- audio_info->manufacture_id = edid_caps->manufacturer_id;
- audio_info->product_id = edid_caps->product_id;
-
- cea_revision = drm_connector->display_info.cea_rev;
-
- strscpy(audio_info->display_name,
- edid_caps->display_name,
- AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
-
- if (cea_revision >= 3) {
- audio_info->mode_count = edid_caps->audio_mode_count;
-
- for (i = 0; i < audio_info->mode_count; ++i) {
- audio_info->modes[i].format_code =
- (enum audio_format_code)
- (edid_caps->audio_modes[i].format_code);
- audio_info->modes[i].channel_count =
- edid_caps->audio_modes[i].channel_count;
- audio_info->modes[i].sample_rates.all =
- edid_caps->audio_modes[i].sample_rate;
- audio_info->modes[i].sample_size =
- edid_caps->audio_modes[i].sample_size;
- }
- }
-
- audio_info->flags.all = edid_caps->speaker_flags;
-
- /* TODO: We only check for the progressive mode, check for interlace mode too */
- if (drm_connector->latency_present[0]) {
- audio_info->video_latency = drm_connector->video_latency[0];
- audio_info->audio_latency = drm_connector->audio_latency[0];
- }
-
- /* TODO: For DP, video and audio latency should be calculated from DPCD caps */
-
-}
-
-static void
-copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode,
- struct drm_display_mode *dst_mode)
-{
- dst_mode->crtc_hdisplay = src_mode->crtc_hdisplay;
- dst_mode->crtc_vdisplay = src_mode->crtc_vdisplay;
- dst_mode->crtc_clock = src_mode->crtc_clock;
- dst_mode->crtc_hblank_start = src_mode->crtc_hblank_start;
- dst_mode->crtc_hblank_end = src_mode->crtc_hblank_end;
- dst_mode->crtc_hsync_start = src_mode->crtc_hsync_start;
- dst_mode->crtc_hsync_end = src_mode->crtc_hsync_end;
- dst_mode->crtc_htotal = src_mode->crtc_htotal;
- dst_mode->crtc_hskew = src_mode->crtc_hskew;
- dst_mode->crtc_vblank_start = src_mode->crtc_vblank_start;
- dst_mode->crtc_vblank_end = src_mode->crtc_vblank_end;
- dst_mode->crtc_vsync_start = src_mode->crtc_vsync_start;
- dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end;
- dst_mode->crtc_vtotal = src_mode->crtc_vtotal;
-}
-
-static void
-decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
- const struct drm_display_mode *native_mode,
- bool scale_enabled)
-{
- if (scale_enabled || (
- native_mode->clock == drm_mode->clock &&
- native_mode->htotal == drm_mode->htotal &&
- native_mode->vtotal == drm_mode->vtotal)) {
- if (native_mode->crtc_clock)
- copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode);
- } else {
- /* no scaling nor amdgpu inserted, no need to patch */
- }
-}
-
-static struct dc_sink *
-create_fake_sink(struct drm_device *dev, struct dc_link *link)
-{
- struct dc_sink_init_data sink_init_data = { 0 };
- struct dc_sink *sink = NULL;
-
- sink_init_data.link = link;
- sink_init_data.sink_signal = link->connector_signal;
-
- sink = dc_sink_create(&sink_init_data);
- if (!sink) {
- drm_err(dev, "Failed to create sink!\n");
- return NULL;
- }
- sink->sink_signal = SIGNAL_TYPE_VIRTUAL;
-
- return sink;
-}
-
-static void set_multisync_trigger_params(
- struct dc_stream_state *stream)
-{
- struct dc_stream_state *master = NULL;
-
- if (stream->triggered_crtc_reset.enabled) {
- master = stream->triggered_crtc_reset.event_source;
- stream->triggered_crtc_reset.event =
- master->timing.flags.VSYNC_POSITIVE_POLARITY ?
- CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING;
- stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL;
- }
-}
-
-static void set_master_stream(struct dc_stream_state *stream_set[],
- int stream_count)
-{
- int j, highest_rfr = 0, master_stream = 0;
-
- for (j = 0; j < stream_count; j++) {
- if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) {
- int refresh_rate = 0;
-
- refresh_rate = (stream_set[j]->timing.pix_clk_100hz*100)/
- (stream_set[j]->timing.h_total*stream_set[j]->timing.v_total);
- if (refresh_rate > highest_rfr) {
- highest_rfr = refresh_rate;
- master_stream = j;
- }
- }
- }
- for (j = 0; j < stream_count; j++) {
- if (stream_set[j])
- stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
- }
-}
-
-static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context)
-{
- int i = 0;
- struct dc_stream_state *stream;
-
- if (context->stream_count < 2)
- return;
- for (i = 0; i < context->stream_count ; i++) {
- if (!context->streams[i])
- continue;
- /*
- * TODO: add a function to read AMD VSDB bits and set
- * crtc_sync_master.multi_sync_enabled flag
- * For now it's set to false
- */
- }
-
- set_master_stream(context->streams, context->stream_count);
-
- for (i = 0; i < context->stream_count ; i++) {
- stream = context->streams[i];
-
- if (!stream)
- continue;
-
- set_multisync_trigger_params(stream);
- }
-}
-
-/**
- * DOC: FreeSync Video
- *
- * When a userspace application wants to play a video, the content follows a
- * standard format definition that usually specifies the FPS for that format.
- * The below list illustrates some video format and the expected FPS,
- * respectively:
- *
- * - TV/NTSC (23.976 FPS)
- * - Cinema (24 FPS)
- * - TV/PAL (25 FPS)
- * - TV/NTSC (29.97 FPS)
- * - TV/NTSC (30 FPS)
- * - Cinema HFR (48 FPS)
- * - TV/PAL (50 FPS)
- * - Commonly used (60 FPS)
- * - Multiples of 24 (48,72,96 FPS)
- *
- * The list of standards video format is not huge and can be added to the
- * connector modeset list beforehand. With that, userspace can leverage
- * FreeSync to extends the front porch in order to attain the target refresh
- * rate. Such a switch will happen seamlessly, without screen blanking or
- * reprogramming of the output in any other way. If the userspace requests a
- * modesetting change compatible with FreeSync modes that only differ in the
- * refresh rate, DC will skip the full update and avoid blink during the
- * transition. For example, the video player can change the modesetting from
- * 60Hz to 30Hz for playing TV/NTSC content when it goes full screen without
- * causing any display blink. This same concept can be applied to a mode
- * setting change.
- */
-static struct drm_display_mode *
-get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector,
- bool use_probed_modes)
-{
- struct drm_display_mode *m, *m_pref = NULL;
- u16 current_refresh, highest_refresh;
- struct list_head *list_head = use_probed_modes ?
- &aconnector->base.probed_modes :
- &aconnector->base.modes;
-
- if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return NULL;
-
- if (aconnector->freesync_vid_base.clock != 0)
- return &aconnector->freesync_vid_base;
-
- /* Find the preferred mode */
- list_for_each_entry(m, list_head, head) {
- if (m->type & DRM_MODE_TYPE_PREFERRED) {
- m_pref = m;
- break;
- }
- }
-
- if (!m_pref) {
- /* Probably an EDID with no preferred mode. Fallback to first entry */
- m_pref = list_first_entry_or_null(
- &aconnector->base.modes, struct drm_display_mode, head);
- if (!m_pref) {
- drm_dbg_driver(aconnector->base.dev, "No preferred mode found in EDID\n");
- return NULL;
- }
- }
-
- highest_refresh = drm_mode_vrefresh(m_pref);
-
- /*
- * Find the mode with highest refresh rate with same resolution.
- * For some monitors, preferred mode is not the mode with highest
- * supported refresh rate.
- */
- list_for_each_entry(m, list_head, head) {
- current_refresh = drm_mode_vrefresh(m);
-
- if (m->hdisplay == m_pref->hdisplay &&
- m->vdisplay == m_pref->vdisplay &&
- highest_refresh < current_refresh) {
- highest_refresh = current_refresh;
- m_pref = m;
- }
- }
-
- drm_mode_copy(&aconnector->freesync_vid_base, m_pref);
- return m_pref;
-}
-
-static bool is_freesync_video_mode(const struct drm_display_mode *mode,
- struct amdgpu_dm_connector *aconnector)
-{
- struct drm_display_mode *high_mode;
- int timing_diff;
-
- high_mode = get_highest_refresh_rate_mode(aconnector, false);
- if (!high_mode || !mode)
- return false;
-
- timing_diff = high_mode->vtotal - mode->vtotal;
-
- if (high_mode->clock == 0 || high_mode->clock != mode->clock ||
- high_mode->hdisplay != mode->hdisplay ||
- high_mode->vdisplay != mode->vdisplay ||
- high_mode->hsync_start != mode->hsync_start ||
- high_mode->hsync_end != mode->hsync_end ||
- high_mode->htotal != mode->htotal ||
- high_mode->hskew != mode->hskew ||
- high_mode->vscan != mode->vscan ||
- high_mode->vsync_start - mode->vsync_start != timing_diff ||
- high_mode->vsync_end - mode->vsync_end != timing_diff)
- return false;
- else
- return true;
-}
-
-#if defined(CONFIG_DRM_AMD_DC_FP)
-static void update_dsc_caps(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps)
-{
- stream->timing.flags.DSC = 0;
- dsc_caps->is_dsc_supported = false;
-
- if (aconnector->dc_link && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
- sink->sink_signal == SIGNAL_TYPE_EDP)) {
- if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE)
- dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
- dsc_caps);
- else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
- if (aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT &&
- !aconnector->dsc_settings.dsc_force_disable_passthrough &&
- aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps > 0 &&
- sink->edid_caps.frl_dsc_support &&
- sink->edid_caps.max_frl_rate > 0 &&
- sink->edid_caps.frl_dsc_max_frl_rate > 0)
- dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
- else
- dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
- dsc_caps);
- }
- } else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
- if (sink->edid_caps.frl_dsc_support &&
- sink->edid_caps.max_frl_rate > 0 &&
- sink->edid_caps.frl_dsc_max_frl_rate > 0)
- dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
- }
-}
-
-static void apply_dsc_policy_for_edp(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps,
- uint32_t max_dsc_target_bpp_limit_override)
-{
- const struct dc_link_settings *verified_link_cap = NULL;
- u32 link_bw_in_kbps;
- u32 edp_min_bpp_x16, edp_max_bpp_x16;
- struct dc *dc = sink->ctx->dc;
- struct dc_dsc_bw_range bw_range = {0};
- struct dc_dsc_config dsc_cfg = {0};
- struct dc_dsc_config_options dsc_options = {0};
-
- dc_dsc_get_default_config_option(dc, &dsc_options);
- dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
-
- verified_link_cap = dc_link_get_link_cap(stream->link);
- link_bw_in_kbps = dc_link_bandwidth_kbps(stream->link, verified_link_cap);
- edp_min_bpp_x16 = 8 * 16;
- edp_max_bpp_x16 = 8 * 16;
-
- if (edp_max_bpp_x16 > dsc_caps->edp_max_bits_per_pixel)
- edp_max_bpp_x16 = dsc_caps->edp_max_bits_per_pixel;
-
- if (edp_max_bpp_x16 < edp_min_bpp_x16)
- edp_min_bpp_x16 = edp_max_bpp_x16;
-
- if (dc_dsc_compute_bandwidth_range(dc->res_pool->dscs[0],
- dc->debug.dsc_min_slice_height_override,
- edp_min_bpp_x16, edp_max_bpp_x16,
- dsc_caps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &bw_range)) {
-
- if (bw_range.max_kbps < link_bw_in_kbps) {
- if (dc_dsc_compute_config(dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- 0,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &dsc_cfg)) {
- stream->timing.dsc_cfg = dsc_cfg;
- stream->timing.flags.DSC = 1;
- stream->timing.dsc_cfg.bits_per_pixel = edp_max_bpp_x16;
- }
- return;
- }
- }
-
- if (dc_dsc_compute_config(dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- link_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &dsc_cfg)) {
- stream->timing.dsc_cfg = dsc_cfg;
- stream->timing.flags.DSC = 1;
- }
-}
-
-static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps)
-{
- struct drm_connector *drm_connector = &aconnector->base;
- u32 link_bandwidth_kbps;
- struct dc *dc = sink->ctx->dc;
- const struct dc_hdmi_frl_link_settings *frl_verified_link_cap = NULL;
- u32 converter_bw_in_kbps;
- u32 sink_bw_in_kbps;
- u32 dsc_sink_bw_in_kbps;
- u32 max_supported_bw_in_kbps, timing_bw_in_kbps;
- u32 dsc_max_supported_bw_in_kbps;
- u32 max_dsc_target_bpp_limit_override =
- drm_connector->display_info.max_dsc_bpp;
- struct dc_dsc_config_options dsc_options = {0};
-
- dc_dsc_get_default_config_option(dc, &dsc_options);
- dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
-
- link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link,
- dc_link_get_link_cap(aconnector->dc_link));
-
- /* Set DSC policy according to dsc_clock_en */
- dc_dsc_policy_set_enable_dsc_when_not_needed(
- aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE);
-
- if (sink->sink_signal == SIGNAL_TYPE_EDP &&
- !aconnector->dc_link->panel_config.dsc.disable_dsc_edp &&
- dc->caps.edp_dsc_support && aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE) {
-
- apply_dsc_policy_for_edp(aconnector, sink, stream, dsc_caps, max_dsc_target_bpp_limit_override);
-
- } else if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
- if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE) {
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- link_bandwidth_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from SST RX\n",
- __func__, drm_connector->name);
- }
- } else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
- timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link));
- converter_bw_in_kbps = aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps;
- sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.max_frl_rate);
- dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate);
-
- if (dsc_caps->is_frl) {
- max_supported_bw_in_kbps = min(link_bandwidth_kbps, converter_bw_in_kbps);
- max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, sink_bw_in_kbps);
- dsc_max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, dsc_sink_bw_in_kbps);
- } else {
- max_supported_bw_in_kbps = link_bandwidth_kbps;
- dsc_max_supported_bw_in_kbps = link_bandwidth_kbps;
- }
-
- if (timing_bw_in_kbps > max_supported_bw_in_kbps &&
- max_supported_bw_in_kbps > 0 &&
- dsc_max_supported_bw_in_kbps > 0)
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- dsc_max_supported_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from %s\n",
- __func__, drm_connector->name,
- (dsc_caps->is_frl == 1) ? "HDMI FRL RX" : "DP-HDMI PCON");
- }
- }
- }
- else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
- struct dc_dsc_policy dsc_policy = {0};
-
- frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link);
- if (frl_verified_link_cap->frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE &&
- aconnector->dc_link->frl_flags.force_frl_dsc) {
- dc_dsc_policy_set_enable_dsc_when_not_needed(true);
- dc_dsc_get_policy_for_timing(&stream->timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link));
- }
-
- timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL);
- link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link, frl_verified_link_cap->frl_link_rate);
- dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate);
-
- if ((timing_bw_in_kbps > link_bandwidth_kbps && dsc_sink_bw_in_kbps > 0) ||
- (dsc_policy.enable_dsc_when_not_needed || dsc_options.force_dsc_when_not_needed)) {
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- dsc_sink_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n",
- __func__, drm_connector->name);
- }
- }
- }
-
- /* Overwrite the stream flag if DSC is enabled through debugfs */
- if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE)
- stream->timing.flags.DSC = 1;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_h)
- stream->timing.dsc_cfg.num_slices_h = aconnector->dsc_settings.dsc_num_slices_h;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_v)
- stream->timing.dsc_cfg.num_slices_v = aconnector->dsc_settings.dsc_num_slices_v;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_bits_per_pixel)
- stream->timing.dsc_cfg.bits_per_pixel = aconnector->dsc_settings.dsc_bits_per_pixel;
-}
-#endif
-
-static struct dc_stream_state *
-create_stream_for_sink(struct drm_connector *connector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream,
- int requested_bpc)
-{
- struct drm_device *dev = connector->dev;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct drm_display_mode *preferred_mode = NULL;
- const struct drm_connector_state *con_state = &dm_state->base;
- struct dc_stream_state *stream = NULL;
- struct drm_display_mode mode;
- struct drm_display_mode saved_mode;
- struct drm_display_mode *freesync_mode = NULL;
- bool native_mode_found = false;
- bool recalculate_timing = false;
- bool scale = dm_state->scaling != RMX_OFF;
- int mode_refresh;
- int preferred_refresh = 0;
- enum color_transfer_func tf = TRANSFER_FUNC_UNKNOWN;
-#if defined(CONFIG_DRM_AMD_DC_FP)
- struct dsc_dec_dpcd_caps dsc_caps = {0};
-#endif
- struct dc_link *link = NULL;
- struct dc_sink *sink = NULL;
-
- drm_mode_init(&mode, drm_mode);
- memset(&saved_mode, 0, sizeof(saved_mode));
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) {
- aconnector = NULL;
- aconnector = to_amdgpu_dm_connector(connector);
- link = aconnector->dc_link;
- } else {
- struct drm_writeback_connector *wbcon = NULL;
- struct amdgpu_dm_wb_connector *dm_wbcon = NULL;
-
- wbcon = drm_connector_to_writeback(connector);
- dm_wbcon = to_amdgpu_dm_wb_connector(wbcon);
- link = dm_wbcon->link;
- }
-
- if (!aconnector || !aconnector->dc_sink) {
- sink = create_fake_sink(dev, link);
- if (!sink)
- return stream;
-
- } else {
- sink = aconnector->dc_sink;
- dc_sink_retain(sink);
- }
-
- stream = dc_create_stream_for_sink(sink);
-
- if (stream == NULL) {
- drm_err(dev, "Failed to create stream for sink!\n");
- goto finish;
- }
-
- /* We leave this NULL for writeback connectors */
- stream->dm_stream_context = aconnector;
-
- stream->timing.flags.LTE_340MCSC_SCRAMBLE =
- connector->display_info.hdmi.scdc.scrambling.low_rates;
-
- list_for_each_entry(preferred_mode, &connector->modes, head) {
- /* Search for preferred mode */
- if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) {
- native_mode_found = true;
- break;
- }
- }
- if (!native_mode_found)
- preferred_mode = list_first_entry_or_null(
- &connector->modes,
- struct drm_display_mode,
- head);
-
- mode_refresh = drm_mode_vrefresh(&mode);
-
- if (preferred_mode == NULL) {
- /*
- * This may not be an error, the use case is when we have no
- * usermode calls to reset and set mode upon hotplug. In this
- * case, we call set mode ourselves to restore the previous mode
- * and the modelist may not be filled in time.
- */
- drm_dbg_driver(dev, "No preferred mode found\n");
- } else if (aconnector) {
- recalculate_timing = amdgpu_freesync_vid_mode &&
- is_freesync_video_mode(&mode, aconnector);
- if (recalculate_timing) {
- freesync_mode = get_highest_refresh_rate_mode(aconnector, false);
- drm_mode_copy(&saved_mode, &mode);
- saved_mode.picture_aspect_ratio = mode.picture_aspect_ratio;
- drm_mode_copy(&mode, freesync_mode);
- mode.picture_aspect_ratio = saved_mode.picture_aspect_ratio;
- } else {
- decide_crtc_timing_for_drm_display_mode(
- &mode, preferred_mode, scale);
-
- preferred_refresh = drm_mode_vrefresh(preferred_mode);
- }
- }
-
- if (recalculate_timing)
- drm_mode_set_crtcinfo(&saved_mode, 0);
-
- /*
- * If scaling is enabled and refresh rate didn't change
- * we copy the vic and polarities of the old timings
- */
- if (!scale || mode_refresh != preferred_refresh)
- fill_stream_properties_from_drm_display_mode(
- stream, &mode, connector, con_state, NULL,
- requested_bpc);
- else
- fill_stream_properties_from_drm_display_mode(
- stream, &mode, connector, con_state, old_stream,
- requested_bpc);
-
- /* The rest isn't needed for writeback connectors */
- if (!aconnector)
- goto finish;
-
- if (aconnector->timing_changed) {
- drm_dbg(aconnector->base.dev,
- "overriding timing for automated test, bpc %d, changing to %d\n",
- stream->timing.display_color_depth,
- aconnector->timing_requested->display_color_depth);
- stream->timing = *aconnector->timing_requested;
- }
-
-#if defined(CONFIG_DRM_AMD_DC_FP)
- /* SST DSC determination policy */
- update_dsc_caps(aconnector, sink, stream, &dsc_caps);
- if (aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE && dsc_caps.is_dsc_supported)
- apply_dsc_policy_for_stream(aconnector, sink, stream, &dsc_caps);
-#endif
-
- update_stream_scaling_settings(dev, &mode, dm_state, stream);
-
- fill_audio_info(
- &stream->audio_info,
- connector,
- sink);
-
- update_stream_signal(stream, sink);
-
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false);
-
- if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
- stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ||
- stream->signal == SIGNAL_TYPE_EDP) {
- const struct dc_edid_caps *edid_caps;
- unsigned int disable_colorimetry = 0;
-
- if (aconnector->dc_sink) {
- edid_caps = &aconnector->dc_sink->edid_caps;
- disable_colorimetry = edid_caps->panel_patch.disable_colorimetry;
- }
-
- //
- // should decide stream support vsc sdp colorimetry capability
- // before building vsc info packet
- //
- stream->use_vsc_sdp_for_colorimetry = stream->link->dpcd_caps.dpcd_rev.raw >= 0x14 &&
- stream->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED &&
- !disable_colorimetry;
-
- if (stream->out_transfer_func.tf == TRANSFER_FUNCTION_GAMMA22)
- tf = TRANSFER_FUNC_GAMMA_22;
- mod_build_vsc_infopacket(stream, &stream->vsc_infopacket, stream->output_color_space, tf);
- aconnector->sr_skip_count = AMDGPU_DM_PSR_ENTRY_DELAY;
-
- }
-finish:
- dc_sink_release(sink);
-
- return stream;
-}
-
-/**
- * amdgpu_dm_connector_poll - Poll a connector to see if it's connected to a display
- * @aconnector: DM connector to poll (owns @base drm_connector and @dc_link)
- * @force: if true, force polling even when DAC load detection was used
- *
- * Used for connectors that don't support HPD (hotplug detection) to
- * periodically check whether the connector is connected to a display.
- *
- * When connection was determined via DAC load detection, we avoid
- * re-running it on normal polls to prevent visible glitches, unless
- * @force is set.
- *
- * Return: The probed connector status (connected/disconnected/unknown).
- */
-static enum drm_connector_status
-amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force)
-{
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dc_link *link = aconnector->dc_link;
- enum dc_connection_type conn_type = dc_connection_none;
- enum drm_connector_status status = connector_status_disconnected;
-
- /* When we determined the connection using DAC load detection,
- * do NOT poll the connector do detect disconnect because
- * that would run DAC load detection again which can cause
- * visible visual glitches.
- *
- * Only allow to poll such a connector again when forcing.
- */
- if (!force && link->local_sink && link->type == dc_connection_analog_load)
- return connector->status;
-
- mutex_lock(&aconnector->hpd_lock);
-
- if (dc_link_detect_connection_type(aconnector->dc_link, &conn_type) &&
- conn_type != dc_connection_none) {
- mutex_lock(&adev->dm.dc_lock);
-
- /* Only call full link detection when a sink isn't created yet,
- * ie. just when the display is plugged in, otherwise we risk flickering.
- */
- if (link->local_sink ||
- dc_link_detect(link, DETECT_REASON_HPD))
- status = connector_status_connected;
-
- mutex_unlock(&adev->dm.dc_lock);
- }
-
- if (connector->status != status) {
- if (status == connector_status_disconnected) {
- if (link->local_sink)
- dc_sink_release(link->local_sink);
-
- link->local_sink = NULL;
- link->dpcd_sink_count = 0;
- link->type = dc_connection_none;
- }
-
- amdgpu_dm_update_connector_after_detect(aconnector);
- }
-
- mutex_unlock(&aconnector->hpd_lock);
- return status;
-}
-
-/**
- * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display
- *
- * A connector is considered connected when it has a sink that is not NULL.
- * For connectors that support HPD (hotplug detection), the connection is
- * handled in the HPD interrupt.
- * For connectors that may not support HPD, such as analog connectors,
- * DRM will call this function repeatedly to poll them.
- *
- * Notes:
- * 1. This interface is NOT called in context of HPD irq.
- * 2. This interface *is called* in context of user-mode ioctl. Which
- * makes it a bad place for *any* MST-related activity.
- *
- * @connector: The DRM connector we are checking. We convert it to
- * amdgpu_dm_connector so we can read the DC link and state.
- * @force: If true, do a full detect again. This is used even when
- * a lighter check would normally be used to avoid flicker.
- *
- * Return: The connector status (connected, disconnected, or unknown).
- *
- */
-static enum drm_connector_status
-amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
-{
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- update_subconnector_property(aconnector);
-
- if (aconnector->base.force == DRM_FORCE_ON ||
- aconnector->base.force == DRM_FORCE_ON_DIGITAL)
- return connector_status_connected;
- else if (aconnector->base.force == DRM_FORCE_OFF)
- return connector_status_disconnected;
-
- /* Poll analog connectors and only when either
- * disconnected or connected to an analog display.
- */
- if (drm_kms_helper_is_poll_worker() &&
- dc_connector_supports_analog(aconnector->dc_link->link_id.id) &&
- (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog))
- return amdgpu_dm_connector_poll(aconnector, force);
-
- return (aconnector->dc_sink ? connector_status_connected :
- connector_status_disconnected);
-}
-
-int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
- struct drm_connector_state *connector_state,
- struct drm_property *property,
- uint64_t val)
-{
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_old_state =
- to_dm_connector_state(connector->state);
- struct dm_connector_state *dm_new_state =
- to_dm_connector_state(connector_state);
-
- int ret = -EINVAL;
-
- if (property == dev->mode_config.scaling_mode_property) {
- enum amdgpu_rmx_type rmx_type;
-
- switch (val) {
- case DRM_MODE_SCALE_CENTER:
- rmx_type = RMX_CENTER;
- break;
- case DRM_MODE_SCALE_ASPECT:
- rmx_type = RMX_ASPECT;
- break;
- case DRM_MODE_SCALE_FULLSCREEN:
- rmx_type = RMX_FULL;
- break;
- case DRM_MODE_SCALE_NONE:
- default:
- rmx_type = RMX_OFF;
- break;
- }
-
- if (dm_old_state->scaling == rmx_type)
- return 0;
-
- dm_new_state->scaling = rmx_type;
- ret = 0;
- } else if (property == adev->mode_info.underscan_hborder_property) {
- dm_new_state->underscan_hborder = val;
- ret = 0;
- } else if (property == adev->mode_info.underscan_vborder_property) {
- dm_new_state->underscan_vborder = val;
- ret = 0;
- } else if (property == adev->mode_info.underscan_property) {
- dm_new_state->underscan_enable = val;
- ret = 0;
- } else if (property == adev->mode_info.abm_level_property) {
- switch (val) {
- case ABM_SYSFS_CONTROL:
- dm_new_state->abm_sysfs_forbidden = false;
- break;
- case ABM_LEVEL_OFF:
- dm_new_state->abm_sysfs_forbidden = true;
- dm_new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
- break;
- default:
- dm_new_state->abm_sysfs_forbidden = true;
- dm_new_state->abm_level = val;
- }
- ret = 0;
- }
-
- return ret;
-}
-
-int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
- const struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t *val)
-{
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_state =
- to_dm_connector_state(state);
- int ret = -EINVAL;
-
- if (property == dev->mode_config.scaling_mode_property) {
- switch (dm_state->scaling) {
- case RMX_CENTER:
- *val = DRM_MODE_SCALE_CENTER;
- break;
- case RMX_ASPECT:
- *val = DRM_MODE_SCALE_ASPECT;
- break;
- case RMX_FULL:
- *val = DRM_MODE_SCALE_FULLSCREEN;
- break;
- case RMX_OFF:
- default:
- *val = DRM_MODE_SCALE_NONE;
- break;
- }
- ret = 0;
- } else if (property == adev->mode_info.underscan_hborder_property) {
- *val = dm_state->underscan_hborder;
- ret = 0;
- } else if (property == adev->mode_info.underscan_vborder_property) {
- *val = dm_state->underscan_vborder;
- ret = 0;
- } else if (property == adev->mode_info.underscan_property) {
- *val = dm_state->underscan_enable;
- ret = 0;
- } else if (property == adev->mode_info.abm_level_property) {
- if (!dm_state->abm_sysfs_forbidden)
- *val = ABM_SYSFS_CONTROL;
- else
- *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ?
- dm_state->abm_level : 0;
- ret = 0;
- }
-
- return ret;
-}
-
-/**
- * DOC: panel power savings
- *
- * The display manager allows you to set your desired **panel power savings**
- * level (between 0-4, with 0 representing off), e.g. using the following::
- *
- * # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings
- *
- * Modifying this value can have implications on color accuracy, so tread
- * carefully.
- */
-
-static ssize_t panel_power_savings_show(struct device *device,
- struct device_attribute *attr,
- char *buf)
-{
- struct drm_connector *connector = dev_get_drvdata(device);
- struct drm_device *dev = connector->dev;
- u8 val;
-
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- val = to_dm_connector_state(connector->state)->abm_level ==
- ABM_LEVEL_IMMEDIATE_DISABLE ? 0 :
- to_dm_connector_state(connector->state)->abm_level;
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- return sysfs_emit(buf, "%u\n", val);
-}
-
-static ssize_t panel_power_savings_store(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct drm_connector *connector = dev_get_drvdata(device);
- struct drm_device *dev = connector->dev;
- long val;
- int ret;
-
- ret = kstrtol(buf, 0, &val);
-
- if (ret)
- return ret;
-
- if (val < 0 || val > 4)
- return -EINVAL;
-
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden)
- ret = -EBUSY;
- else
- to_dm_connector_state(connector->state)->abm_level = val ?:
- ABM_LEVEL_IMMEDIATE_DISABLE;
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- if (ret)
- return ret;
-
- drm_kms_helper_hotplug_event(dev);
-
- return count;
-}
-
-static DEVICE_ATTR_RW(panel_power_savings);
-
-static struct attribute *amdgpu_attrs[] = {
- &dev_attr_panel_power_savings.attr,
- NULL
-};
-
-static const struct attribute_group amdgpu_group = {
- .name = "amdgpu",
- .attrs = amdgpu_attrs
-};
-
-static bool
-amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector)
-{
- if (amdgpu_dm_abm_level >= 0)
- return false;
-
- if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
- return false;
-
- /* check for OLED panels */
- if (amdgpu_dm_connector->bl_idx >= 0) {
- struct drm_device *drm = amdgpu_dm_connector->base.dev;
- struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
- struct amdgpu_dm_backlight_caps *caps;
-
- caps = &dm->backlight_caps[amdgpu_dm_connector->bl_idx];
- if (caps->aux_support)
- return false;
- }
-
- return true;
-}
-
-static void amdgpu_dm_connector_unregister(struct drm_connector *connector)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector);
-
- if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector))
- sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
-
- cec_notifier_conn_unregister(amdgpu_dm_connector->notifier);
- drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux);
-}
-
-static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
-{
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct amdgpu_display_manager *dm = &adev->dm;
-
- /*
- * Call only if mst_mgr was initialized before since it's not done
- * for all connector types.
- */
- if (aconnector->mst_mgr.dev)
- drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr);
-
- /* Cancel and flush any pending HDMI HPD debounce work */
- if (aconnector->hdmi_hpd_debounce_delay_ms) {
- cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work);
- if (aconnector->hdmi_prev_sink) {
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = NULL;
- }
- }
-
- if (aconnector->bl_idx != -1) {
- backlight_device_unregister(dm->backlight_dev[aconnector->bl_idx]);
- dm->backlight_dev[aconnector->bl_idx] = NULL;
- }
-
- if (aconnector->dc_em_sink)
- dc_sink_release(aconnector->dc_em_sink);
- aconnector->dc_em_sink = NULL;
- if (aconnector->dc_sink)
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
-
- drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
- kfree(aconnector->dm_dp_aux.aux.name);
-
- kfree(connector);
-}
-
-void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector)
-{
- struct dm_connector_state *state =
- to_dm_connector_state(connector->state);
-
- if (connector->state)
- __drm_atomic_helper_connector_destroy_state(connector->state);
-
- kfree(state);
-
- state = kzalloc_obj(*state);
-
- if (state) {
- state->scaling = RMX_OFF;
- state->underscan_enable = false;
- state->underscan_hborder = 0;
- state->underscan_vborder = 0;
- state->base.max_requested_bpc = 8;
- state->vcpi_slots = 0;
- state->pbn = 0;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
- if (amdgpu_dm_abm_level <= 0)
- state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
- else
- state->abm_level = amdgpu_dm_abm_level;
- }
-
- __drm_atomic_helper_connector_reset(connector, &state->base);
- }
-}
-
-struct drm_connector_state *
-amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector)
-{
- struct dm_connector_state *state =
- to_dm_connector_state(connector->state);
-
- struct dm_connector_state *new_state =
- kmemdup(state, sizeof(*state), GFP_KERNEL);
-
- if (!new_state)
- return NULL;
-
- __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
-
- new_state->freesync_capable = state->freesync_capable;
- new_state->abm_level = state->abm_level;
- new_state->scaling = state->scaling;
- new_state->underscan_enable = state->underscan_enable;
- new_state->underscan_hborder = state->underscan_hborder;
- new_state->underscan_vborder = state->underscan_vborder;
- new_state->vcpi_slots = state->vcpi_slots;
- new_state->pbn = state->pbn;
- return &new_state->base;
-}
-
-static int
-amdgpu_dm_connector_late_register(struct drm_connector *connector)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- int r;
-
- if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector)) {
- r = sysfs_create_group(&connector->kdev->kobj,
- &amdgpu_group);
- if (r)
- return r;
- }
-
- amdgpu_dm_register_backlight_device(amdgpu_dm_connector);
-
- if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
- (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
- amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev;
- r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux);
- if (r)
- return r;
- }
-
-#if defined(CONFIG_DEBUG_FS)
- connector_debugfs_init(amdgpu_dm_connector);
-#endif
-
- return 0;
-}
-
-static void amdgpu_dm_connector_funcs_force(struct drm_connector *connector)
-{
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct dc_link *dc_link = aconnector->dc_link;
- struct dc_sink *dc_em_sink = aconnector->dc_em_sink;
- const struct drm_edid *drm_edid;
- struct i2c_adapter *ddc;
- struct drm_device *dev = connector->dev;
-
- if (dc_link && dc_link->aux_mode)
- ddc = &aconnector->dm_dp_aux.aux.ddc;
- else
- ddc = &aconnector->i2c->base;
-
- drm_edid = drm_edid_read_ddc(connector, ddc);
- drm_edid_connector_update(connector, drm_edid);
- if (!drm_edid) {
- drm_err(dev, "No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- aconnector->drm_edid = drm_edid;
- /* Update emulated (virtual) sink's EDID */
- if (dc_em_sink && dc_link) {
- // FIXME: Get rid of drm_edid_raw()
- const struct edid *edid = drm_edid_raw(drm_edid);
-
- memset(&dc_em_sink->edid_caps, 0, sizeof(struct dc_edid_caps));
- memmove(dc_em_sink->dc_edid.raw_edid, edid,
- (edid->extensions + 1) * EDID_LENGTH);
- dm_helpers_parse_edid_caps(
- dc_link,
- &dc_em_sink->dc_edid,
- &dc_em_sink->edid_caps);
- }
-}
-
-static const struct drm_connector_funcs amdgpu_dm_connector_funcs = {
- .reset = amdgpu_dm_connector_funcs_reset,
- .detect = amdgpu_dm_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = amdgpu_dm_connector_destroy,
- .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_set_property = amdgpu_dm_connector_atomic_set_property,
- .atomic_get_property = amdgpu_dm_connector_atomic_get_property,
- .late_register = amdgpu_dm_connector_late_register,
- .early_unregister = amdgpu_dm_connector_unregister,
- .force = amdgpu_dm_connector_funcs_force
-};
-
-static int get_modes(struct drm_connector *connector)
-{
- return amdgpu_dm_connector_get_modes(connector);
-}
-
-static void create_eml_sink(struct amdgpu_dm_connector *aconnector)
-{
- struct drm_connector *connector = &aconnector->base;
- struct dc_link *dc_link = aconnector->dc_link;
- struct dc_sink_init_data init_params = {
- .link = aconnector->dc_link,
- .sink_signal = SIGNAL_TYPE_VIRTUAL
- };
- const struct drm_edid *drm_edid;
- const struct edid *edid;
- struct i2c_adapter *ddc;
-
- if (dc_link && dc_link->aux_mode)
- ddc = &aconnector->dm_dp_aux.aux.ddc;
- else
- ddc = &aconnector->i2c->base;
-
- drm_edid = drm_edid_read_ddc(connector, ddc);
- drm_edid_connector_update(connector, drm_edid);
- if (!drm_edid) {
- drm_err(connector->dev, "No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- if (connector->display_info.is_hdmi)
- init_params.sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
-
- aconnector->drm_edid = drm_edid;
-
- edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
- aconnector->dc_em_sink = dc_link_add_remote_sink(
- aconnector->dc_link,
- (uint8_t *)edid,
- (edid->extensions + 1) * EDID_LENGTH,
- &init_params);
-
- if (aconnector->base.force == DRM_FORCE_ON) {
- aconnector->dc_sink = aconnector->dc_link->local_sink ?
- aconnector->dc_link->local_sink :
- aconnector->dc_em_sink;
- if (aconnector->dc_sink)
- dc_sink_retain(aconnector->dc_sink);
- }
-}
-
-static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector)
-{
- struct dc_link *link = (struct dc_link *)aconnector->dc_link;
-
- /*
- * In case of headless boot with force on for DP managed connector
- * Those settings have to be != 0 to get initial modeset
- */
- if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) {
- link->verified_link_cap.lane_count = LANE_COUNT_FOUR;
- link->verified_link_cap.link_rate = LINK_RATE_HIGH2;
- }
-
- create_eml_sink(aconnector);
-}
-
-static enum dc_status dm_validate_stream_and_context(struct dc *dc,
- struct dc_stream_state *stream)
-{
- enum dc_status dc_result = DC_ERROR_UNEXPECTED;
- struct dc_plane_state *dc_plane_state = NULL;
- struct dc_state *dc_state = NULL;
-
- if (!stream)
- goto cleanup;
-
- dc_plane_state = dc_create_plane_state(dc);
- if (!dc_plane_state)
- goto cleanup;
-
- dc_state = dc_state_create(dc, NULL);
- if (!dc_state)
- goto cleanup;
-
- /* populate stream to plane */
- dc_plane_state->src_rect.height = stream->src.height;
- dc_plane_state->src_rect.width = stream->src.width;
- dc_plane_state->dst_rect.height = stream->src.height;
- dc_plane_state->dst_rect.width = stream->src.width;
- dc_plane_state->clip_rect.height = stream->src.height;
- dc_plane_state->clip_rect.width = stream->src.width;
- dc_plane_state->plane_size.surface_pitch = ((stream->src.width + 255) / 256) * 256;
- dc_plane_state->plane_size.surface_size.height = stream->src.height;
- dc_plane_state->plane_size.surface_size.width = stream->src.width;
- dc_plane_state->plane_size.chroma_size.height = stream->src.height;
- dc_plane_state->plane_size.chroma_size.width = stream->src.width;
- dc_plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888;
- dc_plane_state->tiling_info.gfx9.swizzle = DC_SW_UNKNOWN;
- dc_plane_state->rotation = ROTATION_ANGLE_0;
- dc_plane_state->is_tiling_rotated = false;
- dc_plane_state->tiling_info.gfx8.array_mode = DC_ARRAY_LINEAR_GENERAL;
-
- dc_result = dc_validate_stream(dc, stream);
- if (dc_result == DC_OK)
- dc_result = dc_validate_plane(dc, dc_plane_state);
-
- if (dc_result == DC_OK)
- dc_result = dc_state_add_stream(dc, dc_state, stream);
-
- if (dc_result == DC_OK && !dc_state_add_plane(
- dc,
- stream,
- dc_plane_state,
- dc_state))
- dc_result = DC_FAIL_ATTACH_SURFACES;
-
- if (dc_result == DC_OK)
- dc_result = dc_validate_global_state(dc, dc_state, DC_VALIDATE_MODE_ONLY);
-
-cleanup:
- if (dc_state)
- dc_state_release(dc_state);
-
- if (dc_plane_state)
- dc_plane_state_release(dc_plane_state);
-
- return dc_result;
-}
-
-static enum dc_status
-dm_validate_stream_color_format(const struct drm_connector_state *drm_state,
- const struct dc_stream_state *stream)
-{
- enum dc_pixel_encoding encoding;
-
- if (!drm_state->color_format)
- return DC_OK;
-
- switch (drm_state->color_format) {
- case DRM_CONNECTOR_COLOR_FORMAT_AUTO:
- case DRM_CONNECTOR_COLOR_FORMAT_RGB444:
- encoding = PIXEL_ENCODING_RGB;
- break;
- case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444:
- encoding = PIXEL_ENCODING_YCBCR444;
- break;
- case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422:
- encoding = PIXEL_ENCODING_YCBCR422;
- break;
- case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420:
- encoding = PIXEL_ENCODING_YCBCR420;
- break;
- default:
- encoding = PIXEL_ENCODING_UNDEFINED;
- break;
- }
-
- return encoding == stream->timing.pixel_encoding ?
- DC_OK : DC_UNSUPPORTED_VALUE;
-}
-
-struct dc_stream_state *
-create_validate_stream_for_sink(struct drm_connector *connector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream)
-{
- struct amdgpu_dm_connector *aconnector = NULL;
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct dc_stream_state *stream;
- const struct drm_connector_state *drm_state = dm_state ? &dm_state->base : NULL;
- int requested_bpc = drm_state ? drm_state->max_requested_bpc : 8;
- enum dc_status dc_result = DC_OK;
- uint8_t bpc_limit = 6;
-
- if (!dm_state)
- return NULL;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (aconnector &&
- (aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL ||
- aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER))
- bpc_limit = 8;
-
- do {
- drm_dbg_kms(connector->dev, "Trying with %d bpc\n", requested_bpc);
- stream = create_stream_for_sink(connector, drm_mode,
- dm_state, old_stream,
- requested_bpc);
- if (stream == NULL) {
- drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n");
- break;
- }
-
- dc_result = dc_validate_stream(adev->dm.dc, stream);
-
- if (!aconnector) /* writeback connector */
- return stream;
-
- if (dc_result == DC_OK && stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
- dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream);
-
- if (dc_result == DC_OK)
- dc_result = dm_validate_stream_and_context(adev->dm.dc, stream);
-
- if (dc_result == DC_OK)
- dc_result = dm_validate_stream_color_format(drm_state, stream);
-
- if (dc_result != DC_OK) {
- drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n",
- drm_mode->hdisplay,
- drm_mode->vdisplay,
- drm_mode->clock,
- dc_pixel_encoding_to_str(stream->timing.pixel_encoding),
- dc_color_depth_to_str(stream->timing.display_color_depth),
- dc_status_to_str(dc_result));
-
- dc_stream_release(stream);
- stream = NULL;
- requested_bpc -= 2; /* lower bpc to retry validation */
- }
-
- } while (stream == NULL && requested_bpc >= bpc_limit);
-
- switch (dc_result) {
- /*
- * If we failed to validate DP bandwidth stream with the requested RGB color depth,
- * we try to fallback and configure in order:
- * YUV422 (8bpc, 6bpc)
- * YUV420 (8bpc, 6bpc)
- */
- case DC_FAIL_ENC_VALIDATE:
- case DC_EXCEED_DONGLE_CAP:
- case DC_NO_DP_LINK_BANDWIDTH:
- /* recursively entered twice and already tried both YUV422 and YUV420 */
- if (aconnector->force_yuv422_output && aconnector->force_yuv420_output)
- break;
- /* first failure; try YUV422 */
- if (!aconnector->force_yuv422_output) {
- drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV422\n",
- __func__, __LINE__, dc_result);
- aconnector->force_yuv422_output = true;
- /* recursively entered and YUV422 failed, try YUV420 */
- } else if (!aconnector->force_yuv420_output) {
- drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV420\n",
- __func__, __LINE__, dc_result);
- aconnector->force_yuv420_output = true;
- }
- stream = create_validate_stream_for_sink(connector, drm_mode,
- dm_state, old_stream);
- aconnector->force_yuv422_output = false;
- aconnector->force_yuv420_output = false;
- break;
- case DC_OK:
- break;
- default:
- drm_dbg_kms(connector->dev, "%s:%d Unhandled validation failure %d\n",
- __func__, __LINE__, dc_result);
- break;
- }
-
- return stream;
-}
-
-enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
- const struct drm_display_mode *mode)
-{
- int result = MODE_ERROR;
- struct dc_sink *dc_sink;
- struct drm_display_mode *test_mode;
- /* TODO: Unhardcode stream count */
- struct dc_stream_state *stream;
- /* we always have an amdgpu_dm_connector here since we got
- * here via the amdgpu_dm_connector_helper_funcs
- */
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
- (mode->flags & DRM_MODE_FLAG_DBLSCAN))
- return result;
-
- /*
- * Only run this the first time mode_valid is called to initilialize
- * EDID mgmt
- */
- if (aconnector->base.force != DRM_FORCE_UNSPECIFIED &&
- !aconnector->dc_em_sink)
- handle_edid_mgmt(aconnector);
-
- dc_sink = to_amdgpu_dm_connector(connector)->dc_sink;
-
- if (dc_sink == NULL && aconnector->base.force != DRM_FORCE_ON_DIGITAL &&
- aconnector->base.force != DRM_FORCE_ON) {
- drm_err(connector->dev, "dc_sink is NULL!\n");
- goto fail;
- }
-
- test_mode = drm_mode_duplicate(connector->dev, mode);
- if (!test_mode)
- goto fail;
-
- drm_mode_set_crtcinfo(test_mode, 0);
-
- stream = create_validate_stream_for_sink(connector, test_mode,
- to_dm_connector_state(connector->state),
- NULL);
- drm_mode_destroy(connector->dev, test_mode);
- if (stream) {
- dc_stream_release(stream);
- result = MODE_OK;
- }
-
-fail:
- /* TODO: error handling*/
- return result;
-}
-
-static int fill_hdr_info_packet(const struct drm_connector_state *state,
- struct dc_info_packet *out)
-{
- struct hdmi_drm_infoframe frame;
- unsigned char buf[30]; /* 26 + 4 */
- ssize_t len;
- int ret, i;
-
- memset(out, 0, sizeof(*out));
-
- if (!state->hdr_output_metadata)
- return 0;
-
- ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, state);
- if (ret)
- return ret;
-
- len = hdmi_drm_infoframe_pack_only(&frame, buf, sizeof(buf));
- if (len < 0)
- return (int)len;
-
- /* Static metadata is a fixed 26 bytes + 4 byte header. */
- if (len != 30)
- return -EINVAL;
-
- /* Prepare the infopacket for DC. */
- switch (state->connector->connector_type) {
- case DRM_MODE_CONNECTOR_HDMIA:
- out->hb0 = 0x87; /* type */
- out->hb1 = 0x01; /* version */
- out->hb2 = 0x1A; /* length */
- out->sb[0] = buf[3]; /* checksum */
- i = 1;
- break;
-
- case DRM_MODE_CONNECTOR_DisplayPort:
- case DRM_MODE_CONNECTOR_eDP:
- out->hb0 = 0x00; /* sdp id, zero */
- out->hb1 = 0x87; /* type */
- out->hb2 = 0x1D; /* payload len - 1 */
- out->hb3 = (0x13 << 2); /* sdp version */
- out->sb[0] = 0x01; /* version */
- out->sb[1] = 0x1A; /* length */
- i = 2;
- break;
-
- default:
- return -EINVAL;
- }
-
- memcpy(&out->sb[i], &buf[4], 26);
- out->valid = true;
-
- print_hex_dump(KERN_DEBUG, "HDR SB:", DUMP_PREFIX_NONE, 16, 1, out->sb,
- sizeof(out->sb), false);
-
- return 0;
-}
-
-static int
-amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
- struct drm_atomic_commit *state)
-{
- struct drm_connector_state *new_con_state =
- drm_atomic_get_new_connector_state(state, conn);
- struct drm_connector_state *old_con_state =
- drm_atomic_get_old_connector_state(state, conn);
- struct drm_crtc *crtc = new_con_state->crtc;
- struct drm_crtc_state *new_crtc_state;
- struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn);
- int ret;
-
- if (WARN_ON(unlikely(!old_con_state || !new_con_state)))
- return -EINVAL;
-
- trace_amdgpu_dm_connector_atomic_check(new_con_state);
-
- if (conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
- ret = drm_dp_mst_root_conn_atomic_check(new_con_state, &aconn->mst_mgr);
- if (ret < 0)
- return ret;
- }
-
- if (!crtc)
- return 0;
-
- if (new_con_state->privacy_screen_sw_state != old_con_state->privacy_screen_sw_state) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (new_con_state->colorspace != old_con_state->colorspace) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (new_con_state->content_type != old_con_state->content_type) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (!drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state)) {
- struct dc_info_packet hdr_infopacket;
-
- ret = fill_hdr_info_packet(new_con_state, &hdr_infopacket);
- if (ret)
- return ret;
-
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- /*
- * DC considers the stream backends changed if the
- * static metadata changes. Forcing the modeset also
- * gives a simple way for userspace to switch from
- * 8bpc to 10bpc when setting the metadata to enter
- * or exit HDR.
- *
- * Changing the static metadata after it's been
- * set is permissible, however. So only force a
- * modeset if we're entering or exiting HDR.
- */
- new_crtc_state->mode_changed = new_crtc_state->mode_changed ||
- !old_con_state->hdr_output_metadata ||
- !new_con_state->hdr_output_metadata;
- }
-
- return 0;
-}
-
-static const struct drm_connector_helper_funcs
-amdgpu_dm_connector_helper_funcs = {
- /*
- * If hotplugging a second bigger display in FB Con mode, bigger resolution
- * modes will be filtered by drm_mode_validate_size(), and those modes
- * are missing after user start lightdm. So we need to renew modes list.
- * in get_modes call back, not just return the modes count
- */
- .get_modes = get_modes,
- .mode_valid = amdgpu_dm_connector_mode_valid,
- .atomic_check = amdgpu_dm_connector_atomic_check,
-};
-
-static void dm_encoder_helper_disable(struct drm_encoder *encoder)
-{
-
-}
-
-int convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth)
-{
- switch (display_color_depth) {
- case COLOR_DEPTH_666:
- return 6;
- case COLOR_DEPTH_888:
- return 8;
- case COLOR_DEPTH_101010:
- return 10;
- case COLOR_DEPTH_121212:
- return 12;
- case COLOR_DEPTH_141414:
- return 14;
- case COLOR_DEPTH_161616:
- return 16;
- default:
- break;
- }
- return 0;
-}
-
-static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_atomic_commit *state = crtc_state->state;
- struct drm_connector *connector = conn_state->connector;
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct dm_connector_state *dm_new_connector_state = to_dm_connector_state(conn_state);
- const struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
- struct drm_dp_mst_topology_mgr *mst_mgr;
- struct drm_dp_mst_port *mst_port;
- struct drm_dp_mst_topology_state *mst_state;
- enum dc_color_depth color_depth;
- int clock, bpp = 0;
- bool is_y420 = false;
-
- if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) ||
- (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) {
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
- enum drm_mode_status result;
-
- result = drm_crtc_helper_mode_valid_fixed(encoder->crtc, adjusted_mode, native_mode);
- if (result != MODE_OK && dm_new_connector_state->scaling == RMX_OFF) {
- drm_dbg_driver(encoder->dev,
- "mode %dx%d@%dHz is not native, enabling scaling\n",
- adjusted_mode->hdisplay, adjusted_mode->vdisplay,
- drm_mode_vrefresh(adjusted_mode));
- dm_new_connector_state->scaling = RMX_ASPECT;
- }
- return 0;
- }
-
- if (!aconnector->mst_output_port)
- return 0;
-
- mst_port = aconnector->mst_output_port;
- mst_mgr = &aconnector->mst_root->mst_mgr;
-
- if (!crtc_state->connectors_changed && !crtc_state->mode_changed)
- return 0;
-
- mst_state = drm_atomic_get_mst_topology_state(state, mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- mst_state->pbn_div.full = dm_mst_get_pbn_divider(aconnector->mst_root->dc_link);
-
- if (!state->duplicated) {
- int max_bpc = conn_state->max_requested_bpc;
-
- is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) &&
- aconnector->force_yuv420_output;
- color_depth = convert_color_depth_from_display_info(connector,
- is_y420,
- max_bpc);
- bpp = convert_dc_color_depth_into_bpc(color_depth) * 3;
- clock = adjusted_mode->clock;
- dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp << 4);
- }
-
- dm_new_connector_state->vcpi_slots =
- drm_dp_atomic_find_time_slots(state, mst_mgr, mst_port,
- dm_new_connector_state->pbn);
- if (dm_new_connector_state->vcpi_slots < 0) {
- drm_dbg_atomic(connector->dev, "failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots);
- return dm_new_connector_state->vcpi_slots;
- }
- return 0;
-}
-
-const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = {
- .disable = dm_encoder_helper_disable,
- .atomic_check = dm_encoder_helper_atomic_check
-};
-
static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_commit *state,
struct dc_state *dc_state,
struct dsc_mst_fairness_vars *vars)
@@ -8961,762 +3502,6 @@ static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_commit *state,
return 0;
}
-static int to_drm_connector_type(enum signal_type st, uint32_t connector_id)
-{
- switch (st) {
- case SIGNAL_TYPE_HDMI_TYPE_A:
- return DRM_MODE_CONNECTOR_HDMIA;
- case SIGNAL_TYPE_EDP:
- return DRM_MODE_CONNECTOR_eDP;
- case SIGNAL_TYPE_LVDS:
- return DRM_MODE_CONNECTOR_LVDS;
- case SIGNAL_TYPE_RGB:
- return DRM_MODE_CONNECTOR_VGA;
- case SIGNAL_TYPE_DISPLAY_PORT:
- case SIGNAL_TYPE_DISPLAY_PORT_MST:
- /* External DP bridges have a different connector type. */
- if (connector_id == CONNECTOR_ID_VGA)
- return DRM_MODE_CONNECTOR_VGA;
- else if (connector_id == CONNECTOR_ID_LVDS)
- return DRM_MODE_CONNECTOR_LVDS;
-
- return DRM_MODE_CONNECTOR_DisplayPort;
- case SIGNAL_TYPE_DVI_DUAL_LINK:
- case SIGNAL_TYPE_DVI_SINGLE_LINK:
- if (connector_id == CONNECTOR_ID_SINGLE_LINK_DVII ||
- connector_id == CONNECTOR_ID_DUAL_LINK_DVII)
- return DRM_MODE_CONNECTOR_DVII;
-
- return DRM_MODE_CONNECTOR_DVID;
- case SIGNAL_TYPE_VIRTUAL:
- return DRM_MODE_CONNECTOR_VIRTUAL;
-
- default:
- return DRM_MODE_CONNECTOR_Unknown;
- }
-}
-
-static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector)
-{
- struct drm_encoder *encoder;
-
- /* There is only one encoder per connector */
- drm_connector_for_each_possible_encoder(connector, encoder)
- return encoder;
-
- return NULL;
-}
-
-static void amdgpu_dm_get_native_mode(struct drm_connector *connector)
-{
- struct drm_encoder *encoder;
- struct amdgpu_encoder *amdgpu_encoder;
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
-
- if (encoder == NULL)
- return;
-
- amdgpu_encoder = to_amdgpu_encoder(encoder);
-
- amdgpu_encoder->native_mode.clock = 0;
-
- if (!list_empty(&connector->probed_modes)) {
- struct drm_display_mode *preferred_mode = NULL;
-
- list_for_each_entry(preferred_mode,
- &connector->probed_modes,
- head) {
- if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED)
- amdgpu_encoder->native_mode = *preferred_mode;
-
- break;
- }
-
- }
-}
-
-static struct drm_display_mode *
-amdgpu_dm_create_common_mode(struct drm_encoder *encoder,
- const char *name,
- int hdisplay, int vdisplay)
-{
- struct drm_device *dev = encoder->dev;
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *mode = NULL;
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
-
- mode = drm_mode_duplicate(dev, native_mode);
-
- if (mode == NULL)
- return NULL;
-
- mode->hdisplay = hdisplay;
- mode->vdisplay = vdisplay;
- mode->type &= ~DRM_MODE_TYPE_PREFERRED;
- strscpy(mode->name, name, DRM_DISPLAY_MODE_LEN);
-
- return mode;
-
-}
-
-static const struct amdgpu_dm_mode_size {
- char name[DRM_DISPLAY_MODE_LEN];
- int w;
- int h;
-} common_modes[] = {
- { "640x480", 640, 480},
- { "800x600", 800, 600},
- { "1024x768", 1024, 768},
- { "1280x720", 1280, 720},
- { "1280x800", 1280, 800},
- {"1280x1024", 1280, 1024},
- { "1440x900", 1440, 900},
- {"1680x1050", 1680, 1050},
- {"1600x1200", 1600, 1200},
- {"1920x1080", 1920, 1080},
- {"1920x1200", 1920, 1200}
-};
-
-static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder,
- struct drm_connector *connector)
-{
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *mode = NULL;
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- int i;
- int n;
-
- if ((connector->connector_type != DRM_MODE_CONNECTOR_eDP) &&
- (connector->connector_type != DRM_MODE_CONNECTOR_LVDS))
- return;
-
- n = ARRAY_SIZE(common_modes);
-
- for (i = 0; i < n; i++) {
- struct drm_display_mode *curmode = NULL;
- bool mode_existed = false;
-
- if (common_modes[i].w > native_mode->hdisplay ||
- common_modes[i].h > native_mode->vdisplay ||
- (common_modes[i].w == native_mode->hdisplay &&
- common_modes[i].h == native_mode->vdisplay))
- continue;
-
- list_for_each_entry(curmode, &connector->probed_modes, head) {
- if (common_modes[i].w == curmode->hdisplay &&
- common_modes[i].h == curmode->vdisplay) {
- mode_existed = true;
- break;
- }
- }
-
- if (mode_existed)
- continue;
-
- mode = amdgpu_dm_create_common_mode(encoder,
- common_modes[i].name, common_modes[i].w,
- common_modes[i].h);
- if (!mode)
- continue;
-
- drm_mode_probed_add(connector, mode);
- amdgpu_dm_connector->num_modes++;
- }
-}
-
-static void amdgpu_set_panel_orientation(struct drm_connector *connector)
-{
- struct drm_encoder *encoder;
- struct amdgpu_encoder *amdgpu_encoder;
- const struct drm_display_mode *native_mode;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
- connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
- return;
-
- mutex_lock(&connector->dev->mode_config.mutex);
- amdgpu_dm_connector_get_modes(connector);
- mutex_unlock(&connector->dev->mode_config.mutex);
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
- if (!encoder)
- return;
-
- amdgpu_encoder = to_amdgpu_encoder(encoder);
-
- native_mode = &amdgpu_encoder->native_mode;
- if (native_mode->hdisplay == 0 || native_mode->vdisplay == 0)
- return;
-
- drm_connector_set_panel_orientation_with_quirk(connector,
- DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
- native_mode->hdisplay,
- native_mode->vdisplay);
-}
-
-static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (drm_edid) {
- /* empty probed_modes */
- INIT_LIST_HEAD(&connector->probed_modes);
- amdgpu_dm_connector->num_modes =
- drm_edid_connector_add_modes(connector);
-
- /* sorting the probed modes before calling function
- * amdgpu_dm_get_native_mode() since EDID can have
- * more than one preferred mode. The modes that are
- * later in the probed mode list could be of higher
- * and preferred resolution. For example, 3840x2160
- * resolution in base EDID preferred timing and 4096x2160
- * preferred resolution in DID extension block later.
- */
- drm_mode_sort(&connector->probed_modes);
- amdgpu_dm_get_native_mode(connector);
-
- /* Freesync capabilities are reset by calling
- * drm_edid_connector_add_modes() and need to be
- * restored here.
- */
- amdgpu_dm_update_freesync_caps(connector, drm_edid, false);
- } else {
- amdgpu_dm_connector->num_modes = 0;
- }
-}
-
-static bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector,
- struct drm_display_mode *mode)
-{
- struct drm_display_mode *m;
-
- list_for_each_entry(m, &aconnector->base.probed_modes, head) {
- if (drm_mode_equal(m, mode))
- return true;
- }
-
- return false;
-}
-
-static uint add_fs_modes(struct amdgpu_dm_connector *aconnector)
-{
- const struct drm_display_mode *m;
- struct drm_display_mode *new_mode;
- uint i;
- u32 new_modes_count = 0;
-
- /* Standard FPS values
- *
- * 23.976 - TV/NTSC
- * 24 - Cinema
- * 25 - TV/PAL
- * 29.97 - TV/NTSC
- * 30 - TV/NTSC
- * 48 - Cinema HFR
- * 50 - TV/PAL
- * 60 - Commonly used
- * 48,72,96,120 - Multiples of 24
- */
- static const u32 common_rates[] = {
- 23976, 24000, 25000, 29970, 30000,
- 48000, 50000, 60000, 72000, 96000, 120000
- };
-
- /*
- * Find mode with highest refresh rate with the same resolution
- * as the preferred mode. Some monitors report a preferred mode
- * with lower resolution than the highest refresh rate supported.
- */
-
- m = get_highest_refresh_rate_mode(aconnector, true);
- if (!m)
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(common_rates); i++) {
- u64 target_vtotal, target_vtotal_diff;
- u64 num, den;
-
- if (drm_mode_vrefresh(m) * 1000 < common_rates[i])
- continue;
-
- if (common_rates[i] < aconnector->min_vfreq * 1000 ||
- common_rates[i] > aconnector->max_vfreq * 1000)
- continue;
-
- num = (unsigned long long)m->clock * 1000 * 1000;
- den = common_rates[i] * (unsigned long long)m->htotal;
- target_vtotal = div_u64(num, den);
- target_vtotal_diff = target_vtotal - m->vtotal;
-
- /* Check for illegal modes */
- if (m->vsync_start + target_vtotal_diff < m->vdisplay ||
- m->vsync_end + target_vtotal_diff < m->vsync_start ||
- m->vtotal + target_vtotal_diff < m->vsync_end)
- continue;
-
- new_mode = drm_mode_duplicate(aconnector->base.dev, m);
- if (!new_mode)
- goto out;
-
- new_mode->vtotal += (u16)target_vtotal_diff;
- new_mode->vsync_start += (u16)target_vtotal_diff;
- new_mode->vsync_end += (u16)target_vtotal_diff;
- new_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
- new_mode->type |= DRM_MODE_TYPE_DRIVER;
-
- if (!is_duplicate_mode(aconnector, new_mode)) {
- drm_mode_probed_add(&aconnector->base, new_mode);
- new_modes_count += 1;
- } else
- drm_mode_destroy(aconnector->base.dev, new_mode);
- }
- out:
- return new_modes_count;
-}
-
-static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (!(amdgpu_freesync_vid_mode && drm_edid))
- return;
-
- if (!amdgpu_dm_connector->dc_sink || !amdgpu_dm_connector->dc_link)
- return;
-
- if (!dc_supports_vrr(amdgpu_dm_connector->dc_sink->ctx->dce_version))
- return;
-
- if (dc_connector_supports_analog(amdgpu_dm_connector->dc_link->link_id.id) &&
- amdgpu_dm_connector->dc_sink->edid_caps.analog)
- return;
-
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- amdgpu_dm_connector->num_modes +=
- add_fs_modes(amdgpu_dm_connector);
-}
-
-static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
-{
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- struct dc_link *dc_link = amdgpu_dm_connector->dc_link;
- struct drm_encoder *encoder;
- const struct drm_edid *drm_edid = amdgpu_dm_connector->drm_edid;
- struct dc_link_settings *verified_link_cap = &dc_link->verified_link_cap;
- const struct dc *dc = dc_link->dc;
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
-
- if (!drm_edid) {
- amdgpu_dm_connector->num_modes =
- drm_add_modes_noedid(connector, 640, 480);
- if (dc->link_srv->dp_get_encoding_format(verified_link_cap) == DP_128b_132b_ENCODING)
- amdgpu_dm_connector->num_modes +=
- drm_add_modes_noedid(connector, 1920, 1080);
-
- if (amdgpu_dm_connector->dc_sink &&
- amdgpu_dm_connector->dc_sink->edid_caps.analog &&
- dc_connector_supports_analog(dc_link->link_id.id)) {
- /* Analog monitor connected by DAC load detection.
- * Add common modes. It will be up to the user to select one that works.
- */
- for (int i = 0; i < ARRAY_SIZE(common_modes); i++)
- amdgpu_dm_connector->num_modes += drm_add_modes_noedid(
- connector, common_modes[i].w, common_modes[i].h);
- }
- } else {
- amdgpu_dm_connector_ddc_get_modes(connector, drm_edid);
- if (encoder)
- amdgpu_dm_connector_add_common_modes(encoder, connector);
- amdgpu_dm_connector_add_freesync_modes(connector, drm_edid);
- }
- amdgpu_dm_fbc_init(connector);
-
- return amdgpu_dm_connector->num_modes;
-}
-
-static const u32 supported_colorspaces =
- BIT(DRM_MODE_COLORIMETRY_BT709_YCC) |
- BIT(DRM_MODE_COLORIMETRY_OPRGB) |
- BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) |
- BIT(DRM_MODE_COLORIMETRY_BT2020_YCC);
-
-static const u32 supported_colorformats =
- BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) |
- BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420);
-
-void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector,
- int connector_type,
- struct dc_link *link,
- int link_index)
-{
- struct amdgpu_device *adev = drm_to_adev(dm->ddev);
-
- /*
- * Some of the properties below require access to state, like bpc.
- * Allocate some default initial connector state with our reset helper.
- */
- if (aconnector->base.funcs->reset)
- aconnector->base.funcs->reset(&aconnector->base);
-
- aconnector->connector_id = link_index;
- aconnector->bl_idx = -1;
- aconnector->dc_link = link;
- aconnector->base.interlace_allowed = false;
- aconnector->base.doublescan_allowed = false;
- aconnector->base.stereo_allowed = false;
- aconnector->base.dpms = DRM_MODE_DPMS_OFF;
- aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */
- aconnector->audio_inst = -1;
- aconnector->pack_sdp_v1_3 = false;
- aconnector->as_type = ADAPTIVE_SYNC_TYPE_NONE;
- memset(&aconnector->vsdb_info, 0, sizeof(aconnector->vsdb_info));
- mutex_init(&aconnector->hpd_lock);
- mutex_init(&aconnector->handle_mst_msg_ready);
-
- /*
- * If HDMI HPD debounce delay is set, use the minimum between selected
- * value and AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS
- */
- if (amdgpu_hdmi_hpd_debounce_delay_ms) {
- aconnector->hdmi_hpd_debounce_delay_ms = min(amdgpu_hdmi_hpd_debounce_delay_ms,
- AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS);
- INIT_DELAYED_WORK(&aconnector->hdmi_hpd_debounce_work, hdmi_hpd_debounce_work);
- aconnector->hdmi_prev_sink = NULL;
- } else {
- aconnector->hdmi_hpd_debounce_delay_ms = 0;
- }
-
- /*
- * configure support HPD hot plug connector_>polled default value is 0
- * which means HPD hot plug not supported
- */
- switch (connector_type) {
- case DRM_MODE_CONNECTOR_HDMIA:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- aconnector->base.ycbcr_420_allowed =
- link->link_enc->features.hdmi_ycbcr420_supported ? true : false;
- break;
- case DRM_MODE_CONNECTOR_DisplayPort:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- link->link_enc = link_enc_cfg_get_link_enc(link);
- ASSERT(link->link_enc);
- if (link->link_enc)
- aconnector->base.ycbcr_420_allowed =
- link->link_enc->features.dp_ycbcr420_supported ? true : false;
- break;
- case DRM_MODE_CONNECTOR_DVID:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- break;
- case DRM_MODE_CONNECTOR_DVII:
- case DRM_MODE_CONNECTOR_VGA:
- aconnector->base.polled =
- DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
- break;
- default:
- break;
- }
-
- drm_object_attach_property(&aconnector->base.base,
- dm->ddev->mode_config.scaling_mode_property,
- DRM_MODE_SCALE_NONE);
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA
- || (connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root))
- drm_connector_attach_broadcast_rgb_property(&aconnector->base);
-
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_property,
- UNDERSCAN_OFF);
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_hborder_property,
- 0);
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_vborder_property,
- 0);
-
- if (!aconnector->mst_root)
- drm_connector_attach_max_bpc_property(&aconnector->base, 8, 16);
-
- aconnector->base.state->max_bpc = 16;
- aconnector->base.state->max_requested_bpc = aconnector->base.state->max_bpc;
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
- /* Content Type is currently only implemented for HDMI. */
- drm_connector_attach_content_type_property(&aconnector->base);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
- if (!drm_mode_create_hdmi_colorspace_property(&aconnector->base, supported_colorspaces))
- drm_connector_attach_colorspace_property(&aconnector->base);
- } else if ((connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root) ||
- connector_type == DRM_MODE_CONNECTOR_eDP) {
- if (!drm_mode_create_dp_colorspace_property(&aconnector->base, supported_colorspaces))
- drm_connector_attach_colorspace_property(&aconnector->base);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
- connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
- connector_type == DRM_MODE_CONNECTOR_eDP) {
- drm_connector_attach_hdr_output_metadata_property(&aconnector->base);
-
- if (!aconnector->mst_root) {
- drm_connector_attach_vrr_capable_property(&aconnector->base);
- drm_connector_attach_color_format_property(&aconnector->base,
- supported_colorformats);
- }
-
- if (adev->dm.hdcp_workqueue)
- drm_connector_attach_content_protection_property(&aconnector->base, true);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_eDP) {
- struct drm_privacy_screen *privacy_screen;
-
- drm_connector_attach_panel_type_property(&aconnector->base);
-
- privacy_screen = drm_privacy_screen_get(adev_to_drm(adev)->dev, NULL);
- if (!IS_ERR(privacy_screen)) {
- drm_connector_attach_privacy_screen_provider(&aconnector->base,
- privacy_screen);
- } else if (PTR_ERR(privacy_screen) != -ENODEV) {
- drm_warn(adev_to_drm(adev), "Error getting privacy-screen\n");
- }
- }
-}
-
-static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msgs, int num)
-{
- struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
- struct ddc_service *ddc_service = i2c->ddc_service;
- struct i2c_command cmd;
- int i;
- int result = -EIO;
-
- if (!ddc_service->ddc_pin)
- return result;
-
- cmd.payloads = kzalloc_objs(struct i2c_payload, num);
-
- if (!cmd.payloads)
- return result;
-
- cmd.number_of_payloads = num;
- cmd.engine = I2C_COMMAND_ENGINE_DEFAULT;
- cmd.speed = 100;
-
- for (i = 0; i < num; i++) {
- cmd.payloads[i].write = !(msgs[i].flags & I2C_M_RD);
- cmd.payloads[i].address = msgs[i].addr;
- cmd.payloads[i].length = msgs[i].len;
- cmd.payloads[i].data = msgs[i].buf;
- }
-
- if (i2c->oem) {
- if (dc_submit_i2c_oem(
- ddc_service->ctx->dc,
- &cmd))
- result = num;
- } else {
- if (dc_submit_i2c(
- ddc_service->ctx->dc,
- ddc_service->link->link_index,
- &cmd))
- result = num;
- }
-
- kfree(cmd.payloads);
- return result;
-}
-
-static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm amdgpu_dm_i2c_algo = {
- .master_xfer = amdgpu_dm_i2c_xfer,
- .functionality = amdgpu_dm_i2c_func,
-};
-
-static struct amdgpu_i2c_adapter *
-create_i2c(struct ddc_service *ddc_service, bool oem)
-{
- struct amdgpu_device *adev = ddc_service->ctx->driver_context;
- struct amdgpu_i2c_adapter *i2c;
-
- i2c = kzalloc_obj(struct amdgpu_i2c_adapter);
- if (!i2c)
- return NULL;
- i2c->base.owner = THIS_MODULE;
- i2c->base.dev.parent = &adev->pdev->dev;
- i2c->base.algo = &amdgpu_dm_i2c_algo;
- if (oem)
- snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c OEM bus");
- else
- snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d",
- ddc_service->link->link_index);
- i2c_set_adapdata(&i2c->base, i2c);
- i2c->ddc_service = ddc_service;
- i2c->oem = oem;
-
- return i2c;
-}
-
-int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector)
-{
- struct cec_connector_info conn_info;
- struct drm_device *ddev = aconnector->base.dev;
- struct device *hdmi_dev = ddev->dev;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_HDMI_CEC) {
- drm_info(ddev, "HDMI-CEC feature masked\n");
- return -EINVAL;
- }
-
- cec_fill_conn_info_from_drm(&conn_info, &aconnector->base);
- aconnector->notifier =
- cec_notifier_conn_register(hdmi_dev, NULL, &conn_info);
- if (!aconnector->notifier) {
- drm_err(ddev, "Failed to create cec notifier\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-/*
- * Note: this function assumes that dc_link_detect() was called for the
- * dc_link which will be represented by this aconnector.
- */
-static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector,
- u32 link_index,
- struct amdgpu_encoder *aencoder)
-{
- int res = 0;
- int connector_type;
- struct dc *dc = dm->dc;
- struct dc_link *link = dc_get_link_at_index(dc, link_index);
- struct amdgpu_i2c_adapter *i2c;
-
- /* Not needed for writeback connector */
- link->priv = aconnector;
-
-
- i2c = create_i2c(link->ddc, false);
- if (!i2c) {
- drm_err(adev_to_drm(dm->adev), "Failed to create i2c adapter data\n");
- return -ENOMEM;
- }
-
- aconnector->i2c = i2c;
- res = devm_i2c_add_adapter(dm->adev->dev, &i2c->base);
-
- if (res) {
- drm_err(adev_to_drm(dm->adev), "Failed to register hw i2c %d\n", link->link_index);
- goto out_free;
- }
-
- connector_type = to_drm_connector_type(link->connector_signal, link->link_id.id);
-
- res = drm_connector_init_with_ddc(
- dm->ddev,
- &aconnector->base,
- &amdgpu_dm_connector_funcs,
- connector_type,
- &i2c->base);
-
- if (res) {
- drm_err(adev_to_drm(dm->adev), "connector_init failed\n");
- aconnector->connector_id = -1;
- goto out_free;
- }
-
- drm_connector_helper_add(
- &aconnector->base,
- &amdgpu_dm_connector_helper_funcs);
-
- amdgpu_dm_connector_init_helper(
- dm,
- aconnector,
- connector_type,
- link,
- link_index);
-
- drm_connector_attach_encoder(
- &aconnector->base, &aencoder->base);
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
- connector_type == DRM_MODE_CONNECTOR_HDMIB)
- amdgpu_dm_initialize_hdmi_connector(aconnector);
-
- if (dc_is_dp_signal(link->connector_signal))
- amdgpu_dm_initialize_dp_connector(dm, aconnector, link->link_index);
-
-out_free:
- if (res) {
- kfree(i2c);
- aconnector->i2c = NULL;
- }
- return res;
-}
-
-int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev)
-{
- switch (adev->mode_info.num_crtc) {
- case 1:
- return 0x1;
- case 2:
- return 0x3;
- case 3:
- return 0x7;
- case 4:
- return 0xf;
- case 5:
- return 0x1f;
- case 6:
- default:
- return 0x3f;
- }
-}
-
-static int amdgpu_dm_encoder_init(struct drm_device *dev,
- struct amdgpu_encoder *aencoder,
- uint32_t link_index)
-{
- struct amdgpu_device *adev = drm_to_adev(dev);
-
- int res = drm_encoder_init(dev,
- &aencoder->base,
- &amdgpu_dm_encoder_funcs,
- DRM_MODE_ENCODER_TMDS,
- NULL);
-
- aencoder->base.possible_crtcs = amdgpu_dm_get_encoder_crtc_mask(adev);
-
- if (!res)
- aencoder->encoder_id = link_index;
- else
- aencoder->encoder_id = -1;
-
- drm_encoder_helper_add(&aencoder->base, &amdgpu_dm_encoder_helper_funcs);
-
- return res;
-}
-
static void manage_dm_interrupts(struct amdgpu_device *adev,
struct amdgpu_crtc *acrtc,
struct dm_crtc_state *acrtc_state)
@@ -9816,7 +3601,7 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type);
}
-static bool
+STATIC_IFN_KUNIT bool
is_scaling_state_different(const struct dm_connector_state *dm_state,
const struct dm_connector_state *old_dm_state)
{
@@ -9833,6 +3618,7 @@ is_scaling_state_different(const struct dm_connector_state *dm_state,
return true;
return false;
}
+EXPORT_IF_KUNIT(is_scaling_state_different);
static bool is_content_protection_different(struct drm_crtc_state *new_crtc_state,
struct drm_crtc_state *old_crtc_state,
@@ -10421,9 +4207,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
bundle->surface_updates[planes_count].in_transfer_func = &dc_plane->in_transfer_func;
bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix;
bundle->surface_updates[planes_count].hdr_mult = dc_plane->hdr_mult;
- bundle->surface_updates[planes_count].func_shaper = &dc_plane->in_shaper_func;
- bundle->surface_updates[planes_count].lut3d_func = &dc_plane->lut3d_func;
- bundle->surface_updates[planes_count].blend_tf = &dc_plane->blend_tf;
+ bundle->surface_updates[planes_count].cm = &dc_plane->cm;
}
amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state,
@@ -10700,87 +4484,6 @@ cleanup:
kfree(bundle);
}
-static void amdgpu_dm_commit_audio(struct drm_device *dev,
- struct drm_atomic_commit *state)
-{
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_state *old_con_state, *new_con_state;
- struct drm_crtc_state *new_crtc_state;
- struct dm_crtc_state *new_dm_crtc_state;
- const struct dc_stream_status *status;
- int i, inst;
-
- /* Notify device removals. */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- if (old_con_state->crtc != new_con_state->crtc) {
- /* CRTC changes require notification. */
- goto notify;
- }
-
- if (!new_con_state->crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_con_state->crtc);
-
- if (!new_crtc_state)
- continue;
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- continue;
-
-notify:
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- mutex_lock(&adev->dm.audio_lock);
- inst = aconnector->audio_inst;
- aconnector->audio_inst = -1;
- mutex_unlock(&adev->dm.audio_lock);
-
- amdgpu_dm_audio_eld_notify(adev, inst);
- }
-
- /* Notify audio device additions. */
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
- if (!new_con_state->crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_con_state->crtc);
-
- if (!new_crtc_state)
- continue;
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- continue;
-
- new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (!new_dm_crtc_state->stream)
- continue;
-
- status = dc_stream_get_status(new_dm_crtc_state->stream);
- if (!status)
- continue;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- mutex_lock(&adev->dm.audio_lock);
- inst = status->audio_inst;
- aconnector->audio_inst = inst;
- mutex_unlock(&adev->dm.audio_lock);
-
- amdgpu_dm_audio_eld_notify(adev, inst);
- }
-}
-
/*
* amdgpu_dm_crtc_copy_transient_flags - copy mirrored flags from DRM to DC
* @crtc_state: the DRM CRTC state
@@ -10795,10 +4498,55 @@ static void amdgpu_dm_crtc_copy_transient_flags(struct drm_crtc_state *crtc_stat
stream_state->mode_changed = drm_atomic_crtc_needs_modeset(crtc_state);
}
+/**
+ * amdgpu_dm_crtc_complete_writeback - finish a pending writeback job
+ * @acrtc: the CRTC whose pending writeback should be completed
+ *
+ * Clears the pending state, signals the writeback out fence and releases the
+ * vblank reference taken in dm_set_writeback() while the writeback was armed.
+ * The pending flag is tested and cleared under the writeback job lock, so this
+ * is safe to call concurrently from the completion vblank IRQ
+ * (dm_crtc_high_irq()) and from the writeback teardown path
+ * (dm_clear_writeback()); only the caller that observes the pending job
+ * performs the completion.
+ *
+ * Return: true if a pending writeback job was completed by this call.
+ */
+bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc)
+{
+ unsigned long flags;
+ bool pending;
+
+ if (!acrtc->wb_conn)
+ return false;
+
+ spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
+ pending = acrtc->wb_pending;
+ acrtc->wb_pending = false;
+ spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
+
+ if (!pending)
+ return false;
+
+ drm_writeback_signal_completion(acrtc->wb_conn, 0);
+ drm_crtc_vblank_put(&acrtc->base);
+
+ return true;
+}
+
static void dm_clear_writeback(struct amdgpu_display_manager *dm,
+ struct amdgpu_crtc *acrtc,
struct dm_crtc_state *crtc_state)
{
dc_stream_remove_writeback(dm->dc, crtc_state->stream, 0);
+
+ /*
+ * If the writeback is still pending when it is torn down (its
+ * completion vblank IRQ never fired), signal the out fence so a
+ * waiting client does not stall and release the vblank reference
+ * taken in dm_set_writeback().
+ */
+ amdgpu_dm_crtc_complete_writeback(acrtc);
}
/**
@@ -10951,7 +4699,7 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_commit *state,
dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
- dm_clear_writeback(dm, dm_old_crtc_state);
+ dm_clear_writeback(dm, acrtc, dm_old_crtc_state);
acrtc->wb_enabled = false;
}
@@ -11225,9 +4973,24 @@ static void dm_set_writeback(struct amdgpu_display_manager *dm,
dc_stream_add_writeback(dm->dc, crtc_state->stream, wb_info);
- acrtc->wb_pending = true;
acrtc->wb_conn = wb_conn;
drm_writeback_queue_job(wb_conn, new_con_state);
+
+ /*
+ * Writeback completion is detected in the CRTC vblank IRQ
+ * (dm_crtc_high_irq()). Take a vblank reference so the vblank interrupt
+ * stays enabled while the writeback is pending; otherwise a
+ * writeback-only commit right after drm_crtc_vblank_on() (e.g.
+ * re-enabling a CRTC that was disabled) has no other vblank reference,
+ * the IRQ never fires and the out fence times out. The matching put
+ * happens once completion is signalled in dm_crtc_high_irq(), or when
+ * the writeback is torn down in dm_clear_writeback().
+ *
+ * Arm wb_pending only after the reference is held so the completion IRQ
+ * cannot run its matching vblank_put before this get.
+ */
+ WARN_ON(drm_crtc_vblank_get(&acrtc->base));
+ acrtc->wb_pending = true;
}
static void amdgpu_dm_update_hdcp(struct drm_atomic_commit *state)
@@ -11378,6 +5141,74 @@ static int amdgpu_dm_atomic_setup_commit(struct drm_atomic_commit *state)
return 0;
}
+STATIC_IFN_KUNIT void set_multisync_trigger_params(
+ struct dc_stream_state *stream)
+{
+ struct dc_stream_state *master = NULL;
+
+ if (stream->triggered_crtc_reset.enabled) {
+ master = stream->triggered_crtc_reset.event_source;
+ stream->triggered_crtc_reset.event =
+ master->timing.flags.VSYNC_POSITIVE_POLARITY ?
+ CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING;
+ stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL;
+ }
+}
+EXPORT_IF_KUNIT(set_multisync_trigger_params);
+
+STATIC_IFN_KUNIT void set_master_stream(struct dc_stream_state *stream_set[],
+ int stream_count)
+{
+ int j, highest_rfr = 0, master_stream = 0;
+
+ for (j = 0; j < stream_count; j++) {
+ if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) {
+ int refresh_rate = 0;
+
+ refresh_rate = (stream_set[j]->timing.pix_clk_100hz*100)/
+ (stream_set[j]->timing.h_total*stream_set[j]->timing.v_total);
+ if (refresh_rate > highest_rfr) {
+ highest_rfr = refresh_rate;
+ master_stream = j;
+ }
+ }
+ }
+ for (j = 0; j < stream_count; j++) {
+ if (stream_set[j])
+ stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
+ }
+}
+EXPORT_IF_KUNIT(set_master_stream);
+
+static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context)
+{
+ int i = 0;
+ struct dc_stream_state *stream;
+
+ if (context->stream_count < 2)
+ return;
+ for (i = 0; i < context->stream_count ; i++) {
+ if (!context->streams[i])
+ continue;
+ /*
+ * TODO: add a function to read AMD VSDB bits and set
+ * crtc_sync_master.multi_sync_enabled flag
+ * For now it's set to false
+ */
+ }
+
+ set_master_stream(context->streams, context->stream_count);
+
+ for (i = 0; i < context->stream_count ; i++) {
+ stream = context->streams[i];
+
+ if (!stream)
+ continue;
+
+ set_multisync_trigger_params(stream);
+ }
+}
+
/**
* amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation.
* @state: The atomic state to commit
@@ -11446,7 +5277,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
if ((new_con_state->hdmi.broadcast_rgb != old_con_state->hdmi.broadcast_rgb) &&
(dm_old_crtc_state->stream->output_color_space !=
- get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state)))
+ amdgpu_dm_get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state)))
output_color_space_changed = true;
abm_changed = dm_new_crtc_state->abm_level !=
@@ -11460,7 +5291,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
stream_update.stream = dm_new_crtc_state->stream;
if (scaling_changed) {
- update_stream_scaling_settings(dev, &dm_new_con_state->base.crtc->mode,
+ amdgpu_dm_update_stream_scaling_settings(dev, &dm_new_con_state->base.crtc->mode,
dm_new_con_state, dm_new_crtc_state->stream);
stream_update.src = dm_new_crtc_state->stream->src;
@@ -11469,7 +5300,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
if (output_color_space_changed) {
dm_new_crtc_state->stream->output_color_space
- = get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state);
+ = amdgpu_dm_get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state);
stream_update.output_color_space = &dm_new_crtc_state->stream->output_color_space;
}
@@ -11481,7 +5312,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
}
if (hdr_changed) {
- fill_hdr_info_packet(new_con_state, &hdr_packet);
+ amdgpu_dm_fill_hdr_info_packet(new_con_state, &hdr_packet);
stream_update.hdr_static_metadata = &hdr_packet;
}
@@ -11689,104 +5520,6 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
trace_amdgpu_dm_atomic_commit_tail_finish(state);
}
-static int dm_force_atomic_commit(struct drm_connector *connector)
-{
- int ret = 0;
- struct drm_device *ddev = connector->dev;
- struct drm_atomic_commit *state = drm_atomic_commit_alloc(ddev);
- struct amdgpu_crtc *disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc);
- struct drm_plane *plane = disconnected_acrtc->base.primary;
- struct drm_connector_state *conn_state;
- struct drm_crtc_state *crtc_state;
- struct drm_plane_state *plane_state;
-
- if (!state)
- return -ENOMEM;
-
- state->acquire_ctx = ddev->mode_config.acquire_ctx;
-
- /* Construct an atomic state to restore previous display setting */
-
- /*
- * Attach connectors to drm_atomic_commit
- */
- conn_state = drm_atomic_get_connector_state(state, connector);
-
- /* Check for error in getting connector state */
- if (IS_ERR(conn_state)) {
- ret = PTR_ERR(conn_state);
- goto out;
- }
-
- /* Attach crtc to drm_atomic_commit*/
- crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base);
-
- /* Check for error in getting crtc state */
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- goto out;
- }
-
- /* force a restore */
- crtc_state->mode_changed = true;
-
- /* Attach plane to drm_atomic_commit */
- plane_state = drm_atomic_get_plane_state(state, plane);
-
- /* Check for error in getting plane state */
- if (IS_ERR(plane_state)) {
- ret = PTR_ERR(plane_state);
- goto out;
- }
-
- /* Call commit internally with the state we just constructed */
- ret = drm_atomic_commit(state);
-
-out:
- drm_atomic_commit_put(state);
- if (ret)
- drm_err(ddev, "Restoring old state failed with %i\n", ret);
-
- return ret;
-}
-
-/*
- * This function handles all cases when set mode does not come upon hotplug.
- * This includes when a display is unplugged then plugged back into the
- * same port and when running without usermode desktop manager supprot
- */
-void dm_restore_drm_connector_state(struct drm_device *dev,
- struct drm_connector *connector)
-{
- struct amdgpu_dm_connector *aconnector;
- struct amdgpu_crtc *disconnected_acrtc;
- struct dm_crtc_state *acrtc_state;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (!aconnector->dc_sink || !connector->state || !connector->encoder)
- return;
-
- disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc);
- if (!disconnected_acrtc)
- return;
-
- acrtc_state = to_dm_crtc_state(disconnected_acrtc->base.state);
- if (!acrtc_state->stream)
- return;
-
- /*
- * If the previous sink is not released and different from the current,
- * we deduce we are in a state where we can not rely on usermode call
- * to turn on the display, so we do it here
- */
- if (acrtc_state->stream->sink != aconnector->dc_sink)
- dm_force_atomic_commit(&aconnector->base);
-}
-
/*
* Grabs all modesetting locks to serialize against any blocking commits,
* Waits for completion of all non blocking commits.
@@ -11891,7 +5624,7 @@ static void reset_freesync_config_for_crtc(
sizeof(new_crtc_state->vrr_infopacket));
}
-static bool
+STATIC_IFN_KUNIT bool
is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state)
{
@@ -11920,8 +5653,9 @@ is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
return false;
}
+EXPORT_IF_KUNIT(is_timing_unchanged_for_freesync);
-static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
+STATIC_IFN_KUNIT void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
{
u64 num, den, res;
struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base;
@@ -11935,6 +5669,7 @@ static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
res = div_u64(num, den);
dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
}
+EXPORT_IF_KUNIT(set_freesync_fixed_config);
static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
struct drm_atomic_commit *state,
@@ -11988,7 +5723,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
goto skip_modeset;
- new_stream = create_validate_stream_for_sink(connector,
+ new_stream = amdgpu_dm_create_validate_stream_for_sink(connector,
&new_crtc_state->mode,
dm_new_conn_state,
dm_old_crtc_state->stream);
@@ -12016,7 +5751,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level;
- ret = fill_hdr_info_packet(drm_new_conn_state,
+ ret = amdgpu_dm_fill_hdr_info_packet(drm_new_conn_state,
&new_stream->hdr_static_metadata);
if (ret)
goto fail;
@@ -12085,11 +5820,11 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
goto skip_modeset;
} else if (amdgpu_freesync_vid_mode && aconnector &&
- is_freesync_video_mode(&new_crtc_state->mode,
+ amdgpu_dm_is_freesync_video_mode(&new_crtc_state->mode,
aconnector)) {
struct drm_display_mode *high_mode;
- high_mode = get_highest_refresh_rate_mode(aconnector, false);
+ high_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false);
if (!drm_mode_equal(&new_crtc_state->mode, high_mode))
set_freesync_fixed_config(dm_new_crtc_state);
}
@@ -12181,7 +5916,7 @@ skip_modeset:
/* Scaling or underscan settings */
if (is_scaling_state_different(dm_old_conn_state, dm_new_conn_state) ||
drm_atomic_crtc_needs_modeset(new_crtc_state))
- update_stream_scaling_settings(adev_to_drm(adev),
+ amdgpu_dm_update_stream_scaling_settings(adev_to_drm(adev),
&new_crtc_state->mode, dm_new_conn_state, dm_new_crtc_state->stream);
/* ABM settings */
@@ -12655,7 +6390,7 @@ static int dm_update_plane_state(struct dc *dc,
/* Tell DC to do a full surface update every time there
* is a plane change. Inefficient, but works for now.
*/
- dm_new_plane_state->dc_state->update_flags.bits.full_update = 1;
+ dm_new_plane_state->dc_state->update_bits.full_update = 1;
*lock_and_validation_needed = true;
}
@@ -12674,8 +6409,8 @@ out:
return ret;
}
-static void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
- int *src_w, int *src_h)
+STATIC_IFN_KUNIT void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
+ int *src_w, int *src_h)
{
switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) {
case DRM_MODE_ROTATE_90:
@@ -12691,8 +6426,9 @@ static void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
break;
}
}
+EXPORT_IF_KUNIT(dm_get_oriented_plane_size);
-static void
+STATIC_IFN_KUNIT void
dm_get_plane_scale(struct drm_plane_state *plane_state,
int *out_plane_scale_w, int *out_plane_scale_h)
{
@@ -12702,6 +6438,7 @@ dm_get_plane_scale(struct drm_plane_state *plane_state,
*out_plane_scale_w = plane_src_w ? plane_state->crtc_w * 1000 / plane_src_w : 0;
*out_plane_scale_h = plane_src_h ? plane_state->crtc_h * 1000 / plane_src_h : 0;
}
+EXPORT_IF_KUNIT(dm_get_plane_scale);
/*
* The normalized_zpos value cannot be used by this iterator directly. It's only
@@ -13562,373 +7299,6 @@ fail:
return ret;
}
-static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm,
- unsigned int offset,
- unsigned int total_length,
- u8 *data,
- unsigned int length,
- struct amdgpu_hdmi_vsdb_info *vsdb)
-{
- bool res;
- union dmub_rb_cmd cmd;
- struct dmub_cmd_send_edid_cea *input;
- struct dmub_cmd_edid_cea_output *output;
-
- if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES)
- return false;
-
- memset(&cmd, 0, sizeof(cmd));
-
- input = &cmd.edid_cea.data.input;
-
- cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA;
- cmd.edid_cea.header.sub_type = 0;
- cmd.edid_cea.header.payload_bytes =
- sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header);
- input->offset = offset;
- input->length = length;
- input->cea_total_length = total_length;
- memcpy(input->payload, data, length);
-
- res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY);
- if (!res) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n");
- return false;
- }
-
- output = &cmd.edid_cea.data.output;
-
- if (output->type == DMUB_CMD__EDID_CEA_ACK) {
- if (!output->ack.success) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n",
- output->ack.offset);
- }
- } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) {
- if (!output->amd_vsdb.vsdb_found)
- return false;
-
- vsdb->freesync_supported = output->amd_vsdb.freesync_supported;
- vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version;
- vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate;
- vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate;
- vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code;
- } else {
- drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n");
- return false;
- }
-
- return true;
-}
-
-static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- bool res;
- int offset;
-
- /* send 8 bytes a time */
- if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8))
- return false;
-
- if (i+8 == len) {
- /* EDID block sent completed, expect result */
- int version, min_rate, max_rate;
-
- res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate);
- if (res) {
- /* amd vsdb found */
- vsdb_info->freesync_supported = 1;
- vsdb_info->amd_vsdb_version = version;
- vsdb_info->min_refresh_rate_hz = min_rate;
- vsdb_info->max_refresh_rate_hz = max_rate;
- /* Not enabled on DMCU*/
- vsdb_info->freesync_mccs_vcp_code = 0;
- return true;
- }
- /* not amd vsdb */
- return false;
- }
-
- /* check for ack*/
- res = dc_edid_parser_recv_cea_ack(dm->dc, &offset);
- if (!res)
- return false;
- }
-
- return false;
-}
-
-static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- /* send 8 bytes a time */
- if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info))
- return false;
- }
-
- return vsdb_info->freesync_supported;
-}
-
-static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
- bool ret;
-
- mutex_lock(&adev->dm.dc_lock);
- if (adev->dm.dmub_srv)
- ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info);
- else
- ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info);
- mutex_unlock(&adev->dm.dc_lock);
- return ret;
-}
-
-static void parse_edid_displayid_vrr(struct drm_connector *connector,
- const struct edid *edid)
-{
- u8 *edid_ext = NULL;
- int i;
- int j = 0;
- u16 min_vfreq;
- u16 max_vfreq;
-
- if (!edid || !edid->extensions)
- return;
-
- /* Find DisplayID extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (void *)(edid + (i + 1));
- if (edid_ext[0] == DISPLAYID_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return;
-
- while (j < EDID_LENGTH) {
- /* Get dynamic video timing range from DisplayID if available */
- if (EDID_LENGTH - j > 13 && edid_ext[j] == 0x25 &&
- (edid_ext[j+1] & 0xFE) == 0 && (edid_ext[j+2] == 9)) {
- min_vfreq = edid_ext[j+9];
- if (edid_ext[j+1] & 7)
- max_vfreq = edid_ext[j+10] + ((edid_ext[j+11] & 3) << 8);
- else
- max_vfreq = edid_ext[j+10];
-
- if (max_vfreq && min_vfreq) {
- connector->display_info.monitor_range.max_vfreq = max_vfreq;
- connector->display_info.monitor_range.min_vfreq = min_vfreq;
-
- return;
- }
- }
- j++;
- }
-}
-
-static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- struct drm_connector *connector = &aconnector->base;
-
- vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode;
- vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version;
-
- return connector->display_info.amd_vsdb.version != 0;
-}
-
-static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- const struct edid *edid,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
-{
- u8 *edid_ext = NULL;
- int i;
- bool valid_vsdb_found = false;
-
- /*----- drm_find_cea_extension() -----*/
- /* No EDID or EDID extensions */
- if (edid == NULL || edid->extensions == 0)
- return -ENODEV;
-
- /* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == CEA_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return -ENODEV;
-
- /*----- cea_db_offsets() -----*/
- if (edid_ext[0] != CEA_EXT)
- return -ENODEV;
-
- valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info);
-
- return valid_vsdb_found ? i : -ENODEV;
-}
-
-/**
- * amdgpu_dm_update_freesync_caps - Update Freesync capabilities
- *
- * @connector: Connector to query.
- * @drm_edid: DRM EDID from monitor
- * @do_mccs: Controls whether MCCS (Monitor Control Command Set) over
- * DDC (Display Data Channel) transactions are performed. When true,
- * the driver queries the monitor to get or update additional FreeSync
- * capability information. When false, these transactions are skipped.
- *
- * Amdgpu supports Freesync in DP and HDMI displays, and it is required to keep
- * track of some of the display information in the internal data struct used by
- * amdgpu_dm. This function checks which type of connector we need to set the
- * FreeSync parameters.
- */
-void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
- const struct drm_edid *drm_edid, bool do_mccs)
-{
- int i = 0;
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- struct dm_connector_state *dm_con_state = NULL;
- struct dc_sink *sink;
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
- const struct edid *edid;
- bool freesync_capable = false;
- enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE;
-
- if (!connector->state) {
- drm_err(adev_to_drm(adev), "%s - Connector has no state", __func__);
- goto update;
- }
-
- sink = amdgpu_dm_connector->dc_sink ?
- amdgpu_dm_connector->dc_sink :
- amdgpu_dm_connector->dc_em_sink;
-
- drm_edid_connector_update(connector, drm_edid);
-
- if (!drm_edid || !sink) {
- dm_con_state = to_dm_connector_state(connector->state);
-
- amdgpu_dm_connector->min_vfreq = 0;
- amdgpu_dm_connector->max_vfreq = 0;
- freesync_capable = false;
-
- goto update;
- }
-
- dm_con_state = to_dm_connector_state(connector->state);
-
- if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version))
- goto update;
-
- edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
-
- /* Some eDP panels only have the refresh rate range info in DisplayID */
- if ((connector->display_info.monitor_range.min_vfreq == 0 ||
- connector->display_info.monitor_range.max_vfreq == 0))
- parse_edid_displayid_vrr(connector, edid);
-
- if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
- sink->sink_signal == SIGNAL_TYPE_EDP)) {
- if (amdgpu_dm_connector->dc_link &&
- amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
- amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
- amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
- }
-
- get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
-
- if (vsdb_info.replay_mode) {
- amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode;
- amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
- amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP;
- }
-
- } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported) {
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
- }
- }
-
- if (amdgpu_dm_connector->dc_link)
- as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link);
-
- if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
- amdgpu_dm_connector->pack_sdp_v1_3 = true;
- amdgpu_dm_connector->as_type = as_type;
-
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
- }
- }
-
- /* Handle MCCS */
- if (do_mccs) {
- dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
-
- if (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported)
- freesync_capable = false;
-
- if (sink->mccs_caps.freesync_supported && freesync_capable)
- dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
- }
-
-update:
- if (dm_con_state)
- dm_con_state->freesync_capable = freesync_capable;
-
- if (connector->state && amdgpu_dm_connector->dc_link && !freesync_capable &&
- amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported) {
- amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported = false;
- amdgpu_dm_connector->dc_link->replay_settings.replay_feature_enabled = false;
- }
-
- if (connector->vrr_capable_property)
- drm_connector_set_vrr_capable_property(connector,
- freesync_capable);
-}
-
void amdgpu_dm_trigger_timing_sync(struct drm_device *dev)
{
struct amdgpu_device *adev = drm_to_adev(dev);
@@ -13948,12 +7318,6 @@ void amdgpu_dm_trigger_timing_sync(struct drm_device *dev)
mutex_unlock(&adev->dm.dc_lock);
}
-static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc)
-{
- if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter)
- dc_exit_ips_for_hw_access(dc);
-}
-
void dm_write_reg_func(const struct dc_context *ctx, uint32_t address,
u32 value, const char *func_name)
{
@@ -13982,13 +7346,6 @@ uint32_t dm_read_reg_func(const struct dc_context *ctx, uint32_t address,
}
#endif
- if (ctx->dmub_srv &&
- ctx->dmub_srv->reg_helper_offload.gather_in_progress &&
- !ctx->dmub_srv->reg_helper_offload.should_burst_write) {
- ASSERT(false);
- return 0;
- }
-
amdgpu_dm_exit_ips_for_hw_access(ctx->dc);
value = cgs_read_register(ctx->cgs_device, address);
@@ -13998,179 +7355,6 @@ uint32_t dm_read_reg_func(const struct dc_context *ctx, uint32_t address,
return value;
}
-int amdgpu_dm_process_dmub_aux_transfer_sync(
- struct dc_context *ctx,
- unsigned int link_index,
- struct aux_payload *payload,
- enum aux_return_code_type *operation_result)
-{
- struct amdgpu_device *adev = ctx->driver_context;
- struct dmub_notification *p_notify = adev->dm.dmub_notify;
- int ret = -1;
-
- mutex_lock(&adev->dm.dpia_aux_lock);
- if (!dc_process_dmub_aux_transfer_async(ctx->dc, link_index, payload)) {
- *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE;
- goto out;
- }
-
- if (!wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
- drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
- *operation_result = AUX_RET_ERROR_TIMEOUT;
- goto out;
- }
-
- if (p_notify->result != AUX_RET_SUCCESS) {
- /*
- * Transient states before tunneling is enabled could
- * lead to this error. We can ignore this for now.
- */
- if (p_notify->result == AUX_RET_ERROR_PROTOCOL_ERROR) {
- drm_warn(adev_to_drm(adev), "DPIA AUX failed on 0x%x(%d), error %d\n",
- payload->address, payload->length,
- p_notify->result);
- }
- *operation_result = p_notify->result;
- goto out;
- }
-
- payload->reply[0] = adev->dm.dmub_notify->aux_reply.command & 0xF;
- if (adev->dm.dmub_notify->aux_reply.command & 0xF0)
- /* The reply is stored in the top nibble of the command. */
- payload->reply[0] = (adev->dm.dmub_notify->aux_reply.command >> 4) & 0xF;
-
- /*write req may receive a byte indicating partially written number as well*/
- if (p_notify->aux_reply.length)
- memcpy(payload->data, p_notify->aux_reply.data,
- p_notify->aux_reply.length);
-
- /* success */
- ret = p_notify->aux_reply.length;
- *operation_result = p_notify->result;
-out:
- reinit_completion(&adev->dm.dmub_aux_transfer_done);
- mutex_unlock(&adev->dm.dpia_aux_lock);
- return ret;
-}
-
-static void abort_fused_io(
- struct dc_context *ctx,
- const struct dmub_cmd_fused_request *request
-)
-{
- union dmub_rb_cmd command = { 0 };
- struct dmub_rb_cmd_fused_io *io = &command.fused_io;
-
- io->header.type = DMUB_CMD__FUSED_IO;
- io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT;
- io->header.payload_bytes = sizeof(*io) - sizeof(io->header);
- io->request = *request;
- dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT);
-}
-
-static bool execute_fused_io(
- struct amdgpu_device *dev,
- struct dc_context *ctx,
- union dmub_rb_cmd *commands,
- uint8_t count,
- uint32_t timeout_us
-)
-{
- const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line;
-
- if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io))
- return false;
-
- struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line];
- struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io;
- const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)
- && first->header.ret_status
- && first->request.status == FUSED_REQUEST_STATUS_SUCCESS;
-
- if (!result)
- return false;
-
- while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) {
- reinit_completion(&sync->replied);
-
- struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data;
-
- static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch");
-
- if (reply->identifier == first->request.identifier) {
- first->request = *reply;
- return true;
- }
- }
-
- reinit_completion(&sync->replied);
- first->request.status = FUSED_REQUEST_STATUS_TIMEOUT;
- abort_fused_io(ctx, &first->request);
- return false;
-}
-
-bool amdgpu_dm_execute_fused_io(
- struct amdgpu_device *dev,
- struct dc_link *link,
- union dmub_rb_cmd *commands,
- uint8_t count,
- uint32_t timeout_us)
-{
- struct amdgpu_display_manager *dm = &dev->dm;
-
- mutex_lock(&dm->dpia_aux_lock);
-
- const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us);
-
- mutex_unlock(&dm->dpia_aux_lock);
- return result;
-}
-
-int amdgpu_dm_process_dmub_set_config_sync(
- struct dc_context *ctx,
- unsigned int link_index,
- struct set_config_cmd_payload *payload,
- enum set_config_status *operation_result)
-{
- struct amdgpu_device *adev = ctx->driver_context;
- bool is_cmd_complete;
- int ret;
-
- mutex_lock(&adev->dm.dpia_aux_lock);
- is_cmd_complete = dc_process_dmub_set_config_async(ctx->dc,
- link_index, payload, adev->dm.dmub_notify);
-
- if (is_cmd_complete || wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
- ret = 0;
- *operation_result = adev->dm.dmub_notify->sc_status;
- } else {
- drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
- ret = -1;
- *operation_result = SET_CONFIG_UNKNOWN_ERROR;
- }
-
- if (!is_cmd_complete)
- reinit_completion(&adev->dm.dmub_aux_transfer_done);
- mutex_unlock(&adev->dm.dpia_aux_lock);
- return ret;
-}
-
-bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
-{
- struct amdgpu_device *adev = ctx->driver_context;
-
- guard(spinlock_irqsave)(&adev->dm.dmub_lock);
- return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type);
-}
-
-bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
-{
- struct amdgpu_device *adev = ctx->driver_context;
-
- guard(spinlock_irqsave)(&adev->dm.dmub_lock);
- return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type);
-}
-
void dm_acpi_process_phy_transition_interlock(
const struct dc_context *ctx,
struct dm_process_phy_transition_init_params process_phy_transition_init_params)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index dd199e0b7922..91affbdb2d6c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -877,6 +877,9 @@ struct amdgpu_dm_connector {
unsigned int hdmi_hpd_debounce_delay_ms;
struct delayed_work hdmi_hpd_debounce_work;
struct dc_sink *hdmi_prev_sink;
+
+ /* HDMI compliance automation */
+ bool hdmi_comp_auto;
};
static inline void amdgpu_dm_set_mst_status(uint8_t *status,
@@ -1061,35 +1064,7 @@ struct dm_connector_state {
#define to_dm_connector_state(x)\
container_of((x), struct dm_connector_state, base)
-void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector);
-struct drm_connector_state *
-amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector);
-int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
- struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t val);
-
-int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
- const struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t *val);
-
-int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev);
-
-void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector,
- int connector_type,
- struct dc_link *link,
- int link_index);
-
-enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
- const struct drm_display_mode *mode);
-
-void dm_restore_drm_connector_state(struct drm_device *dev,
- struct drm_connector *connector);
-
-void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
- const struct drm_edid *drm_edid, bool do_mccs);
+#include "amdgpu_dm_connector.h"
void amdgpu_dm_trigger_timing_sync(struct drm_device *dev);
@@ -1113,14 +1088,9 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
struct drm_plane_state *plane_state,
struct dc_plane_state *dc_plane_state);
-void amdgpu_dm_update_connector_after_detect(
- struct amdgpu_dm_connector *aconnector);
-
void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *info,
struct dc_edid_caps *edid_caps);
-extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs;
-
int amdgpu_dm_process_dmub_aux_transfer_sync(struct dc_context *ctx, unsigned int link_index,
struct aux_payload *payload, enum aux_return_code_type *operation_result);
@@ -1135,20 +1105,9 @@ bool amdgpu_dm_execute_fused_io(
int amdgpu_dm_process_dmub_set_config_sync(struct dc_context *ctx, unsigned int link_index,
struct set_config_cmd_payload *payload, enum set_config_status *operation_result);
-struct dc_stream_state *
- create_validate_stream_for_sink(struct drm_connector *connector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream);
-
int dm_atomic_get_state(struct drm_atomic_commit *state,
struct dm_atomic_state **dm_state);
-struct drm_connector *
-amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
- struct drm_crtc *crtc);
-
-int convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth);
struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev);
void *dm_allocate_gpu_mem(struct amdgpu_device *adev,
@@ -1161,11 +1120,33 @@ void dm_free_gpu_mem(struct amdgpu_device *adev,
bool amdgpu_dm_is_headless(struct amdgpu_device *adev);
-void hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector);
-void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector);
-int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector);
+bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc);
void retrieve_dmi_info(struct amdgpu_display_manager *dm);
-void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm, int bl_idx);
+void amdgpu_dm_emulated_link_detect(struct dc_link *link);
+void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
+ struct dc_sink *sink);
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+int dm_plane_layer_index_cmp(const void *a, const void *b);
+int fill_plane_color_attributes(const struct drm_plane_state *plane_state,
+ const enum surface_pixel_format format,
+ enum dc_color_space *color_space);
+bool modereset_required(struct drm_crtc_state *crtc_state);
+void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
+ int *src_w, int *src_h);
+void dm_get_plane_scale(struct drm_plane_state *plane_state,
+ int *out_plane_scale_w, int *out_plane_scale_h);
+bool is_scaling_state_different(const struct dm_connector_state *dm_state,
+ const struct dm_connector_state *old_dm_state);
+bool is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
+ struct drm_crtc_state *new_crtc_state);
+void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state);
+bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
+ struct dm_crtc_state *new_state);
+void set_multisync_trigger_params(struct dc_stream_state *stream);
+void set_master_stream(struct dc_stream_state *stream_set[], int stream_count);
+#endif
+
#endif /* __AMDGPU_DM_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c
new file mode 100644
index 000000000000..13c9a9d145ba
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ */
+
+#include "amdgpu.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_audio.h"
+#include "amdgpu_dm_kunit_helpers.h"
+#include "dc.h"
+
+#include <linux/component.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_audio_component.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
+
+#include "dc/inc/core_types.h"
+
+static int amdgpu_dm_audio_component_get_eld(struct device *kdev, int port,
+ int pipe, bool *enabled,
+ unsigned char *buf, int max_bytes)
+{
+ struct drm_device *dev = dev_get_drvdata(kdev);
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ struct amdgpu_dm_connector *aconnector;
+ int ret = 0;
+
+ *enabled = false;
+
+ mutex_lock(&adev->dm.audio_lock);
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ continue;
+
+ aconnector = to_amdgpu_dm_connector(connector);
+ if (aconnector->audio_inst != port)
+ continue;
+
+ *enabled = true;
+ mutex_lock(&connector->eld_mutex);
+ ret = drm_eld_size(connector->eld);
+ memcpy(buf, connector->eld, min(max_bytes, ret));
+ mutex_unlock(&connector->eld_mutex);
+
+ break;
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ mutex_unlock(&adev->dm.audio_lock);
+
+ drm_dbg_kms(adev_to_drm(adev), "Get ELD : idx=%d ret=%d en=%d\n", port, ret, *enabled);
+
+ return ret;
+}
+
+static const struct drm_audio_component_ops amdgpu_dm_audio_component_ops = {
+ .get_eld = amdgpu_dm_audio_component_get_eld,
+};
+
+STATIC_IFN_KUNIT int amdgpu_dm_audio_component_bind(struct device *kdev,
+ struct device *hda_kdev, void *data)
+{
+ struct drm_device *dev = dev_get_drvdata(kdev);
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct drm_audio_component *acomp = data;
+
+ acomp->ops = &amdgpu_dm_audio_component_ops;
+ acomp->dev = kdev;
+ adev->dm.audio_component = acomp;
+
+ return 0;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_component_bind);
+
+STATIC_IFN_KUNIT void amdgpu_dm_audio_component_unbind(struct device *kdev,
+ struct device *hda_kdev, void *data)
+{
+ struct amdgpu_device *adev = drm_to_adev(dev_get_drvdata(kdev));
+ struct drm_audio_component *acomp = data;
+
+ acomp->ops = NULL;
+ acomp->dev = NULL;
+ adev->dm.audio_component = NULL;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_component_unbind);
+
+static const struct component_ops amdgpu_dm_audio_component_bind_ops = {
+ .bind = amdgpu_dm_audio_component_bind,
+ .unbind = amdgpu_dm_audio_component_unbind,
+};
+
+int amdgpu_dm_audio_init(struct amdgpu_device *adev)
+{
+ int i, ret;
+
+ if (!amdgpu_audio)
+ return 0;
+
+ adev->mode_info.audio.enabled = true;
+
+ adev->mode_info.audio.num_pins = adev->dm.dc->res_pool->audio_count;
+
+ for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
+ adev->mode_info.audio.pin[i].channels = -1;
+ adev->mode_info.audio.pin[i].rate = -1;
+ adev->mode_info.audio.pin[i].bits_per_sample = -1;
+ adev->mode_info.audio.pin[i].status_bits = 0;
+ adev->mode_info.audio.pin[i].category_code = 0;
+ adev->mode_info.audio.pin[i].connected = false;
+ adev->mode_info.audio.pin[i].id =
+ adev->dm.dc->res_pool->audios[i]->inst;
+ adev->mode_info.audio.pin[i].offset = 0;
+ }
+
+ ret = component_add(adev->dev, &amdgpu_dm_audio_component_bind_ops);
+ if (ret < 0)
+ return ret;
+
+ adev->dm.audio_registered = true;
+
+ return 0;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_init);
+
+void amdgpu_dm_audio_fini(struct amdgpu_device *adev)
+{
+ if (!amdgpu_audio)
+ return;
+
+ if (!adev->mode_info.audio.enabled)
+ return;
+
+ if (adev->dm.audio_registered) {
+ component_del(adev->dev, &amdgpu_dm_audio_component_bind_ops);
+ adev->dm.audio_registered = false;
+ }
+
+ /* TODO: Disable audio? */
+
+ adev->mode_info.audio.enabled = false;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_fini);
+
+STATIC_IFN_KUNIT void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin)
+{
+ struct drm_audio_component *acomp = adev->dm.audio_component;
+
+ if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) {
+ drm_dbg_kms(adev_to_drm(adev), "Notify ELD: %d\n", pin);
+
+ acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
+ pin, -1);
+ }
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_eld_notify);
+
+void amdgpu_dm_fill_audio_info(struct audio_info *audio_info,
+ const struct drm_connector *drm_connector,
+ const struct dc_sink *dc_sink)
+{
+ int i = 0;
+ int cea_revision = 0;
+ const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps;
+
+ audio_info->manufacture_id = edid_caps->manufacturer_id;
+ audio_info->product_id = edid_caps->product_id;
+
+ cea_revision = drm_connector->display_info.cea_rev;
+
+ strscpy(audio_info->display_name,
+ edid_caps->display_name,
+ AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
+
+ if (cea_revision >= 3) {
+ audio_info->mode_count = edid_caps->audio_mode_count;
+
+ for (i = 0; i < audio_info->mode_count; ++i) {
+ audio_info->modes[i].format_code =
+ (enum audio_format_code)
+ (edid_caps->audio_modes[i].format_code);
+ audio_info->modes[i].channel_count =
+ edid_caps->audio_modes[i].channel_count;
+ audio_info->modes[i].sample_rates.all =
+ edid_caps->audio_modes[i].sample_rate;
+ audio_info->modes[i].sample_size =
+ edid_caps->audio_modes[i].sample_size;
+ }
+ }
+
+ audio_info->flags.all = edid_caps->speaker_flags;
+
+ /* TODO: We only check for the progressive mode, check for interlace mode too */
+ if (drm_connector->latency_present[0]) {
+ audio_info->video_latency = drm_connector->video_latency[0];
+ audio_info->audio_latency = drm_connector->audio_latency[0];
+ }
+
+ /* TODO: For DP, video and audio latency should be calculated from DPCD caps */
+
+}
+EXPORT_IF_KUNIT(amdgpu_dm_fill_audio_info);
+
+void amdgpu_dm_commit_audio(struct drm_device *dev,
+ struct drm_atomic_commit *state)
+{
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_connector *connector;
+ struct drm_connector_state *old_con_state, *new_con_state;
+ struct drm_crtc_state *new_crtc_state;
+ struct dm_crtc_state *new_dm_crtc_state;
+ const struct dc_stream_status *status;
+ int i, inst;
+
+ /* Notify device removals. */
+ for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
+ if (old_con_state->crtc != new_con_state->crtc) {
+ /* CRTC changes require notification. */
+ goto notify;
+ }
+
+ if (!new_con_state->crtc)
+ continue;
+
+ new_crtc_state = drm_atomic_get_new_crtc_state(
+ state, new_con_state->crtc);
+
+ if (!new_crtc_state)
+ continue;
+
+ if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
+ continue;
+
+notify:
+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ continue;
+
+ aconnector = to_amdgpu_dm_connector(connector);
+
+ mutex_lock(&adev->dm.audio_lock);
+ inst = aconnector->audio_inst;
+ aconnector->audio_inst = -1;
+ mutex_unlock(&adev->dm.audio_lock);
+
+ amdgpu_dm_audio_eld_notify(adev, inst);
+ }
+
+ /* Notify audio device additions. */
+ for_each_new_connector_in_state(state, connector, new_con_state, i) {
+ if (!new_con_state->crtc)
+ continue;
+
+ new_crtc_state = drm_atomic_get_new_crtc_state(
+ state, new_con_state->crtc);
+
+ if (!new_crtc_state)
+ continue;
+
+ if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
+ continue;
+
+ new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
+ if (!new_dm_crtc_state->stream)
+ continue;
+
+ status = dc_stream_get_status(new_dm_crtc_state->stream);
+ if (!status)
+ continue;
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ continue;
+
+ aconnector = to_amdgpu_dm_connector(connector);
+
+ mutex_lock(&adev->dm.audio_lock);
+ inst = status->audio_inst;
+ aconnector->audio_inst = inst;
+ mutex_unlock(&adev->dm.audio_lock);
+
+ amdgpu_dm_audio_eld_notify(adev, inst);
+ }
+}
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+int amdgpu_dm_audio_get_param(void)
+{
+ return amdgpu_audio;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_get_param);
+
+void amdgpu_dm_audio_set_param(int val)
+{
+ amdgpu_audio = val;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_audio_set_param);
+#endif
diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h
index bf0c5901b4ee..7acfc5ef69b3 100644
--- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h
@@ -1,4 +1,6 @@
-/* Copyright 2018 Advanced Micro Devices, Inc.
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -19,21 +21,36 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
- *
*/
-#include "power_helpers.h"
-#include "dc/inc/hw/dmcu.h"
-#include "dc/inc/hw/abm.h"
-#include "dc.h"
-#include "core_types.h"
-#include "dmub_cmd.h"
+#ifndef __AMDGPU_DM_AUDIO_H__
+#define __AMDGPU_DM_AUDIO_H__
+
+struct amdgpu_device;
+struct drm_device;
+struct drm_atomic_state;
+struct drm_connector;
+struct audio_info;
+struct dc_sink;
+
+int amdgpu_dm_audio_init(struct amdgpu_device *adev);
+void amdgpu_dm_audio_fini(struct amdgpu_device *adev);
+void amdgpu_dm_commit_audio(struct drm_device *dev,
+ struct drm_atomic_commit *state);
+void amdgpu_dm_fill_audio_info(struct audio_info *audio_info,
+ const struct drm_connector *drm_connector,
+ const struct dc_sink *dc_sink);
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+struct device;
-#define DIV_ROUNDUP(a, b) (((a)+((b)/2))/(b))
-#define bswap16_based_on_endian(big_endian, value) \
- ((big_endian) ? cpu_to_be16(value) : cpu_to_le16(value))
+int amdgpu_dm_audio_component_bind(struct device *kdev,
+ struct device *hda_kdev, void *data);
+void amdgpu_dm_audio_component_unbind(struct device *kdev,
+ struct device *hda_kdev, void *data);
+void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin);
+int amdgpu_dm_audio_get_param(void);
+void amdgpu_dm_audio_set_param(int val);
+#endif
-bool mod_power_only_edp(const struct dc_state *context, const struct dc_stream_state *stream)
-{
- return context && context->stream_count == 1 && dc_is_embedded_signal(stream->signal);
-}
+#endif /* __AMDGPU_DM_AUDIO_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
new file mode 100644
index 000000000000..33f4be403a65
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
@@ -0,0 +1,726 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ */
+
+#include "dc.h"
+#include "dc/dc_dmub_srv.h"
+#include "dc/dc_state.h"
+#include "dc/dc_stat.h"
+
+#include "amdgpu.h"
+#include "amdgpu_display.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_backlight.h"
+#include "amdgpu_dm_psr.h"
+#include "amdgpu_dm_replay.h"
+#include "amdgpu_atombios.h"
+
+#include "modules/inc/mod_power.h"
+
+#include <linux/backlight.h>
+#include <linux/power_supply.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_utils.h>
+
+#include <acpi/video.h>
+
+#include "amdgpu_dm_trace.h"
+#include "amd_shared.h"
+#include "amdgpu_dm_kunit_helpers.h"
+
+void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
+ int bl_idx)
+{
+ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx];
+
+ if (caps->caps_valid)
+ return;
+
+#if defined(CONFIG_ACPI)
+ amdgpu_acpi_get_backlight_caps(caps);
+
+ /* validate the firmware value is sane */
+ if (caps->caps_valid) {
+ int spread = caps->max_input_signal - caps->min_input_signal;
+
+ if (caps->max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
+ caps->min_input_signal < 0 ||
+ spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
+ spread < AMDGPU_DM_MIN_SPREAD) {
+ drm_dbg_kms(adev_to_drm(dm->adev), "DM: Invalid backlight caps: min=%d, max=%d\n",
+ caps->min_input_signal, caps->max_input_signal);
+ caps->caps_valid = false;
+ }
+ }
+#else
+ if (caps->aux_support)
+ return;
+#endif
+ if (!caps->caps_valid) {
+ caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps->ac_level = caps->dc_level = 50;
+ caps->caps_valid = true;
+ }
+}
+EXPORT_IF_KUNIT(amdgpu_dm_update_backlight_caps);
+
+STATIC_IFN_KUNIT
+int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
+ unsigned int *min, unsigned int *max)
+{
+ if (!caps)
+ return 0;
+
+ if (caps->aux_support) {
+ /* Firmware limits are in nits, DC API wants millinits. */
+ *max = 1000 * caps->aux_max_input_signal;
+ *min = 1000 * caps->aux_min_input_signal;
+ } else {
+ /* Firmware limits are 8-bit, PWM control is 16-bit. */
+ *max = 0x101 * caps->max_input_signal;
+ *min = 0x101 * caps->min_input_signal;
+ }
+ return 1;
+}
+EXPORT_IF_KUNIT(get_brightness_range);
+
+/* Rescale from [min..max] to [0..AMDGPU_MAX_BL_LEVEL] */
+static inline u32 scale_input_to_fw(int min, int max, u64 input)
+{
+ return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max - min);
+}
+
+/* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */
+static inline u32 scale_fw_to_input(int min, int max, u64 input)
+{
+ return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);
+}
+
+STATIC_IFN_KUNIT
+void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
+ unsigned int min, unsigned int max,
+ uint32_t *user_brightness)
+{
+ u32 brightness = scale_input_to_fw(min, max, *user_brightness);
+ u8 lower_signal, upper_signal, upper_lum, lower_lum, lum;
+ int left, right;
+
+ if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
+ return;
+
+ if (!caps->data_points)
+ return;
+
+ /*
+ * Handle the case where brightness is below the first data point
+ * Interpolate between (0,0) and (first_signal, first_lum)
+ */
+ if (brightness < caps->luminance_data[0].input_signal) {
+ lum = DIV_ROUND_CLOSEST(caps->luminance_data[0].luminance * brightness,
+ caps->luminance_data[0].input_signal);
+ goto scale;
+ }
+
+ left = 0;
+ right = caps->data_points - 1;
+ while (left <= right) {
+ int mid = left + (right - left) / 2;
+ u8 signal = caps->luminance_data[mid].input_signal;
+
+ /* Exact match found */
+ if (signal == brightness) {
+ lum = caps->luminance_data[mid].luminance;
+ goto scale;
+ }
+
+ if (signal < brightness)
+ left = mid + 1;
+ else
+ right = mid - 1;
+ }
+
+ /* verify bound */
+ if (left >= caps->data_points)
+ left = caps->data_points - 1;
+
+ /* At this point, left > right */
+ lower_signal = caps->luminance_data[right].input_signal;
+ upper_signal = caps->luminance_data[left].input_signal;
+ lower_lum = caps->luminance_data[right].luminance;
+ upper_lum = caps->luminance_data[left].luminance;
+
+ /* interpolate */
+ if (right == left || !lower_lum)
+ lum = upper_lum;
+ else
+ lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) *
+ (brightness - lower_signal),
+ upper_signal - lower_signal);
+scale:
+ *user_brightness = scale_fw_to_input(min, max,
+ DIV_ROUND_CLOSEST(lum * brightness, 101));
+}
+
+EXPORT_IF_KUNIT(convert_custom_brightness);
+
+STATIC_IFN_KUNIT
+u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps,
+ uint32_t brightness)
+{
+ unsigned int min, max;
+
+ if (!get_brightness_range(caps, &min, &max))
+ return brightness;
+
+ convert_custom_brightness(caps, min, max, &brightness);
+
+ /* Rescale 0..max to min..max */
+ return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max);
+}
+
+EXPORT_IF_KUNIT(convert_brightness_from_user);
+
+STATIC_IFN_KUNIT
+u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
+ uint32_t brightness)
+{
+ unsigned int min, max;
+
+ if (!get_brightness_range(caps, &min, &max))
+ return brightness;
+
+ if (brightness < min)
+ return 0;
+ /* Rescale min..max to 0..max */
+ return DIV_ROUND_CLOSEST_ULL((u64)max * (brightness - min),
+ max - min);
+}
+EXPORT_IF_KUNIT(convert_brightness_to_user);
+
+static struct dc_stream_state *dm_find_stream_with_link(
+ struct amdgpu_display_manager *dm,
+ struct dc_link *link)
+{
+ struct dc_state *cur_dc_state = dm->dc->current_state;
+ struct dc_stream_state *stream = NULL;
+ int i;
+
+ for (i = 0; i < cur_dc_state->stream_count; i++) {
+ stream = cur_dc_state->streams[i];
+ if (stream->link == link)
+ return stream;
+ }
+
+ return NULL;
+}
+
+STATIC_IFN_KUNIT
+int amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager *dm,
+ struct backlight_device *bd)
+{
+ int i;
+
+ for (i = 0; i < dm->num_of_edps; i++) {
+ if (bd == dm->backlight_dev[i])
+ return i;
+ }
+
+ return 0;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_device_index);
+
+void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
+ int bl_idx,
+ u32 user_brightness)
+{
+ struct amdgpu_dm_backlight_caps *caps;
+ struct dc_link *link;
+ u32 brightness = 0;
+ bool rc = false, reallow_idle = false;
+ struct drm_connector *connector;
+ struct dc_stream_state *stream;
+ unsigned int min, max;
+
+ list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) {
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+
+ if (aconnector->bl_idx != bl_idx)
+ continue;
+
+ /* if connector is off, save the brightness for next time it's on */
+ if (!aconnector->base.encoder) {
+ dm->brightness[bl_idx] = user_brightness;
+ dm->actual_brightness[bl_idx] = 0;
+ return;
+ }
+ }
+
+ amdgpu_dm_update_backlight_caps(dm, bl_idx);
+ caps = &dm->backlight_caps[bl_idx];
+
+ dm->brightness[bl_idx] = user_brightness;
+ /* update scratch register */
+ if (bl_idx == 0)
+ amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]);
+ brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
+ link = (struct dc_link *)dm->backlight_link[bl_idx];
+
+ /* Apply brightness quirk */
+ if (caps->brightness_mask)
+ brightness |= caps->brightness_mask;
+
+ if (trace_amdgpu_dm_brightness_enabled()) {
+ trace_amdgpu_dm_brightness(__builtin_return_address(0),
+ user_brightness,
+ brightness,
+ caps->aux_support,
+ power_supply_is_system_supplied() > 0);
+ }
+
+ stream = dm_find_stream_with_link(dm, link);
+ if (!stream)
+ return;
+
+ mutex_lock(&dm->dc_lock);
+ if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) {
+ dc_allow_idle_optimizations(dm->dc, false);
+ reallow_idle = true;
+ }
+
+ if (caps->aux_support) {
+ rc = mod_power_set_backlight_nits(dm->power_module, stream, brightness,
+ AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true);
+ } else {
+ /* power module uses millipercent */
+ get_brightness_range(caps, &min, &max);
+ brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000;
+ rc = mod_power_set_backlight_percent(dm->power_module, stream,
+ brightness, 0, false);
+ }
+
+ /*
+ * Some kms clients create a ramped backlight transition effect
+ * by rapidly changing the backlight. Yet we must wait on dmcub
+ * fw to exit psr/replay before programming backlight. To
+ * prevent lag, keep disable psr/replay and let the next atomic
+ * flip clear the event.
+ *
+ * ToDo: use ISM to handle rapidly backlight change
+ *
+ * Rapidly backlight change is similar to rapidly cursor events,
+ * which is now handled by ISM. ISM can delay the event until system
+ * is really idle, so we may use ISM to handle backlight change as well.
+ */
+ amdgpu_dm_psr_set_event(dm, stream, true,
+ psr_event_hw_programming, true);
+ amdgpu_dm_replay_set_event(dm, stream, true,
+ replay_event_hw_programming, true);
+
+ if (dm->dc->caps.ips_support && reallow_idle)
+ dc_allow_idle_optimizations(dm->dc, true);
+
+ mutex_unlock(&dm->dc_lock);
+
+ if (rc)
+ dm->actual_brightness[bl_idx] = user_brightness;
+}
+
+static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
+{
+ struct amdgpu_display_manager *dm = bl_get_data(bd);
+ int i = amdgpu_dm_backlight_get_device_index(dm, bd);
+
+ amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness);
+
+ return 0;
+}
+
+static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm,
+ int bl_idx)
+{
+ int ret;
+ struct amdgpu_dm_backlight_caps caps;
+ struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx];
+
+ amdgpu_dm_update_backlight_caps(dm, bl_idx);
+ caps = dm->backlight_caps[bl_idx];
+
+ if (caps.aux_support) {
+ u32 avg, peak;
+
+ if (!dc_link_get_backlight_level_nits(link, &avg, &peak))
+ return dm->brightness[bl_idx];
+ return convert_brightness_to_user(&caps, avg);
+ }
+
+ ret = dc_link_get_backlight_level(link);
+
+ if (ret == DC_ERROR_UNEXPECTED)
+ return dm->brightness[bl_idx];
+
+ return convert_brightness_to_user(&caps, ret);
+}
+
+static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
+{
+ struct amdgpu_display_manager *dm = bl_get_data(bd);
+ int i = amdgpu_dm_backlight_get_device_index(dm, bd);
+
+ return amdgpu_dm_backlight_get_level(dm, i);
+}
+
+static const struct backlight_ops amdgpu_dm_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .get_brightness = amdgpu_dm_backlight_get_brightness,
+ .update_status = amdgpu_dm_backlight_update_status,
+};
+
+STATIC_IFN_KUNIT
+void amdgpu_dm_backlight_fill_props(const struct amdgpu_dm_backlight_caps *caps,
+ bool is_system_supplied,
+ bool custom_curve_enabled,
+ struct backlight_properties *props)
+{
+ unsigned int min, max;
+
+ if (get_brightness_range(caps, &min, &max)) {
+ if (is_system_supplied)
+ props->brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level,
+ 100);
+ else
+ props->brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level,
+ 100);
+ props->max_brightness = max - min;
+ } else {
+ props->brightness = MAX_BACKLIGHT_LEVEL;
+ props->max_brightness = MAX_BACKLIGHT_LEVEL;
+ }
+
+ if (caps && caps->data_points && custom_curve_enabled)
+ props->scale = BACKLIGHT_SCALE_NON_LINEAR;
+ else
+ props->scale = BACKLIGHT_SCALE_LINEAR;
+ props->type = BACKLIGHT_RAW;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_backlight_fill_props);
+
+void
+amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
+{
+ struct drm_device *drm = aconnector->base.dev;
+ struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
+ struct backlight_properties props = { 0 };
+ struct amdgpu_dm_backlight_caps *caps;
+ char bl_name[16];
+ int real_brightness;
+ int init_brightness;
+
+ if (aconnector->bl_idx == -1)
+ return;
+
+ if (!acpi_video_backlight_use_native()) {
+ drm_info(drm, "Skipping amdgpu DM backlight registration\n");
+ /* Try registering an ACPI video backlight device instead. */
+ acpi_video_register_backlight();
+ return;
+ }
+
+ caps = &dm->backlight_caps[aconnector->bl_idx];
+ amdgpu_dm_backlight_fill_props(caps, power_supply_is_system_supplied() > 0,
+ !(amdgpu_dc_debug_mask &
+ DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE),
+ &props);
+ drm_dbg(drm, "Backlight caps: max_brightness: %d, ac %d, dc %d\n",
+ props.max_brightness, caps->ac_level, caps->dc_level);
+
+ init_brightness = props.brightness;
+
+ if (props.scale == BACKLIGHT_SCALE_NON_LINEAR)
+ drm_info(drm, "Using custom brightness curve\n");
+
+ snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d",
+ drm->primary->index + aconnector->bl_idx);
+
+ dm->backlight_dev[aconnector->bl_idx] =
+ backlight_device_register(bl_name, aconnector->base.kdev, dm,
+ &amdgpu_dm_backlight_ops, &props);
+ dm->brightness[aconnector->bl_idx] = props.brightness;
+
+ if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) {
+ drm_err(drm, "DM: Backlight registration failed!\n");
+ dm->backlight_dev[aconnector->bl_idx] = NULL;
+ } else {
+ /*
+ * dm->brightness[x] can be inconsistent just after startup until
+ * ops.get_brightness is called.
+ */
+ real_brightness =
+ amdgpu_dm_backlight_ops.get_brightness(dm->backlight_dev[aconnector->bl_idx]);
+
+ if (real_brightness != init_brightness) {
+ dm->actual_brightness[aconnector->bl_idx] = real_brightness;
+ dm->brightness[aconnector->bl_idx] = real_brightness;
+ }
+ drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name);
+ }
+}
+
+void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
+{
+ const struct drm_panel_backlight_quirk *panel_backlight_quirk;
+ struct amdgpu_dm_backlight_caps *caps;
+ struct drm_connector *conn_base;
+ struct amdgpu_device *adev;
+ struct drm_luminance_range_info *luminance_range;
+ struct drm_device *drm;
+
+ if (aconnector->bl_idx == -1 ||
+ aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP)
+ return;
+
+ conn_base = &aconnector->base;
+ drm = conn_base->dev;
+ adev = drm_to_adev(drm);
+
+ caps = &adev->dm.backlight_caps[aconnector->bl_idx];
+ caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
+ caps->aux_support = false;
+
+ if (caps->ext_caps->bits.oled == 1
+ /*
+ * ||
+ * caps->ext_caps->bits.sdr_aux_backlight_control == 1 ||
+ * caps->ext_caps->bits.hdr_aux_backlight_control == 1
+ */)
+ caps->aux_support = true;
+
+ if (amdgpu_backlight == 0)
+ caps->aux_support = false;
+ else if (amdgpu_backlight == 1)
+ caps->aux_support = true;
+ if (caps->aux_support)
+ aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
+
+ luminance_range = &conn_base->display_info.luminance_range;
+
+ if (luminance_range->max_luminance)
+ caps->aux_max_input_signal = luminance_range->max_luminance;
+ else
+ caps->aux_max_input_signal = 512;
+
+ if (luminance_range->min_luminance)
+ caps->aux_min_input_signal = luminance_range->min_luminance;
+ else
+ caps->aux_min_input_signal = 1;
+
+ panel_backlight_quirk =
+ drm_get_panel_backlight_quirk(aconnector->drm_edid);
+ if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
+ if (panel_backlight_quirk->min_brightness) {
+ caps->min_input_signal =
+ panel_backlight_quirk->min_brightness - 1;
+ drm_info(drm,
+ "Applying panel backlight quirk, min_brightness: %d\n",
+ caps->min_input_signal);
+ }
+ if (panel_backlight_quirk->brightness_mask) {
+ drm_info(drm,
+ "Applying panel backlight quirk, brightness_mask: 0x%X\n",
+ panel_backlight_quirk->brightness_mask);
+ caps->brightness_mask =
+ panel_backlight_quirk->brightness_mask;
+ }
+ }
+}
+EXPORT_IF_KUNIT(amdgpu_dm_update_connector_ext_caps);
+
+void amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager *dm,
+ struct amdgpu_dm_connector *aconnector)
+{
+ struct amdgpu_dm_backlight_caps *caps;
+ struct dc_link *link = aconnector->dc_link;
+ int bl_idx = dm->num_of_edps;
+
+ if (!(link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) ||
+ link->type == dc_connection_none)
+ return;
+
+ if (dm->num_of_edps >= AMDGPU_DM_MAX_NUM_EDP) {
+ drm_warn(adev_to_drm(dm->adev), "Too much eDP connections, skipping backlight setup for additional eDPs\n");
+ return;
+ }
+
+ aconnector->bl_idx = bl_idx;
+
+ amdgpu_dm_update_backlight_caps(dm, bl_idx);
+ dm->backlight_link[bl_idx] = link;
+ dm->num_of_edps++;
+
+ amdgpu_dm_update_connector_ext_caps(aconnector);
+ caps = &dm->backlight_caps[aconnector->bl_idx];
+
+ /* Only offer ABM property when non-OLED and user didn't turn off by module parameter */
+ if (caps->ext_caps && !caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
+ drm_object_attach_property(&aconnector->base.base,
+ dm->adev->mode_info.abm_level_property,
+ ABM_SYSFS_CONTROL);
+}
+EXPORT_IF_KUNIT(amdgpu_dm_setup_backlight_device);
+
+/**
+ * DOC: panel power savings
+ *
+ * The display manager allows you to set your desired **panel power savings**
+ * level (between 0-4, with 0 representing off), e.g. using the following::
+ *
+ * # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings
+ *
+ * Modifying this value can have implications on color accuracy, so tread
+ * carefully.
+ */
+
+static ssize_t panel_power_savings_show(struct device *device,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_connector *connector = dev_get_drvdata(device);
+ struct drm_device *dev = connector->dev;
+ u8 val;
+
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+ val = to_dm_connector_state(connector->state)->abm_level ==
+ ABM_LEVEL_IMMEDIATE_DISABLE ? 0 :
+ to_dm_connector_state(connector->state)->abm_level;
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+ return sysfs_emit(buf, "%u\n", val);
+}
+
+static ssize_t panel_power_savings_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct drm_connector *connector = dev_get_drvdata(device);
+ struct drm_device *dev = connector->dev;
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 0, &val);
+
+ if (ret)
+ return ret;
+
+ if (val < 0 || val > 4)
+ return -EINVAL;
+
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+ if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden)
+ ret = -EBUSY;
+ else
+ to_dm_connector_state(connector->state)->abm_level = val ?:
+ ABM_LEVEL_IMMEDIATE_DISABLE;
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+ if (ret)
+ return ret;
+
+ drm_kms_helper_hotplug_event(dev);
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(panel_power_savings);
+
+static struct attribute *amdgpu_attrs[] = {
+ &dev_attr_panel_power_savings.attr,
+ NULL
+};
+
+const struct attribute_group amdgpu_group = {
+ .name = "amdgpu",
+ .attrs = amdgpu_attrs
+};
+
+bool
+amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector)
+{
+ if (amdgpu_dm_abm_level >= 0)
+ return false;
+
+ if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
+ return false;
+
+ /* check for OLED panels */
+ if (amdgpu_dm_connector->bl_idx >= 0) {
+ struct drm_device *drm = amdgpu_dm_connector->base.dev;
+ struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
+ struct amdgpu_dm_backlight_caps *caps;
+
+ caps = &dm->backlight_caps[amdgpu_dm_connector->bl_idx];
+ if (caps->aux_support)
+ return false;
+ }
+
+ return true;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_should_create_sysfs);
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+uint amdgpu_dm_get_dc_debug_mask(void)
+{
+ return amdgpu_dc_debug_mask;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_get_dc_debug_mask);
+
+void amdgpu_dm_set_dc_debug_mask(uint val)
+{
+ amdgpu_dc_debug_mask = val;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_set_dc_debug_mask);
+
+int amdgpu_dm_get_abm_level_param(void)
+{
+ return amdgpu_dm_abm_level;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_get_abm_level_param);
+
+void amdgpu_dm_set_abm_level_param(int val)
+{
+ amdgpu_dm_abm_level = val;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_set_abm_level_param);
+
+int amdgpu_dm_get_backlight_param(void)
+{
+ return amdgpu_backlight;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_get_backlight_param);
+
+void amdgpu_dm_set_backlight_param(int val)
+{
+ amdgpu_backlight = val;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_set_backlight_param);
+#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
new file mode 100644
index 000000000000..98d612c60ae9
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __AMDGPU_DM_BACKLIGHT_H__
+#define __AMDGPU_DM_BACKLIGHT_H__
+
+struct amdgpu_display_manager;
+struct amdgpu_dm_connector;
+struct backlight_device;
+struct backlight_properties;
+struct drm_connector;
+struct attribute_group;
+
+#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
+#define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
+#define AMDGPU_DM_MIN_SPREAD ((AMDGPU_DM_DEFAULT_MAX_BACKLIGHT - AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) / 2)
+#define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
+
+void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
+ int bl_idx);
+void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
+ int bl_idx, u32 user_brightness);
+void amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector);
+void amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager *dm,
+ struct amdgpu_dm_connector *aconnector);
+void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector);
+bool amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *aconnector);
+
+extern const struct attribute_group amdgpu_group;
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
+ unsigned int *min, unsigned int *max);
+void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
+ unsigned int min, unsigned int max,
+ uint32_t *user_brightness);
+u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps,
+ uint32_t brightness);
+u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
+ uint32_t brightness);
+int amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager *dm,
+ struct backlight_device *bd);
+void amdgpu_dm_backlight_fill_props(const struct amdgpu_dm_backlight_caps *caps,
+ bool is_system_supplied,
+ bool custom_curve_enabled,
+ struct backlight_properties *props);
+uint amdgpu_dm_get_dc_debug_mask(void);
+void amdgpu_dm_set_dc_debug_mask(uint val);
+int amdgpu_dm_get_abm_level_param(void);
+void amdgpu_dm_set_abm_level_param(int val);
+int amdgpu_dm_get_backlight_param(void);
+void amdgpu_dm_set_backlight_param(int val);
+#endif
+
+#endif /* __AMDGPU_DM_BACKLIGHT_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index 86086d10c543..357c7c5c85cf 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -1051,26 +1051,28 @@ EXPORT_IF_KUNIT(__drm_3dlut32_to_dc_3dlut);
/* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream
* @drm_lut3d: user 3D LUT
* @drm_lut3d_size: size of 3D LUT
- * @lut3d: DC 3D LUT
+ * @cm: DC Color Manager (includes 3D LUT)
*
* Map user 3D LUT data to DC 3D LUT and all necessary bits to program it
* on DCN accordingly.
*/
STATIC_IFN_KUNIT void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d,
uint32_t drm_lut3d_size,
- struct dc_3dlut *lut)
+ struct dc_plane_cm *cm)
{
if (!drm_lut3d_size) {
- lut->state.bits.initialized = 0;
+ cm->lut3d_func.state.bits.initialized = 0;
+ cm->flags.bits.lut3d_enable = 0;
} else {
/* Stride and bit depth are not programmable by API yet.
* Therefore, only supports 17x17x17 3D LUT (12-bit).
*/
- lut->lut_3d.use_tetrahedral_9 = false;
- lut->lut_3d.use_12bits = true;
- lut->state.bits.initialized = 1;
- __drm_3dlut_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &lut->lut_3d,
- lut->lut_3d.use_tetrahedral_9,
+ cm->lut3d_func.lut_3d.use_tetrahedral_9 = false;
+ cm->lut3d_func.lut_3d.use_12bits = true;
+ cm->lut3d_func.state.bits.initialized = 1;
+ cm->flags.bits.lut3d_enable = 1;
+ __drm_3dlut_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &cm->lut3d_func.lut_3d,
+ cm->lut3d_func.lut_3d.use_tetrahedral_9,
MAX_COLOR_3DLUT_BITDEPTH);
}
}
@@ -1080,7 +1082,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *sha
bool has_rom,
enum dc_transfer_func_predefined tf,
uint32_t shaper_size,
- struct dc_transfer_func *func_shaper)
+ struct dc_plane_cm *cm)
{
int ret = 0;
@@ -1089,10 +1091,13 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *sha
* If user shaper LUT is set, we assume a linear color space
* (linearized by degamma 1D LUT or not).
*/
- __set_tf_distributed_points(func_shaper, tf);
- ret = __set_output_tf(func_shaper, shaper_lut, shaper_size, has_rom);
+ __set_tf_distributed_points(&cm->shaper_func, tf);
+ cm->flags.bits.shaper_enable = 1;
+
+ ret = __set_output_tf(&cm->shaper_func, shaper_lut, shaper_size, has_rom);
} else {
- __set_tf_bypass(func_shaper);
+ __set_tf_bypass(&cm->shaper_func);
+ cm->flags.bits.shaper_enable = 0;
}
return ret;
@@ -1103,7 +1108,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blen
bool has_rom,
enum dc_transfer_func_predefined tf,
uint32_t blend_size,
- struct dc_transfer_func *func_blend)
+ struct dc_plane_cm *cm)
{
int ret = 0;
@@ -1115,10 +1120,13 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blen
* module to fill the parameters that will be translated to HW
* points.
*/
- __set_tf_distributed_points(func_blend, tf);
- ret = __set_input_tf(NULL, func_blend, blend_lut, blend_size);
+ __set_tf_distributed_points(&cm->blend_func, tf);
+ cm->flags.bits.blend_enable = 1;
+
+ ret = __set_input_tf(NULL, &cm->blend_func, blend_lut, blend_size);
} else {
- __set_tf_bypass(func_blend);
+ __set_tf_bypass(&cm->blend_func);
+ cm->flags.bits.blend_enable = 0;
}
return ret;
@@ -1461,7 +1469,7 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state,
const struct drm_color_lut *degamma_lut;
enum amdgpu_transfer_function tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
uint32_t degamma_size;
- bool has_degamma_lut;
+ bool has_degamma_lut, is_subsampled_format;
int ret;
degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut,
@@ -1491,12 +1499,20 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state,
if (ret)
return ret;
} else {
- dc_plane_state->in_transfer_func.type =
- TF_TYPE_PREDEFINED;
+ /* Check if format requires post-scale color processing (subsampled formats) */
+ is_subsampled_format = (dc_plane_state->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN &&
+ dc_plane_state->format < SURFACE_PIXEL_FORMAT_SUBSAMPLE_END);
+
+ dc_plane_state->in_transfer_func.type = TF_TYPE_PREDEFINED;
if (!mod_color_calculate_degamma_params(color_caps,
- &dc_plane_state->in_transfer_func, NULL, false))
+ &dc_plane_state->in_transfer_func,
+ NULL,
+ is_subsampled_format)) {
+ drm_err(plane_state->state->dev,
+ "Failed to calculate degamma params.\n");
return -ENOMEM;
+ }
}
return 0;
}
@@ -1632,63 +1648,64 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
struct drm_colorop *colorop)
{
struct drm_colorop *old_colorop;
- struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct drm_colorop_state *new_colorop_state;
+ struct drm_colorop_state *tf_state = NULL, *lut_state = NULL;
struct drm_atomic_commit *state = plane_state->state;
+ struct drm_colorop *lut_colorop;
enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR;
- struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func;
+ struct dc_transfer_func *tf = &dc_plane_state->cm.shaper_func;
const struct drm_color_lut32 *shaper_lut;
struct drm_device *dev = colorop->dev;
bool enabled = false;
u32 shaper_size;
int i = 0, ret = 0;
- /* 1D Curve - SHAPER TF */
+ /* 1D Curve - SHAPER TF: find state */
old_colorop = colorop;
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == old_colorop &&
(BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) {
- colorop_state = new_colorop_state;
+ tf_state = new_colorop_state;
break;
}
}
- if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE) {
- drm_dbg(dev, "Shaper TF colorop with ID: %d\n", colorop->base.id);
- tf->type = TF_TYPE_DISTRIBUTED_POINTS;
- tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
- tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
- ret = __set_output_tf(tf, 0, 0, false);
- if (ret)
- return ret;
- enabled = true;
- }
-
- /* 1D LUT - SHAPER LUT */
- colorop = old_colorop->next;
- if (!colorop) {
+ /* 1D LUT - SHAPER LUT: find state */
+ lut_colorop = old_colorop->next;
+ if (!lut_colorop) {
drm_dbg(dev, "no Shaper LUT colorop found\n");
return -EINVAL;
}
- old_colorop = colorop;
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
- if (new_colorop_state->colorop == old_colorop &&
+ if (new_colorop_state->colorop == lut_colorop &&
new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) {
- colorop_state = new_colorop_state;
+ lut_state = new_colorop_state;
break;
}
}
- if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_LUT) {
- drm_dbg(dev, "Shaper LUT colorop with ID: %d\n", colorop->base.id);
+ if (tf_state && !tf_state->bypass) {
+ drm_dbg(dev, "Shaper TF colorop with ID: %d\n", old_colorop->base.id);
+ tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+ tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(tf_state->curve_1d_type);
+ tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+ ret = __set_output_tf(tf, 0, 0, false);
+ if (ret)
+ return ret;
+ enabled = true;
+ }
+
+ if (lut_state && !lut_state->bypass) {
+ drm_dbg(dev, "Shaper LUT colorop with ID: %d\n", lut_colorop->base.id);
tf->type = TF_TYPE_DISTRIBUTED_POINTS;
tf->tf = default_tf;
tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
- shaper_lut = __extract_blob_lut32(colorop_state->data, &shaper_size);
+ shaper_lut = __extract_blob_lut32(lut_state->data, &shaper_size);
shaper_size = shaper_lut != NULL ? shaper_size : 0;
/* Custom LUT size must be the same as supported size */
- if (shaper_size == colorop->size) {
+ if (shaper_size == lut_colorop->size) {
ret = __set_output_tf_32(tf, shaper_lut, shaper_size, false);
if (ret)
return ret;
@@ -1696,8 +1713,12 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
}
}
- if (!enabled)
+ if (!enabled) {
tf->type = TF_TYPE_BYPASS;
+ dc_plane_state->cm.flags.bits.shaper_enable = 0;
+ } else {
+ dc_plane_state->cm.flags.bits.shaper_enable = 1;
+ }
return 0;
}
@@ -1741,7 +1762,7 @@ __set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state,
{
struct drm_colorop *old_colorop;
struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
- struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func;
+ struct dc_transfer_func *tf = &dc_plane_state->cm.shaper_func;
struct drm_atomic_commit *state = plane_state->state;
const struct amdgpu_device *adev = drm_to_adev(colorop->dev);
bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut || adev->dm.dc->caps.color.mpc.preblend;
@@ -1769,13 +1790,15 @@ __set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state,
drm_dbg(dev, "3D LUT colorop with ID: %d\n", colorop->base.id);
lut3d = __extract_blob_lut32(colorop_state->data, &lut3d_size);
lut3d_size = lut3d != NULL ? lut3d_size : 0;
- ret = __set_colorop_3dlut(lut3d, lut3d_size, &dc_plane_state->lut3d_func);
+ ret = __set_colorop_3dlut(lut3d, lut3d_size, &dc_plane_state->cm.lut3d_func);
if (ret) {
drm_dbg(dev, "3D LUT colorop with ID: %d has LUT size = %d\n",
colorop->base.id, lut3d_size);
return ret;
}
+ dc_plane_state->cm.flags.bits.lut3d_enable = 1;
+
/* 3D LUT requires shaper. If shaper colorop is bypassed, enable shaper curve
* with TRANSFER_FUNCTION_LINEAR
*/
@@ -1785,6 +1808,8 @@ __set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state,
tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
ret = __set_output_tf_32(tf, NULL, 0, false);
}
+ } else {
+ dc_plane_state->cm.flags.bits.lut3d_enable = 0;
}
return ret;
@@ -1796,61 +1821,64 @@ __set_dm_plane_colorop_blend(struct drm_plane_state *plane_state,
struct drm_colorop *colorop)
{
struct drm_colorop *old_colorop;
- struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct drm_colorop_state *new_colorop_state;
+ struct drm_colorop_state *tf_state = NULL, *lut_state = NULL;
struct drm_atomic_commit *state = plane_state->state;
+ struct drm_colorop *lut_colorop;
enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR;
- struct dc_transfer_func *tf = &dc_plane_state->blend_tf;
+ struct dc_transfer_func *tf = &dc_plane_state->cm.blend_func;
const struct drm_color_lut32 *blend_lut = NULL;
struct drm_device *dev = colorop->dev;
uint32_t blend_size = 0;
int i = 0;
- /* 1D Curve - BLND TF */
+ dc_plane_state->cm.flags.bits.blend_enable = 0;
+
+ /* 1D Curve - BLND TF: find state */
old_colorop = colorop;
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == old_colorop &&
(BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) {
- colorop_state = new_colorop_state;
+ tf_state = new_colorop_state;
break;
}
}
- if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE &&
- (BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) {
- drm_dbg(dev, "Blend TF colorop with ID: %d\n", colorop->base.id);
- tf->type = TF_TYPE_DISTRIBUTED_POINTS;
- tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
- tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
- __set_input_tf_32(NULL, tf, blend_lut, blend_size);
- }
-
- /* 1D Curve - BLND LUT */
- colorop = old_colorop->next;
- if (!colorop) {
+ /* 1D LUT - BLND LUT: find state */
+ lut_colorop = old_colorop->next;
+ if (!lut_colorop) {
drm_dbg(dev, "no Blend LUT colorop found\n");
return -EINVAL;
}
- old_colorop = colorop;
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
- if (new_colorop_state->colorop == old_colorop &&
+ if (new_colorop_state->colorop == lut_colorop &&
new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) {
- colorop_state = new_colorop_state;
+ lut_state = new_colorop_state;
break;
}
}
- if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_LUT &&
- (BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) {
- drm_dbg(dev, "Blend LUT colorop with ID: %d\n", colorop->base.id);
+ if (tf_state && !tf_state->bypass) {
+ drm_dbg(dev, "Blend TF colorop with ID: %d\n", old_colorop->base.id);
+ tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+ tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(tf_state->curve_1d_type);
+ tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+ dc_plane_state->cm.flags.bits.blend_enable = 1;
+ __set_input_tf_32(NULL, tf, blend_lut, blend_size);
+ }
+
+ if (lut_state && !lut_state->bypass) {
+ drm_dbg(dev, "Blend LUT colorop with ID: %d\n", lut_colorop->base.id);
tf->type = TF_TYPE_DISTRIBUTED_POINTS;
tf->tf = default_tf;
tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
- blend_lut = __extract_blob_lut32(colorop_state->data, &blend_size);
+ dc_plane_state->cm.flags.bits.blend_enable = 1;
+ blend_lut = __extract_blob_lut32(lut_state->data, &blend_size);
blend_size = blend_lut != NULL ? blend_size : 0;
/* Custom LUT size must be the same as supported size */
- if (blend_size == colorop->size)
+ if (blend_size == lut_colorop->size)
__set_input_tf_32(NULL, tf, blend_lut, blend_size);
}
@@ -1876,11 +1904,11 @@ amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state,
lut3d = __extract_blob_lut(dm_plane_state->lut3d, &lut3d_size);
lut3d_size = lut3d != NULL ? lut3d_size : 0;
- amdgpu_dm_atomic_lut3d(lut3d, lut3d_size, &dc_plane_state->lut3d_func);
+ amdgpu_dm_atomic_lut3d(lut3d, lut3d_size, &dc_plane_state->cm);
ret = amdgpu_dm_atomic_shaper_lut(shaper_lut, false,
amdgpu_tf_to_dc_tf(shaper_tf),
shaper_size,
- &dc_plane_state->in_shaper_func);
+ &dc_plane_state->cm);
if (ret) {
drm_dbg_kms(plane_state->plane->dev,
"setting plane %d shaper LUT failed.\n",
@@ -1895,7 +1923,8 @@ amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state,
ret = amdgpu_dm_atomic_blend_lut(blend_lut, false,
amdgpu_tf_to_dc_tf(blend_tf),
- blend_size, &dc_plane_state->blend_tf);
+ blend_size, &dc_plane_state->cm);
+
if (ret) {
drm_dbg_kms(plane_state->plane->dev,
"setting plane %d gamma lut failed.\n",
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h
index e4f53b7bc753..8dbbcb3ab156 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h
@@ -87,10 +87,10 @@ void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut,
struct tetrahedral_params *params,
bool use_tetrahedral_9,
int bit_depth);
-struct dc_3dlut;
+struct dc_plane_cm;
void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d,
uint32_t drm_lut3d_size,
- struct dc_3dlut *lut);
+ struct dc_plane_cm *cm);
int __set_colorop_3dlut(const struct drm_color_lut32 *drm_lut3d,
uint32_t drm_lut3d_size,
struct dc_3dlut *lut);
@@ -105,12 +105,12 @@ int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut,
bool has_rom,
enum dc_transfer_func_predefined tf,
uint32_t shaper_size,
- struct dc_transfer_func *func_shaper);
+ struct dc_plane_cm *cm);
int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blend_lut,
bool has_rom,
enum dc_transfer_func_predefined tf,
uint32_t blend_size,
- struct dc_transfer_func *func_blend);
+ struct dc_plane_cm *cm);
int __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state,
struct drm_colorop_state *colorop_state);
#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
index 48f5c431eaf9..056a76b88f43 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -235,3 +235,4 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr
return amdgpu_dm_build_default_pipeline(dev, plane, hw_3d_lut, list);
}
+EXPORT_IF_KUNIT(amdgpu_dm_initialize_default_pipeline);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
new file mode 100644
index 000000000000..40688d35bde6
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -0,0 +1,3601 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dm_services_types.h"
+#include "dc.h"
+#include "dc/dc_dmub_srv.h"
+#include "dc/dc_edid_parser.h"
+#include "dc/dc_stat.h"
+#include "dc/dc_state.h"
+#include "dc/dc_stream.h"
+#include "dc/inc/core_types.h"
+#include "link_enc_cfg.h"
+#include "link/protocols/link_dpcd.h"
+#include "link_service_types.h"
+#include "link/protocols/link_dp_capability.h"
+#include "link/protocols/link_ddc.h"
+
+#include "amdgpu.h"
+#include "amdgpu_display.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_connector.h"
+#include "amdgpu_dm_kunit_helpers.h"
+#include "amdgpu_dm_plane.h"
+#include "amdgpu_dm_crtc.h"
+#include "amdgpu_dm_wb.h"
+#include "amdgpu_dm_mst_types.h"
+#if defined(CONFIG_DEBUG_FS)
+#include "amdgpu_dm_debugfs.h"
+#endif
+#include "amdgpu_dm_backlight.h"
+#include "amdgpu_dm_audio.h"
+#include "amdgpu_dm_irq.h"
+#include "amdgpu_dm_psr.h"
+#include "dm_helpers.h"
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
+#include <drm/drm_fixed.h>
+#include <drm/drm_mode.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_utils.h>
+#include <drm/display/drm_dp_mst_helper.h>
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/drm_privacy_screen_consumer.h>
+#include <drm/display/drm_hdcp_helper.h>
+
+#include <linux/backlight.h>
+
+#include <media/cec-notifier.h>
+
+#include "modules/inc/mod_freesync.h"
+#include "modules/inc/mod_power.h"
+
+#include "amdgpu_dm_trace.h"
+
+/* Encoder functions */
+
+static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+ kfree(encoder);
+}
+
+static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = {
+ .destroy = amdgpu_dm_encoder_destroy,
+};
+
+static void dm_encoder_helper_disable(struct drm_encoder *encoder)
+{
+}
+
+static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_atomic_commit *state = crtc_state->state;
+ struct drm_connector *connector = conn_state->connector;
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+ struct dm_connector_state *dm_new_connector_state = to_dm_connector_state(conn_state);
+ const struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+ struct drm_dp_mst_topology_mgr *mst_mgr;
+ struct drm_dp_mst_port *mst_port;
+ struct drm_dp_mst_topology_state *mst_state;
+ enum dc_color_depth color_depth;
+ int clock, bpp = 0;
+ bool is_y420 = false;
+
+ if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) ||
+ (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) {
+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
+ enum drm_mode_status result;
+
+ result = drm_crtc_helper_mode_valid_fixed(encoder->crtc, adjusted_mode, native_mode);
+ if (result != MODE_OK && dm_new_connector_state->scaling == RMX_OFF) {
+ drm_dbg_driver(encoder->dev,
+ "mode %dx%d@%dHz is not native, enabling scaling\n",
+ adjusted_mode->hdisplay, adjusted_mode->vdisplay,
+ drm_mode_vrefresh(adjusted_mode));
+ dm_new_connector_state->scaling = RMX_ASPECT;
+ }
+ return 0;
+ }
+
+ if (!aconnector->mst_output_port)
+ return 0;
+
+ mst_port = aconnector->mst_output_port;
+ mst_mgr = &aconnector->mst_root->mst_mgr;
+
+ if (!crtc_state->connectors_changed && !crtc_state->mode_changed)
+ return 0;
+
+ mst_state = drm_atomic_get_mst_topology_state(state, mst_mgr);
+ if (IS_ERR(mst_state))
+ return PTR_ERR(mst_state);
+
+ mst_state->pbn_div.full = dm_mst_get_pbn_divider(aconnector->mst_root->dc_link);
+
+ if (!state->duplicated) {
+ int max_bpc = conn_state->max_requested_bpc;
+
+ is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) &&
+ aconnector->force_yuv420_output;
+ color_depth = amdgpu_dm_convert_color_depth_from_display_info(connector,
+ is_y420,
+ max_bpc);
+ bpp = amdgpu_dm_convert_dc_color_depth_into_bpc(color_depth) * 3;
+ clock = adjusted_mode->clock;
+ dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp << 4);
+ }
+
+ dm_new_connector_state->vcpi_slots =
+ drm_dp_atomic_find_time_slots(state, mst_mgr, mst_port,
+ dm_new_connector_state->pbn);
+ if (dm_new_connector_state->vcpi_slots < 0) {
+ drm_dbg_atomic(connector->dev, "failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots);
+ return dm_new_connector_state->vcpi_slots;
+ }
+ return 0;
+}
+
+const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = {
+ .disable = dm_encoder_helper_disable,
+ .atomic_check = dm_encoder_helper_atomic_check
+};
+
+int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev)
+{
+ switch (adev->mode_info.num_crtc) {
+ case 1:
+ return 0x1;
+ case 2:
+ return 0x3;
+ case 3:
+ return 0x7;
+ case 4:
+ return 0xf;
+ case 5:
+ return 0x1f;
+ case 6:
+ default:
+ return 0x3f;
+ }
+}
+EXPORT_IF_KUNIT(amdgpu_dm_get_encoder_crtc_mask);
+
+int amdgpu_dm_encoder_init(struct drm_device *dev,
+ struct amdgpu_encoder *aencoder,
+ uint32_t link_index)
+{
+ struct amdgpu_device *adev = drm_to_adev(dev);
+
+ int res = drm_encoder_init(dev,
+ &aencoder->base,
+ &amdgpu_dm_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS,
+ NULL);
+
+ aencoder->base.possible_crtcs = amdgpu_dm_get_encoder_crtc_mask(adev);
+
+ if (!res)
+ aencoder->encoder_id = link_index;
+ else
+ aencoder->encoder_id = -1;
+
+ drm_encoder_helper_add(&aencoder->base, &amdgpu_dm_encoder_helper_funcs);
+
+ return res;
+}
+
+STATIC_IFN_KUNIT enum drm_mode_subconnector get_subconnector_type(struct dc_link *link)
+{
+ switch (link->dpcd_caps.dongle_type) {
+ case DISPLAY_DONGLE_NONE:
+ return DRM_MODE_SUBCONNECTOR_Native;
+ case DISPLAY_DONGLE_DP_VGA_CONVERTER:
+ return DRM_MODE_SUBCONNECTOR_VGA;
+ case DISPLAY_DONGLE_DP_DVI_CONVERTER:
+ case DISPLAY_DONGLE_DP_DVI_DONGLE:
+ return DRM_MODE_SUBCONNECTOR_DVID;
+ case DISPLAY_DONGLE_DP_HDMI_CONVERTER:
+ case DISPLAY_DONGLE_DP_HDMI_DONGLE:
+ return DRM_MODE_SUBCONNECTOR_HDMIA;
+ case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE:
+ default:
+ return DRM_MODE_SUBCONNECTOR_Unknown;
+ }
+}
+EXPORT_IF_KUNIT(get_subconnector_type);
+
+static void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
+{
+ struct dc_link *link = aconnector->dc_link;
+ struct drm_connector *connector = &aconnector->base;
+ enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+
+ if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
+ return;
+
+ if (aconnector->dc_sink)
+ subconnector = get_subconnector_type(link);
+
+ drm_object_property_set_value(&connector->base,
+ connector->dev->mode_config.dp_subconnector_property,
+ subconnector);
+}
+
+static int amdgpu_dm_connector_get_modes(struct drm_connector *connector);
+
+static void amdgpu_dm_fbc_init(struct drm_connector *connector)
+{
+ struct amdgpu_device *adev = drm_to_adev(connector->dev);
+ struct dm_compressor_info *compressor = &adev->dm.compressor;
+ struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(connector);
+ struct drm_display_mode *mode;
+ unsigned long max_size = 0;
+
+ if (adev->dm.dc->fbc_compressor == NULL)
+ return;
+
+ if (aconn->dc_link->connector_signal != SIGNAL_TYPE_EDP)
+ return;
+
+ if (compressor->bo_ptr)
+ return;
+
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (max_size < (unsigned long) mode->htotal * mode->vtotal)
+ max_size = (unsigned long) mode->htotal * mode->vtotal;
+ }
+
+ if (max_size) {
+ int r = amdgpu_bo_create_kernel(adev, max_size * 4, PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_GTT, &compressor->bo_ptr,
+ &compressor->gpu_addr, &compressor->cpu_addr);
+
+ if (r)
+ drm_err(adev_to_drm(adev), "DM: Failed to initialize FBC\n");
+ else {
+ adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr;
+ drm_info(adev_to_drm(adev), "DM: FBC alloc %lu\n", max_size*4);
+ }
+
+ }
+
+}
+
+
+int amdgpu_dm_detect_mst_link_for_all_connectors(struct drm_device *dev)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter iter;
+ int ret = 0;
+
+ drm_connector_list_iter_begin(dev, &iter);
+ drm_for_each_connector_iter(connector, &iter) {
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ continue;
+
+ aconnector = to_amdgpu_dm_connector(connector);
+ if (aconnector->dc_link->type == dc_connection_mst_branch &&
+ aconnector->mst_mgr.aux) {
+ drm_dbg_kms(dev, "DM_MST: starting TM on aconnector: %p [id: %d]\n",
+ aconnector,
+ aconnector->base.base.id);
+
+ ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
+ if (ret < 0) {
+ drm_err(dev, "DM_MST: Failed to start MST\n");
+ aconnector->dc_link->type =
+ dc_connection_single;
+ ret = dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx,
+ aconnector->dc_link);
+ break;
+ }
+ }
+ }
+ drm_connector_list_iter_end(&iter);
+
+ return ret;
+}
+
+static void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector)
+{
+ struct cec_notifier *n = aconnector->notifier;
+
+ if (!n)
+ return;
+
+ cec_notifier_phys_addr_invalidate(n);
+}
+
+void amdgpu_dm_hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector)
+{
+ struct drm_connector *connector = &aconnector->base;
+ struct cec_notifier *n = aconnector->notifier;
+
+ if (!n)
+ return;
+
+ cec_notifier_set_phys_addr(n,
+ connector->display_info.source_physical_address);
+}
+
+void amdgpu_dm_s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(ddev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ continue;
+
+ aconnector = to_amdgpu_dm_connector(connector);
+ if (suspend)
+ hdmi_cec_unset_edid(aconnector);
+ else
+ amdgpu_dm_hdmi_cec_set_edid(aconnector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+
+struct drm_connector *
+amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
+ struct drm_crtc *crtc)
+{
+ u32 i;
+ struct drm_connector_state *new_con_state;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc_from_state;
+
+ for_each_new_connector_in_state(state, connector, new_con_state, i) {
+ crtc_from_state = new_con_state->crtc;
+
+ if (crtc_from_state == crtc)
+ return connector;
+ }
+
+ return NULL;
+}
+
+static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
+{
+ struct drm_connector *connector = &aconnector->base;
+ struct drm_display_info *display_info = &connector->display_info;
+ struct dc_link *link = aconnector->dc_link;
+ struct amdgpu_device *adev;
+
+ adev = drm_to_adev(connector->dev);
+
+ link->panel_type = PANEL_TYPE_NONE;
+
+ switch (display_info->amd_vsdb.panel_type) {
+ case AMD_VSDB_PANEL_TYPE_OLED:
+ link->panel_type = PANEL_TYPE_OLED;
+ break;
+ case AMD_VSDB_PANEL_TYPE_MINILED:
+ link->panel_type = PANEL_TYPE_MINILED;
+ break;
+ }
+
+ /* If VSDB didn't determine panel type, check DPCD ext caps */
+ if (link->panel_type == PANEL_TYPE_NONE) {
+ if (link->dpcd_sink_ext_caps.bits.miniled == 1)
+ link->panel_type = PANEL_TYPE_MINILED;
+ if (link->dpcd_sink_ext_caps.bits.oled == 1)
+ link->panel_type = PANEL_TYPE_OLED;
+ }
+
+ /* If VSDB and DPCD didn't determine panel type, check DID */
+ if (link->panel_type == PANEL_TYPE_NONE) {
+ if (display_info->panel_type == DRM_MODE_PANEL_TYPE_LCD)
+ link->panel_type = PANEL_TYPE_LCD;
+ else if (display_info->panel_type == DRM_MODE_PANEL_TYPE_OLED)
+ link->panel_type = PANEL_TYPE_OLED;
+ }
+
+ if (link->panel_type == PANEL_TYPE_NONE) {
+ struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb;
+ u32 lum1_max = vsdb->luminance_range1.max_luminance;
+ u32 lum2_max = vsdb->luminance_range2.max_luminance;
+
+ if (vsdb->version && link->local_sink &&
+ link->local_sink->edid_caps.manufacturer_id ==
+ DDC_MANUFACTURERNAME_SAMSUNG &&
+ lum1_max >= ((lum2_max * 3) / 2))
+ link->panel_type = PANEL_TYPE_MINILED;
+ }
+
+ if (link->panel_type == PANEL_TYPE_OLED)
+ drm_object_property_set_value(&connector->base,
+ adev_to_drm(adev)->mode_config.panel_type_property,
+ DRM_MODE_PANEL_TYPE_OLED);
+ else if (link->panel_type == PANEL_TYPE_LCD)
+ drm_object_property_set_value(&connector->base,
+ adev_to_drm(adev)->mode_config.panel_type_property,
+ DRM_MODE_PANEL_TYPE_LCD);
+ else
+ drm_object_property_set_value(&connector->base,
+ adev_to_drm(adev)->mode_config.panel_type_property,
+ DRM_MODE_PANEL_TYPE_UNKNOWN);
+
+ drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type);
+}
+
+DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T))
+
+void amdgpu_dm_update_connector_after_detect(
+ struct amdgpu_dm_connector *aconnector)
+{
+ struct drm_connector *connector = &aconnector->base;
+ struct dc_sink *sink __free(sink_release) = NULL;
+ struct drm_device *dev = connector->dev;
+
+ /* MST handled by drm_mst framework */
+ if (aconnector->mst_mgr.mst_state)
+ return;
+
+ sink = aconnector->dc_link->local_sink;
+ if (sink)
+ dc_sink_retain(sink);
+
+ /*
+ * Edid mgmt connector gets first update only in mode_valid hook and then
+ * the connector sink is set to either fake or physical sink depends on link status.
+ * Skip if already done during boot.
+ */
+ if (aconnector->base.force != DRM_FORCE_UNSPECIFIED
+ && aconnector->dc_em_sink) {
+
+ /*
+ * For S3 resume with headless use eml_sink to fake stream
+ * because on resume connector->sink is set to NULL
+ */
+ guard(mutex)(&dev->mode_config.mutex);
+
+ if (sink) {
+ if (aconnector->dc_sink) {
+ amdgpu_dm_update_freesync_caps(connector, NULL, true);
+ /*
+ * retain and release below are used to
+ * bump up refcount for sink because the link doesn't point
+ * to it anymore after disconnect, so on next crtc to connector
+ * reshuffle by UMD we will get into unwanted dc_sink release
+ */
+ dc_sink_release(aconnector->dc_sink);
+ }
+ aconnector->dc_sink = sink;
+ dc_sink_retain(aconnector->dc_sink);
+ amdgpu_dm_update_freesync_caps(connector,
+ aconnector->drm_edid, true);
+ } else {
+ amdgpu_dm_update_freesync_caps(connector, NULL, true);
+ if (!aconnector->dc_sink) {
+ aconnector->dc_sink = aconnector->dc_em_sink;
+ dc_sink_retain(aconnector->dc_sink);
+ }
+ }
+
+ return;
+ }
+
+ /*
+ * TODO: temporary guard to look for proper fix
+ * if this sink is MST sink, we should not do anything
+ */
+ if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
+ return;
+
+ if (aconnector->dc_sink == sink) {
+ /*
+ * We got a DP short pulse (Link Loss, DP CTS, etc...).
+ * Do nothing!!
+ */
+ drm_dbg_kms(dev, "DCHPD: connector_id=%d: dc_sink didn't change.\n",
+ aconnector->connector_id);
+ return;
+ }
+
+ drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n",
+ aconnector->connector_id, aconnector->dc_sink, sink);
+
+ /* When polling, DRM has already locked the mutex for us. */
+ if (!drm_kms_helper_is_poll_worker())
+ mutex_lock(&dev->mode_config.mutex);
+
+ /*
+ * 1. Update status of the drm connector
+ * 2. Send an event and let userspace tell us what to do
+ */
+ if (sink) {
+ /*
+ * TODO: check if we still need the S3 mode update workaround.
+ * If yes, put it here.
+ */
+ if (aconnector->dc_sink) {
+ amdgpu_dm_update_freesync_caps(connector, NULL, true);
+ dc_sink_release(aconnector->dc_sink);
+ }
+
+ aconnector->dc_sink = sink;
+ dc_sink_retain(aconnector->dc_sink);
+ drm_edid_free(aconnector->drm_edid);
+ aconnector->drm_edid = NULL;
+ if (sink->dc_edid.length == 0) {
+ hdmi_cec_unset_edid(aconnector);
+ if (aconnector->dc_link->aux_mode)
+ drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
+ } else {
+ const struct edid *edid = (const struct edid *)sink->dc_edid.raw_edid;
+
+ aconnector->drm_edid = drm_edid_alloc(edid, sink->dc_edid.length);
+ drm_edid_connector_update(connector, aconnector->drm_edid);
+
+ amdgpu_dm_hdmi_cec_set_edid(aconnector);
+ if (aconnector->dc_link->aux_mode)
+ drm_dp_cec_attach(&aconnector->dm_dp_aux.aux,
+ connector->display_info.source_physical_address);
+ }
+
+ if (!aconnector->timing_requested) {
+ aconnector->timing_requested =
+ kzalloc_obj(struct dc_crtc_timing);
+ if (!aconnector->timing_requested)
+ drm_err(dev,
+ "failed to create aconnector->requested_timing\n");
+ }
+
+ amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid, true);
+ amdgpu_dm_update_connector_ext_caps(aconnector);
+ dm_set_panel_type(aconnector);
+
+ if (aconnector->hdmi_comp_auto) {
+ if (sink->sink_signal != SIGNAL_TYPE_HDMI_FRL)
+ sink->sink_signal = SIGNAL_TYPE_HDMI_FRL;
+ }
+ } else {
+ hdmi_cec_unset_edid(aconnector);
+ drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
+ amdgpu_dm_update_freesync_caps(connector, NULL, true);
+ aconnector->num_modes = 0;
+ dc_sink_release(aconnector->dc_sink);
+ aconnector->dc_sink = NULL;
+ drm_edid_free(aconnector->drm_edid);
+ aconnector->drm_edid = NULL;
+ kfree(aconnector->timing_requested);
+ aconnector->timing_requested = NULL;
+ /* Set CP to DESIRED if it was ENABLED, so we can re-enable it again on hotplug */
+ if (connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+ connector->state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ }
+
+ update_subconnector_property(aconnector);
+
+ /* When polling, the mutex will be unlocked for us by DRM. */
+ if (!drm_kms_helper_is_poll_worker())
+ mutex_unlock(&dev->mode_config.mutex);
+}
+
+enum dc_color_depth
+amdgpu_dm_convert_color_depth_from_display_info(const struct drm_connector *connector,
+ bool is_y420, int requested_bpc)
+{
+ u8 bpc;
+
+ if (is_y420) {
+ bpc = 8;
+
+ /* Cap display bpc based on HDMI 2.0 HF-VSDB */
+ if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)
+ bpc = 16;
+ else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
+ bpc = 12;
+ else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
+ bpc = 10;
+ } else {
+ bpc = (uint8_t)connector->display_info.bpc;
+ /* Assume 8 bpc by default if no bpc is specified. */
+ bpc = bpc ? bpc : 8;
+ }
+
+ if (requested_bpc > 0) {
+ /*
+ * Cap display bpc based on the user requested value.
+ *
+ * The value for state->max_bpc may not correctly updated
+ * depending on when the connector gets added to the state
+ * or if this was called outside of atomic check, so it
+ * can't be used directly.
+ */
+ bpc = min_t(u8, bpc, requested_bpc);
+
+ /* Round down to the nearest even number. */
+ bpc = bpc - (bpc & 1);
+ }
+
+ switch (bpc) {
+ case 0:
+ /*
+ * Temporary Work around, DRM doesn't parse color depth for
+ * EDID revision before 1.4
+ * TODO: Fix edid parsing
+ */
+ return COLOR_DEPTH_888;
+ case 6:
+ return COLOR_DEPTH_666;
+ case 8:
+ return COLOR_DEPTH_888;
+ case 10:
+ return COLOR_DEPTH_101010;
+ case 12:
+ return COLOR_DEPTH_121212;
+ case 14:
+ return COLOR_DEPTH_141414;
+ case 16:
+ return COLOR_DEPTH_161616;
+ default:
+ return COLOR_DEPTH_UNDEFINED;
+ }
+}
+EXPORT_IF_KUNIT(amdgpu_dm_convert_color_depth_from_display_info);
+
+STATIC_IFN_KUNIT enum dc_aspect_ratio
+get_aspect_ratio(const struct drm_display_mode *mode_in)
+{
+ /* 1-1 mapping, since both enums follow the HDMI spec. */
+ return (enum dc_aspect_ratio) mode_in->picture_aspect_ratio;
+}
+EXPORT_IF_KUNIT(get_aspect_ratio);
+
+enum dc_color_space
+amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
+ const struct drm_connector_state *connector_state)
+{
+ enum dc_color_space color_space = COLOR_SPACE_SRGB;
+
+ switch (connector_state->colorspace) {
+ case DRM_MODE_COLORIMETRY_BT601_YCC:
+ if (dc_crtc_timing->flags.Y_ONLY)
+ color_space = COLOR_SPACE_YCBCR601_LIMITED;
+ else
+ color_space = COLOR_SPACE_YCBCR601;
+ break;
+ case DRM_MODE_COLORIMETRY_BT709_YCC:
+ if (dc_crtc_timing->flags.Y_ONLY)
+ color_space = COLOR_SPACE_YCBCR709_LIMITED;
+ else
+ color_space = COLOR_SPACE_YCBCR709;
+ break;
+ case DRM_MODE_COLORIMETRY_OPRGB:
+ color_space = COLOR_SPACE_ADOBERGB;
+ break;
+ case DRM_MODE_COLORIMETRY_BT2020_RGB:
+ case DRM_MODE_COLORIMETRY_BT2020_YCC:
+ if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB)
+ color_space = COLOR_SPACE_2020_RGB_FULLRANGE;
+ else
+ color_space = COLOR_SPACE_2020_YCBCR_LIMITED;
+ break;
+ case DRM_MODE_COLORIMETRY_DEFAULT: /* ITU601 */
+ default:
+ if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
+ color_space = COLOR_SPACE_SRGB;
+ if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
+ color_space = COLOR_SPACE_SRGB_LIMITED;
+ /*
+ * 27030khz is the separation point between HDTV and SDTV
+ * according to HDMI spec, we use YCbCr709 and YCbCr601
+ * respectively
+ */
+ } else if (dc_crtc_timing->pix_clk_100hz > 270300) {
+ if (dc_crtc_timing->flags.Y_ONLY)
+ color_space =
+ COLOR_SPACE_YCBCR709_LIMITED;
+ else
+ color_space = COLOR_SPACE_YCBCR709;
+ } else {
+ if (dc_crtc_timing->flags.Y_ONLY)
+ color_space =
+ COLOR_SPACE_YCBCR601_LIMITED;
+ else
+ color_space = COLOR_SPACE_YCBCR601;
+ }
+ break;
+ }
+
+ return color_space;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_get_output_color_space);
+
+STATIC_IFN_KUNIT enum display_content_type
+get_output_content_type(const struct drm_connector_state *connector_state)
+{
+ switch (connector_state->content_type) {
+ default:
+ case DRM_MODE_CONTENT_TYPE_NO_DATA:
+ return DISPLAY_CONTENT_TYPE_NO_DATA;
+ case DRM_MODE_CONTENT_TYPE_GRAPHICS:
+ return DISPLAY_CONTENT_TYPE_GRAPHICS;
+ case DRM_MODE_CONTENT_TYPE_PHOTO:
+ return DISPLAY_CONTENT_TYPE_PHOTO;
+ case DRM_MODE_CONTENT_TYPE_CINEMA:
+ return DISPLAY_CONTENT_TYPE_CINEMA;
+ case DRM_MODE_CONTENT_TYPE_GAME:
+ return DISPLAY_CONTENT_TYPE_GAME;
+ }
+}
+EXPORT_IF_KUNIT(get_output_content_type);
+
+STATIC_IFN_KUNIT bool adjust_colour_depth_from_display_info(
+ struct dc_crtc_timing *timing_out,
+ const struct drm_display_info *info)
+{
+ enum dc_color_depth depth = timing_out->display_color_depth;
+ int normalized_clk;
+
+ do {
+ normalized_clk = timing_out->pix_clk_100hz / 10;
+ /* YCbCr 4:2:0 requires additional adjustment of 1/2 */
+ if (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420)
+ normalized_clk /= 2;
+ /* Adjusting pix clock following on HDMI spec based on colour depth */
+ switch (depth) {
+ case COLOR_DEPTH_888:
+ break;
+ case COLOR_DEPTH_101010:
+ normalized_clk = (normalized_clk * 30) / 24;
+ break;
+ case COLOR_DEPTH_121212:
+ normalized_clk = (normalized_clk * 36) / 24;
+ break;
+ case COLOR_DEPTH_161616:
+ normalized_clk = (normalized_clk * 48) / 24;
+ break;
+ default:
+ /* The above depths are the only ones valid for HDMI. */
+ return false;
+ }
+ if (normalized_clk <= info->max_tmds_clock) {
+ timing_out->display_color_depth = depth;
+ return true;
+ }
+ } while (--depth > COLOR_DEPTH_666);
+ return false;
+}
+EXPORT_IF_KUNIT(adjust_colour_depth_from_display_info);
+
+static void fill_stream_properties_from_drm_display_mode(
+ struct dc_stream_state *stream,
+ const struct drm_display_mode *mode_in,
+ const struct drm_connector *connector,
+ const struct drm_connector_state *connector_state,
+ const struct dc_stream_state *old_stream,
+ int requested_bpc)
+{
+ struct dc_crtc_timing *timing_out = &stream->timing;
+ const struct drm_display_info *info = &connector->display_info;
+ struct amdgpu_dm_connector *aconnector = NULL;
+ struct hdmi_vendor_infoframe hv_frame;
+ struct hdmi_avi_infoframe avi_frame;
+ ssize_t err;
+
+ if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
+ aconnector = to_amdgpu_dm_connector(connector);
+
+ memset(&hv_frame, 0, sizeof(hv_frame));
+ memset(&avi_frame, 0, sizeof(avi_frame));
+
+ timing_out->h_border_left = 0;
+ timing_out->h_border_right = 0;
+ timing_out->v_border_top = 0;
+ timing_out->v_border_bottom = 0;
+ /* TODO: un-hardcode */
+ if (drm_mode_is_420_only(info, mode_in) ||
+ (aconnector &&
+ (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420 ||
+ aconnector->force_yuv420_output) &&
+ drm_mode_is_420_also(info, mode_in)))
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
+ && aconnector
+ && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422
+ || aconnector->force_yuv422_output))
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422;
+ else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
+ && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ stream->signal == SIGNAL_TYPE_HDMI_FRL)
+ && aconnector
+ && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR444)
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
+ else
+ timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
+
+ timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
+ timing_out->display_color_depth = amdgpu_dm_convert_color_depth_from_display_info(
+ connector,
+ (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420),
+ requested_bpc);
+ timing_out->scan_type = SCANNING_TYPE_NODATA;
+ timing_out->hdmi_vic = 0;
+
+ if (old_stream) {
+ timing_out->vic = old_stream->timing.vic;
+ timing_out->flags.HSYNC_POSITIVE_POLARITY = old_stream->timing.flags.HSYNC_POSITIVE_POLARITY;
+ timing_out->flags.VSYNC_POSITIVE_POLARITY = old_stream->timing.flags.VSYNC_POSITIVE_POLARITY;
+ } else {
+ timing_out->vic = drm_match_cea_mode(mode_in);
+ if (mode_in->flags & DRM_MODE_FLAG_PHSYNC)
+ timing_out->flags.HSYNC_POSITIVE_POLARITY = 1;
+ if (mode_in->flags & DRM_MODE_FLAG_PVSYNC)
+ timing_out->flags.VSYNC_POSITIVE_POLARITY = 1;
+ }
+
+ if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ stream->signal == SIGNAL_TYPE_HDMI_FRL) {
+ err = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame,
+ (struct drm_connector *)connector,
+ mode_in);
+ if (err < 0)
+ drm_warn_once(connector->dev, "Failed to setup avi infoframe on connector %s: %zd\n",
+ connector->name, err);
+ timing_out->vic = avi_frame.video_code;
+ err = drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame,
+ (struct drm_connector *)connector,
+ mode_in);
+ if (err < 0)
+ drm_warn_once(connector->dev, "Failed to setup vendor infoframe on connector %s: %zd\n",
+ connector->name, err);
+ timing_out->hdmi_vic = hv_frame.vic;
+ }
+
+ if (aconnector && amdgpu_dm_is_freesync_video_mode(mode_in, aconnector)) {
+ timing_out->h_addressable = mode_in->hdisplay;
+ timing_out->h_total = mode_in->htotal;
+ timing_out->h_sync_width = mode_in->hsync_end - mode_in->hsync_start;
+ timing_out->h_front_porch = mode_in->hsync_start - mode_in->hdisplay;
+ timing_out->v_total = mode_in->vtotal;
+ timing_out->v_addressable = mode_in->vdisplay;
+ timing_out->v_front_porch = mode_in->vsync_start - mode_in->vdisplay;
+ timing_out->v_sync_width = mode_in->vsync_end - mode_in->vsync_start;
+ timing_out->pix_clk_100hz = mode_in->clock * 10;
+ } else {
+ timing_out->h_addressable = mode_in->crtc_hdisplay;
+ timing_out->h_total = mode_in->crtc_htotal;
+ timing_out->h_sync_width = mode_in->crtc_hsync_end - mode_in->crtc_hsync_start;
+ timing_out->h_front_porch = mode_in->crtc_hsync_start - mode_in->crtc_hdisplay;
+ timing_out->v_total = mode_in->crtc_vtotal;
+ timing_out->v_addressable = mode_in->crtc_vdisplay;
+ timing_out->v_front_porch = mode_in->crtc_vsync_start - mode_in->crtc_vdisplay;
+ timing_out->v_sync_width = mode_in->crtc_vsync_end - mode_in->crtc_vsync_start;
+ timing_out->pix_clk_100hz = mode_in->crtc_clock * 10;
+ }
+
+ timing_out->aspect_ratio = get_aspect_ratio(mode_in);
+
+ stream->out_transfer_func.type = TF_TYPE_PREDEFINED;
+ stream->out_transfer_func.tf = TRANSFER_FUNCTION_SRGB;
+ if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) {
+ if (!adjust_colour_depth_from_display_info(timing_out, info) &&
+ drm_mode_is_420_also(info, mode_in) &&
+ timing_out->pixel_encoding != PIXEL_ENCODING_YCBCR420) {
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ adjust_colour_depth_from_display_info(timing_out, info);
+ }
+ }
+
+ stream->output_color_space = amdgpu_dm_get_output_color_space(timing_out, connector_state);
+ stream->content_type = get_output_content_type(connector_state);
+}
+
+static void
+copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode,
+ struct drm_display_mode *dst_mode)
+{
+ dst_mode->crtc_hdisplay = src_mode->crtc_hdisplay;
+ dst_mode->crtc_vdisplay = src_mode->crtc_vdisplay;
+ dst_mode->crtc_clock = src_mode->crtc_clock;
+ dst_mode->crtc_hblank_start = src_mode->crtc_hblank_start;
+ dst_mode->crtc_hblank_end = src_mode->crtc_hblank_end;
+ dst_mode->crtc_hsync_start = src_mode->crtc_hsync_start;
+ dst_mode->crtc_hsync_end = src_mode->crtc_hsync_end;
+ dst_mode->crtc_htotal = src_mode->crtc_htotal;
+ dst_mode->crtc_hskew = src_mode->crtc_hskew;
+ dst_mode->crtc_vblank_start = src_mode->crtc_vblank_start;
+ dst_mode->crtc_vblank_end = src_mode->crtc_vblank_end;
+ dst_mode->crtc_vsync_start = src_mode->crtc_vsync_start;
+ dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end;
+ dst_mode->crtc_vtotal = src_mode->crtc_vtotal;
+}
+
+STATIC_IFN_KUNIT void
+decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
+ const struct drm_display_mode *native_mode,
+ bool scale_enabled)
+{
+ if (scale_enabled || (
+ native_mode->clock == drm_mode->clock &&
+ native_mode->htotal == drm_mode->htotal &&
+ native_mode->vtotal == drm_mode->vtotal)) {
+ if (native_mode->crtc_clock)
+ copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode);
+ } else {
+ /* no scaling nor amdgpu inserted, no need to patch */
+ }
+}
+EXPORT_IF_KUNIT(decide_crtc_timing_for_drm_display_mode);
+
+static struct dc_sink *
+create_fake_sink(struct drm_device *dev, struct dc_link *link)
+{
+ struct dc_sink_init_data sink_init_data = { 0 };
+ struct dc_sink *sink = NULL;
+
+ sink_init_data.link = link;
+ sink_init_data.sink_signal = link->connector_signal;
+
+ sink = dc_sink_create(&sink_init_data);
+ if (!sink) {
+ drm_err(dev, "Failed to create sink!\n");
+ return NULL;
+ }
+ sink->sink_signal = SIGNAL_TYPE_VIRTUAL;
+
+ return sink;
+}
+
+/**
+ * DOC: FreeSync Video
+ *
+ * When a userspace application wants to play a video, the content follows a
+ * standard format definition that usually specifies the FPS for that format.
+ * The below list illustrates some video format and the expected FPS,
+ * respectively:
+ *
+ * - TV/NTSC (23.976 FPS)
+ * - Cinema (24 FPS)
+ * - TV/PAL (25 FPS)
+ * - TV/NTSC (29.97 FPS)
+ * - TV/NTSC (30 FPS)
+ * - Cinema HFR (48 FPS)
+ * - TV/PAL (50 FPS)
+ * - Commonly used (60 FPS)
+ * - Multiples of 24 (48,72,96 FPS)
+ *
+ * The list of standards video format is not huge and can be added to the
+ * connector modeset list beforehand. With that, userspace can leverage
+ * FreeSync to extends the front porch in order to attain the target refresh
+ * rate. Such a switch will happen seamlessly, without screen blanking or
+ * reprogramming of the output in any other way. If the userspace requests a
+ * modesetting change compatible with FreeSync modes that only differ in the
+ * refresh rate, DC will skip the full update and avoid blink during the
+ * transition. For example, the video player can change the modesetting from
+ * 60Hz to 30Hz for playing TV/NTSC content when it goes full screen without
+ * causing any display blink. This same concept can be applied to a mode
+ * setting change.
+ */
+struct drm_display_mode *
+amdgpu_dm_get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector,
+ bool use_probed_modes)
+{
+ struct drm_display_mode *m, *m_pref = NULL;
+ u16 current_refresh, highest_refresh;
+ struct list_head *list_head = use_probed_modes ?
+ &aconnector->base.probed_modes :
+ &aconnector->base.modes;
+
+ if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ return NULL;
+
+ if (aconnector->freesync_vid_base.clock != 0)
+ return &aconnector->freesync_vid_base;
+
+ /* Find the preferred mode */
+ list_for_each_entry(m, list_head, head) {
+ if (m->type & DRM_MODE_TYPE_PREFERRED) {
+ m_pref = m;
+ break;
+ }
+ }
+
+ if (!m_pref) {
+ /* Probably an EDID with no preferred mode. Fallback to first entry */
+ m_pref = list_first_entry_or_null(
+ &aconnector->base.modes, struct drm_display_mode, head);
+ if (!m_pref) {
+ drm_dbg_driver(aconnector->base.dev, "No preferred mode found in EDID\n");
+ return NULL;
+ }
+ }
+
+ highest_refresh = drm_mode_vrefresh(m_pref);
+
+ /*
+ * Find the mode with highest refresh rate with same resolution.
+ * For some monitors, preferred mode is not the mode with highest
+ * supported refresh rate.
+ */
+ list_for_each_entry(m, list_head, head) {
+ current_refresh = drm_mode_vrefresh(m);
+
+ if (m->hdisplay == m_pref->hdisplay &&
+ m->vdisplay == m_pref->vdisplay &&
+ highest_refresh < current_refresh) {
+ highest_refresh = current_refresh;
+ m_pref = m;
+ }
+ }
+
+ drm_mode_copy(&aconnector->freesync_vid_base, m_pref);
+ return m_pref;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_get_highest_refresh_rate_mode);
+
+bool amdgpu_dm_is_freesync_video_mode(const struct drm_display_mode *mode,
+ struct amdgpu_dm_connector *aconnector)
+{
+ struct drm_display_mode *high_mode;
+ int timing_diff;
+
+ high_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false);
+ if (!high_mode || !mode)
+ return false;
+
+ timing_diff = high_mode->vtotal - mode->vtotal;
+
+ if (high_mode->clock == 0 || high_mode->clock != mode->clock ||
+ high_mode->hdisplay != mode->hdisplay ||
+ high_mode->vdisplay != mode->vdisplay ||
+ high_mode->hsync_start != mode->hsync_start ||
+ high_mode->hsync_end != mode->hsync_end ||
+ high_mode->htotal != mode->htotal ||
+ high_mode->hskew != mode->hskew ||
+ high_mode->vscan != mode->vscan ||
+ high_mode->vsync_start - mode->vsync_start != timing_diff ||
+ high_mode->vsync_end - mode->vsync_end != timing_diff)
+ return false;
+ else
+ return true;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_is_freesync_video_mode);
+
+#if defined(CONFIG_DRM_AMD_DC_FP)
+static void update_dsc_caps(struct amdgpu_dm_connector *aconnector,
+ struct dc_sink *sink, struct dc_stream_state *stream,
+ struct dsc_dec_dpcd_caps *dsc_caps)
+{
+ stream->timing.flags.DSC = 0;
+ dsc_caps->is_dsc_supported = false;
+
+ if (aconnector->dc_link && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
+ sink->sink_signal == SIGNAL_TYPE_EDP)) {
+ if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE)
+ dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
+ aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
+ aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
+ dsc_caps);
+ else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
+ if (aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT &&
+ !aconnector->dsc_settings.dsc_force_disable_passthrough &&
+ aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps > 0 &&
+ sink->edid_caps.frl_dsc_support &&
+ sink->edid_caps.max_frl_rate > 0 &&
+ sink->edid_caps.frl_dsc_max_frl_rate > 0)
+ dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
+ else
+ dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
+ aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
+ aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
+ dsc_caps);
+ }
+ } else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
+ if (sink->edid_caps.frl_dsc_support &&
+ sink->edid_caps.max_frl_rate > 0 &&
+ sink->edid_caps.frl_dsc_max_frl_rate > 0)
+ dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
+ }
+}
+
+static void apply_dsc_policy_for_edp(struct amdgpu_dm_connector *aconnector,
+ struct dc_sink *sink, struct dc_stream_state *stream,
+ struct dsc_dec_dpcd_caps *dsc_caps,
+ uint32_t max_dsc_target_bpp_limit_override)
+{
+ const struct dc_link_settings *verified_link_cap = NULL;
+ u32 link_bw_in_kbps;
+ u32 edp_min_bpp_x16, edp_max_bpp_x16;
+ struct dc *dc = sink->ctx->dc;
+ struct dc_dsc_bw_range bw_range = {0};
+ struct dc_dsc_config dsc_cfg = {0};
+ struct dc_dsc_config_options dsc_options = {0};
+
+ dc_dsc_get_default_config_option(dc, &dsc_options);
+ dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
+
+ verified_link_cap = dc_link_get_link_cap(stream->link);
+ link_bw_in_kbps = dc_link_bandwidth_kbps(stream->link, verified_link_cap);
+ edp_min_bpp_x16 = 8 * 16;
+ edp_max_bpp_x16 = 8 * 16;
+
+ if (edp_max_bpp_x16 > dsc_caps->edp_max_bits_per_pixel)
+ edp_max_bpp_x16 = dsc_caps->edp_max_bits_per_pixel;
+
+ if (edp_max_bpp_x16 < edp_min_bpp_x16)
+ edp_min_bpp_x16 = edp_max_bpp_x16;
+
+ if (dc_dsc_compute_bandwidth_range(dc->res_pool->dscs[0],
+ dc->debug.dsc_min_slice_height_override,
+ edp_min_bpp_x16, edp_max_bpp_x16,
+ dsc_caps,
+ &stream->timing,
+ dc_link_get_highest_encoding_format(aconnector->dc_link),
+ &bw_range)) {
+
+ if (bw_range.max_kbps < link_bw_in_kbps) {
+ if (dc_dsc_compute_config(dc->res_pool->dscs[0],
+ dsc_caps,
+ &dsc_options,
+ 0,
+ &stream->timing,
+ dc_link_get_highest_encoding_format(aconnector->dc_link),
+ &dsc_cfg)) {
+ stream->timing.dsc_cfg = dsc_cfg;
+ stream->timing.flags.DSC = 1;
+ stream->timing.dsc_cfg.bits_per_pixel = edp_max_bpp_x16;
+ }
+ return;
+ }
+ }
+
+ if (dc_dsc_compute_config(dc->res_pool->dscs[0],
+ dsc_caps,
+ &dsc_options,
+ link_bw_in_kbps,
+ &stream->timing,
+ dc_link_get_highest_encoding_format(aconnector->dc_link),
+ &dsc_cfg)) {
+ stream->timing.dsc_cfg = dsc_cfg;
+ stream->timing.flags.DSC = 1;
+ }
+}
+
+static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
+ struct dc_sink *sink, struct dc_stream_state *stream,
+ struct dsc_dec_dpcd_caps *dsc_caps)
+{
+ struct drm_connector *drm_connector = &aconnector->base;
+ u32 link_bandwidth_kbps;
+ struct dc *dc = sink->ctx->dc;
+ const struct dc_hdmi_frl_link_settings *frl_verified_link_cap = NULL;
+ u32 converter_bw_in_kbps;
+ u32 sink_bw_in_kbps;
+ u32 dsc_sink_bw_in_kbps;
+ u32 max_supported_bw_in_kbps, timing_bw_in_kbps;
+ u32 dsc_max_supported_bw_in_kbps;
+ u32 max_dsc_target_bpp_limit_override =
+ drm_connector->display_info.max_dsc_bpp;
+ struct dc_dsc_config_options dsc_options = {0};
+
+ dc_dsc_get_default_config_option(dc, &dsc_options);
+ dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
+
+ link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link,
+ dc_link_get_link_cap(aconnector->dc_link));
+
+ /* Set DSC policy according to dsc_clock_en */
+ dc_dsc_policy_set_enable_dsc_when_not_needed(
+ aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE);
+
+ if (sink->sink_signal == SIGNAL_TYPE_EDP &&
+ !aconnector->dc_link->panel_config.dsc.disable_dsc_edp &&
+ dc->caps.edp_dsc_support && aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE) {
+
+ apply_dsc_policy_for_edp(aconnector, sink, stream, dsc_caps, max_dsc_target_bpp_limit_override);
+
+ } else if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
+ if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE) {
+ if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
+ dsc_caps,
+ &dsc_options,
+ link_bandwidth_kbps,
+ &stream->timing,
+ dc_link_get_highest_encoding_format(aconnector->dc_link),
+ &stream->timing.dsc_cfg)) {
+ stream->timing.flags.DSC = 1;
+ drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from SST RX\n",
+ __func__, drm_connector->name);
+ }
+ } else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
+ timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing,
+ dc_link_get_highest_encoding_format(aconnector->dc_link));
+ converter_bw_in_kbps = aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps;
+ sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.max_frl_rate);
+ dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate);
+
+ if (dsc_caps->is_frl) {
+ max_supported_bw_in_kbps = min(link_bandwidth_kbps, converter_bw_in_kbps);
+ max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, sink_bw_in_kbps);
+ dsc_max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, dsc_sink_bw_in_kbps);
+ } else {
+ max_supported_bw_in_kbps = link_bandwidth_kbps;
+ dsc_max_supported_bw_in_kbps = link_bandwidth_kbps;
+ }
+
+ if (timing_bw_in_kbps > max_supported_bw_in_kbps &&
+ max_supported_bw_in_kbps > 0 &&
+ dsc_max_supported_bw_in_kbps > 0)
+ if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
+ dsc_caps,
+ &dsc_options,
+ dsc_max_supported_bw_in_kbps,
+ &stream->timing,
+ dc_link_get_highest_encoding_format(aconnector->dc_link),
+ &stream->timing.dsc_cfg)) {
+ stream->timing.flags.DSC = 1;
+ drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from %s\n",
+ __func__, drm_connector->name,
+ (dsc_caps->is_frl == 1) ? "HDMI FRL RX" : "DP-HDMI PCON");
+ }
+ }
+ } else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
+ struct dc_dsc_policy dsc_policy = {0};
+
+ frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link);
+ if (frl_verified_link_cap->frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE &&
+ aconnector->dc_link->frl_flags.force_frl_dsc) {
+ dc_dsc_policy_set_enable_dsc_when_not_needed(true);
+ dc_dsc_get_policy_for_timing(&stream->timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link));
+ }
+
+ timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL);
+ link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link, frl_verified_link_cap->frl_link_rate);
+ dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate);
+
+ if ((timing_bw_in_kbps > link_bandwidth_kbps && dsc_sink_bw_in_kbps > 0) ||
+ (dsc_policy.enable_dsc_when_not_needed || dsc_options.force_dsc_when_not_needed)) {
+ if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
+ dsc_caps,
+ &dsc_options,
+ dsc_sink_bw_in_kbps,
+ &stream->timing,
+ dc_link_get_highest_encoding_format(aconnector->dc_link),
+ &stream->timing.dsc_cfg)) {
+ stream->timing.flags.DSC = 1;
+ drm_dbg_driver(drm_connector->dev, "%s: HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n",
+ __func__, drm_connector->name);
+ }
+ }
+ }
+
+ /* Overwrite the stream flag if DSC is enabled through debugfs */
+ if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE)
+ stream->timing.flags.DSC = 1;
+
+ if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_h)
+ stream->timing.dsc_cfg.num_slices_h = aconnector->dsc_settings.dsc_num_slices_h;
+
+ if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_v)
+ stream->timing.dsc_cfg.num_slices_v = aconnector->dsc_settings.dsc_num_slices_v;
+
+ if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_bits_per_pixel)
+ stream->timing.dsc_cfg.bits_per_pixel = aconnector->dsc_settings.dsc_bits_per_pixel;
+}
+#endif
+
+static struct dc_stream_state *
+create_stream_for_sink(struct drm_connector *connector,
+ const struct drm_display_mode *drm_mode,
+ const struct dm_connector_state *dm_state,
+ const struct dc_stream_state *old_stream,
+ int requested_bpc)
+{
+ struct drm_device *dev = connector->dev;
+ struct amdgpu_dm_connector *aconnector = NULL;
+ struct drm_display_mode *preferred_mode = NULL;
+ const struct drm_connector_state *con_state = &dm_state->base;
+ struct dc_stream_state *stream = NULL;
+ struct drm_display_mode mode;
+ struct drm_display_mode saved_mode;
+ struct drm_display_mode *freesync_mode = NULL;
+ bool native_mode_found = false;
+ bool recalculate_timing = false;
+ bool scale = dm_state->scaling != RMX_OFF;
+ int mode_refresh;
+ int preferred_refresh = 0;
+ enum color_transfer_func tf = TRANSFER_FUNC_UNKNOWN;
+#if defined(CONFIG_DRM_AMD_DC_FP)
+ struct dsc_dec_dpcd_caps dsc_caps = {0};
+#endif
+ struct dc_link *link = NULL;
+ struct dc_sink *sink = NULL;
+
+ drm_mode_init(&mode, drm_mode);
+ memset(&saved_mode, 0, sizeof(saved_mode));
+
+ if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) {
+ aconnector = NULL;
+ aconnector = to_amdgpu_dm_connector(connector);
+ link = aconnector->dc_link;
+ } else {
+ struct drm_writeback_connector *wbcon = NULL;
+ struct amdgpu_dm_wb_connector *dm_wbcon = NULL;
+
+ wbcon = drm_connector_to_writeback(connector);
+ dm_wbcon = to_amdgpu_dm_wb_connector(wbcon);
+ link = dm_wbcon->link;
+ }
+
+ if (!aconnector || !aconnector->dc_sink) {
+ sink = create_fake_sink(dev, link);
+ if (!sink)
+ return stream;
+
+ } else {
+ sink = aconnector->dc_sink;
+ dc_sink_retain(sink);
+ }
+
+ stream = dc_create_stream_for_sink(sink);
+
+ if (stream == NULL) {
+ drm_err(dev, "Failed to create stream for sink!\n");
+ goto finish;
+ }
+
+ /* We leave this NULL for writeback connectors */
+ stream->dm_stream_context = aconnector;
+
+ stream->timing.flags.LTE_340MCSC_SCRAMBLE =
+ connector->display_info.hdmi.scdc.scrambling.low_rates;
+
+ list_for_each_entry(preferred_mode, &connector->modes, head) {
+ /* Search for preferred mode */
+ if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) {
+ native_mode_found = true;
+ break;
+ }
+ }
+ if (!native_mode_found)
+ preferred_mode = list_first_entry_or_null(
+ &connector->modes,
+ struct drm_display_mode,
+ head);
+
+ mode_refresh = drm_mode_vrefresh(&mode);
+
+ if (preferred_mode == NULL) {
+ /*
+ * This may not be an error, the use case is when we have no
+ * usermode calls to reset and set mode upon hotplug. In this
+ * case, we call set mode ourselves to restore the previous mode
+ * and the modelist may not be filled in time.
+ */
+ drm_dbg_driver(dev, "No preferred mode found\n");
+ } else if (aconnector) {
+ recalculate_timing = amdgpu_freesync_vid_mode &&
+ amdgpu_dm_is_freesync_video_mode(&mode, aconnector);
+ if (recalculate_timing) {
+ freesync_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false);
+ drm_mode_copy(&saved_mode, &mode);
+ saved_mode.picture_aspect_ratio = mode.picture_aspect_ratio;
+ drm_mode_copy(&mode, freesync_mode);
+ mode.picture_aspect_ratio = saved_mode.picture_aspect_ratio;
+ } else {
+ decide_crtc_timing_for_drm_display_mode(
+ &mode, preferred_mode, scale);
+
+ preferred_refresh = drm_mode_vrefresh(preferred_mode);
+ }
+ }
+
+ if (recalculate_timing)
+ drm_mode_set_crtcinfo(&saved_mode, 0);
+
+ /*
+ * If scaling is enabled and refresh rate didn't change
+ * we copy the vic and polarities of the old timings
+ */
+ if (!scale || mode_refresh != preferred_refresh)
+ fill_stream_properties_from_drm_display_mode(
+ stream, &mode, connector, con_state, NULL,
+ requested_bpc);
+ else
+ fill_stream_properties_from_drm_display_mode(
+ stream, &mode, connector, con_state, old_stream,
+ requested_bpc);
+
+ /* The rest isn't needed for writeback connectors */
+ if (!aconnector)
+ goto finish;
+
+ if (aconnector->timing_changed) {
+ drm_dbg(aconnector->base.dev,
+ "overriding timing for automated test, bpc %d, changing to %d\n",
+ stream->timing.display_color_depth,
+ aconnector->timing_requested->display_color_depth);
+ stream->timing = *aconnector->timing_requested;
+ }
+
+#if defined(CONFIG_DRM_AMD_DC_FP)
+ /* SST DSC determination policy */
+ update_dsc_caps(aconnector, sink, stream, &dsc_caps);
+ if (aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE && dsc_caps.is_dsc_supported)
+ apply_dsc_policy_for_stream(aconnector, sink, stream, &dsc_caps);
+#endif
+
+ amdgpu_dm_update_stream_scaling_settings(dev, &mode, dm_state, stream);
+
+ amdgpu_dm_fill_audio_info(
+ &stream->audio_info,
+ connector,
+ sink);
+
+ update_stream_signal(stream, sink);
+
+ if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ stream->signal == SIGNAL_TYPE_HDMI_FRL)
+ mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false);
+
+ if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
+ stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ||
+ stream->signal == SIGNAL_TYPE_EDP) {
+ const struct dc_edid_caps *edid_caps;
+ unsigned int disable_colorimetry = 0;
+
+ if (aconnector->dc_sink) {
+ edid_caps = &aconnector->dc_sink->edid_caps;
+ disable_colorimetry = edid_caps->panel_patch.disable_colorimetry;
+ }
+
+ /*
+ * should decide stream support vsc sdp colorimetry capability
+ * before building vsc info packet
+ */
+ stream->use_vsc_sdp_for_colorimetry = stream->link->dpcd_caps.dpcd_rev.raw >= 0x14 &&
+ stream->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED &&
+ !disable_colorimetry;
+
+ if (stream->out_transfer_func.tf == TRANSFER_FUNCTION_GAMMA22)
+ tf = TRANSFER_FUNC_GAMMA_22;
+ mod_build_vsc_infopacket(stream, &stream->vsc_infopacket, stream->output_color_space, tf);
+ aconnector->sr_skip_count = AMDGPU_DM_PSR_ENTRY_DELAY;
+
+ }
+finish:
+ dc_sink_release(sink);
+
+ return stream;
+}
+
+/**
+ * amdgpu_dm_connector_poll - Poll a connector to see if it's connected to a display
+ * @aconnector: DM connector to poll (owns @base drm_connector and @dc_link)
+ * @force: if true, force polling even when DAC load detection was used
+ *
+ * Used for connectors that don't support HPD (hotplug detection) to
+ * periodically check whether the connector is connected to a display.
+ *
+ * When connection was determined via DAC load detection, we avoid
+ * re-running it on normal polls to prevent visible glitches, unless
+ * @force is set.
+ *
+ * Return: The probed connector status (connected/disconnected/unknown).
+ */
+static enum drm_connector_status
+amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force)
+{
+ struct drm_connector *connector = &aconnector->base;
+ struct drm_device *dev = connector->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct dc_link *link = aconnector->dc_link;
+ enum dc_connection_type conn_type = dc_connection_none;
+ enum drm_connector_status status = connector_status_disconnected;
+
+ /* When we determined the connection using DAC load detection,
+ * do NOT poll the connector do detect disconnect because
+ * that would run DAC load detection again which can cause
+ * visible visual glitches.
+ *
+ * Only allow to poll such a connector again when forcing.
+ */
+ if (!force && link->local_sink && link->type == dc_connection_analog_load)
+ return connector->status;
+
+ mutex_lock(&aconnector->hpd_lock);
+
+ if (dc_link_detect_connection_type(aconnector->dc_link, &conn_type) &&
+ conn_type != dc_connection_none) {
+ mutex_lock(&adev->dm.dc_lock);
+
+ /* Only call full link detection when a sink isn't created yet,
+ * ie. just when the display is plugged in, otherwise we risk flickering.
+ */
+ if (link->local_sink ||
+ dc_link_detect(link, DETECT_REASON_HPD))
+ status = connector_status_connected;
+
+ mutex_unlock(&adev->dm.dc_lock);
+ }
+
+ if (connector->status != status) {
+ if (status == connector_status_disconnected) {
+ if (link->local_sink)
+ dc_sink_release(link->local_sink);
+
+ link->local_sink = NULL;
+ link->dpcd_sink_count = 0;
+ link->type = dc_connection_none;
+ }
+
+ amdgpu_dm_update_connector_after_detect(aconnector);
+ }
+
+ mutex_unlock(&aconnector->hpd_lock);
+ return status;
+}
+
+/**
+ * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display
+ *
+ * A connector is considered connected when it has a sink that is not NULL.
+ * For connectors that support HPD (hotplug detection), the connection is
+ * handled in the HPD interrupt.
+ * For connectors that may not support HPD, such as analog connectors,
+ * DRM will call this function repeatedly to poll them.
+ *
+ * Notes:
+ * 1. This interface is NOT called in context of HPD irq.
+ * 2. This interface *is called* in context of user-mode ioctl. Which
+ * makes it a bad place for *any* MST-related activity.
+ *
+ * @connector: The DRM connector we are checking. We convert it to
+ * amdgpu_dm_connector so we can read the DC link and state.
+ * @force: If true, do a full detect again. This is used even when
+ * a lighter check would normally be used to avoid flicker.
+ *
+ * Return: The connector status (connected, disconnected, or unknown).
+ *
+ */
+static enum drm_connector_status
+amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+
+ update_subconnector_property(aconnector);
+
+ if (aconnector->base.force == DRM_FORCE_ON ||
+ aconnector->base.force == DRM_FORCE_ON_DIGITAL)
+ return connector_status_connected;
+ else if (aconnector->base.force == DRM_FORCE_OFF)
+ return connector_status_disconnected;
+
+ /* Poll analog connectors and only when either
+ * disconnected or connected to an analog display.
+ */
+ if (drm_kms_helper_is_poll_worker() &&
+ dc_connector_supports_analog(aconnector->dc_link->link_id.id) &&
+ (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog))
+ return amdgpu_dm_connector_poll(aconnector, force);
+
+ return (aconnector->dc_sink ? connector_status_connected :
+ connector_status_disconnected);
+}
+
+int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
+ struct drm_connector_state *connector_state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct drm_device *dev = connector->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct dm_connector_state *dm_old_state =
+ to_dm_connector_state(connector->state);
+ struct dm_connector_state *dm_new_state =
+ to_dm_connector_state(connector_state);
+
+ int ret = -EINVAL;
+
+ if (property == dev->mode_config.scaling_mode_property) {
+ enum amdgpu_rmx_type rmx_type;
+
+ switch (val) {
+ case DRM_MODE_SCALE_CENTER:
+ rmx_type = RMX_CENTER;
+ break;
+ case DRM_MODE_SCALE_ASPECT:
+ rmx_type = RMX_ASPECT;
+ break;
+ case DRM_MODE_SCALE_FULLSCREEN:
+ rmx_type = RMX_FULL;
+ break;
+ case DRM_MODE_SCALE_NONE:
+ default:
+ rmx_type = RMX_OFF;
+ break;
+ }
+
+ if (dm_old_state->scaling == rmx_type)
+ return 0;
+
+ dm_new_state->scaling = rmx_type;
+ ret = 0;
+ } else if (property == adev->mode_info.underscan_hborder_property) {
+ dm_new_state->underscan_hborder = val;
+ ret = 0;
+ } else if (property == adev->mode_info.underscan_vborder_property) {
+ dm_new_state->underscan_vborder = val;
+ ret = 0;
+ } else if (property == adev->mode_info.underscan_property) {
+ dm_new_state->underscan_enable = val;
+ ret = 0;
+ } else if (property == adev->mode_info.abm_level_property) {
+ switch (val) {
+ case ABM_SYSFS_CONTROL:
+ dm_new_state->abm_sysfs_forbidden = false;
+ break;
+ case ABM_LEVEL_OFF:
+ dm_new_state->abm_sysfs_forbidden = true;
+ dm_new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
+ break;
+ default:
+ dm_new_state->abm_sysfs_forbidden = true;
+ dm_new_state->abm_level = val;
+ }
+ ret = 0;
+ }
+
+ return ret;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_connector_atomic_set_property);
+
+int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
+ const struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct drm_device *dev = connector->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct dm_connector_state *dm_state =
+ to_dm_connector_state(state);
+ int ret = -EINVAL;
+
+ if (property == dev->mode_config.scaling_mode_property) {
+ switch (dm_state->scaling) {
+ case RMX_CENTER:
+ *val = DRM_MODE_SCALE_CENTER;
+ break;
+ case RMX_ASPECT:
+ *val = DRM_MODE_SCALE_ASPECT;
+ break;
+ case RMX_FULL:
+ *val = DRM_MODE_SCALE_FULLSCREEN;
+ break;
+ case RMX_OFF:
+ default:
+ *val = DRM_MODE_SCALE_NONE;
+ break;
+ }
+ ret = 0;
+ } else if (property == adev->mode_info.underscan_hborder_property) {
+ *val = dm_state->underscan_hborder;
+ ret = 0;
+ } else if (property == adev->mode_info.underscan_vborder_property) {
+ *val = dm_state->underscan_vborder;
+ ret = 0;
+ } else if (property == adev->mode_info.underscan_property) {
+ *val = dm_state->underscan_enable;
+ ret = 0;
+ } else if (property == adev->mode_info.abm_level_property) {
+ if (!dm_state->abm_sysfs_forbidden)
+ *val = ABM_SYSFS_CONTROL;
+ else
+ *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ?
+ dm_state->abm_level : 0;
+ ret = 0;
+ }
+
+ return ret;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_connector_atomic_get_property);
+
+static void amdgpu_dm_connector_unregister(struct drm_connector *connector)
+{
+ struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector);
+
+ if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector))
+ sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
+
+ cec_notifier_conn_unregister(amdgpu_dm_connector->notifier);
+ drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux);
+}
+
+static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
+{
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+ struct amdgpu_device *adev = drm_to_adev(connector->dev);
+ struct amdgpu_display_manager *dm = &adev->dm;
+
+ /*
+ * Call only if mst_mgr was initialized before since it's not done
+ * for all connector types.
+ */
+ if (aconnector->mst_mgr.dev)
+ drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr);
+
+ /* Cancel and flush any pending HDMI HPD debounce work */
+ if (aconnector->hdmi_hpd_debounce_delay_ms) {
+ cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work);
+ if (aconnector->hdmi_prev_sink) {
+ dc_sink_release(aconnector->hdmi_prev_sink);
+ aconnector->hdmi_prev_sink = NULL;
+ }
+ }
+
+ if (aconnector->bl_idx != -1) {
+ backlight_device_unregister(dm->backlight_dev[aconnector->bl_idx]);
+ dm->backlight_dev[aconnector->bl_idx] = NULL;
+ }
+
+ if (aconnector->dc_em_sink)
+ dc_sink_release(aconnector->dc_em_sink);
+ aconnector->dc_em_sink = NULL;
+ if (aconnector->dc_sink)
+ dc_sink_release(aconnector->dc_sink);
+ aconnector->dc_sink = NULL;
+
+ drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux);
+ drm_connector_unregister(connector);
+ drm_connector_cleanup(connector);
+ kfree(aconnector->dm_dp_aux.aux.name);
+
+ kfree(connector);
+}
+
+void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector)
+{
+ struct dm_connector_state *old_state =
+ to_dm_connector_state(connector->state);
+ struct dm_connector_state *state;
+
+ state = kzalloc_obj(*state);
+ if (!state)
+ return;
+
+ if (connector->state)
+ __drm_atomic_helper_connector_destroy_state(connector->state);
+
+ kfree(old_state);
+
+ __drm_atomic_helper_connector_reset(connector, &state->base);
+
+ state->scaling = RMX_OFF;
+ state->underscan_enable = false;
+ state->underscan_hborder = 0;
+ state->underscan_vborder = 0;
+ state->base.max_requested_bpc = 8;
+ state->vcpi_slots = 0;
+ state->pbn = 0;
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ if (amdgpu_dm_abm_level <= 0)
+ state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
+ else
+ state->abm_level = amdgpu_dm_abm_level;
+ }
+}
+EXPORT_IF_KUNIT(amdgpu_dm_connector_funcs_reset);
+
+struct drm_connector_state *
+amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector)
+{
+ struct dm_connector_state *state =
+ to_dm_connector_state(connector->state);
+
+ struct dm_connector_state *new_state =
+ kmemdup(state, sizeof(*state), GFP_KERNEL);
+
+ if (!new_state)
+ return NULL;
+
+ __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
+
+ new_state->freesync_capable = state->freesync_capable;
+ new_state->abm_level = state->abm_level;
+ new_state->scaling = state->scaling;
+ new_state->underscan_enable = state->underscan_enable;
+ new_state->underscan_hborder = state->underscan_hborder;
+ new_state->underscan_vborder = state->underscan_vborder;
+ new_state->vcpi_slots = state->vcpi_slots;
+ new_state->pbn = state->pbn;
+ return &new_state->base;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_connector_atomic_duplicate_state);
+
+static int
+amdgpu_dm_connector_late_register(struct drm_connector *connector)
+{
+ struct amdgpu_dm_connector *amdgpu_dm_connector =
+ to_amdgpu_dm_connector(connector);
+ int r;
+
+ if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector)) {
+ r = sysfs_create_group(&connector->kdev->kobj,
+ &amdgpu_group);
+ if (r)
+ return r;
+ }
+
+ amdgpu_dm_register_backlight_device(amdgpu_dm_connector);
+
+ if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
+ (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
+ amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev;
+ r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux);
+ if (r)
+ return r;
+ }
+
+#if defined(CONFIG_DEBUG_FS)
+ connector_debugfs_init(amdgpu_dm_connector);
+#endif
+
+ return 0;
+}
+
+static void amdgpu_dm_connector_funcs_force(struct drm_connector *connector)
+{
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+ struct dc_link *dc_link = aconnector->dc_link;
+ struct dc_sink *dc_em_sink = aconnector->dc_em_sink;
+ const struct drm_edid *drm_edid;
+ struct i2c_adapter *ddc;
+ struct drm_device *dev = connector->dev;
+
+ if (dc_link && dc_link->aux_mode)
+ ddc = &aconnector->dm_dp_aux.aux.ddc;
+ else
+ ddc = &aconnector->i2c->base;
+
+ drm_edid = drm_edid_read_ddc(connector, ddc);
+ drm_edid_connector_update(connector, drm_edid);
+ if (!drm_edid) {
+ drm_err(dev, "No EDID found on connector: %s.\n", connector->name);
+ return;
+ }
+
+ aconnector->drm_edid = drm_edid;
+ /* Update emulated (virtual) sink's EDID */
+ if (dc_em_sink && dc_link) {
+ /* FIXME: Get rid of drm_edid_raw() */
+ const struct edid *edid = drm_edid_raw(drm_edid);
+
+ memset(&dc_em_sink->edid_caps, 0, sizeof(struct dc_edid_caps));
+ memmove(dc_em_sink->dc_edid.raw_edid, edid,
+ (edid->extensions + 1) * EDID_LENGTH);
+ dm_helpers_parse_edid_caps(
+ dc_link,
+ &dc_em_sink->dc_edid,
+ &dc_em_sink->edid_caps);
+ }
+}
+
+static const struct drm_connector_funcs amdgpu_dm_connector_funcs = {
+ .reset = amdgpu_dm_connector_funcs_reset,
+ .detect = amdgpu_dm_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = amdgpu_dm_connector_destroy,
+ .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_set_property = amdgpu_dm_connector_atomic_set_property,
+ .atomic_get_property = amdgpu_dm_connector_atomic_get_property,
+ .late_register = amdgpu_dm_connector_late_register,
+ .early_unregister = amdgpu_dm_connector_unregister,
+ .force = amdgpu_dm_connector_funcs_force
+};
+
+static int get_modes(struct drm_connector *connector)
+{
+ return amdgpu_dm_connector_get_modes(connector);
+}
+
+static void create_eml_sink(struct amdgpu_dm_connector *aconnector)
+{
+ struct drm_connector *connector = &aconnector->base;
+ struct dc_link *dc_link = aconnector->dc_link;
+ struct dc_sink_init_data init_params = {
+ .link = aconnector->dc_link,
+ .sink_signal = SIGNAL_TYPE_VIRTUAL
+ };
+ const struct drm_edid *drm_edid;
+ const struct edid *edid;
+ struct i2c_adapter *ddc;
+
+ if (dc_link && dc_link->aux_mode)
+ ddc = &aconnector->dm_dp_aux.aux.ddc;
+ else
+ ddc = &aconnector->i2c->base;
+
+ drm_edid = drm_edid_read_ddc(connector, ddc);
+ drm_edid_connector_update(connector, drm_edid);
+ if (!drm_edid) {
+ drm_err(connector->dev, "No EDID found on connector: %s.\n", connector->name);
+ return;
+ }
+
+ if (connector->display_info.is_hdmi)
+ init_params.sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+
+ aconnector->drm_edid = drm_edid;
+
+ /* FIXME: Get rid of drm_edid_raw() */
+ edid = drm_edid_raw(drm_edid);
+ aconnector->dc_em_sink = dc_link_add_remote_sink(
+ aconnector->dc_link,
+ (uint8_t *)edid,
+ (edid->extensions + 1) * EDID_LENGTH,
+ &init_params);
+
+ if (aconnector->base.force == DRM_FORCE_ON) {
+ aconnector->dc_sink = aconnector->dc_link->local_sink ?
+ aconnector->dc_link->local_sink :
+ aconnector->dc_em_sink;
+ if (aconnector->dc_sink)
+ dc_sink_retain(aconnector->dc_sink);
+ }
+}
+
+static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector)
+{
+ struct dc_link *link = (struct dc_link *)aconnector->dc_link;
+
+ /*
+ * In case of headless boot with force on for DP managed connector
+ * Those settings have to be != 0 to get initial modeset
+ */
+ if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) {
+ link->verified_link_cap.lane_count = LANE_COUNT_FOUR;
+ link->verified_link_cap.link_rate = LINK_RATE_HIGH2;
+ }
+
+ create_eml_sink(aconnector);
+}
+
+static enum dc_status dm_validate_stream_and_context(struct dc *dc,
+ struct dc_stream_state *stream)
+{
+ enum dc_status dc_result = DC_ERROR_UNEXPECTED;
+ struct dc_plane_state *dc_plane_state = NULL;
+ struct dc_state *dc_state = NULL;
+
+ if (!stream)
+ goto cleanup;
+
+ dc_plane_state = dc_create_plane_state(dc);
+ if (!dc_plane_state)
+ goto cleanup;
+
+ dc_state = dc_state_create(dc, NULL);
+ if (!dc_state)
+ goto cleanup;
+
+ /* populate stream to plane */
+ dc_plane_state->src_rect.height = stream->src.height;
+ dc_plane_state->src_rect.width = stream->src.width;
+ dc_plane_state->dst_rect.height = stream->src.height;
+ dc_plane_state->dst_rect.width = stream->src.width;
+ dc_plane_state->clip_rect.height = stream->src.height;
+ dc_plane_state->clip_rect.width = stream->src.width;
+ dc_plane_state->plane_size.surface_pitch = ((stream->src.width + 255) / 256) * 256;
+ dc_plane_state->plane_size.surface_size.height = stream->src.height;
+ dc_plane_state->plane_size.surface_size.width = stream->src.width;
+ dc_plane_state->plane_size.chroma_size.height = stream->src.height;
+ dc_plane_state->plane_size.chroma_size.width = stream->src.width;
+ dc_plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888;
+ dc_plane_state->tiling_info.gfx9.swizzle = DC_SW_UNKNOWN;
+ dc_plane_state->rotation = ROTATION_ANGLE_0;
+ dc_plane_state->is_tiling_rotated = false;
+ dc_plane_state->tiling_info.gfx8.array_mode = DC_ARRAY_LINEAR_GENERAL;
+
+ dc_result = dc_validate_stream(dc, stream);
+ if (dc_result == DC_OK)
+ dc_result = dc_validate_plane(dc, dc_plane_state);
+
+ if (dc_result == DC_OK)
+ dc_result = dc_state_add_stream(dc, dc_state, stream);
+
+ if (dc_result == DC_OK && !dc_state_add_plane(
+ dc,
+ stream,
+ dc_plane_state,
+ dc_state))
+ dc_result = DC_FAIL_ATTACH_SURFACES;
+
+ if (dc_result == DC_OK)
+ dc_result = dc_validate_global_state(dc, dc_state, DC_VALIDATE_MODE_ONLY);
+
+cleanup:
+ if (dc_state)
+ dc_state_release(dc_state);
+
+ if (dc_plane_state)
+ dc_plane_state_release(dc_plane_state);
+
+ return dc_result;
+}
+
+struct dc_stream_state *
+amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector,
+ const struct drm_display_mode *drm_mode,
+ const struct dm_connector_state *dm_state,
+ const struct dc_stream_state *old_stream)
+{
+ struct amdgpu_dm_connector *aconnector = NULL;
+ struct amdgpu_device *adev = drm_to_adev(connector->dev);
+ struct dc_stream_state *stream;
+ const struct drm_connector_state *drm_state = dm_state ? &dm_state->base : NULL;
+ int requested_bpc = drm_state ? drm_state->max_requested_bpc : 8;
+ enum dc_status dc_result = DC_OK;
+ uint8_t bpc_limit = 6;
+
+ if (!dm_state)
+ return NULL;
+
+ if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
+ aconnector = to_amdgpu_dm_connector(connector);
+
+ if (aconnector &&
+ (aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL ||
+ aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER))
+ bpc_limit = 8;
+
+ do {
+ drm_dbg_kms(connector->dev, "Trying with %d bpc\n", requested_bpc);
+ stream = create_stream_for_sink(connector, drm_mode,
+ dm_state, old_stream,
+ requested_bpc);
+ if (stream == NULL) {
+ drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n");
+ break;
+ }
+
+ dc_result = dc_validate_stream(adev->dm.dc, stream);
+
+ if (!aconnector) /* writeback connector */
+ return stream;
+
+ if (dc_result == DC_OK && stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
+ dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream);
+
+ if (dc_result == DC_OK)
+ dc_result = dm_validate_stream_and_context(adev->dm.dc, stream);
+
+ if (dc_result != DC_OK) {
+ drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n",
+ drm_mode->hdisplay,
+ drm_mode->vdisplay,
+ drm_mode->clock,
+ dc_pixel_encoding_to_str(stream->timing.pixel_encoding),
+ dc_color_depth_to_str(stream->timing.display_color_depth),
+ dc_status_to_str(dc_result));
+
+ dc_stream_release(stream);
+ stream = NULL;
+ requested_bpc -= 2; /* lower bpc to retry validation */
+ }
+
+ } while (stream == NULL && requested_bpc >= bpc_limit);
+
+ switch (dc_result) {
+ /*
+ * If we failed to validate DP bandwidth stream with the requested RGB color depth,
+ * we try to fallback and configure in order:
+ * YUV422 (8bpc, 6bpc)
+ * YUV420 (8bpc, 6bpc)
+ */
+ case DC_FAIL_ENC_VALIDATE:
+ case DC_EXCEED_DONGLE_CAP:
+ case DC_NO_DP_LINK_BANDWIDTH:
+ /* recursively entered twice and already tried both YUV422 and YUV420 */
+ if (aconnector->force_yuv422_output && aconnector->force_yuv420_output)
+ break;
+ /* first failure; try YUV422 */
+ if (!aconnector->force_yuv422_output) {
+ drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV422\n",
+ __func__, __LINE__, dc_result);
+ aconnector->force_yuv422_output = true;
+ /* recursively entered and YUV422 failed, try YUV420 */
+ } else if (!aconnector->force_yuv420_output) {
+ drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV420\n",
+ __func__, __LINE__, dc_result);
+ aconnector->force_yuv420_output = true;
+ }
+ stream = amdgpu_dm_create_validate_stream_for_sink(connector, drm_mode,
+ dm_state, old_stream);
+ aconnector->force_yuv422_output = false;
+ aconnector->force_yuv420_output = false;
+ break;
+ case DC_OK:
+ break;
+ default:
+ drm_dbg_kms(connector->dev, "%s:%d Unhandled validation failure %d\n",
+ __func__, __LINE__, dc_result);
+ break;
+ }
+
+ return stream;
+}
+
+enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+{
+ int result = MODE_ERROR;
+ struct dc_sink *dc_sink;
+ struct drm_display_mode *test_mode;
+ /* TODO: Unhardcode stream count */
+ struct dc_stream_state *stream;
+ /* we always have an amdgpu_dm_connector here since we got
+ * here via the amdgpu_dm_connector_helper_funcs
+ */
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
+ (mode->flags & DRM_MODE_FLAG_DBLSCAN))
+ return result;
+
+ /*
+ * Only run this the first time mode_valid is called to initilialize
+ * EDID mgmt
+ */
+ if (aconnector->base.force != DRM_FORCE_UNSPECIFIED &&
+ !aconnector->dc_em_sink)
+ handle_edid_mgmt(aconnector);
+
+ dc_sink = to_amdgpu_dm_connector(connector)->dc_sink;
+
+ if (dc_sink == NULL && aconnector->base.force != DRM_FORCE_ON_DIGITAL &&
+ aconnector->base.force != DRM_FORCE_ON) {
+ drm_err(connector->dev, "dc_sink is NULL!\n");
+ goto fail;
+ }
+
+ test_mode = drm_mode_duplicate(connector->dev, mode);
+ if (!test_mode)
+ goto fail;
+
+ drm_mode_set_crtcinfo(test_mode, 0);
+
+ stream = amdgpu_dm_create_validate_stream_for_sink(connector, test_mode,
+ to_dm_connector_state(connector->state),
+ NULL);
+ drm_mode_destroy(connector->dev, test_mode);
+ if (stream) {
+ dc_stream_release(stream);
+ result = MODE_OK;
+ }
+
+fail:
+ /* TODO: error handling*/
+ return result;
+}
+
+int amdgpu_dm_fill_hdr_info_packet(const struct drm_connector_state *state,
+ struct dc_info_packet *out)
+{
+ struct hdmi_drm_infoframe frame;
+ unsigned char buf[30]; /* 26 + 4 */
+ ssize_t len;
+ int ret, i;
+
+ memset(out, 0, sizeof(*out));
+
+ if (!state->hdr_output_metadata)
+ return 0;
+
+ ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, state);
+ if (ret)
+ return ret;
+
+ len = hdmi_drm_infoframe_pack_only(&frame, buf, sizeof(buf));
+ if (len < 0)
+ return (int)len;
+
+ /* Static metadata is a fixed 26 bytes + 4 byte header. */
+ if (len != 30)
+ return -EINVAL;
+
+ /* Prepare the infopacket for DC. */
+ switch (state->connector->connector_type) {
+ case DRM_MODE_CONNECTOR_HDMIA:
+ out->hb0 = 0x87; /* type */
+ out->hb1 = 0x01; /* version */
+ out->hb2 = 0x1A; /* length */
+ out->sb[0] = buf[3]; /* checksum */
+ i = 1;
+ break;
+
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ case DRM_MODE_CONNECTOR_eDP:
+ out->hb0 = 0x00; /* sdp id, zero */
+ out->hb1 = 0x87; /* type */
+ out->hb2 = 0x1D; /* payload len - 1 */
+ out->hb3 = (0x13 << 2); /* sdp version */
+ out->sb[0] = 0x01; /* version */
+ out->sb[1] = 0x1A; /* length */
+ i = 2;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ memcpy(&out->sb[i], &buf[4], 26);
+ out->valid = true;
+
+ print_hex_dump(KERN_DEBUG, "HDR SB:", DUMP_PREFIX_NONE, 16, 1, out->sb,
+ sizeof(out->sb), false);
+
+ return 0;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_fill_hdr_info_packet);
+
+static int
+amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
+ struct drm_atomic_commit *state)
+{
+ struct drm_connector_state *new_con_state =
+ drm_atomic_get_new_connector_state(state, conn);
+ struct drm_connector_state *old_con_state =
+ drm_atomic_get_old_connector_state(state, conn);
+ struct drm_crtc *crtc = new_con_state->crtc;
+ struct drm_crtc_state *new_crtc_state;
+ struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn);
+ int ret;
+
+ if (WARN_ON(unlikely(!old_con_state || !new_con_state)))
+ return -EINVAL;
+
+ trace_amdgpu_dm_connector_atomic_check(new_con_state);
+
+ if (conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+ ret = drm_dp_mst_root_conn_atomic_check(new_con_state, &aconn->mst_mgr);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!crtc)
+ return 0;
+
+ if (new_con_state->privacy_screen_sw_state != old_con_state->privacy_screen_sw_state) {
+ new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(new_crtc_state))
+ return PTR_ERR(new_crtc_state);
+
+ new_crtc_state->mode_changed = true;
+ }
+
+ if (new_con_state->colorspace != old_con_state->colorspace) {
+ new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(new_crtc_state))
+ return PTR_ERR(new_crtc_state);
+
+ new_crtc_state->mode_changed = true;
+ }
+
+ if (new_con_state->content_type != old_con_state->content_type) {
+ new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(new_crtc_state))
+ return PTR_ERR(new_crtc_state);
+
+ new_crtc_state->mode_changed = true;
+ }
+
+ if (!drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state)) {
+ struct dc_info_packet hdr_infopacket;
+
+ ret = amdgpu_dm_fill_hdr_info_packet(new_con_state, &hdr_infopacket);
+ if (ret)
+ return ret;
+
+ new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(new_crtc_state))
+ return PTR_ERR(new_crtc_state);
+
+ /*
+ * DC considers the stream backends changed if the
+ * static metadata changes. Forcing the modeset also
+ * gives a simple way for userspace to switch from
+ * 8bpc to 10bpc when setting the metadata to enter
+ * or exit HDR.
+ *
+ * Changing the static metadata after it's been
+ * set is permissible, however. So only force a
+ * modeset if we're entering or exiting HDR.
+ */
+ new_crtc_state->mode_changed = new_crtc_state->mode_changed ||
+ !old_con_state->hdr_output_metadata ||
+ !new_con_state->hdr_output_metadata;
+ }
+
+ return 0;
+}
+
+static const struct drm_connector_helper_funcs
+amdgpu_dm_connector_helper_funcs = {
+ /*
+ * If hotplugging a second bigger display in FB Con mode, bigger resolution
+ * modes will be filtered by drm_mode_validate_size(), and those modes
+ * are missing after user start lightdm. So we need to renew modes list.
+ * in get_modes call back, not just return the modes count
+ */
+ .get_modes = get_modes,
+ .mode_valid = amdgpu_dm_connector_mode_valid,
+ .atomic_check = amdgpu_dm_connector_atomic_check,
+};
+
+int amdgpu_dm_convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth)
+{
+ switch (display_color_depth) {
+ case COLOR_DEPTH_666:
+ return 6;
+ case COLOR_DEPTH_888:
+ return 8;
+ case COLOR_DEPTH_101010:
+ return 10;
+ case COLOR_DEPTH_121212:
+ return 12;
+ case COLOR_DEPTH_141414:
+ return 14;
+ case COLOR_DEPTH_161616:
+ return 16;
+ default:
+ break;
+ }
+ return 0;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_convert_dc_color_depth_into_bpc);
+
+STATIC_IFN_KUNIT int to_drm_connector_type(enum signal_type st, uint32_t connector_id)
+{
+ switch (st) {
+ case SIGNAL_TYPE_HDMI_TYPE_A:
+ return DRM_MODE_CONNECTOR_HDMIA;
+ case SIGNAL_TYPE_EDP:
+ return DRM_MODE_CONNECTOR_eDP;
+ case SIGNAL_TYPE_LVDS:
+ return DRM_MODE_CONNECTOR_LVDS;
+ case SIGNAL_TYPE_RGB:
+ return DRM_MODE_CONNECTOR_VGA;
+ case SIGNAL_TYPE_DISPLAY_PORT:
+ case SIGNAL_TYPE_DISPLAY_PORT_MST:
+ /* External DP bridges have a different connector type. */
+ if (connector_id == CONNECTOR_ID_VGA)
+ return DRM_MODE_CONNECTOR_VGA;
+ else if (connector_id == CONNECTOR_ID_LVDS)
+ return DRM_MODE_CONNECTOR_LVDS;
+
+ return DRM_MODE_CONNECTOR_DisplayPort;
+ case SIGNAL_TYPE_DVI_DUAL_LINK:
+ case SIGNAL_TYPE_DVI_SINGLE_LINK:
+ if (connector_id == CONNECTOR_ID_SINGLE_LINK_DVII ||
+ connector_id == CONNECTOR_ID_DUAL_LINK_DVII)
+ return DRM_MODE_CONNECTOR_DVII;
+
+ return DRM_MODE_CONNECTOR_DVID;
+ case SIGNAL_TYPE_VIRTUAL:
+ return DRM_MODE_CONNECTOR_VIRTUAL;
+
+ default:
+ return DRM_MODE_CONNECTOR_Unknown;
+ }
+}
+EXPORT_IF_KUNIT(to_drm_connector_type);
+
+static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector)
+{
+ struct drm_encoder *encoder;
+
+ /* There is only one encoder per connector */
+ drm_connector_for_each_possible_encoder(connector, encoder)
+ return encoder;
+
+ return NULL;
+}
+
+static void amdgpu_dm_get_native_mode(struct drm_connector *connector)
+{
+ struct drm_encoder *encoder;
+ struct amdgpu_encoder *amdgpu_encoder;
+
+ encoder = amdgpu_dm_connector_to_encoder(connector);
+
+ if (encoder == NULL)
+ return;
+
+ amdgpu_encoder = to_amdgpu_encoder(encoder);
+
+ amdgpu_encoder->native_mode.clock = 0;
+
+ if (!list_empty(&connector->probed_modes)) {
+ struct drm_display_mode *preferred_mode = NULL;
+
+ list_for_each_entry(preferred_mode,
+ &connector->probed_modes,
+ head) {
+ if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED)
+ amdgpu_encoder->native_mode = *preferred_mode;
+
+ break;
+ }
+
+ }
+}
+
+static struct drm_display_mode *
+amdgpu_dm_create_common_mode(struct drm_encoder *encoder,
+ const char *name,
+ int hdisplay, int vdisplay)
+{
+ struct drm_device *dev = encoder->dev;
+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+ struct drm_display_mode *mode = NULL;
+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
+
+ mode = drm_mode_duplicate(dev, native_mode);
+
+ if (mode == NULL)
+ return NULL;
+
+ mode->hdisplay = hdisplay;
+ mode->vdisplay = vdisplay;
+ mode->type &= ~DRM_MODE_TYPE_PREFERRED;
+ strscpy(mode->name, name, DRM_DISPLAY_MODE_LEN);
+
+ return mode;
+
+}
+
+static const struct amdgpu_dm_mode_size {
+ char name[DRM_DISPLAY_MODE_LEN];
+ int w;
+ int h;
+} common_modes[] = {
+ { "640x480", 640, 480},
+ { "800x600", 800, 600},
+ { "1024x768", 1024, 768},
+ { "1280x720", 1280, 720},
+ { "1280x800", 1280, 800},
+ {"1280x1024", 1280, 1024},
+ { "1440x900", 1440, 900},
+ {"1680x1050", 1680, 1050},
+ {"1600x1200", 1600, 1200},
+ {"1920x1080", 1920, 1080},
+ {"1920x1200", 1920, 1200}
+};
+
+static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+{
+ struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+ struct drm_display_mode *mode = NULL;
+ struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
+ struct amdgpu_dm_connector *amdgpu_dm_connector =
+ to_amdgpu_dm_connector(connector);
+ int i;
+ int n;
+
+ if ((connector->connector_type != DRM_MODE_CONNECTOR_eDP) &&
+ (connector->connector_type != DRM_MODE_CONNECTOR_LVDS))
+ return;
+
+ n = ARRAY_SIZE(common_modes);
+
+ for (i = 0; i < n; i++) {
+ struct drm_display_mode *curmode = NULL;
+ bool mode_existed = false;
+
+ if (common_modes[i].w > native_mode->hdisplay ||
+ common_modes[i].h > native_mode->vdisplay ||
+ (common_modes[i].w == native_mode->hdisplay &&
+ common_modes[i].h == native_mode->vdisplay))
+ continue;
+
+ list_for_each_entry(curmode, &connector->probed_modes, head) {
+ if (common_modes[i].w == curmode->hdisplay &&
+ common_modes[i].h == curmode->vdisplay) {
+ mode_existed = true;
+ break;
+ }
+ }
+
+ if (mode_existed)
+ continue;
+
+ mode = amdgpu_dm_create_common_mode(encoder,
+ common_modes[i].name, common_modes[i].w,
+ common_modes[i].h);
+ if (!mode)
+ continue;
+
+ drm_mode_probed_add(connector, mode);
+ amdgpu_dm_connector->num_modes++;
+ }
+}
+
+void amdgpu_set_panel_orientation(struct drm_connector *connector)
+{
+ struct drm_encoder *encoder;
+ struct amdgpu_encoder *amdgpu_encoder;
+ const struct drm_display_mode *native_mode;
+
+ if (connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
+ connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
+ return;
+
+ mutex_lock(&connector->dev->mode_config.mutex);
+ amdgpu_dm_connector_get_modes(connector);
+ mutex_unlock(&connector->dev->mode_config.mutex);
+
+ encoder = amdgpu_dm_connector_to_encoder(connector);
+ if (!encoder)
+ return;
+
+ amdgpu_encoder = to_amdgpu_encoder(encoder);
+
+ native_mode = &amdgpu_encoder->native_mode;
+ if (native_mode->hdisplay == 0 || native_mode->vdisplay == 0)
+ return;
+
+ drm_connector_set_panel_orientation_with_quirk(connector,
+ DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
+ native_mode->hdisplay,
+ native_mode->vdisplay);
+}
+
+static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
+{
+ struct amdgpu_dm_connector *amdgpu_dm_connector =
+ to_amdgpu_dm_connector(connector);
+
+ if (drm_edid) {
+ /* empty probed_modes */
+ INIT_LIST_HEAD(&connector->probed_modes);
+ amdgpu_dm_connector->num_modes =
+ drm_edid_connector_add_modes(connector);
+
+ /* sorting the probed modes before calling function
+ * amdgpu_dm_get_native_mode() since EDID can have
+ * more than one preferred mode. The modes that are
+ * later in the probed mode list could be of higher
+ * and preferred resolution. For example, 3840x2160
+ * resolution in base EDID preferred timing and 4096x2160
+ * preferred resolution in DID extension block later.
+ */
+ drm_mode_sort(&connector->probed_modes);
+ amdgpu_dm_get_native_mode(connector);
+
+ /* Freesync capabilities are reset by calling
+ * drm_edid_connector_add_modes() and need to be
+ * restored here.
+ */
+ amdgpu_dm_update_freesync_caps(connector, drm_edid, false);
+ } else {
+ amdgpu_dm_connector->num_modes = 0;
+ }
+}
+
+STATIC_IFN_KUNIT bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector,
+ struct drm_display_mode *mode)
+{
+ struct drm_display_mode *m;
+
+ list_for_each_entry(m, &aconnector->base.probed_modes, head) {
+ if (drm_mode_equal(m, mode))
+ return true;
+ }
+
+ return false;
+}
+EXPORT_IF_KUNIT(is_duplicate_mode);
+
+static uint add_fs_modes(struct amdgpu_dm_connector *aconnector)
+{
+ const struct drm_display_mode *m;
+ struct drm_display_mode *new_mode;
+ uint i;
+ u32 new_modes_count = 0;
+
+ /* Standard FPS values
+ *
+ * 23.976 - TV/NTSC
+ * 24 - Cinema
+ * 25 - TV/PAL
+ * 29.97 - TV/NTSC
+ * 30 - TV/NTSC
+ * 48 - Cinema HFR
+ * 50 - TV/PAL
+ * 60 - Commonly used
+ * 48,72,96,120 - Multiples of 24
+ */
+ static const u32 common_rates[] = {
+ 23976, 24000, 25000, 29970, 30000,
+ 48000, 50000, 60000, 72000, 96000, 120000
+ };
+
+ /*
+ * Find mode with highest refresh rate with the same resolution
+ * as the preferred mode. Some monitors report a preferred mode
+ * with lower resolution than the highest refresh rate supported.
+ */
+
+ m = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, true);
+ if (!m)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(common_rates); i++) {
+ u64 target_vtotal, target_vtotal_diff;
+ u64 num, den;
+
+ if (drm_mode_vrefresh(m) * 1000 < common_rates[i])
+ continue;
+
+ if (common_rates[i] < aconnector->min_vfreq * 1000 ||
+ common_rates[i] > aconnector->max_vfreq * 1000)
+ continue;
+
+ num = (unsigned long long)m->clock * 1000 * 1000;
+ den = common_rates[i] * (unsigned long long)m->htotal;
+ target_vtotal = div_u64(num, den);
+ target_vtotal_diff = target_vtotal - m->vtotal;
+
+ /* Check for illegal modes */
+ if (m->vsync_start + target_vtotal_diff < m->vdisplay ||
+ m->vsync_end + target_vtotal_diff < m->vsync_start ||
+ m->vtotal + target_vtotal_diff < m->vsync_end)
+ continue;
+
+ new_mode = drm_mode_duplicate(aconnector->base.dev, m);
+ if (!new_mode)
+ goto out;
+
+ new_mode->vtotal += (u16)target_vtotal_diff;
+ new_mode->vsync_start += (u16)target_vtotal_diff;
+ new_mode->vsync_end += (u16)target_vtotal_diff;
+ new_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
+ new_mode->type |= DRM_MODE_TYPE_DRIVER;
+
+ if (!is_duplicate_mode(aconnector, new_mode)) {
+ drm_mode_probed_add(&aconnector->base, new_mode);
+ new_modes_count += 1;
+ } else
+ drm_mode_destroy(aconnector->base.dev, new_mode);
+ }
+ out:
+ return new_modes_count;
+}
+
+static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector,
+ const struct drm_edid *drm_edid)
+{
+ struct amdgpu_dm_connector *amdgpu_dm_connector =
+ to_amdgpu_dm_connector(connector);
+
+ if (!(amdgpu_freesync_vid_mode && drm_edid))
+ return;
+
+ if (!amdgpu_dm_connector->dc_sink || !amdgpu_dm_connector->dc_link)
+ return;
+
+ if (!dc_supports_vrr(amdgpu_dm_connector->dc_sink->ctx->dce_version))
+ return;
+
+ if (dc_connector_supports_analog(amdgpu_dm_connector->dc_link->link_id.id) &&
+ amdgpu_dm_connector->dc_sink->edid_caps.analog)
+ return;
+
+ if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ amdgpu_dm_connector->num_modes +=
+ add_fs_modes(amdgpu_dm_connector);
+}
+
+static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
+{
+ struct amdgpu_dm_connector *amdgpu_dm_connector =
+ to_amdgpu_dm_connector(connector);
+ struct dc_link *dc_link = amdgpu_dm_connector->dc_link;
+ struct drm_encoder *encoder;
+ const struct drm_edid *drm_edid = amdgpu_dm_connector->drm_edid;
+ struct dc_link_settings *verified_link_cap = &dc_link->verified_link_cap;
+ const struct dc *dc = dc_link->dc;
+
+ encoder = amdgpu_dm_connector_to_encoder(connector);
+
+ if (!drm_edid) {
+ amdgpu_dm_connector->num_modes =
+ drm_add_modes_noedid(connector, 640, 480);
+ if (dc->link_srv->dp_get_encoding_format(verified_link_cap) == DP_128b_132b_ENCODING)
+ amdgpu_dm_connector->num_modes +=
+ drm_add_modes_noedid(connector, 1920, 1080);
+
+ if (amdgpu_dm_connector->dc_sink &&
+ amdgpu_dm_connector->dc_sink->edid_caps.analog &&
+ dc_connector_supports_analog(dc_link->link_id.id)) {
+ /* Analog monitor connected by DAC load detection.
+ * Add common modes. It will be up to the user to select one that works.
+ */
+ for (int i = 0; i < ARRAY_SIZE(common_modes); i++)
+ amdgpu_dm_connector->num_modes += drm_add_modes_noedid(
+ connector, common_modes[i].w, common_modes[i].h);
+ }
+ } else {
+ amdgpu_dm_connector_ddc_get_modes(connector, drm_edid);
+ if (encoder)
+ amdgpu_dm_connector_add_common_modes(encoder, connector);
+ amdgpu_dm_connector_add_freesync_modes(connector, drm_edid);
+ }
+ amdgpu_dm_fbc_init(connector);
+
+ return amdgpu_dm_connector->num_modes;
+}
+
+static const u32 supported_colorspaces =
+ BIT(DRM_MODE_COLORIMETRY_BT709_YCC) |
+ BIT(DRM_MODE_COLORIMETRY_OPRGB) |
+ BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) |
+ BIT(DRM_MODE_COLORIMETRY_BT2020_YCC);
+
+static void hdmi_frl_status_polling_work(struct work_struct *work)
+{
+ struct amdgpu_display_manager *dm =
+ container_of(to_delayed_work(work), struct amdgpu_display_manager,
+ hdmi_frl_status_polling_work);
+ struct dc *dc = dm->dc;
+ struct dc_link *dc_link;
+ bool link_update = false;
+
+ for (int i = 0; i < MAX_LINKS; i++) {
+ dc_link = dc->links[i];
+
+
+ if (!dc_link || !dc_link->local_sink)
+ continue;
+
+ if (!dc_is_hdmi_signal(dc_link->connector_signal))
+ continue;
+
+ if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL)
+ continue;
+
+ link_update = dc_link_frl_poll_status_flag(dc_link);
+ if (link_update) {
+ mutex_lock(&dm->dc_lock);
+ dc_link_detect(dc_link, DETECT_REASON_RETRAIN);
+ mutex_unlock(&dm->dc_lock);
+ }
+ }
+
+ queue_delayed_work(dm->hdmi_frl_status_polling_wq,
+ &dm->hdmi_frl_status_polling_work,
+ msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms));
+}
+
+void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
+ struct amdgpu_dm_connector *aconnector,
+ int connector_type,
+ struct dc_link *link,
+ int link_index)
+{
+ struct amdgpu_device *adev = drm_to_adev(dm->ddev);
+
+ /*
+ * Some of the properties below require access to state, like bpc.
+ * Allocate some default initial connector state with our reset helper.
+ */
+ if (aconnector->base.funcs->reset)
+ aconnector->base.funcs->reset(&aconnector->base);
+
+ aconnector->connector_id = link_index;
+ aconnector->bl_idx = -1;
+ aconnector->dc_link = link;
+ aconnector->base.interlace_allowed = false;
+ aconnector->base.doublescan_allowed = false;
+ aconnector->base.stereo_allowed = false;
+ aconnector->base.dpms = DRM_MODE_DPMS_OFF;
+ aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */
+ aconnector->audio_inst = -1;
+ aconnector->pack_sdp_v1_3 = false;
+ aconnector->as_type = ADAPTIVE_SYNC_TYPE_NONE;
+ memset(&aconnector->vsdb_info, 0, sizeof(aconnector->vsdb_info));
+ mutex_init(&aconnector->hpd_lock);
+ mutex_init(&aconnector->handle_mst_msg_ready);
+
+ /*
+ * If HDMI HPD debounce delay is set, use the minimum between selected
+ * value and AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS
+ */
+ if (amdgpu_hdmi_hpd_debounce_delay_ms) {
+ aconnector->hdmi_hpd_debounce_delay_ms = min(amdgpu_hdmi_hpd_debounce_delay_ms,
+ AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS);
+ INIT_DELAYED_WORK(&aconnector->hdmi_hpd_debounce_work, amdgpu_dm_hdmi_hpd_debounce_work);
+ aconnector->hdmi_prev_sink = NULL;
+ } else {
+ aconnector->hdmi_hpd_debounce_delay_ms = 0;
+ }
+
+ dm->hdmi_frl_status_polling_delay_ms = 200;
+ INIT_DELAYED_WORK(&dm->hdmi_frl_status_polling_work, hdmi_frl_status_polling_work);
+ /*
+ * configure support HPD hot plug connector_>polled default value is 0
+ * which means HPD hot plug not supported
+ */
+ switch (connector_type) {
+ case DRM_MODE_CONNECTOR_HDMIA:
+ aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
+ aconnector->base.ycbcr_420_allowed =
+ link->link_enc->features.hdmi_ycbcr420_supported ? true : false;
+ break;
+ case DRM_MODE_CONNECTOR_DisplayPort:
+ aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
+ link->link_enc = link_enc_cfg_get_link_enc(link);
+ ASSERT(link->link_enc);
+ if (link->link_enc)
+ aconnector->base.ycbcr_420_allowed =
+ link->link_enc->features.dp_ycbcr420_supported ? true : false;
+ break;
+ case DRM_MODE_CONNECTOR_DVID:
+ aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
+ break;
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_VGA:
+ aconnector->base.polled =
+ DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+ break;
+ default:
+ break;
+ }
+
+ drm_object_attach_property(&aconnector->base.base,
+ dm->ddev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_NONE);
+
+ if (connector_type == DRM_MODE_CONNECTOR_HDMIA
+ || (connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root))
+ drm_connector_attach_broadcast_rgb_property(&aconnector->base);
+
+ drm_object_attach_property(&aconnector->base.base,
+ adev->mode_info.underscan_property,
+ UNDERSCAN_OFF);
+ drm_object_attach_property(&aconnector->base.base,
+ adev->mode_info.underscan_hborder_property,
+ 0);
+ drm_object_attach_property(&aconnector->base.base,
+ adev->mode_info.underscan_vborder_property,
+ 0);
+
+ if (!aconnector->mst_root)
+ drm_connector_attach_max_bpc_property(&aconnector->base, 8, 16);
+
+ aconnector->base.state->max_bpc = 16;
+ aconnector->base.state->max_requested_bpc = aconnector->base.state->max_bpc;
+
+ if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
+ /* Content Type is currently only implemented for HDMI. */
+ drm_connector_attach_content_type_property(&aconnector->base);
+ }
+
+ if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
+ if (!drm_mode_create_hdmi_colorspace_property(&aconnector->base, supported_colorspaces))
+ drm_connector_attach_colorspace_property(&aconnector->base);
+ } else if ((connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root) ||
+ connector_type == DRM_MODE_CONNECTOR_eDP) {
+ if (!drm_mode_create_dp_colorspace_property(&aconnector->base, supported_colorspaces))
+ drm_connector_attach_colorspace_property(&aconnector->base);
+ }
+
+ if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
+ connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
+ connector_type == DRM_MODE_CONNECTOR_eDP) {
+ drm_connector_attach_hdr_output_metadata_property(&aconnector->base);
+
+ if (!aconnector->mst_root)
+ drm_connector_attach_vrr_capable_property(&aconnector->base);
+
+
+ if (adev->dm.hdcp_workqueue)
+ drm_connector_attach_content_protection_property(&aconnector->base, true);
+ }
+
+ if (connector_type == DRM_MODE_CONNECTOR_eDP) {
+ struct drm_privacy_screen *privacy_screen;
+
+ drm_connector_attach_panel_type_property(&aconnector->base);
+
+ privacy_screen = drm_privacy_screen_get(adev_to_drm(adev)->dev, NULL);
+ if (!IS_ERR(privacy_screen)) {
+ drm_connector_attach_privacy_screen_provider(&aconnector->base,
+ privacy_screen);
+ } else if (PTR_ERR(privacy_screen) != -ENODEV) {
+ drm_warn(adev_to_drm(adev), "Error getting privacy-screen\n");
+ }
+ }
+}
+
+static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
+ struct ddc_service *ddc_service = i2c->ddc_service;
+ struct i2c_command cmd;
+ int i;
+ int result = -EIO;
+
+ if (!ddc_service->ddc_pin)
+ return result;
+
+ cmd.payloads = kzalloc_objs(struct i2c_payload, num);
+
+ if (!cmd.payloads)
+ return result;
+
+ cmd.number_of_payloads = num;
+ cmd.engine = I2C_COMMAND_ENGINE_DEFAULT;
+ cmd.speed = 100;
+
+ for (i = 0; i < num; i++) {
+ cmd.payloads[i].write = !(msgs[i].flags & I2C_M_RD);
+ cmd.payloads[i].address = msgs[i].addr;
+ cmd.payloads[i].length = msgs[i].len;
+ cmd.payloads[i].data = msgs[i].buf;
+ }
+
+ if (i2c->oem) {
+ if (dc_submit_i2c_oem(
+ ddc_service->ctx->dc,
+ &cmd))
+ result = num;
+ } else {
+ if (dc_submit_i2c(
+ ddc_service->ctx->dc,
+ ddc_service->link->link_index,
+ &cmd))
+ result = num;
+ }
+
+ kfree(cmd.payloads);
+ return result;
+}
+
+static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm amdgpu_dm_i2c_algo = {
+ .master_xfer = amdgpu_dm_i2c_xfer,
+ .functionality = amdgpu_dm_i2c_func,
+};
+
+struct amdgpu_i2c_adapter *
+amdgpu_dm_create_i2c(struct ddc_service *ddc_service, bool oem)
+{
+ struct amdgpu_device *adev = ddc_service->ctx->driver_context;
+ struct amdgpu_i2c_adapter *i2c;
+
+ i2c = kzalloc_obj(struct amdgpu_i2c_adapter);
+ if (!i2c)
+ return NULL;
+ i2c->base.owner = THIS_MODULE;
+ i2c->base.dev.parent = &adev->pdev->dev;
+ i2c->base.algo = &amdgpu_dm_i2c_algo;
+ if (oem)
+ snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c OEM bus");
+ else
+ snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d",
+ ddc_service->link->link_index);
+ i2c_set_adapdata(&i2c->base, i2c);
+ i2c->ddc_service = ddc_service;
+ i2c->oem = oem;
+
+ return i2c;
+}
+
+int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector)
+{
+ struct cec_connector_info conn_info;
+ struct drm_device *ddev = aconnector->base.dev;
+ struct device *hdmi_dev = ddev->dev;
+
+ if (amdgpu_dc_debug_mask & DC_DISABLE_HDMI_CEC) {
+ drm_info(ddev, "HDMI-CEC feature masked\n");
+ return -EINVAL;
+ }
+
+ cec_fill_conn_info_from_drm(&conn_info, &aconnector->base);
+ aconnector->notifier =
+ cec_notifier_conn_register(hdmi_dev, NULL, &conn_info);
+ if (!aconnector->notifier) {
+ drm_err(ddev, "Failed to create cec notifier\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/*
+ * Note: this function assumes that dc_link_detect() was called for the
+ * dc_link which will be represented by this aconnector.
+ */
+int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
+ struct amdgpu_dm_connector *aconnector,
+ u32 link_index,
+ struct amdgpu_encoder *aencoder)
+{
+ int res = 0;
+ int connector_type;
+ struct dc *dc = dm->dc;
+ struct dc_link *link = dc_get_link_at_index(dc, link_index);
+ struct amdgpu_i2c_adapter *i2c;
+
+ /* Not needed for writeback connector */
+ link->priv = aconnector;
+
+
+ i2c = amdgpu_dm_create_i2c(link->ddc, false);
+ if (!i2c) {
+ drm_err(adev_to_drm(dm->adev), "Failed to create i2c adapter data\n");
+ return -ENOMEM;
+ }
+
+ aconnector->i2c = i2c;
+ res = devm_i2c_add_adapter(dm->adev->dev, &i2c->base);
+
+ if (res) {
+ drm_err(adev_to_drm(dm->adev), "Failed to register hw i2c %d\n", link->link_index);
+ goto out_free;
+ }
+
+ connector_type = to_drm_connector_type(link->connector_signal, link->link_id.id);
+
+ res = drm_connector_init_with_ddc(
+ dm->ddev,
+ &aconnector->base,
+ &amdgpu_dm_connector_funcs,
+ connector_type,
+ &i2c->base);
+
+ if (res) {
+ drm_err(adev_to_drm(dm->adev), "connector_init failed\n");
+ aconnector->connector_id = -1;
+ goto out_free;
+ }
+
+ drm_connector_helper_add(
+ &aconnector->base,
+ &amdgpu_dm_connector_helper_funcs);
+
+ amdgpu_dm_connector_init_helper(
+ dm,
+ aconnector,
+ connector_type,
+ link,
+ link_index);
+
+ drm_connector_attach_encoder(
+ &aconnector->base, &aencoder->base);
+
+ if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
+ connector_type == DRM_MODE_CONNECTOR_HDMIB)
+ amdgpu_dm_initialize_hdmi_connector(aconnector);
+
+ if (dc_is_dp_signal(link->connector_signal))
+ amdgpu_dm_initialize_dp_connector(dm, aconnector, link->link_index);
+
+out_free:
+ if (res) {
+ kfree(i2c);
+ aconnector->i2c = NULL;
+ }
+ return res;
+}
+
+static int dm_force_atomic_commit(struct drm_connector *connector)
+{
+ int ret = 0;
+ struct drm_device *ddev = connector->dev;
+ struct drm_atomic_commit *state = drm_atomic_commit_alloc(ddev);
+ struct amdgpu_crtc *disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc);
+ struct drm_plane *plane = disconnected_acrtc->base.primary;
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_plane_state *plane_state;
+
+ if (!state)
+ return -ENOMEM;
+
+ state->acquire_ctx = ddev->mode_config.acquire_ctx;
+
+ /* Construct an atomic state to restore previous display setting */
+
+ /*
+ * Attach connectors to drm_atomic_commit
+ */
+ conn_state = drm_atomic_get_connector_state(state, connector);
+
+ /* Check for error in getting connector state */
+ if (IS_ERR(conn_state)) {
+ ret = PTR_ERR(conn_state);
+ goto out;
+ }
+
+ /* Attach crtc to drm_atomic_commit*/
+ crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base);
+
+ /* Check for error in getting crtc state */
+ if (IS_ERR(crtc_state)) {
+ ret = PTR_ERR(crtc_state);
+ goto out;
+ }
+
+ /* force a restore */
+ crtc_state->mode_changed = true;
+
+ /* Attach plane to drm_atomic_commit */
+ plane_state = drm_atomic_get_plane_state(state, plane);
+
+ /* Check for error in getting plane state */
+ if (IS_ERR(plane_state)) {
+ ret = PTR_ERR(plane_state);
+ goto out;
+ }
+
+ /* Call commit internally with the state we just constructed */
+ ret = drm_atomic_commit(state);
+
+out:
+ drm_atomic_commit_put(state);
+ if (ret)
+ drm_err(ddev, "Restoring old state failed with %i\n", ret);
+
+ return ret;
+}
+
+/*
+ * This function handles all cases when set mode does not come upon hotplug.
+ * This includes when a display is unplugged then plugged back into the
+ * same port and when running without usermode desktop manager support
+ */
+void dm_restore_drm_connector_state(struct drm_device *dev,
+ struct drm_connector *connector)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct amdgpu_crtc *disconnected_acrtc;
+ struct dm_crtc_state *acrtc_state;
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ return;
+
+ aconnector = to_amdgpu_dm_connector(connector);
+
+ if (!aconnector->dc_sink || !connector->state || !connector->encoder)
+ return;
+
+ disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc);
+ if (!disconnected_acrtc)
+ return;
+
+ acrtc_state = to_dm_crtc_state(disconnected_acrtc->base.state);
+ if (!acrtc_state->stream)
+ return;
+
+ /*
+ * If the previous sink is not released and different from the current,
+ * we deduce we are in a state where we can not rely on usermode call
+ * to turn on the display, so we do it here
+ */
+ if (acrtc_state->stream->sink != aconnector->dc_sink)
+ dm_force_atomic_commit(&aconnector->base);
+}
+
+static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm,
+ unsigned int offset,
+ unsigned int total_length,
+ u8 *data,
+ unsigned int length,
+ struct amdgpu_hdmi_vsdb_info *vsdb)
+{
+ bool res;
+ union dmub_rb_cmd cmd;
+ struct dmub_cmd_send_edid_cea *input;
+ struct dmub_cmd_edid_cea_output *output;
+
+ if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES)
+ return false;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ input = &cmd.edid_cea.data.input;
+
+ cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA;
+ cmd.edid_cea.header.sub_type = 0;
+ cmd.edid_cea.header.payload_bytes =
+ sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header);
+ input->offset = offset;
+ input->length = length;
+ input->cea_total_length = total_length;
+ memcpy(input->payload, data, length);
+
+ res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY);
+ if (!res) {
+ drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n");
+ return false;
+ }
+
+ output = &cmd.edid_cea.data.output;
+
+ if (output->type == DMUB_CMD__EDID_CEA_ACK) {
+ if (!output->ack.success) {
+ drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n",
+ output->ack.offset);
+ }
+ } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) {
+ if (!output->amd_vsdb.vsdb_found)
+ return false;
+
+ vsdb->freesync_supported = output->amd_vsdb.freesync_supported;
+ vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version;
+ vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate;
+ vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate;
+ vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code;
+ } else {
+ drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm,
+ u8 *edid_ext, int len,
+ struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+ int i;
+
+ /* send extension block to DMCU for parsing */
+ for (i = 0; i < len; i += 8) {
+ bool res;
+ int offset;
+
+ /* send 8 bytes a time */
+ if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8))
+ return false;
+
+ if (i+8 == len) {
+ /* EDID block sent completed, expect result */
+ int version, min_rate, max_rate;
+
+ res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate);
+ if (res) {
+ /* amd vsdb found */
+ vsdb_info->freesync_supported = 1;
+ vsdb_info->amd_vsdb_version = version;
+ vsdb_info->min_refresh_rate_hz = min_rate;
+ vsdb_info->max_refresh_rate_hz = max_rate;
+ /* Not enabled on DMCU*/
+ vsdb_info->freesync_mccs_vcp_code = 0;
+ return true;
+ }
+ /* not amd vsdb */
+ return false;
+ }
+
+ /* check for ack*/
+ res = dc_edid_parser_recv_cea_ack(dm->dc, &offset);
+ if (!res)
+ return false;
+ }
+
+ return false;
+}
+
+static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm,
+ u8 *edid_ext, int len,
+ struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+ int i;
+
+ /* send extension block to DMCU for parsing */
+ for (i = 0; i < len; i += 8) {
+ /* send 8 bytes a time */
+ if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info))
+ return false;
+ }
+
+ return vsdb_info->freesync_supported;
+}
+
+static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
+ u8 *edid_ext, int len,
+ struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+ struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
+ bool ret;
+
+ mutex_lock(&adev->dm.dc_lock);
+ if (adev->dm.dmub_srv)
+ ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info);
+ else
+ ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info);
+ mutex_unlock(&adev->dm.dc_lock);
+ return ret;
+}
+
+static void parse_edid_displayid_vrr(struct drm_connector *connector,
+ const struct edid *edid)
+{
+ u8 *edid_ext = NULL;
+ int i;
+ int j = 0;
+ u16 min_vfreq;
+ u16 max_vfreq;
+
+ if (!edid || !edid->extensions)
+ return;
+
+ /* Find DisplayID extension */
+ for (i = 0; i < edid->extensions; i++) {
+ edid_ext = (void *)(edid + (i + 1));
+ if (edid_ext[0] == DISPLAYID_EXT)
+ break;
+ }
+
+ if (i == edid->extensions)
+ return;
+
+ while (j < EDID_LENGTH) {
+ /* Get dynamic video timing range from DisplayID if available */
+ if (EDID_LENGTH - j > 13 && edid_ext[j] == 0x25 &&
+ (edid_ext[j+1] & 0xFE) == 0 && (edid_ext[j+2] == 9)) {
+ min_vfreq = edid_ext[j+9];
+ if (edid_ext[j+1] & 7)
+ max_vfreq = edid_ext[j+10] + ((edid_ext[j+11] & 3) << 8);
+ else
+ max_vfreq = edid_ext[j+10];
+
+ if (max_vfreq && min_vfreq) {
+ connector->display_info.monitor_range.max_vfreq = max_vfreq;
+ connector->display_info.monitor_range.min_vfreq = min_vfreq;
+
+ return;
+ }
+ }
+ j++;
+ }
+}
+
+static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
+ struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+ struct drm_connector *connector = &aconnector->base;
+
+ vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode;
+ vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version;
+
+ return connector->display_info.amd_vsdb.version != 0;
+}
+
+static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
+ const struct edid *edid,
+ struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+ u8 *edid_ext = NULL;
+ int i;
+ bool valid_vsdb_found = false;
+
+ /*----- drm_find_cea_extension() -----*/
+ /* No EDID or EDID extensions */
+ if (edid == NULL || edid->extensions == 0)
+ return -ENODEV;
+
+ /* Find CEA extension */
+ for (i = 0; i < edid->extensions; i++) {
+ edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1);
+ if (edid_ext[0] == CEA_EXT)
+ break;
+ }
+
+ if (i == edid->extensions)
+ return -ENODEV;
+
+ /*----- cea_db_offsets() -----*/
+ if (edid_ext[0] != CEA_EXT)
+ return -ENODEV;
+
+ valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info);
+
+ return valid_vsdb_found ? i : -ENODEV;
+}
+
+/**
+ * amdgpu_dm_update_freesync_caps - Update Freesync capabilities
+ *
+ * @connector: Connector to query.
+ * @drm_edid: DRM EDID from monitor
+ * @do_mccs: Controls whether MCCS (Monitor Control Command Set) over
+ * DDC (Display Data Channel) transactions are performed. When true,
+ * the driver queries the monitor to get or update additional FreeSync
+ * capability information. When false, these transactions are skipped.
+ *
+ * Amdgpu supports Freesync in DP and HDMI displays, and it is required to keep
+ * track of some of the display information in the internal data struct used by
+ * amdgpu_dm. This function checks which type of connector we need to set the
+ * FreeSync parameters.
+ */
+void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
+ const struct drm_edid *drm_edid, bool do_mccs)
+{
+ int i = 0;
+ struct amdgpu_dm_connector *amdgpu_dm_connector =
+ to_amdgpu_dm_connector(connector);
+ struct dm_connector_state *dm_con_state = NULL;
+ struct dc_sink *sink;
+ struct amdgpu_device *adev = drm_to_adev(connector->dev);
+ struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
+ const struct edid *edid;
+ bool freesync_capable = false;
+ enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE;
+
+ if (!connector->state) {
+ drm_err(adev_to_drm(adev), "%s - Connector has no state", __func__);
+ goto update;
+ }
+
+ sink = amdgpu_dm_connector->dc_sink ?
+ amdgpu_dm_connector->dc_sink :
+ amdgpu_dm_connector->dc_em_sink;
+
+ drm_edid_connector_update(connector, drm_edid);
+
+ if (!drm_edid || !sink) {
+ dm_con_state = to_dm_connector_state(connector->state);
+
+ amdgpu_dm_connector->min_vfreq = 0;
+ amdgpu_dm_connector->max_vfreq = 0;
+ freesync_capable = false;
+
+ goto update;
+ }
+
+ dm_con_state = to_dm_connector_state(connector->state);
+
+ if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version))
+ goto update;
+
+ /* FIXME: Get rid of drm_edid_raw() */
+ edid = drm_edid_raw(drm_edid);
+
+ /* Some eDP panels only have the refresh rate range info in DisplayID */
+ if ((connector->display_info.monitor_range.min_vfreq == 0 ||
+ connector->display_info.monitor_range.max_vfreq == 0))
+ parse_edid_displayid_vrr(connector, edid);
+
+ if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
+ sink->sink_signal == SIGNAL_TYPE_EDP)) {
+ if (amdgpu_dm_connector->dc_link &&
+ amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
+ amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
+ amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
+ if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
+ }
+
+ get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
+
+ if (vsdb_info.replay_mode) {
+ amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode;
+ amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
+ amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP;
+ }
+
+ } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
+ i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
+ if (i >= 0) {
+ amdgpu_dm_connector->vsdb_info = vsdb_info;
+ sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
+
+ if (vsdb_info.freesync_supported) {
+ amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
+ amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
+ if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
+
+ connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
+ connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
+ }
+ }
+ }
+
+ if (amdgpu_dm_connector->dc_link)
+ as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link);
+
+ if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
+ i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
+ if (i >= 0) {
+ amdgpu_dm_connector->vsdb_info = vsdb_info;
+ sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
+
+ if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
+ amdgpu_dm_connector->pack_sdp_v1_3 = true;
+ amdgpu_dm_connector->as_type = as_type;
+
+ amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
+ amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
+ if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
+ freesync_capable = true;
+
+ connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
+ connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
+ }
+ }
+ }
+
+ /* Handle MCCS */
+ if (do_mccs)
+ dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
+
+ if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) &&
+ (!sink->edid_caps.freesync_vcp_code ||
+ (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported)))
+ freesync_capable = false;
+
+ if (do_mccs && sink->mccs_caps.freesync_supported && freesync_capable)
+ dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
+
+update:
+ if (dm_con_state)
+ dm_con_state->freesync_capable = freesync_capable;
+
+ if (connector->state && amdgpu_dm_connector->dc_link && !freesync_capable &&
+ amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported) {
+ amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported = false;
+ amdgpu_dm_connector->dc_link->replay_settings.replay_feature_enabled = false;
+ }
+
+ if (connector->vrr_capable_property)
+ drm_connector_set_vrr_capable_property(connector,
+ freesync_capable);
+}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h
new file mode 100644
index 000000000000..c5b8b13f8f06
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef __AMDGPU_DM_CONNECTOR_H__
+#define __AMDGPU_DM_CONNECTOR_H__
+
+struct amdgpu_device;
+struct amdgpu_dm_connector;
+struct amdgpu_display_manager;
+struct amdgpu_encoder;
+struct amdgpu_i2c_adapter;
+struct dc_crtc_timing;
+struct dc_link;
+enum signal_type;
+struct dc_state;
+struct dc_stream_state;
+struct ddc_service;
+struct dm_connector_state;
+struct drm_atomic_commit;
+struct drm_device;
+struct drm_encoder_helper_funcs;
+struct drm_connector;
+struct drm_connector_state;
+struct drm_crtc;
+struct drm_device;
+struct drm_display_mode;
+struct drm_edid;
+struct drm_property;
+
+void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector);
+
+struct drm_connector_state *
+amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector);
+
+int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
+ struct drm_connector_state *connector_state,
+ struct drm_property *property,
+ uint64_t val);
+
+int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
+ const struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t *val);
+
+void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
+ struct amdgpu_dm_connector *aconnector,
+ int connector_type,
+ struct dc_link *link,
+ int link_index);
+
+enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
+ const struct drm_display_mode *mode);
+
+void dm_restore_drm_connector_state(struct drm_device *dev,
+ struct drm_connector *connector);
+
+void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
+ const struct drm_edid *drm_edid,
+ bool do_mccs);
+
+void amdgpu_dm_update_connector_after_detect(
+ struct amdgpu_dm_connector *aconnector);
+
+void amdgpu_dm_hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector);
+int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector);
+
+struct drm_connector *
+amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
+ struct drm_crtc *crtc);
+
+int amdgpu_dm_convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth);
+
+struct dc_stream_state *
+amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector,
+ const struct drm_display_mode *drm_mode,
+ const struct dm_connector_state *dm_state,
+ const struct dc_stream_state *old_stream);
+
+int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
+ struct amdgpu_dm_connector *amdgpu_dm_connector,
+ u32 link_index,
+ struct amdgpu_encoder *amdgpu_encoder);
+
+void amdgpu_dm_s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend);
+
+int amdgpu_dm_detect_mst_link_for_all_connectors(struct drm_device *dev);
+
+void amdgpu_set_panel_orientation(struct drm_connector *connector);
+
+enum dc_color_depth
+amdgpu_dm_convert_color_depth_from_display_info(const struct drm_connector *connector,
+ bool is_y420, int requested_bpc);
+
+void amdgpu_dm_update_stream_scaling_settings(struct drm_device *dev,
+ const struct drm_display_mode *mode,
+ const struct dm_connector_state *dm_state,
+ struct dc_stream_state *stream);
+
+bool amdgpu_dm_is_freesync_video_mode(const struct drm_display_mode *mode,
+ struct amdgpu_dm_connector *aconnector);
+
+int amdgpu_dm_fill_hdr_info_packet(const struct drm_connector_state *state,
+ struct dc_info_packet *out);
+
+enum dc_color_space
+amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
+ const struct drm_connector_state *connector_state);
+
+struct drm_display_mode *
+amdgpu_dm_get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector,
+ bool use_probed_modes);
+
+struct amdgpu_i2c_adapter *
+amdgpu_dm_create_i2c(struct ddc_service *ddc_service, bool oem);
+
+#define DDC_MANUFACTURERNAME_SAMSUNG 0x2D4C
+
+/* Encoder functions */
+extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs;
+int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev);
+int amdgpu_dm_encoder_init(struct drm_device *dev,
+ struct amdgpu_encoder *aencoder,
+ uint32_t link_index);
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+enum drm_mode_subconnector get_subconnector_type(struct dc_link *link);
+enum display_content_type
+get_output_content_type(const struct drm_connector_state *connector_state);
+bool adjust_colour_depth_from_display_info(struct dc_crtc_timing *timing_out,
+ const struct drm_display_info *info);
+
+int to_drm_connector_type(enum signal_type st, uint32_t connector_id);
+bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector, struct drm_display_mode *mode);
+enum dc_aspect_ratio get_aspect_ratio(const struct drm_display_mode *mode_in);
+void decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
+ const struct drm_display_mode *native_mode,
+ bool scale_enabled);
+#endif
+#endif /* __AMDGPU_DM_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
index 88f7cfea5624..970490c401e9 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
@@ -87,6 +87,65 @@ bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src)
}
EXPORT_IF_KUNIT(dm_need_crc_dither);
+/**
+ * dm_need_dp_aux() - Does this source transition require the DP AUX handle?
+ * @source: Requested CRC source.
+ * @cur_crc_src: Current CRC source.
+ *
+ * Returns true when either the new source is DPRX-based (starting DPRX CRC),
+ * or the current source is DPRX-based and the new source is NONE (stopping it).
+ *
+ * Return: true if the DP AUX handle is needed, false otherwise.
+ */
+STATIC_IFN_KUNIT
+bool dm_need_dp_aux(enum amdgpu_dm_pipe_crc_source source,
+ enum amdgpu_dm_pipe_crc_source cur_crc_src)
+{
+ return dm_is_crc_source_dprx(source) ||
+ (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE && dm_is_crc_source_dprx(cur_crc_src));
+}
+EXPORT_IF_KUNIT(dm_need_dp_aux);
+
+/**
+ * dm_crc_source_should_start_dprx() - Should drm_dp_start_crc() be called?
+ * @source: Requested CRC source.
+ * @cur_crc_src: Current CRC source.
+ *
+ * True when CRC is transitioning from off to a DPRX source
+ * (!enabled && enable && is_dprx(@source)).
+ *
+ * Return: true if drm_dp_start_crc() should be called, false otherwise.
+ */
+STATIC_IFN_KUNIT
+bool dm_crc_source_should_start_dprx(enum amdgpu_dm_pipe_crc_source source,
+ enum amdgpu_dm_pipe_crc_source cur_crc_src)
+{
+ return !amdgpu_dm_is_valid_crc_source(cur_crc_src) &&
+ amdgpu_dm_is_valid_crc_source(source) &&
+ dm_is_crc_source_dprx(source);
+}
+EXPORT_IF_KUNIT(dm_crc_source_should_start_dprx);
+
+/**
+ * dm_crc_source_should_stop_dprx() - Should drm_dp_stop_crc() be called?
+ * @source: Requested CRC source.
+ * @cur_crc_src: Current CRC source.
+ *
+ * True when CRC is transitioning from a DPRX source to off
+ * (enabled && !enable && is_dprx(@cur_crc_src)).
+ *
+ * Return: true if drm_dp_stop_crc() should be called, false otherwise.
+ */
+STATIC_IFN_KUNIT
+bool dm_crc_source_should_stop_dprx(enum amdgpu_dm_pipe_crc_source source,
+ enum amdgpu_dm_pipe_crc_source cur_crc_src)
+{
+ return amdgpu_dm_is_valid_crc_source(cur_crc_src) &&
+ !amdgpu_dm_is_valid_crc_source(source) &&
+ dm_is_crc_source_dprx(cur_crc_src);
+}
+EXPORT_IF_KUNIT(dm_crc_source_should_stop_dprx);
+
const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc,
size_t *count)
{
@@ -496,7 +555,7 @@ amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
{
enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
- if (source < 0) {
+ if (source == AMDGPU_DM_PIPE_CRC_SOURCE_INVALID) {
DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
src_name, crtc->index);
return -EINVAL;
@@ -595,7 +654,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
bool enabled = false;
int ret = 0;
- if (source < 0) {
+ if (source == AMDGPU_DM_PIPE_CRC_SOURCE_INVALID) {
DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
src_name, crtc->index);
return -EINVAL;
@@ -622,7 +681,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
*/
ret = wait_for_completion_interruptible_timeout(
&commit->hw_done, 10 * HZ);
- if (ret < 0)
+ if (ret < 0 && ret != -ERESTARTSYS)
goto cleanup;
if (ret == 0) {
@@ -650,9 +709,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
* CRTC DITHER | XXXX | Enable CRTC CRC, set dither
* DPRX DITHER | XXXX | Enable DPRX CRC, need 'aux', set dither
*/
- if (dm_is_crc_source_dprx(source) ||
- (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE &&
- dm_is_crc_source_dprx(cur_crc_src))) {
+ if (dm_need_dp_aux(source, cur_crc_src)) {
struct amdgpu_dm_connector *aconn = NULL;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
@@ -714,23 +771,24 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
goto cleanup;
}
- if (!enabled && enable) {
- if (dm_is_crc_source_dprx(source)) {
- if (drm_dp_start_crc(aux, crtc)) {
- DRM_DEBUG_DRIVER("dp start crc failed\n");
- ret = -EINVAL;
- goto cleanup;
- }
+ if (dm_crc_source_should_start_dprx(source, cur_crc_src)) {
+ /* !enabled && enable && is_dprx(source): CRC off → DPRX on */
+ if (drm_dp_start_crc(aux, crtc)) {
+ DRM_DEBUG_DRIVER("dp start crc failed\n");
+ ret = -EINVAL;
+ goto cleanup;
}
- } else if (enabled && !enable) {
+ } else if (dm_crc_source_should_stop_dprx(source, cur_crc_src)) {
+ /* enabled && !enable && is_dprx(cur_crc_src): DPRX on → CRC off */
drm_crtc_vblank_put(crtc);
- if (dm_is_crc_source_dprx(source)) {
- if (drm_dp_stop_crc(aux)) {
- DRM_DEBUG_DRIVER("dp stop crc failed\n");
- ret = -EINVAL;
- goto cleanup;
- }
+ if (drm_dp_stop_crc(aux)) {
+ DRM_DEBUG_DRIVER("dp stop crc failed\n");
+ ret = -EINVAL;
+ goto cleanup;
}
+ } else if (enabled && !enable) {
+ /* Non-DPRX source (e.g. CRTC) turning off: release vblank ref */
+ drm_crtc_vblank_put(crtc);
}
spin_lock_irq(&drm_dev->event_lock);
@@ -767,9 +825,9 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc)
{
struct dm_crtc_state *crtc_state;
struct dc_stream_state *stream_state;
- struct drm_device *drm_dev = NULL;
+ struct drm_device *drm_dev;
enum amdgpu_dm_pipe_crc_source cur_crc_src;
- struct amdgpu_crtc *acrtc = NULL;
+ struct amdgpu_crtc *acrtc;
uint32_t crcs[3];
unsigned long flags;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h
index c9aa0c82038f..8bb8a6f6c148 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h
@@ -156,6 +156,12 @@ enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source);
bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src);
bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src);
bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src);
+bool dm_need_dp_aux(enum amdgpu_dm_pipe_crc_source source,
+ enum amdgpu_dm_pipe_crc_source cur_crc_src);
+bool dm_crc_source_should_start_dprx(enum amdgpu_dm_pipe_crc_source source,
+ enum amdgpu_dm_pipe_crc_source cur_crc_src);
+bool dm_crc_source_should_stop_dprx(enum amdgpu_dm_pipe_crc_source source,
+ enum amdgpu_dm_pipe_crc_source cur_crc_src);
#endif
#endif /* AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 3dcedaa67ed8..0ad7704800d9 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -34,6 +34,7 @@
#include "amdgpu_dm_plane.h"
#include "amdgpu_dm_trace.h"
#include "amdgpu_dm_debugfs.h"
+#include "amdgpu_dm_kunit_helpers.h"
#include "modules/inc/mod_power.h"
#define HPD_DETECTION_PERIOD_uS 2000000
@@ -65,6 +66,7 @@ bool amdgpu_dm_crtc_modeset_required(struct drm_crtc_state *crtc_state,
{
return crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
}
+EXPORT_IF_KUNIT(amdgpu_dm_crtc_modeset_required);
bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc)
@@ -74,6 +76,7 @@ bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc)
acrtc->dm_irq_params.freesync_config.state ==
VRR_STATE_ACTIVE_FIXED;
}
+EXPORT_IF_KUNIT(amdgpu_dm_crtc_vrr_active_irq);
int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
{
@@ -93,12 +96,14 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
acrtc->crtc_id, enable ? "en" : "dis", rc);
return rc;
}
+EXPORT_IF_KUNIT(amdgpu_dm_crtc_set_vupdate_irq);
bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state)
{
return dm_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE ||
dm_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED;
}
+EXPORT_IF_KUNIT(amdgpu_dm_crtc_vrr_active);
/**
* amdgpu_dm_crtc_set_static_screen_optimze() - Toggle static screen optimizations.
@@ -156,6 +161,7 @@ bool amdgpu_dm_is_headless(struct amdgpu_device *adev)
drm_connector_list_iter_end(&iter);
return is_headless;
}
+EXPORT_IF_KUNIT(amdgpu_dm_is_headless);
static void amdgpu_dm_idle_worker(struct work_struct *work)
{
@@ -207,6 +213,7 @@ struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev)
return idle_work;
}
+EXPORT_IF_KUNIT(idle_create_workqueue);
static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
{
@@ -437,13 +444,13 @@ static void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc)
{
struct dm_crtc_state *state;
- if (crtc->state)
- amdgpu_dm_crtc_destroy_state(crtc, crtc->state);
-
state = kzalloc_obj(*state);
- if (WARN_ON(!state))
+ if (!state)
return;
+ if (crtc->state)
+ amdgpu_dm_crtc_destroy_state(crtc, crtc->state);
+
__drm_atomic_helper_crtc_reset(crtc, &state->base);
}
@@ -595,12 +602,13 @@ static void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
amdgpu_dm_crtc_count_crtc_active_planes(new_crtc_state);
}
-static bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+STATIC_IFN_KUNIT bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
return true;
}
+EXPORT_IF_KUNIT(amdgpu_dm_crtc_helper_mode_fixup);
static int amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *state)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h
index e9fb52f0e66d..d8b004f613ab 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h
@@ -42,6 +42,12 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable);
bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc);
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+#endif
+
bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state);
int amdgpu_dm_crtc_enable_vblank(struct drm_crtc *crtc);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
index 7db38ad3f848..830cf8da06b4 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
@@ -606,7 +606,7 @@ static int dp_lttpr_status_show(struct seq_file *m, void *unused)
break;
}
- seq_puts(m, "\n");
+ seq_putc(m, '\n');
return 0;
}
@@ -1081,7 +1081,7 @@ static int psr_capability_show(struct seq_file *m, void *data)
seq_printf(m, "Driver support: %s", str_yes_no(link->psr_settings.psr_feature_enabled));
if (link->psr_settings.psr_version)
seq_printf(m, " [0x%02x]", link->psr_settings.psr_version);
- seq_puts(m, "\n");
+ seq_putc(m, '\n');
return 0;
}
@@ -1266,7 +1266,7 @@ static int hdcp_sink_capability_show(struct seq_file *m, void *data)
if (!hdcp_cap && !hdcp2_cap)
seq_printf(m, "%s ", "None");
- seq_puts(m, "\n");
+ seq_putc(m, '\n');
return 0;
}
@@ -2710,11 +2710,10 @@ static int ips_status_show(struct seq_file *m, void *unused)
rcg_count = ips_fw->rcg_exit_count;
ips1_count = ips_fw->ips1_exit_count;
ips2_count = ips_fw->ips2_exit_count;
- seq_printf(m, "exit counts: rcg=%u ips1=%u ips2=%u",
+ seq_printf(m, "exit counts: rcg=%u ips1=%u ips2=%u\n",
rcg_count,
ips1_count,
ips2_count);
- seq_puts(m, "\n");
}
return 0;
}
@@ -2971,7 +2970,7 @@ static ssize_t hdmi_cec_state_write(struct file *f, const char __user *buf,
ret = amdgpu_dm_initialize_hdmi_connector(aconnector);
if (ret)
return ret;
- hdmi_cec_set_edid(aconnector);
+ amdgpu_dm_hdmi_cec_set_edid(aconnector);
} else {
if (!aconnector->notifier)
return -EINVAL;
@@ -2982,6 +2981,64 @@ static ssize_t hdmi_cec_state_write(struct file *f, const char __user *buf,
return size;
}
+/**
+ * hdmi_automation_enable - Enable/Disable HDMI automation feature
+ * @f: file structure.
+ * @buf: userspace buffer. set to '1' to enable; '0' to disable automation feature.
+ * @size: size of buffer from userpsace.
+ * @pos: unused.
+ *
+ * Return size on success, error code on failure
+ */
+static ssize_t hdmi_automation_enable(struct file *f, const char __user *buf,
+ size_t size, loff_t *pos)
+{
+ struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
+ char *wr_buf = NULL;
+ const uint32_t wr_buf_size = 40;
+ int max_param_num = 1;
+ uint8_t param_nums = 0;
+ long param[2];
+ bool hdmi_comp_auto;
+
+ if (size == 0)
+ return -EINVAL;
+
+ wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
+ if (!wr_buf)
+ return -ENOSPC;
+
+ if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
+ (long *)param, buf,
+ max_param_num,
+ &param_nums)) {
+ kfree(wr_buf);
+ return -EINVAL;
+ }
+
+ if (param_nums <= 0) {
+ kfree(wr_buf);
+ DRM_DEBUG_DRIVER("user data not be read\n");
+ return -EINVAL;
+ }
+
+ switch (param[0]) {
+ case 0:
+ hdmi_comp_auto = false;
+ break;
+ case 1:
+ default:
+ hdmi_comp_auto = true;
+ break;
+ }
+
+ /* Persist setting across sink re-detection/hotplug. */
+ aconnector->hdmi_comp_auto = hdmi_comp_auto;
+
+ kfree(wr_buf);
+ return size;
+}
+
DEFINE_SHOW_ATTRIBUTE(dp_dsc_fec_support);
DEFINE_SHOW_ATTRIBUTE(dmub_fw_state);
DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer);
@@ -3099,6 +3156,12 @@ static const struct file_operations dp_mst_link_settings_debugfs_fops = {
.llseek = default_llseek
};
+static const struct file_operations hdmi_automation_debugfs_fops = {
+ .owner = THIS_MODULE,
+ .write = hdmi_automation_enable,
+ .llseek = default_llseek
+};
+
static const struct {
char *name;
const struct file_operations *fops;
@@ -3131,7 +3194,8 @@ static const struct {
const struct file_operations *fops;
} hdmi_debugfs_entries[] = {
{"hdcp_sink_capability", &hdcp_sink_capability_fops},
- {"hdmi_cec_state", &hdmi_cec_state_fops}
+ {"hdmi_cec_state", &hdmi_cec_state_fops},
+ {"hdmi_automation", &hdmi_automation_debugfs_fops}
};
/*
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
new file mode 100644
index 000000000000..0aa99d1a542f
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
@@ -0,0 +1,943 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dm_services_types.h"
+#include "dc.h"
+#include "dc/inc/core_types.h"
+#include "dc/dc_dmub_srv.h"
+#include "dmub/dmub_srv.h"
+#include "dc/inc/hw/dmcu.h"
+#include "dc/inc/hw/abm.h"
+#include "dal_asic_id.h"
+
+#include "amdgpu.h"
+#include "amdgpu_display.h"
+#include "amdgpu_ucode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_dmub.h"
+#include "amdgpu_dm_kunit_helpers.h"
+#include <linux/component.h>
+#include <linux/firmware.h>
+
+static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch");
+
+MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB);
+MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID_DMUB);
+MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER_DMUB);
+MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE_DMUB);
+MODULE_FIRMWARE(FIRMWARE_VANGOGH_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH_DMUB);
+MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY_DMUB);
+MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_314_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_35_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_351_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB);
+MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
+
+/**
+ * dm_dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command.
+ * @adev: amdgpu_device pointer
+ * @notify: dmub notification structure
+ *
+ * Dmub AUX or SET_CONFIG command completion processing callback
+ * Copies dmub notification to DM which is to be read by AUX command.
+ * issuing thread and also signals the event to wake up the thread.
+ */
+void dm_dmub_aux_setconfig_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify)
+{
+ if (adev->dm.dmub_notify)
+ memcpy(adev->dm.dmub_notify, notify, sizeof(struct dmub_notification));
+ if (notify->type == DMUB_NOTIFICATION_AUX_REPLY)
+ complete(&adev->dm.dmub_aux_transfer_done);
+}
+EXPORT_IF_KUNIT(dm_dmub_aux_setconfig_callback);
+
+void dm_dmub_aux_fused_io_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify)
+{
+ if (!adev || !notify) {
+ ASSERT(false);
+ return;
+ }
+
+ const struct dmub_cmd_fused_request *req = &notify->fused_request;
+ const uint8_t ddc_line = req->u.aux.ddc_line;
+
+ if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) {
+ ASSERT(false);
+ return;
+ }
+
+ struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line];
+
+ static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch");
+ memcpy(sync->reply_data, req, sizeof(*req));
+ complete(&sync->replied);
+}
+EXPORT_IF_KUNIT(dm_dmub_aux_fused_io_callback);
+
+/**
+ * dm_register_dmub_notify_callback - Sets callback for DMUB notify
+ * @adev: amdgpu_device pointer
+ * @type: Type of dmub notification
+ * @callback: Dmub interrupt callback function
+ * @dmub_int_thread_offload: offload indicator
+ *
+ * API to register a dmub callback handler for a dmub notification
+ * Also sets indicator whether callback processing to be offloaded.
+ * to dmub interrupt handling thread
+ * Return: true if successfully registered, false if there is existing registration
+ */
+bool dm_register_dmub_notify_callback(struct amdgpu_device *adev,
+ enum dmub_notification_type type,
+ dmub_notify_interrupt_callback_t callback,
+ bool dmub_int_thread_offload)
+{
+ if (!callback || type >= ARRAY_SIZE(adev->dm.dmub_thread_offload))
+ return false;
+
+ adev->dm.dmub_callback[type] = callback;
+ adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload;
+
+ return true;
+}
+EXPORT_IF_KUNIT(dm_register_dmub_notify_callback);
+
+int dm_dmub_hw_init(struct amdgpu_device *adev)
+{
+ const struct dmcub_firmware_header_v1_0 *hdr;
+ struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
+ struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
+ const struct firmware *dmub_fw = adev->dm.dmub_fw;
+ struct dc *dc = adev->dm.dc;
+ struct dmcu *dmcu = adev->dm.dc->res_pool->dmcu;
+ struct abm *abm = adev->dm.dc->res_pool->abm;
+ struct dc_context *ctx = adev->dm.dc->ctx;
+ struct dmub_srv_hw_params hw_params;
+ enum dmub_status status;
+ const unsigned char *fw_inst_const, *fw_bss_data;
+ u32 i, fw_inst_const_size, fw_bss_data_size;
+ bool has_hw_support;
+
+ if (!dmub_srv)
+ /* DMUB isn't supported on the ASIC. */
+ return 0;
+
+ if (!fb_info) {
+ drm_err(adev_to_drm(adev), "No framebuffer info for DMUB service.\n");
+ return -EINVAL;
+ }
+
+ if (!dmub_fw) {
+ /* Firmware required for DMUB support. */
+ drm_err(adev_to_drm(adev), "No firmware provided for DMUB.\n");
+ return -EINVAL;
+ }
+
+ /* initialize register offsets for ASICs with runtime initialization available */
+ if (dmub_srv->hw_funcs.init_reg_offsets)
+ dmub_srv->hw_funcs.init_reg_offsets(dmub_srv, ctx);
+
+ status = dmub_srv_has_hw_support(dmub_srv, &has_hw_support);
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error checking HW support for DMUB: %d\n", status);
+ return -EINVAL;
+ }
+
+ if (!has_hw_support) {
+ drm_info(adev_to_drm(adev), "DMUB unsupported on ASIC\n");
+ return 0;
+ }
+
+ /* Reset DMCUB if it was previously running - before we overwrite its memory. */
+ status = dmub_srv_hw_reset(dmub_srv);
+ if (status != DMUB_STATUS_OK)
+ drm_warn(adev_to_drm(adev), "Error resetting DMUB HW: %d\n", status);
+
+ hdr = (const struct dmcub_firmware_header_v1_0 *)dmub_fw->data;
+
+ fw_inst_const = dmub_fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+ PSP_HEADER_BYTES_256;
+
+ fw_bss_data = dmub_fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+ le32_to_cpu(hdr->inst_const_bytes);
+
+ /* Copy firmware and bios info into FB memory. */
+ fw_inst_const_size = adev->dm.fw_inst_size;
+
+ fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
+
+ /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP,
+ * amdgpu_ucode_init_single_fw will load dmub firmware
+ * fw_inst_const part to cw0; otherwise, the firmware back door load
+ * will be done by dm_dmub_hw_init
+ */
+ if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
+ memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const,
+ fw_inst_const_size);
+ }
+
+ if (fw_bss_data_size)
+ memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr,
+ fw_bss_data, fw_bss_data_size);
+
+ /* Copy firmware bios info into FB memory. */
+ memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios,
+ adev->bios_size);
+
+ /* Reset regions that need to be reset. */
+ memset(fb_info->fb[DMUB_WINDOW_4_MAILBOX].cpu_addr, 0,
+ fb_info->fb[DMUB_WINDOW_4_MAILBOX].size);
+
+ memset(fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr, 0,
+ fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size);
+
+ memset(fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr, 0,
+ fb_info->fb[DMUB_WINDOW_6_FW_STATE].size);
+
+ memset(fb_info->fb[DMUB_WINDOW_SHARED_STATE].cpu_addr, 0,
+ fb_info->fb[DMUB_WINDOW_SHARED_STATE].size);
+
+ /* Initialize hardware. */
+ memset(&hw_params, 0, sizeof(hw_params));
+ hw_params.soc_fb_info.fb_base = adev->gmc.fb_start;
+ hw_params.soc_fb_info.fb_offset = adev->vm_manager.vram_base_offset;
+
+ /* backdoor load firmware and trigger dmub running */
+ if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
+ hw_params.load_inst_const = true;
+
+ if (dmcu)
+ hw_params.psp_version = dmcu->psp_version;
+
+ for (i = 0; i < fb_info->num_fb; ++i)
+ hw_params.fb[i] = &fb_info->fb[i];
+
+ /* Enable usb4 dpia in the FW APU */
+ if (dc->caps.is_apu &&
+ dc->res_pool->usb4_dpia_count != 0 &&
+ !dc->debug.dpia_debug.bits.disable_dpia) {
+ hw_params.dpia_supported = true;
+ hw_params.disable_dpia = dc->debug.dpia_debug.bits.disable_dpia;
+ hw_params.dpia_hpd_int_enable_supported = false;
+ hw_params.enable_non_transparent_setconfig = dc->config.consolidated_dpia_dp_lt;
+ hw_params.disable_dpia_bw_allocation = !dc->config.usb4_bw_alloc_support;
+ }
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(3, 5, 0):
+ case IP_VERSION(3, 5, 1):
+ case IP_VERSION(3, 6, 0):
+ case IP_VERSION(4, 2, 0):
+ case IP_VERSION(4, 2, 1):
+ hw_params.ips_sequential_ono = adev->external_rev_id > 0x10;
+ hw_params.lower_hbr3_phy_ssc = true;
+ break;
+ default:
+ break;
+ }
+
+ status = dmub_srv_hw_init(dmub_srv, &hw_params);
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error initializing DMUB HW: %d\n", status);
+ return -EINVAL;
+ }
+
+ /* Wait for firmware load to finish. */
+ status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
+ if (status != DMUB_STATUS_OK)
+ drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
+
+ /* Init DMCU and ABM if available. */
+ if (dmcu && abm) {
+ dmcu->funcs->dmcu_init(dmcu);
+ abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu);
+ }
+
+ if (!adev->dm.dc->ctx->dmub_srv)
+ adev->dm.dc->ctx->dmub_srv = dc_dmub_srv_create(adev->dm.dc, dmub_srv);
+ if (!adev->dm.dc->ctx->dmub_srv) {
+ drm_err(adev_to_drm(adev), "Couldn't allocate DC DMUB server!\n");
+ return -ENOMEM;
+ }
+
+ drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n",
+ adev->dm.dmcub_fw_version);
+
+ /* Keeping sanity checks off if
+ * DCN31 >= 4.0.59.0
+ * DCN314 >= 8.0.16.0
+ * Otherwise, turn on sanity checks
+ */
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(3, 1, 2):
+ case IP_VERSION(3, 1, 3):
+ if (adev->dm.dmcub_fw_version &&
+ adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
+ adev->dm.dmcub_fw_version < DMUB_FW_VERSION(4, 0, 59))
+ adev->dm.dc->debug.sanity_checks = true;
+ break;
+ case IP_VERSION(3, 1, 4):
+ if (adev->dm.dmcub_fw_version &&
+ adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
+ adev->dm.dmcub_fw_version < DMUB_FW_VERSION(8, 0, 16))
+ adev->dm.dc->debug.sanity_checks = true;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+EXPORT_IF_KUNIT(dm_dmub_hw_init);
+
+void dm_dmub_hw_resume(struct amdgpu_device *adev)
+{
+ struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
+ enum dmub_status status;
+ bool init;
+ int r;
+
+ if (!dmub_srv) {
+ /* DMUB isn't supported on the ASIC. */
+ return;
+ }
+
+ status = dmub_srv_is_hw_init(dmub_srv, &init);
+ if (status != DMUB_STATUS_OK)
+ drm_warn(adev_to_drm(adev), "DMUB hardware init check failed: %d\n", status);
+
+ if (status == DMUB_STATUS_OK && init) {
+ /* Wait for firmware load to finish. */
+ status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
+ if (status != DMUB_STATUS_OK)
+ drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
+ } else {
+ /* Perform the full hardware initialization. */
+ r = dm_dmub_hw_init(adev);
+ if (r)
+ drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r);
+ }
+}
+EXPORT_IF_KUNIT(dm_dmub_hw_resume);
+
+static enum dmub_status
+dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev,
+ enum dmub_gpint_command command_code,
+ uint16_t param,
+ uint32_t timeout_us)
+{
+ union dmub_gpint_data_register reg, test;
+ uint32_t i;
+
+ /* Assume that VBIOS DMUB is ready to take commands */
+
+ reg.bits.status = 1;
+ reg.bits.command_code = command_code;
+ reg.bits.param = param;
+
+ cgs_write_register(adev->dm.cgs_device, 0x34c0 + 0x01f8, reg.all);
+
+ for (i = 0; i < timeout_us; ++i) {
+ udelay(1);
+
+ /* Check if our GPINT got acked */
+ reg.bits.status = 0;
+ test = (union dmub_gpint_data_register)
+ cgs_read_register(adev->dm.cgs_device, 0x34c0 + 0x01f8);
+
+ if (test.all == reg.all)
+ return DMUB_STATUS_OK;
+ }
+
+ return DMUB_STATUS_TIMEOUT;
+}
+
+static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev)
+{
+ void *bb;
+ long long addr;
+ unsigned int bb_size;
+ int i = 0;
+ uint16_t chunk;
+ enum dmub_gpint_command send_addrs[] = {
+ DMUB_GPINT__SET_BB_ADDR_WORD0,
+ DMUB_GPINT__SET_BB_ADDR_WORD1,
+ DMUB_GPINT__SET_BB_ADDR_WORD2,
+ DMUB_GPINT__SET_BB_ADDR_WORD3,
+ };
+ enum dmub_status ret;
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(4, 0, 1):
+ bb_size = sizeof(struct dml2_soc_bb);
+ break;
+ case IP_VERSION(4, 2, 0):
+ case IP_VERSION(4, 2, 1):
+ bb_size = sizeof(struct dml2_soc_bb);
+ break;
+ default:
+ return NULL;
+ }
+
+ bb = dm_allocate_gpu_mem(adev,
+ DC_MEM_ALLOC_TYPE_GART,
+ bb_size,
+ &addr);
+ if (!bb)
+ return NULL;
+
+ for (i = 0; i < 4; i++) {
+ /* Extract 16-bit chunk */
+ chunk = ((uint64_t) addr >> (i * 16)) & 0xFFFF;
+ /* Send the chunk */
+ ret = dm_dmub_send_vbios_gpint_command(adev, send_addrs[i], chunk, 30000);
+ if (ret != DMUB_STATUS_OK)
+ goto free_bb;
+ }
+
+ /* Now ask DMUB to copy the bb */
+ ret = dm_dmub_send_vbios_gpint_command(adev, DMUB_GPINT__BB_COPY, 1, 200000);
+ if (ret != DMUB_STATUS_OK)
+ goto free_bb;
+
+ return bb;
+
+free_bb:
+ dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, (void *) bb);
+ return NULL;
+
+}
+
+enum dmub_ips_disable_type dm_get_default_ips_mode(
+ struct amdgpu_device *adev)
+{
+ enum dmub_ips_disable_type ret = DMUB_IPS_ENABLE;
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(3, 5, 0):
+ case IP_VERSION(3, 6, 0):
+ case IP_VERSION(3, 5, 1):
+ ret = DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF;
+ break;
+ default:
+ /* ASICs older than DCN35 do not have IPSs */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0))
+ ret = DMUB_IPS_DISABLE_ALL;
+ break;
+ }
+
+ return ret;
+}
+EXPORT_IF_KUNIT(dm_get_default_ips_mode);
+
+static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address)
+{
+ struct amdgpu_device *adev = ctx;
+
+ return dm_read_reg(adev->dm.dc->ctx, address);
+}
+
+static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
+ uint32_t value)
+{
+ struct amdgpu_device *adev = ctx;
+
+ return dm_write_reg(adev->dm.dc->ctx, address, value);
+}
+
+int dm_dmub_sw_init(struct amdgpu_device *adev)
+{
+ struct dmub_srv_create_params create_params;
+ struct dmub_srv_fw_meta_info_params fw_meta_info_params;
+ struct dmub_srv_region_params region_params;
+ struct dmub_srv_region_info region_info;
+ struct dmub_srv_memory_params memory_params;
+ struct dmub_fw_meta_info fw_info;
+ struct dmub_srv_fb_info *fb_info;
+ struct dmub_srv *dmub_srv;
+ const struct dmcub_firmware_header_v1_0 *hdr;
+ enum dmub_asic dmub_asic;
+ enum dmub_status status;
+ static enum dmub_window_memory_type window_memory_type[DMUB_WINDOW_TOTAL] = {
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_0_INST_CONST */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_1_STACK */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_2_BSS_DATA */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_3_VBIOS */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_4_MAILBOX */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_5_TRACEBUFF */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_6_FW_STATE */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_7_SCRATCH_MEM */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_IB_MEM */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_SHARED_STATE */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_LSDMA_BUFFER */
+ DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_CURSOR_OFFLOAD */
+ };
+ int r;
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(2, 1, 0):
+ dmub_asic = DMUB_ASIC_DCN21;
+ break;
+ case IP_VERSION(3, 0, 0):
+ dmub_asic = DMUB_ASIC_DCN30;
+ break;
+ case IP_VERSION(3, 0, 1):
+ dmub_asic = DMUB_ASIC_DCN301;
+ break;
+ case IP_VERSION(3, 0, 2):
+ dmub_asic = DMUB_ASIC_DCN302;
+ break;
+ case IP_VERSION(3, 0, 3):
+ dmub_asic = DMUB_ASIC_DCN303;
+ break;
+ case IP_VERSION(3, 1, 2):
+ case IP_VERSION(3, 1, 3):
+ dmub_asic = (adev->external_rev_id == YELLOW_CARP_B0) ? DMUB_ASIC_DCN31B : DMUB_ASIC_DCN31;
+ break;
+ case IP_VERSION(3, 1, 4):
+ dmub_asic = DMUB_ASIC_DCN314;
+ break;
+ case IP_VERSION(3, 1, 5):
+ dmub_asic = DMUB_ASIC_DCN315;
+ break;
+ case IP_VERSION(3, 1, 6):
+ dmub_asic = DMUB_ASIC_DCN316;
+ break;
+ case IP_VERSION(3, 2, 0):
+ dmub_asic = DMUB_ASIC_DCN32;
+ break;
+ case IP_VERSION(3, 2, 1):
+ dmub_asic = DMUB_ASIC_DCN321;
+ break;
+ case IP_VERSION(3, 5, 0):
+ case IP_VERSION(3, 5, 1):
+ dmub_asic = DMUB_ASIC_DCN35;
+ break;
+ case IP_VERSION(3, 6, 0):
+ dmub_asic = DMUB_ASIC_DCN36;
+ break;
+ case IP_VERSION(4, 0, 1):
+ dmub_asic = DMUB_ASIC_DCN401;
+ break;
+ case IP_VERSION(4, 2, 0):
+ dmub_asic = DMUB_ASIC_DCN42;
+ break;
+ case IP_VERSION(4, 2, 1):
+ dmub_asic = DMUB_ASIC_DCN42B;
+ break;
+ default:
+ /* ASIC doesn't support DMUB. */
+ return 0;
+ }
+
+ hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data;
+ adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
+
+ if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
+ adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id =
+ AMDGPU_UCODE_ID_DMCUB;
+ adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw =
+ adev->dm.dmub_fw;
+ adev->firmware.fw_size +=
+ ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
+
+ drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n",
+ adev->dm.dmcub_fw_version);
+ }
+
+
+ adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv);
+ dmub_srv = adev->dm.dmub_srv;
+
+ if (!dmub_srv) {
+ drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n");
+ return -ENOMEM;
+ }
+
+ memset(&create_params, 0, sizeof(create_params));
+ create_params.user_ctx = adev;
+ create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read;
+ create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write;
+ create_params.asic = dmub_asic;
+
+ /* Create the DMUB service. */
+ status = dmub_srv_create(dmub_srv, &create_params);
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status);
+ return -EINVAL;
+ }
+
+ /* Extract the FW meta info. */
+ memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params));
+
+ fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
+ PSP_HEADER_BYTES_256;
+ fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
+ fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+ PSP_HEADER_BYTES_256;
+ fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data +
+ le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
+ le32_to_cpu(hdr->inst_const_bytes) : NULL;
+ fw_meta_info_params.custom_psp_footer_size = 0;
+
+ status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info);
+ if (status != DMUB_STATUS_OK) {
+ /* Skip returning early, just log the error. */
+ drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status);
+ }
+
+ /* Calculate the size of all the regions for the DMUB service. */
+ memset(&region_params, 0, sizeof(region_params));
+
+ region_params.inst_const_size = fw_meta_info_params.inst_const_size;
+ region_params.bss_data_size = fw_meta_info_params.bss_data_size;
+ region_params.vbios_size = adev->bios_size;
+ region_params.fw_bss_data = fw_meta_info_params.fw_bss_data;
+ region_params.fw_inst_const = fw_meta_info_params.fw_inst_const;
+ region_params.window_memory_type = window_memory_type;
+ region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL;
+
+ status = dmub_srv_calc_region_info(dmub_srv, &region_params,
+ &region_info);
+
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error calculating DMUB region info: %d\n", status);
+ return -EINVAL;
+ }
+
+ /*
+ * Allocate a framebuffer based on the total size of all the regions.
+ * TODO: Move this into GART.
+ */
+ r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM |
+ AMDGPU_GEM_DOMAIN_GTT,
+ &adev->dm.dmub_bo,
+ &adev->dm.dmub_bo_gpu_addr,
+ &adev->dm.dmub_bo_cpu_addr);
+ if (r)
+ return r;
+
+ /* Rebase the regions on the framebuffer address. */
+ memset(&memory_params, 0, sizeof(memory_params));
+ memory_params.cpu_fb_addr = adev->dm.dmub_bo_cpu_addr;
+ memory_params.gpu_fb_addr = adev->dm.dmub_bo_gpu_addr;
+ memory_params.region_info = &region_info;
+ memory_params.window_memory_type = window_memory_type;
+
+ adev->dm.dmub_fb_info = kzalloc_obj(*adev->dm.dmub_fb_info);
+ fb_info = adev->dm.dmub_fb_info;
+
+ if (!fb_info) {
+ drm_err(adev_to_drm(adev),
+ "Failed to allocate framebuffer info for DMUB service!\n");
+ return -ENOMEM;
+ }
+
+ status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info);
+ if (status != DMUB_STATUS_OK) {
+ drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status);
+ return -EINVAL;
+ }
+
+ adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
+ adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size;
+
+ return 0;
+}
+EXPORT_IF_KUNIT(dm_dmub_sw_init);
+
+int dm_init_microcode(struct amdgpu_device *adev)
+{
+ char *fw_name_dmub;
+ int r;
+
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(2, 1, 0):
+ fw_name_dmub = FIRMWARE_RENOIR_DMUB;
+ if (ASICREV_IS_GREEN_SARDINE(adev->external_rev_id))
+ fw_name_dmub = FIRMWARE_GREEN_SARDINE_DMUB;
+ break;
+ case IP_VERSION(3, 0, 0):
+ if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(10, 3, 0))
+ fw_name_dmub = FIRMWARE_SIENNA_CICHLID_DMUB;
+ else
+ fw_name_dmub = FIRMWARE_NAVY_FLOUNDER_DMUB;
+ break;
+ case IP_VERSION(3, 0, 1):
+ fw_name_dmub = FIRMWARE_VANGOGH_DMUB;
+ break;
+ case IP_VERSION(3, 0, 2):
+ fw_name_dmub = FIRMWARE_DIMGREY_CAVEFISH_DMUB;
+ break;
+ case IP_VERSION(3, 0, 3):
+ fw_name_dmub = FIRMWARE_BEIGE_GOBY_DMUB;
+ break;
+ case IP_VERSION(3, 1, 2):
+ case IP_VERSION(3, 1, 3):
+ fw_name_dmub = FIRMWARE_YELLOW_CARP_DMUB;
+ break;
+ case IP_VERSION(3, 1, 4):
+ fw_name_dmub = FIRMWARE_DCN_314_DMUB;
+ break;
+ case IP_VERSION(3, 1, 5):
+ fw_name_dmub = FIRMWARE_DCN_315_DMUB;
+ break;
+ case IP_VERSION(3, 1, 6):
+ fw_name_dmub = FIRMWARE_DCN316_DMUB;
+ break;
+ case IP_VERSION(3, 2, 0):
+ fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB;
+ break;
+ case IP_VERSION(3, 2, 1):
+ fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB;
+ break;
+ case IP_VERSION(3, 5, 0):
+ fw_name_dmub = FIRMWARE_DCN_35_DMUB;
+ break;
+ case IP_VERSION(3, 5, 1):
+ fw_name_dmub = FIRMWARE_DCN_351_DMUB;
+ break;
+ case IP_VERSION(3, 6, 0):
+ fw_name_dmub = FIRMWARE_DCN_36_DMUB;
+ break;
+ case IP_VERSION(4, 0, 1):
+ fw_name_dmub = FIRMWARE_DCN_401_DMUB;
+ break;
+ case IP_VERSION(4, 2, 0):
+ fw_name_dmub = FIRMWARE_DCN_42_DMUB;
+ break;
+ case IP_VERSION(4, 2, 1):
+ fw_name_dmub = FIRMWARE_DCN_42B_DMUB;
+ break;
+ default:
+ /* ASIC doesn't support DMUB. */
+ return 0;
+ }
+ r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED,
+ "%s", fw_name_dmub);
+ return r;
+}
+EXPORT_IF_KUNIT(dm_init_microcode);
+
+int amdgpu_dm_process_dmub_aux_transfer_sync(
+ struct dc_context *ctx,
+ unsigned int link_index,
+ struct aux_payload *payload,
+ enum aux_return_code_type *operation_result)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
+ struct dmub_notification *p_notify = adev->dm.dmub_notify;
+ int ret = -1;
+
+ mutex_lock(&adev->dm.dpia_aux_lock);
+ if (!dc_process_dmub_aux_transfer_async(ctx->dc, link_index, payload)) {
+ *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE;
+ goto out;
+ }
+
+ if (!wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
+ drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
+ *operation_result = AUX_RET_ERROR_TIMEOUT;
+ goto out;
+ }
+
+ if (p_notify->result != AUX_RET_SUCCESS) {
+ /*
+ * Transient states before tunneling is enabled could
+ * lead to this error. We can ignore this for now.
+ */
+ if (p_notify->result == AUX_RET_ERROR_PROTOCOL_ERROR) {
+ drm_warn(adev_to_drm(adev), "DPIA AUX failed on 0x%x(%d), error %d\n",
+ payload->address, payload->length,
+ p_notify->result);
+ }
+ *operation_result = p_notify->result;
+ goto out;
+ }
+
+ payload->reply[0] = adev->dm.dmub_notify->aux_reply.command & 0xF;
+ if (adev->dm.dmub_notify->aux_reply.command & 0xF0)
+ /* The reply is stored in the top nibble of the command. */
+ payload->reply[0] = (adev->dm.dmub_notify->aux_reply.command >> 4) & 0xF;
+
+ /*write req may receive a byte indicating partially written number as well*/
+ if (p_notify->aux_reply.length && payload->data) {
+ /* Bound the reply to the scratch buffer it was read into. */
+ ret = min((uint32_t)p_notify->aux_reply.length,
+ (uint32_t)sizeof(p_notify->aux_reply.data));
+
+ /*
+ * During a write-status-update retry the caller zeroes
+ * payload->length while still expecting the partial-write
+ * status byte in payload->data (see dce_aux_transfer_with_retries),
+ * so only clamp to payload->length for regular transfers.
+ */
+ if (!payload->write_status_update)
+ ret = min(ret, payload->length);
+
+ memcpy(payload->data, p_notify->aux_reply.data, ret);
+ } else {
+ /* success */
+ ret = p_notify->aux_reply.length;
+ }
+
+ *operation_result = p_notify->result;
+out:
+ reinit_completion(&adev->dm.dmub_aux_transfer_done);
+ mutex_unlock(&adev->dm.dpia_aux_lock);
+ return ret;
+}
+
+static void abort_fused_io(
+ struct dc_context *ctx,
+ const struct dmub_cmd_fused_request *request
+)
+{
+ union dmub_rb_cmd command = { 0 };
+ struct dmub_rb_cmd_fused_io *io = &command.fused_io;
+
+ io->header.type = DMUB_CMD__FUSED_IO;
+ io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT;
+ io->header.payload_bytes = sizeof(*io) - sizeof(io->header);
+ io->request = *request;
+ dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT);
+}
+
+static bool execute_fused_io(
+ struct amdgpu_device *dev,
+ struct dc_context *ctx,
+ union dmub_rb_cmd *commands,
+ uint8_t count,
+ uint32_t timeout_us
+)
+{
+ const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line;
+
+ if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io))
+ return false;
+
+ struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line];
+ struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io;
+ const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)
+ && first->header.ret_status
+ && first->request.status == FUSED_REQUEST_STATUS_SUCCESS;
+
+ if (!result)
+ return false;
+
+ while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) {
+ reinit_completion(&sync->replied);
+
+ struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data;
+
+ static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch");
+
+ if (reply->identifier == first->request.identifier) {
+ first->request = *reply;
+ return true;
+ }
+ }
+
+ reinit_completion(&sync->replied);
+ first->request.status = FUSED_REQUEST_STATUS_TIMEOUT;
+ abort_fused_io(ctx, &first->request);
+ return false;
+}
+
+bool amdgpu_dm_execute_fused_io(
+ struct amdgpu_device *dev,
+ struct dc_link *link,
+ union dmub_rb_cmd *commands,
+ uint8_t count,
+ uint32_t timeout_us)
+{
+ struct amdgpu_display_manager *dm = &dev->dm;
+
+ mutex_lock(&dm->dpia_aux_lock);
+
+ const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us);
+
+ mutex_unlock(&dm->dpia_aux_lock);
+ return result;
+}
+
+int amdgpu_dm_process_dmub_set_config_sync(
+ struct dc_context *ctx,
+ unsigned int link_index,
+ struct set_config_cmd_payload *payload,
+ enum set_config_status *operation_result)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
+ bool is_cmd_complete;
+ int ret;
+
+ mutex_lock(&adev->dm.dpia_aux_lock);
+ is_cmd_complete = dc_process_dmub_set_config_async(ctx->dc,
+ link_index, payload, adev->dm.dmub_notify);
+
+ if (is_cmd_complete || wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
+ ret = 0;
+ *operation_result = adev->dm.dmub_notify->sc_status;
+ } else {
+ drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
+ ret = -1;
+ *operation_result = SET_CONFIG_UNKNOWN_ERROR;
+ }
+
+ if (!is_cmd_complete)
+ reinit_completion(&adev->dm.dmub_aux_transfer_done);
+ mutex_unlock(&adev->dm.dpia_aux_lock);
+ return ret;
+}
+
+bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
+
+ guard(spinlock_irqsave)(&adev->dm.dmub_lock);
+ return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type);
+}
+
+bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
+
+ guard(spinlock_irqsave)(&adev->dm.dmub_lock);
+ return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type);
+}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
new file mode 100644
index 000000000000..a4a03e40ec37
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef AMDGPU_DM_AMDGPU_DM_DMUB_H_
+#define AMDGPU_DM_AMDGPU_DM_DMUB_H_
+
+#include "amdgpu.h"
+
+void dm_dmub_aux_setconfig_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify);
+void dm_dmub_aux_fused_io_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify);
+bool dm_register_dmub_notify_callback(struct amdgpu_device *adev,
+ enum dmub_notification_type type,
+ dmub_notify_interrupt_callback_t callback,
+ bool dmub_int_thread_offload);
+int dm_dmub_hw_init(struct amdgpu_device *adev);
+void dm_dmub_hw_resume(struct amdgpu_device *adev);
+enum dmub_ips_disable_type dm_get_default_ips_mode(struct amdgpu_device *adev);
+int dm_dmub_sw_init(struct amdgpu_device *adev);
+int dm_init_microcode(struct amdgpu_device *adev);
+
+#define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin"
+#define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin"
+#define FIRMWARE_NAVY_FLOUNDER_DMUB "amdgpu/navy_flounder_dmcub.bin"
+#define FIRMWARE_GREEN_SARDINE_DMUB "amdgpu/green_sardine_dmcub.bin"
+#define FIRMWARE_VANGOGH_DMUB "amdgpu/vangogh_dmcub.bin"
+#define FIRMWARE_DIMGREY_CAVEFISH_DMUB "amdgpu/dimgrey_cavefish_dmcub.bin"
+#define FIRMWARE_BEIGE_GOBY_DMUB "amdgpu/beige_goby_dmcub.bin"
+#define FIRMWARE_YELLOW_CARP_DMUB "amdgpu/yellow_carp_dmcub.bin"
+#define FIRMWARE_DCN_314_DMUB "amdgpu/dcn_3_1_4_dmcub.bin"
+#define FIRMWARE_DCN_315_DMUB "amdgpu/dcn_3_1_5_dmcub.bin"
+#define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin"
+#define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin"
+#define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin"
+#define FIRMWARE_DCN_35_DMUB "amdgpu/dcn_3_5_dmcub.bin"
+#define FIRMWARE_DCN_351_DMUB "amdgpu/dcn_3_5_1_dmcub.bin"
+#define FIRMWARE_DCN_36_DMUB "amdgpu/dcn_3_6_dmcub.bin"
+#define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
+#define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin"
+#define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin"
+#define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin"
+#define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin"
+
+#endif /* AMDGPU_DM_AMDGPU_DM_DMUB_H_ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
index 4c164ae4a4f9..5dbeb1e017d4 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
@@ -182,6 +182,70 @@ void process_output(struct hdcp_workqueue *hdcp_work)
}
EXPORT_IF_KUNIT(process_output);
+STATIC_IFN_KUNIT
+bool hdcp_get_content_protection_from_status(
+ unsigned int hdcp_content_type,
+ enum mod_hdcp_encryption_status encryption_status,
+ unsigned int *content_protection)
+{
+ if (encryption_status == MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) {
+ *content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ return true;
+ }
+
+ if (hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE0 &&
+ encryption_status <= MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) {
+ *content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ return true;
+ }
+
+ if (hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE1 &&
+ encryption_status == MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) {
+ *content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ return true;
+ }
+
+ return false;
+}
+EXPORT_IF_KUNIT(hdcp_get_content_protection_from_status);
+
+STATIC_IFN_KUNIT
+void hdcp_get_link_display_adjustments(
+ bool enable_encryption,
+ u8 content_type,
+ bool fused_io_supported,
+ bool hdcp_lc_force_fw_enable,
+ bool hdcp_lc_enable_sw_fallback,
+ struct mod_hdcp_link_adjustment *link_adjust,
+ struct mod_hdcp_display_adjustment *display_adjust)
+{
+ memset(link_adjust, 0, sizeof(*link_adjust));
+ memset(display_adjust, 0, sizeof(*display_adjust));
+
+ if (!enable_encryption) {
+ display_adjust->disable =
+ MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
+ return;
+ }
+
+ display_adjust->disable = MOD_HDCP_DISPLAY_NOT_DISABLE;
+ link_adjust->auth_delay = 2;
+ link_adjust->retry_limit = MAX_NUM_OF_ATTEMPTS;
+
+ if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
+ link_adjust->hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
+ } else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
+ link_adjust->hdcp1.disable = 1;
+ link_adjust->hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1;
+ }
+
+ link_adjust->hdcp2.use_fw_locality_check =
+ fused_io_supported || hdcp_lc_force_fw_enable;
+ link_adjust->hdcp2.use_sw_locality_fallback =
+ hdcp_lc_enable_sw_fallback;
+}
+EXPORT_IF_KUNIT(hdcp_get_link_display_adjustments);
+
static void link_lock(struct hdcp_workqueue *work, bool lock)
{
int i = 0;
@@ -212,8 +276,11 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
drm_connector_put(&hdcp_w->aconnector[conn_index]->base);
hdcp_w->aconnector[conn_index] = aconnector;
- memset(&link_adjust, 0, sizeof(link_adjust));
- memset(&display_adjust, 0, sizeof(display_adjust));
+ hdcp_get_link_display_adjustments(enable_encryption, content_type,
+ dc->caps.fused_io_supported,
+ dc->debug.hdcp_lc_force_fw_enable,
+ dc->debug.hdcp_lc_enable_sw_fallback,
+ &link_adjust, &display_adjust);
if (enable_encryption) {
/* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp
@@ -224,25 +291,9 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
hdcp_work->srm_size,
&hdcp_work->srm_version);
- display_adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE;
-
- link_adjust.auth_delay = 2;
- link_adjust.retry_limit = MAX_NUM_OF_ATTEMPTS;
-
- if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
- link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
- } else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
- link_adjust.hdcp1.disable = 1;
- link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1;
- }
- link_adjust.hdcp2.use_fw_locality_check =
- (dc->caps.fused_io_supported || dc->debug.hdcp_lc_force_fw_enable);
- link_adjust.hdcp2.use_sw_locality_fallback = dc->debug.hdcp_lc_enable_sw_fallback;
-
schedule_delayed_work(&hdcp_w->property_validate_dwork,
msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
} else {
- display_adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
cancel_delayed_work(&hdcp_w->property_validate_dwork);
}
@@ -336,6 +387,7 @@ static void event_property_update(struct work_struct *work)
property_update_work);
struct amdgpu_dm_connector *aconnector = NULL;
struct drm_device *dev;
+ unsigned int content_protection;
long ret;
unsigned int conn_index;
struct drm_connector *connector;
@@ -375,26 +427,15 @@ static void event_property_update(struct work_struct *work)
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
}
}
- if (hdcp_work->encryption_status[conn_index] !=
- MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) {
- if (conn_state->hdcp_content_type ==
- DRM_MODE_HDCP_CONTENT_TYPE0 &&
- hdcp_work->encryption_status[conn_index] <=
- MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) {
+ if (hdcp_get_content_protection_from_status(conn_state->hdcp_content_type,
+ hdcp_work->encryption_status[conn_index],
+ &content_protection)) {
+ if (content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_ENABLED\n");
- drm_hdcp_update_content_protection(connector,
- DRM_MODE_CONTENT_PROTECTION_ENABLED);
- } else if (conn_state->hdcp_content_type ==
- DRM_MODE_HDCP_CONTENT_TYPE1 &&
- hdcp_work->encryption_status[conn_index] ==
- MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) {
- drm_hdcp_update_content_protection(connector,
- DRM_MODE_CONTENT_PROTECTION_ENABLED);
- }
- } else {
- DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n");
- drm_hdcp_update_content_protection(connector,
- DRM_MODE_CONTENT_PROTECTION_DESIRED);
+ else
+ DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n");
+
+ drm_hdcp_update_content_protection(connector, content_protection);
}
drm_modeset_unlock(&dev->mode_config.connection_mutex);
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h
index 90b18c450ca6..3ba5823aed9f 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h
@@ -96,6 +96,18 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
void process_output(struct hdcp_workqueue *hdcp_work);
+bool hdcp_get_content_protection_from_status(
+ unsigned int hdcp_content_type,
+ enum mod_hdcp_encryption_status encryption_status,
+ unsigned int *content_protection);
+void hdcp_get_link_display_adjustments(
+ bool enable_encryption,
+ u8 content_type,
+ bool fused_io_supported,
+ bool hdcp_lc_force_fw_enable,
+ bool hdcp_lc_enable_sw_fallback,
+ struct mod_hdcp_link_adjustment *link_adjust,
+ struct mod_hdcp_display_adjustment *display_adjust);
#endif
#endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index c6f94eb71ffa..71e2627f9a9d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -48,6 +48,8 @@
#include "dm_helpers.h"
#include "ddc_service_types.h"
#include "clk_mgr.h"
+#include "amdgpu_dm_kunit_helpers.h"
+#include "amdgpu_dm_helpers.h"
#define MCCS_DEST_ADDR (0x6E >> 1)
#define MCCS_SRC_ADDR 0x51
@@ -88,12 +90,13 @@ union vcp_reply {
unsigned char raw[11];
};
-static u32 edid_extract_panel_id(struct edid *edid)
+STATIC_IFN_KUNIT u32 edid_extract_panel_id(struct edid *edid)
{
return (u32)edid->mfg_id[0] << 24 |
(u32)edid->mfg_id[1] << 16 |
(u32)EDID_PRODUCT_ID(edid);
}
+EXPORT_IF_KUNIT(edid_extract_panel_id);
static void apply_edid_quirks(struct dc_link *link, struct edid *edid,
struct dc_edid_caps *edid_caps)
@@ -193,6 +196,12 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
__func__, connector->name, edid_caps->frl_dsc_10bpc, edid_caps->frl_dsc_12bpc, \
edid_caps->frl_dsc_all_bpp, edid_caps->frl_dsc_native_420, edid_caps->frl_dsc_max_slices, \
edid_caps->frl_dsc_max_frl_rate, edid_caps->frl_dsc_total_chunk_kbytes);
+ if (aconnector->hdmi_comp_auto) {
+ edid_caps->panel_patch.hdmi_comp_auto = true;
+ link->ctx->dc->debug.force_frl_max = true;
+ link->ctx->dc->debug.force_frl_dsc = true;
+ drm_dbg_driver(connector->dev, "%s: HDMI_FRL [%s] hdmi_comp_auto --> enabled\n", __func__, connector->name);
+ }
}
apply_edid_quirks(link, edid_buf, edid_caps);
@@ -489,6 +498,7 @@ void dm_dtn_log_begin(struct dc_context *ctx,
dm_dtn_log_append_v(ctx, log_ctx, "%s", msg);
}
+EXPORT_IF_KUNIT(dm_dtn_log_begin);
__printf(3, 4)
void dm_dtn_log_append_v(struct dc_context *ctx,
@@ -551,6 +561,7 @@ void dm_dtn_log_append_v(struct dc_context *ctx,
if (n > 0)
log_ctx->pos += n;
}
+EXPORT_IF_KUNIT(dm_dtn_log_append_v);
void dm_dtn_log_end(struct dc_context *ctx,
struct dc_log_buffer_ctx *log_ctx)
@@ -564,6 +575,7 @@ void dm_dtn_log_end(struct dc_context *ctx,
dm_dtn_log_append_v(ctx, log_ctx, "%s", msg);
}
+EXPORT_IF_KUNIT(dm_dtn_log_end);
bool dm_helpers_dp_mst_start_top_mgr(
struct dc_context *ctx,
@@ -598,6 +610,7 @@ bool dm_helpers_dp_mst_start_top_mgr(
return true;
}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_start_top_mgr);
bool dm_helpers_dp_mst_stop_top_mgr(
struct dc_context *ctx,
@@ -620,6 +633,7 @@ bool dm_helpers_dp_mst_stop_top_mgr(
return false;
}
+EXPORT_IF_KUNIT(dm_helpers_dp_mst_stop_top_mgr);
bool dm_helpers_dp_read_dpcd(
struct dc_context *ctx,
@@ -637,6 +651,7 @@ bool dm_helpers_dp_read_dpcd(
return drm_dp_dpcd_read(&aconnector->dm_dp_aux.aux, address, data,
size) == size;
}
+EXPORT_IF_KUNIT(dm_helpers_dp_read_dpcd);
bool dm_helpers_dp_write_dpcd(
struct dc_context *ctx,
@@ -653,6 +668,7 @@ bool dm_helpers_dp_write_dpcd(
return drm_dp_dpcd_write(&aconnector->dm_dp_aux.aux,
address, (uint8_t *)data, size) > 0;
}
+EXPORT_IF_KUNIT(dm_helpers_dp_write_dpcd);
bool dm_helpers_submit_i2c(
struct dc_context *ctx,
@@ -968,6 +984,7 @@ bool dm_helpers_dp_write_hblank_reduction(struct dc_context *ctx, const struct d
// TODO
return false;
}
+EXPORT_IF_KUNIT(dm_helpers_dp_write_hblank_reduction);
bool dm_helpers_is_dp_sink_present(struct dc_link *link)
{
@@ -1085,7 +1102,7 @@ dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_conn
return edid;
}
-static uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane)
+STATIC_IFN_KUNIT uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane)
{
uint8_t max_frl_rate;
@@ -1106,6 +1123,7 @@ static uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane)
return max_frl_rate;
}
+EXPORT_IF_KUNIT(get_max_frl_rate);
static uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice)
{
@@ -1150,6 +1168,7 @@ void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *hd
}
}
}
+EXPORT_IF_KUNIT(populate_hdmi_info_from_connector);
enum dc_edid_status dm_helpers_read_local_edid(
struct dc_context *ctx,
@@ -1550,24 +1569,32 @@ void dm_helpers_dp_mst_update_branch_bandwidth(
// TODO
}
-static bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id)
+STATIC_IFN_KUNIT const uint32_t dm_freesync_pcon_whitelist[] = {
+ DP_BRANCH_DEVICE_ID_0060AD,
+ DP_BRANCH_DEVICE_ID_00E04C,
+ DP_BRANCH_DEVICE_ID_90CC24,
+ DP_BRANCH_DEVICE_ID_001CF8,
+ DP_BRANCH_DEVICE_ID_001FF2,
+};
+EXPORT_IF_KUNIT(dm_freesync_pcon_whitelist);
+
+STATIC_IFN_KUNIT uint32_t dm_freesync_pcon_whitelist_count(void)
{
- bool ret_val = false;
-
- switch (branch_dev_id) {
- case DP_BRANCH_DEVICE_ID_0060AD:
- case DP_BRANCH_DEVICE_ID_00E04C:
- case DP_BRANCH_DEVICE_ID_90CC24:
- case DP_BRANCH_DEVICE_ID_001CF8:
- case DP_BRANCH_DEVICE_ID_001FF2:
- ret_val = true;
- break;
- default:
- break;
- }
+ return ARRAY_SIZE(dm_freesync_pcon_whitelist);
+}
+EXPORT_IF_KUNIT(dm_freesync_pcon_whitelist_count);
+
+STATIC_IFN_KUNIT bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id)
+{
+ u32 i;
+
+ for (i = 0; i < dm_freesync_pcon_whitelist_count(); i++)
+ if (dm_freesync_pcon_whitelist[i] == branch_dev_id)
+ return true;
- return ret_val;
+ return false;
}
+EXPORT_IF_KUNIT(dm_is_freesync_pcon_whitelist);
enum adaptive_sync_type dm_get_adaptive_sync_support_type(struct dc_link *link)
{
@@ -1587,18 +1614,21 @@ enum adaptive_sync_type dm_get_adaptive_sync_support_type(struct dc_link *link)
return as_type;
}
+EXPORT_IF_KUNIT(dm_get_adaptive_sync_support_type);
bool dm_helpers_is_fullscreen(struct dc_context *ctx, struct dc_stream_state *stream)
{
// TODO
return false;
}
+EXPORT_IF_KUNIT(dm_helpers_is_fullscreen);
bool dm_helpers_is_hdr_on(struct dc_context *ctx, struct dc_stream_state *stream)
{
// TODO
return false;
}
+EXPORT_IF_KUNIT(dm_helpers_is_hdr_on);
static int mccs_operation_vcp_request(unsigned int vcp_code, struct dc_link *link,
union vcp_reply *reply)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h
new file mode 100644
index 000000000000..2ac9762895ec
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef __AMDGPU_DM_HELPERS_H__
+#define __AMDGPU_DM_HELPERS_H__
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+#include <drm/drm_edid.h>
+
+/* Exported for KUnit testing */
+u32 edid_extract_panel_id(struct edid *edid);
+uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane);
+bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id);
+extern const uint32_t dm_freesync_pcon_whitelist[];
+uint32_t dm_freesync_pcon_whitelist_count(void);
+#endif /* CONFIG_DRM_AMD_DC_KUNIT_TEST */
+
+#endif /* __AMDGPU_DM_HELPERS_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
index e49803a90eda..551901c7598a 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
@@ -26,10 +26,25 @@
#include "dm_services_types.h"
#include "dc.h"
+#include "dc/dc_dmub_srv.h"
+#include "dc/dc_stat.h"
#include "amdgpu.h"
+#include "amdgpu_display.h"
#include "amdgpu_dm.h"
#include "amdgpu_dm_irq.h"
+#include "amdgpu_dm_kunit_helpers.h"
+#include "amdgpu_dm_crtc.h"
+#include "amdgpu_dm_hdcp.h"
+#include "amdgpu_dm_mst_types.h"
+#include "amdgpu_dm_dmub.h"
+#include "amdgpu_dm_trace.h"
+#include "link/protocols/link_dpcd.h"
+#include "link_service_types.h"
+#include "ivsrcid/ivsrcid_vislands30.h"
+#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h"
+#include "modules/inc/mod_freesync.h"
+#include <drm/drm_vblank.h>
/**
* DOC: overview
@@ -55,7 +70,8 @@
* are all set to the DM generic handler amdgpu_dm_irq_handler(), which looks up
* DM's IRQ tables. However, in order for base driver to recognize this hook, DM
* still needs to register the IRQ with the base driver. See
- * dce110_register_irq_handlers() and dcn10_register_irq_handlers().
+ * amdgpu_dm_dce110_register_irq_handlers() and
+ * amdgpu_dm_dcn10_register_irq_handlers().
*
* To expose DC's hardware interrupt toggle to the base driver, DM implements
* &amdgpu_irq_src_funcs.set hooks. Base driver calls it through
@@ -172,7 +188,7 @@ static struct list_head *remove_irq_handler(struct amdgpu_device *adev,
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
- if (handler_removed == false) {
+ if (!handler_removed) {
/* Not necessarily an error - caller may not
* know the context.
*/
@@ -310,7 +326,7 @@ void *amdgpu_dm_irq_register_interrupt(struct amdgpu_device *adev,
unsigned long irq_table_flags;
enum dc_irq_source irq_source;
- if (false == validate_irq_registration_params(int_params, ih))
+ if (!validate_irq_registration_params(int_params, ih))
return DAL_INVALID_IRQ_HANDLER_IDX;
handler_data = kzalloc_obj(*handler_data);
@@ -357,6 +373,7 @@ void *amdgpu_dm_irq_register_interrupt(struct amdgpu_device *adev,
return handler_data;
}
+EXPORT_IF_KUNIT(amdgpu_dm_irq_register_interrupt);
/**
* amdgpu_dm_irq_unregister_interrupt() - Remove a handler from the DM IRQ table
@@ -375,7 +392,7 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev,
struct dc_interrupt_params int_params;
int i;
- if (false == validate_irq_unregistration_params(irq_source, ih))
+ if (!validate_irq_unregistration_params(irq_source, ih))
return;
memset(&int_params, 0, sizeof(int_params));
@@ -401,6 +418,7 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev,
ih, irq_source);
}
}
+EXPORT_IF_KUNIT(amdgpu_dm_irq_unregister_interrupt);
/**
* amdgpu_dm_irq_init() - Initialize DM IRQ management
@@ -435,6 +453,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev)
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_irq_init);
/**
* amdgpu_dm_irq_fini() - Tear down DM IRQ management
@@ -473,6 +492,7 @@ void amdgpu_dm_irq_fini(struct amdgpu_device *adev)
/* Deallocate handlers from the table. */
unregister_all_irq_handlers(adev);
}
+EXPORT_IF_KUNIT(amdgpu_dm_irq_fini);
void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
{
@@ -675,7 +695,7 @@ static int amdgpu_dm_irq_handler(struct amdgpu_device *adev,
return 0;
}
-static enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type)
+STATIC_IFN_KUNIT enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type)
{
switch (type) {
case AMDGPU_HPD_1:
@@ -694,6 +714,7 @@ static enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type)
return DC_IRQ_SOURCE_INVALID;
}
}
+EXPORT_IF_KUNIT(amdgpu_dm_hpd_to_dal_irq_source);
static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
@@ -1020,3 +1041,1520 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
if (dev->mode_config.poll_enabled)
drm_kms_helper_poll_fini(dev);
}
+
+/* ========== HPD handling ========== */
+static void force_connector_state(
+ struct amdgpu_dm_connector *aconnector,
+ enum drm_connector_force force_state)
+{
+ struct drm_connector *connector = &aconnector->base;
+
+ mutex_lock(&connector->dev->mode_config.mutex);
+ aconnector->base.force = force_state;
+ mutex_unlock(&connector->dev->mode_config.mutex);
+
+ mutex_lock(&aconnector->hpd_lock);
+ drm_kms_helper_connector_hotplug_event(connector);
+ mutex_unlock(&aconnector->hpd_lock);
+}
+
+static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
+{
+ struct hpd_rx_irq_offload_work *offload_work;
+ struct amdgpu_dm_connector *aconnector;
+ struct dc_link *dc_link;
+ struct amdgpu_device *adev;
+ enum dc_connection_type new_connection_type = dc_connection_none;
+ unsigned long flags;
+ union test_response test_response;
+
+ memset(&test_response, 0, sizeof(test_response));
+
+ offload_work = container_of(work, struct hpd_rx_irq_offload_work, work);
+ aconnector = offload_work->offload_wq->aconnector;
+ adev = offload_work->adev;
+
+ if (!aconnector) {
+ drm_err(adev_to_drm(adev), "Can't retrieve aconnector in hpd_rx_irq_offload_work");
+ goto skip;
+ }
+
+ dc_link = aconnector->dc_link;
+
+ mutex_lock(&aconnector->hpd_lock);
+ if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
+ drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
+ mutex_unlock(&aconnector->hpd_lock);
+
+ if (new_connection_type == dc_connection_none)
+ goto skip;
+
+ if (amdgpu_in_reset(adev))
+ goto skip;
+
+ if (offload_work->data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY ||
+ offload_work->data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
+ dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr, DOWN_OR_UP_MSG_RDY_EVENT);
+ spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
+ offload_work->offload_wq->is_handling_mst_msg_rdy_event = false;
+ spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
+ goto skip;
+ }
+
+ mutex_lock(&adev->dm.dc_lock);
+ if (offload_work->data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
+ dc_link_dp_handle_automated_test(dc_link);
+
+ if (aconnector->timing_changed) {
+ /* force connector disconnect and reconnect */
+ force_connector_state(aconnector, DRM_FORCE_OFF);
+ msleep(100);
+ force_connector_state(aconnector, DRM_FORCE_UNSPECIFIED);
+ }
+
+ test_response.bits.ACK = 1;
+
+ core_link_write_dpcd(
+ dc_link,
+ DP_TEST_RESPONSE,
+ &test_response.raw,
+ sizeof(test_response));
+ } else if ((dc_link->connector_signal != SIGNAL_TYPE_EDP) &&
+ dc_link_check_link_loss_status(dc_link, &offload_work->data) &&
+ dc_link_dp_allow_hpd_rx_irq(dc_link)) {
+ /* offload_work->data is from handle_hpd_rx_irq->
+ * schedule_hpd_rx_offload_work.this is defer handle
+ * for hpd short pulse. upon here, link status may be
+ * changed, need get latest link status from dpcd
+ * registers. if link status is good, skip run link
+ * training again.
+ */
+ union hpd_irq_data irq_data;
+
+ memset(&irq_data, 0, sizeof(irq_data));
+
+ /* before dc_link_dp_handle_link_loss, allow new link lost handle
+ * request be added to work queue if link lost at end of dc_link_
+ * dp_handle_link_loss
+ */
+ spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
+ offload_work->offload_wq->is_handling_link_loss = false;
+ spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
+
+ if ((dc_link_dp_read_hpd_rx_irq_data(dc_link, &irq_data) == DC_OK) &&
+ dc_link_check_link_loss_status(dc_link, &irq_data))
+ dc_link_dp_handle_link_loss(dc_link);
+ }
+ mutex_unlock(&adev->dm.dc_lock);
+
+skip:
+ kfree(offload_work);
+
+}
+
+struct hpd_rx_irq_offload_work_queue *amdgpu_dm_hpd_rx_irq_create_workqueue(struct amdgpu_device *adev)
+{
+ struct dc *dc = adev->dm.dc;
+ int max_caps = dc->caps.max_links;
+ int i = 0;
+ struct hpd_rx_irq_offload_work_queue *hpd_rx_offload_wq = NULL;
+
+ hpd_rx_offload_wq = kzalloc_objs(*hpd_rx_offload_wq, max_caps);
+
+ if (!hpd_rx_offload_wq)
+ return NULL;
+
+
+ for (i = 0; i < max_caps; i++) {
+ hpd_rx_offload_wq[i].wq =
+ create_singlethread_workqueue("amdgpu_dm_hpd_rx_offload_wq");
+
+ if (hpd_rx_offload_wq[i].wq == NULL) {
+ drm_err(adev_to_drm(adev), "create amdgpu_dm_hpd_rx_offload_wq fail!");
+ goto out_err;
+ }
+
+ spin_lock_init(&hpd_rx_offload_wq[i].offload_lock);
+ }
+
+ return hpd_rx_offload_wq;
+
+out_err:
+ for (i = 0; i < max_caps; i++) {
+ if (hpd_rx_offload_wq[i].wq)
+ destroy_workqueue(hpd_rx_offload_wq[i].wq);
+ }
+ kfree(hpd_rx_offload_wq);
+ return NULL;
+}
+
+void amdgpu_dm_hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm)
+{
+ int i;
+
+ if (dm->hpd_rx_offload_wq) {
+ for (i = 0; i < dm->dc->caps.max_links; i++)
+ flush_workqueue(dm->hpd_rx_offload_wq[i].wq);
+ }
+}
+
+STATIC_IFN_KUNIT bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2)
+{
+ if (!sink1 || !sink2)
+ return false;
+ if (sink1->sink_signal != sink2->sink_signal)
+ return false;
+
+ if (sink1->dc_edid.length != sink2->dc_edid.length)
+ return false;
+
+ if (memcmp(sink1->dc_edid.raw_edid, sink2->dc_edid.raw_edid,
+ sink1->dc_edid.length) != 0)
+ return false;
+ return true;
+}
+EXPORT_IF_KUNIT(are_sinks_equal);
+
+
+/**
+ * DOC: amdgpu_dm_hdmi_hpd_debounce_work
+ *
+ * HDMI HPD debounce delay in milliseconds. When an HDMI display toggles HPD
+ * (such as during power save transitions), this delay determines how long to
+ * wait before processing the HPD event. This allows distinguishing between a
+ * physical unplug (>hdmi_hpd_debounce_delay)
+ * and a spontaneous RX HPD toggle (<hdmi_hpd_debounce_delay).
+ *
+ * If the toggle is less than this delay, the driver compares sink capabilities
+ * and permits a hotplug event if they changed.
+ *
+ * The default value of 1500ms was chosen based on experimental testing with
+ * various monitors that exhibit spontaneous HPD toggling behavior.
+ */
+void amdgpu_dm_hdmi_hpd_debounce_work(struct work_struct *work)
+{
+ struct amdgpu_dm_connector *aconnector =
+ container_of(to_delayed_work(work), struct amdgpu_dm_connector,
+ hdmi_hpd_debounce_work);
+ struct drm_connector *connector = &aconnector->base;
+ struct drm_device *dev = connector->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct dc *dc = aconnector->dc_link->ctx->dc;
+ bool fake_reconnect = false;
+ bool reallow_idle = false;
+ bool ret = false;
+
+ guard(mutex)(&aconnector->hpd_lock);
+
+ /* Re-detect the display */
+ scoped_guard(mutex, &adev->dm.dc_lock) {
+ if (dc->caps.ips_support && dc->ctx->dmub_srv->idle_allowed) {
+ dc_allow_idle_optimizations(dc, false);
+ reallow_idle = true;
+ }
+ ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
+ }
+
+ if (ret) {
+ /* Apply workaround delay for certain panels */
+ amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
+ /* Compare sinks to determine if this was a spontaneous HPD toggle */
+ if (are_sinks_equal(aconnector->dc_link->local_sink, aconnector->hdmi_prev_sink)) {
+ /*
+ * Sinks match - this was a spontaneous HDMI HPD toggle.
+ */
+ drm_dbg_kms(dev, "HDMI HPD: Sink unchanged after debounce, internal re-enable\n");
+ fake_reconnect = true;
+ }
+
+ /* Update connector state */
+ amdgpu_dm_update_connector_after_detect(aconnector);
+
+ drm_modeset_lock_all(dev);
+ dm_restore_drm_connector_state(dev, connector);
+ drm_modeset_unlock_all(dev);
+
+ /* Only notify OS if sink actually changed */
+ if (!fake_reconnect && aconnector->base.force == DRM_FORCE_UNSPECIFIED)
+ drm_kms_helper_hotplug_event(dev);
+ }
+
+ /* Release the cached sink reference */
+ if (aconnector->hdmi_prev_sink) {
+ dc_sink_release(aconnector->hdmi_prev_sink);
+ aconnector->hdmi_prev_sink = NULL;
+ }
+
+ scoped_guard(mutex, &adev->dm.dc_lock) {
+ if (reallow_idle && dc->caps.ips_support)
+ dc_allow_idle_optimizations(dc, true);
+ }
+}
+
+static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
+ enum dc_detect_reason reason)
+{
+ struct drm_connector *connector = &aconnector->base;
+ struct drm_device *dev = connector->dev;
+ enum dc_connection_type new_connection_type = dc_connection_none;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state);
+ struct dc *dc = aconnector->dc_link->ctx->dc;
+ bool ret = false;
+ bool debounce_required = false;
+
+ if (adev->dm.disable_hpd_irq)
+ return;
+
+ /*
+ * In case of failure or MST no need to update connector status or notify the OS
+ * since (for MST case) MST does this in its own context.
+ */
+ guard(mutex)(&aconnector->hpd_lock);
+
+ if (adev->dm.hdcp_workqueue) {
+ hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index);
+ dm_con_state->update_hdcp = true;
+ }
+ if (aconnector->fake_enable)
+ aconnector->fake_enable = false;
+
+ aconnector->timing_changed = false;
+
+ if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type))
+ drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
+
+ /*
+ * Check for HDMI disconnect with debounce enabled.
+ */
+ debounce_required = (aconnector->hdmi_hpd_debounce_delay_ms > 0 &&
+ dc_is_hdmi_signal(aconnector->dc_link->connector_signal) &&
+ new_connection_type == dc_connection_none &&
+ aconnector->dc_link->local_sink != NULL);
+
+ if (aconnector->base.force && new_connection_type == dc_connection_none) {
+ amdgpu_dm_emulated_link_detect(aconnector->dc_link);
+
+ drm_modeset_lock_all(dev);
+ dm_restore_drm_connector_state(dev, connector);
+ drm_modeset_unlock_all(dev);
+
+ if (aconnector->base.force == DRM_FORCE_UNSPECIFIED ||
+ reason == DETECT_REASON_HPDRX)
+ drm_kms_helper_connector_hotplug_event(connector);
+ } else if (debounce_required) {
+ /*
+ * HDMI disconnect detected - schedule delayed work instead of
+ * processing immediately. This allows us to coalesce spurious
+ * HDMI signals from physical unplugs.
+ */
+ drm_dbg_kms(dev, "HDMI HPD: Disconnect detected, scheduling debounce work (%u ms)\n",
+ aconnector->hdmi_hpd_debounce_delay_ms);
+
+ /* Cache the current sink for later comparison */
+ if (aconnector->hdmi_prev_sink)
+ dc_sink_release(aconnector->hdmi_prev_sink);
+ aconnector->hdmi_prev_sink = aconnector->dc_link->local_sink;
+ if (aconnector->hdmi_prev_sink)
+ dc_sink_retain(aconnector->hdmi_prev_sink);
+
+ /* Schedule delayed detection. */
+ if (mod_delayed_work(system_percpu_wq,
+ &aconnector->hdmi_hpd_debounce_work,
+ msecs_to_jiffies(aconnector->hdmi_hpd_debounce_delay_ms)))
+ drm_dbg_kms(dev, "HDMI HPD: Re-scheduled debounce work\n");
+
+ } else {
+
+ /* If the aconnector->hdmi_hpd_debounce_work is scheduled, exit early */
+ if (delayed_work_pending(&aconnector->hdmi_hpd_debounce_work))
+ return;
+
+ scoped_guard(mutex, &adev->dm.dc_lock) {
+ dc_exit_ips_for_hw_access(dc);
+ ret = dc_link_detect(aconnector->dc_link, reason);
+ }
+ if (ret) {
+ /* w/a delay for certain panels */
+ amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
+ amdgpu_dm_update_connector_after_detect(aconnector);
+
+ drm_modeset_lock_all(dev);
+ dm_restore_drm_connector_state(dev, connector);
+ drm_modeset_unlock_all(dev);
+
+ if (aconnector->base.force == DRM_FORCE_UNSPECIFIED ||
+ reason == DETECT_REASON_HPDRX)
+ drm_kms_helper_connector_hotplug_event(connector);
+ }
+ }
+}
+
+static void handle_hpd_irq(void *param)
+{
+ struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
+
+ handle_hpd_irq_helper(aconnector, DETECT_REASON_HPD);
+
+}
+
+static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq,
+ union hpd_irq_data hpd_irq_data)
+{
+ struct hpd_rx_irq_offload_work *offload_work = kzalloc_obj(*offload_work);
+
+ if (!offload_work) {
+ drm_err(adev_to_drm(adev), "Failed to allocate hpd_rx_irq_offload_work.\n");
+ return;
+ }
+
+ INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work);
+ offload_work->data = hpd_irq_data;
+ offload_work->offload_wq = offload_wq;
+ offload_work->adev = adev;
+
+ queue_work(offload_wq->wq, &offload_work->work);
+ drm_dbg_kms(adev_to_drm(adev), "queue work to handle hpd_rx offload work");
+}
+
+static void handle_hpd_rx_irq(void *param)
+{
+ struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
+ struct drm_connector *connector = &aconnector->base;
+ struct drm_device *dev = connector->dev;
+ struct dc_link *dc_link = aconnector->dc_link;
+ bool is_mst_root_connector = aconnector->mst_mgr.mst_state;
+ bool result = false;
+ enum dc_connection_type new_connection_type = dc_connection_none;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ union hpd_irq_data hpd_irq_data;
+ bool link_loss = false;
+ bool has_left_work = false;
+ int idx = dc_link->link_index;
+ struct hpd_rx_irq_offload_work_queue *offload_wq = &adev->dm.hpd_rx_offload_wq[idx];
+ struct dc *dc = aconnector->dc_link->ctx->dc;
+
+ memset(&hpd_irq_data, 0, sizeof(hpd_irq_data));
+
+ if (adev->dm.disable_hpd_irq)
+ return;
+
+ /*
+ * TODO:Temporary add mutex to protect hpd interrupt not have a gpio
+ * conflict, after implement i2c helper, this mutex should be
+ * retired.
+ */
+ mutex_lock(&aconnector->hpd_lock);
+
+ result = dc_link_handle_hpd_rx_irq(dc_link, &hpd_irq_data,
+ &link_loss, true, &has_left_work);
+
+ if (!has_left_work)
+ goto out;
+
+ if (hpd_irq_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
+ schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
+ goto out;
+ }
+
+ if (dc_link_dp_allow_hpd_rx_irq(dc_link)) {
+ if (hpd_irq_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY ||
+ hpd_irq_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
+ bool skip = false;
+
+ /*
+ * DOWN_REP_MSG_RDY is also handled by polling method
+ * mgr->cbs->poll_hpd_irq()
+ */
+ spin_lock(&offload_wq->offload_lock);
+ skip = offload_wq->is_handling_mst_msg_rdy_event;
+
+ if (!skip)
+ offload_wq->is_handling_mst_msg_rdy_event = true;
+
+ spin_unlock(&offload_wq->offload_lock);
+
+ if (!skip)
+ schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
+
+ goto out;
+ }
+
+ if (link_loss) {
+ bool skip = false;
+
+ spin_lock(&offload_wq->offload_lock);
+ skip = offload_wq->is_handling_link_loss;
+
+ if (!skip)
+ offload_wq->is_handling_link_loss = true;
+
+ spin_unlock(&offload_wq->offload_lock);
+
+ if (!skip)
+ schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
+
+ goto out;
+ }
+ }
+
+out:
+ if (result && !is_mst_root_connector) {
+ /* Downstream Port status changed. */
+ if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
+ drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
+
+ if (aconnector->base.force && new_connection_type == dc_connection_none) {
+ amdgpu_dm_emulated_link_detect(dc_link);
+
+ if (aconnector->fake_enable)
+ aconnector->fake_enable = false;
+
+ amdgpu_dm_update_connector_after_detect(aconnector);
+
+
+ drm_modeset_lock_all(dev);
+ dm_restore_drm_connector_state(dev, connector);
+ drm_modeset_unlock_all(dev);
+
+ drm_kms_helper_connector_hotplug_event(connector);
+ } else {
+ bool ret = false;
+
+ mutex_lock(&adev->dm.dc_lock);
+ dc_exit_ips_for_hw_access(dc);
+ ret = dc_link_detect(dc_link, DETECT_REASON_HPDRX);
+ mutex_unlock(&adev->dm.dc_lock);
+
+ if (ret) {
+ if (aconnector->fake_enable)
+ aconnector->fake_enable = false;
+
+ amdgpu_dm_update_connector_after_detect(aconnector);
+
+ drm_modeset_lock_all(dev);
+ dm_restore_drm_connector_state(dev, connector);
+ drm_modeset_unlock_all(dev);
+
+ drm_kms_helper_connector_hotplug_event(connector);
+ }
+ }
+ }
+ if (hpd_irq_data.bytes.device_service_irq.bits.CP_IRQ) {
+ if (adev->dm.hdcp_workqueue)
+ hdcp_handle_cpirq(adev->dm.hdcp_workqueue, aconnector->base.index);
+ }
+
+ if (dc_link->type != dc_connection_mst_branch)
+ drm_dp_cec_irq(&aconnector->dm_dp_aux.aux);
+
+ mutex_unlock(&aconnector->hpd_lock);
+}
+
+/**
+ * dmub_hpd_callback - DMUB HPD interrupt processing callback.
+ * @adev: amdgpu_device pointer
+ * @notify: dmub notification structure
+ *
+ * Dmub Hpd interrupt processing callback. Gets displayindex through the
+ * ink index and calls helper to do the processing.
+ */
+static void dmub_hpd_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct amdgpu_dm_connector *hpd_aconnector = NULL;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter iter;
+ struct dc_link *link;
+ u8 link_index = 0;
+ struct drm_device *dev;
+
+ if (adev == NULL)
+ return;
+
+ if (notify == NULL) {
+ drm_err(adev_to_drm(adev), "DMUB HPD callback notification was NULL");
+ return;
+ }
+
+ if (notify->link_index > adev->dm.dc->link_count) {
+ drm_err(adev_to_drm(adev), "DMUB HPD index (%u)is abnormal", notify->link_index);
+ return;
+ }
+
+ /* Skip DMUB HPD IRQ in suspend/resume. We will probe them later. */
+ if (notify->type == DMUB_NOTIFICATION_HPD && adev->in_suspend) {
+ drm_info(adev_to_drm(adev), "Skip DMUB HPD IRQ callback in suspend/resume\n");
+ return;
+ }
+
+ link_index = notify->link_index;
+ link = adev->dm.dc->links[link_index];
+ dev = adev->dm.ddev;
+
+ drm_connector_list_iter_begin(dev, &iter);
+ drm_for_each_connector_iter(connector, &iter) {
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ continue;
+
+ aconnector = to_amdgpu_dm_connector(connector);
+ if (link && aconnector->dc_link == link) {
+ if (notify->type == DMUB_NOTIFICATION_HPD)
+ drm_info(adev_to_drm(adev), "DMUB HPD IRQ callback: link_index=%u\n", link_index);
+ else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ)
+ drm_info(adev_to_drm(adev), "DMUB HPD RX IRQ callback: link_index=%u\n", link_index);
+ else
+ drm_warn(adev_to_drm(adev), "DMUB Unknown HPD callback type %d, link_index=%u\n",
+ notify->type, link_index);
+
+ hpd_aconnector = aconnector;
+ break;
+ }
+ }
+ drm_connector_list_iter_end(&iter);
+
+ if (hpd_aconnector) {
+ if (notify->type == DMUB_NOTIFICATION_HPD) {
+ if (hpd_aconnector->dc_link->hpd_status == (notify->hpd_status == DP_HPD_PLUG))
+ drm_warn(adev_to_drm(adev), "DMUB reported hpd status unchanged. link_index=%u\n", link_index);
+ handle_hpd_irq_helper(hpd_aconnector, DETECT_REASON_HPD);
+ } else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ) {
+ handle_hpd_rx_irq(hpd_aconnector);
+ }
+ }
+}
+
+/**
+ * dmub_hpd_sense_callback - DMUB HPD sense processing callback.
+ * @adev: amdgpu_device pointer
+ * @notify: dmub notification structure
+ *
+ * HPD sense changes can occur during low power states and need to be
+ * notified from firmware to driver.
+ */
+static void dmub_hpd_sense_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify)
+{
+ drm_dbg_driver(adev_to_drm(adev), "DMUB HPD SENSE callback.\n");
+}
+
+int amdgpu_dm_register_hpd_handlers(struct amdgpu_device *adev)
+{
+ struct drm_device *dev = adev_to_drm(adev);
+ struct drm_connector *connector;
+ struct amdgpu_dm_connector *aconnector;
+ const struct dc_link *dc_link;
+ struct dc_interrupt_params int_params = {0};
+
+ int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
+ int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
+
+ if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD,
+ dmub_hpd_callback, true)) {
+ drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
+ return -EINVAL;
+ }
+
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ,
+ dmub_hpd_callback, true)) {
+ drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
+ return -EINVAL;
+ }
+
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_SENSE_NOTIFY,
+ dmub_hpd_sense_callback, true)) {
+ drm_err(adev_to_drm(adev), "fail to register dmub hpd sense callback");
+ return -EINVAL;
+ }
+ }
+
+ list_for_each_entry(connector,
+ &dev->mode_config.connector_list, head) {
+
+ if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
+ continue;
+
+ aconnector = to_amdgpu_dm_connector(connector);
+ dc_link = aconnector->dc_link;
+
+ if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) {
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source = dc_link->irq_source_hpd;
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_HPD1 ||
+ int_params.irq_source > DC_IRQ_SOURCE_HPD6) {
+ drm_err(adev_to_drm(adev), "Failed to register hpd irq!\n");
+ return -EINVAL;
+ }
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ handle_hpd_irq, (void *) aconnector))
+ return -ENOMEM;
+ }
+
+ if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) {
+
+ /* Also register for DP short pulse (hpd_rx). */
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source = dc_link->irq_source_hpd_rx;
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_HPD1RX ||
+ int_params.irq_source > DC_IRQ_SOURCE_HPD6RX) {
+ drm_err(adev_to_drm(adev), "Failed to register hpd rx irq!\n");
+ return -EINVAL;
+ }
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ handle_hpd_rx_irq, (void *) aconnector))
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/* ========== IRQ handlers ========== */
+struct amdgpu_crtc *
+amdgpu_dm_get_crtc_by_otg_inst(struct amdgpu_device *adev,
+ int otg_inst)
+{
+ struct drm_device *dev = adev_to_drm(adev);
+ struct drm_crtc *crtc;
+ struct amdgpu_crtc *amdgpu_crtc;
+
+ if (WARN_ON(otg_inst == -1))
+ return adev->mode_info.crtcs[0];
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ amdgpu_crtc = to_amdgpu_crtc(crtc);
+
+ if (amdgpu_crtc->otg_inst == otg_inst)
+ return amdgpu_crtc;
+ }
+
+ return NULL;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_get_crtc_by_otg_inst);
+
+/**
+ * dm_pflip_high_irq() - Handle pageflip interrupt
+ * @interrupt_params: ignored
+ *
+ * Handles the pageflip interrupt by notifying all interested parties
+ * that the pageflip has been completed.
+ */
+static void dm_pflip_high_irq(void *interrupt_params)
+{
+ struct amdgpu_crtc *amdgpu_crtc;
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct drm_device *dev = adev_to_drm(adev);
+ unsigned long flags;
+ struct drm_pending_vblank_event *e;
+ u32 vpos, hpos, v_blank_start, v_blank_end;
+ bool vrr_active;
+
+ amdgpu_crtc = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP);
+
+ /* IRQ could occur when in initial stage */
+ /* TODO work and BO cleanup */
+ if (amdgpu_crtc == NULL) {
+ drm_dbg_state(dev, "CRTC is null, returning.\n");
+ return;
+ }
+
+ spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
+
+ if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED) {
+ drm_dbg_state(dev,
+ "amdgpu_crtc->pflip_status = %d != AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p]\n",
+ amdgpu_crtc->pflip_status, AMDGPU_FLIP_SUBMITTED,
+ amdgpu_crtc->crtc_id, amdgpu_crtc);
+ spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
+ return;
+ }
+
+ /* page flip completed. */
+ e = amdgpu_crtc->event;
+ amdgpu_crtc->event = NULL;
+
+ WARN_ON(!e);
+
+ vrr_active = amdgpu_dm_crtc_vrr_active_irq(amdgpu_crtc);
+
+ /* Fixed refresh rate, or VRR scanout position outside front-porch? */
+ if (!vrr_active ||
+ !dc_stream_get_scanoutpos(amdgpu_crtc->dm_irq_params.stream, &v_blank_start,
+ &v_blank_end, &hpos, &vpos) ||
+ (vpos < v_blank_start)) {
+ /* Update to correct count and vblank timestamp if racing with
+ * vblank irq. This also updates to the correct vblank timestamp
+ * even in VRR mode, as scanout is past the front-porch atm.
+ */
+ drm_crtc_accurate_vblank_count(&amdgpu_crtc->base);
+
+ /* Wake up userspace by sending the pageflip event with proper
+ * count and timestamp of vblank of flip completion.
+ */
+ if (e) {
+ drm_crtc_send_vblank_event(&amdgpu_crtc->base, e);
+
+ /* Event sent, so done with vblank for this flip */
+ drm_crtc_vblank_put(&amdgpu_crtc->base);
+ }
+ } else if (e) {
+ /* VRR active and inside front-porch: vblank count and
+ * timestamp for pageflip event will only be up to date after
+ * drm_crtc_handle_vblank() has been executed from late vblank
+ * irq handler after start of back-porch (vline 0). We queue the
+ * pageflip event for send-out by drm_crtc_handle_vblank() with
+ * updated timestamp and count, once it runs after us.
+ *
+ * We need to open-code this instead of using the helper
+ * drm_crtc_arm_vblank_event(), as that helper would
+ * call drm_crtc_accurate_vblank_count(), which we must
+ * not call in VRR mode while we are in front-porch!
+ */
+
+ /* sequence will be replaced by real count during send-out. */
+ e->sequence = drm_crtc_vblank_count(&amdgpu_crtc->base);
+ e->pipe = amdgpu_crtc->crtc_id;
+
+ list_add_tail(&e->base.link, &adev_to_drm(adev)->vblank_event_list);
+ e = NULL;
+ }
+
+ /* Keep track of vblank of this flip for flip throttling. We use the
+ * cooked hw counter, as that one incremented at start of this vblank
+ * of pageflip completion, so last_flip_vblank is the forbidden count
+ * for queueing new pageflips if vsync + VRR is enabled.
+ */
+ amdgpu_crtc->dm_irq_params.last_flip_vblank =
+ amdgpu_get_vblank_counter_kms(&amdgpu_crtc->base);
+
+ amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
+ spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
+
+ drm_dbg_state(dev,
+ "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n",
+ amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e);
+}
+
+static void dm_handle_vmin_vmax_update(struct work_struct *offload_work)
+{
+ struct vupdate_offload_work *work = container_of(offload_work, struct vupdate_offload_work, work);
+ struct amdgpu_device *adev = work->adev;
+ struct dc_stream_state *stream = work->stream;
+ struct dc_crtc_timing_adjust *adjust = work->adjust;
+
+ mutex_lock(&adev->dm.dc_lock);
+ dc_stream_adjust_vmin_vmax(adev->dm.dc, stream, adjust);
+ mutex_unlock(&adev->dm.dc_lock);
+
+ dc_stream_release(stream);
+ kfree(work->adjust);
+ kfree(work);
+}
+
+static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
+ struct dc_stream_state *stream,
+ struct dc_crtc_timing_adjust *adjust)
+{
+ struct vupdate_offload_work *offload_work = kzalloc_obj(*offload_work,
+ GFP_NOWAIT);
+ if (!offload_work) {
+ drm_dbg_driver(adev_to_drm(adev), "Failed to allocate vupdate_offload_work\n");
+ return;
+ }
+
+ struct dc_crtc_timing_adjust *adjust_copy = kzalloc_obj(*adjust_copy,
+ GFP_NOWAIT);
+ if (!adjust_copy) {
+ drm_dbg_driver(adev_to_drm(adev), "Failed to allocate adjust_copy\n");
+ kfree(offload_work);
+ return;
+ }
+
+ dc_stream_retain(stream);
+ memcpy(adjust_copy, adjust, sizeof(*adjust_copy));
+
+ INIT_WORK(&offload_work->work, dm_handle_vmin_vmax_update);
+ offload_work->adev = adev;
+ offload_work->stream = stream;
+ offload_work->adjust = adjust_copy;
+
+ queue_work(system_percpu_wq, &offload_work->work);
+}
+
+static void dm_vupdate_high_irq(void *interrupt_params)
+{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_crtc *acrtc;
+ struct drm_device *drm_dev;
+ struct drm_vblank_crtc *vblank;
+ ktime_t frame_duration_ns, previous_timestamp;
+ unsigned long flags;
+ int vrr_active;
+
+ acrtc = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
+
+ if (acrtc) {
+ vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
+ drm_dev = acrtc->base.dev;
+ vblank = drm_crtc_vblank_crtc(&acrtc->base);
+ previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
+ frame_duration_ns = vblank->time - previous_timestamp;
+
+ if (frame_duration_ns > 0) {
+ trace_amdgpu_refresh_rate_track(acrtc->base.index,
+ frame_duration_ns,
+ ktime_divns(NSEC_PER_SEC, frame_duration_ns));
+ atomic64_set(&irq_params->previous_timestamp, vblank->time);
+ }
+
+ drm_dbg_vbl(drm_dev,
+ "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
+ vrr_active);
+
+ /* Core vblank handling is done here after end of front-porch in
+ * vrr mode, as vblank timestamping will give valid results
+ * while now done after front-porch. This will also deliver
+ * page-flip completion events that have been queued to us
+ * if a pageflip happened inside front-porch.
+ */
+ if (vrr_active && acrtc->dm_irq_params.stream) {
+ bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
+ bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
+ bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
+ == VRR_STATE_ACTIVE_VARIABLE;
+
+ amdgpu_dm_crtc_handle_vblank(acrtc);
+
+ /* BTR processing for pre-DCE12 ASICs */
+ if (adev->family < AMDGPU_FAMILY_AI) {
+ spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
+ mod_freesync_handle_v_update(
+ adev->dm.freesync_module,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params);
+
+ if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
+ schedule_dc_vmin_vmax(adev,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params.adjust);
+ }
+ spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
+ }
+ }
+ }
+}
+
+/**
+ * dm_crtc_high_irq() - Handles CRTC interrupt
+ * @interrupt_params: used for determining the CRTC instance
+ *
+ * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK
+ * event handler.
+ */
+static void dm_crtc_high_irq(void *interrupt_params)
+{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_crtc *acrtc;
+ unsigned long flags;
+ int vrr_active;
+
+ acrtc = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
+ if (!acrtc)
+ return;
+
+ if (acrtc->wb_conn && acrtc->wb_pending) {
+ struct dc_stream_state *stream = acrtc->dm_irq_params.stream;
+ unsigned int v_total, refresh_hz;
+
+ v_total = stream->adjust.v_total_max ?
+ stream->adjust.v_total_max : stream->timing.v_total;
+ refresh_hz = div_u64((uint64_t) stream->timing.pix_clk_100hz *
+ 100LL, (v_total * stream->timing.h_total));
+ mdelay(1000 / refresh_hz);
+
+ /*
+ * Completion (signalling the out fence and releasing the vblank
+ * reference taken in dm_set_writeback()) is handled by the shared
+ * helper, which is also used by the teardown path.
+ */
+ if (amdgpu_dm_crtc_complete_writeback(acrtc))
+ dc_stream_fc_disable_writeback(adev->dm.dc,
+ acrtc->dm_irq_params.stream, 0);
+ }
+
+ vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
+
+ drm_dbg_vbl(adev_to_drm(adev),
+ "crtc:%d, vupdate-vrr:%d, planes:%d\n", acrtc->crtc_id,
+ vrr_active, acrtc->dm_irq_params.active_planes);
+
+ /**
+ * Core vblank handling at start of front-porch is only possible
+ * in non-vrr mode, as only there vblank timestamping will give
+ * valid results while done in front-porch. Otherwise defer it
+ * to dm_vupdate_high_irq after end of front-porch.
+ */
+ if (!vrr_active)
+ amdgpu_dm_crtc_handle_vblank(acrtc);
+
+ /**
+ * Following stuff must happen at start of vblank, for crc
+ * computation and below-the-range btr support in vrr mode.
+ */
+ amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
+
+ /* BTR updates need to happen before VUPDATE on Vega and above. */
+ if (adev->family < AMDGPU_FAMILY_AI)
+ return;
+
+ spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
+
+ if (acrtc->dm_irq_params.stream &&
+ acrtc->dm_irq_params.vrr_params.supported) {
+ bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
+ bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
+ bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state == VRR_STATE_ACTIVE_VARIABLE;
+
+ mod_freesync_handle_v_update(adev->dm.freesync_module,
+ acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params);
+
+ /* update vmin_vmax only if freesync is enabled, or only if PSR and REPLAY are disabled */
+ if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
+ schedule_dc_vmin_vmax(adev, acrtc->dm_irq_params.stream,
+ &acrtc->dm_irq_params.vrr_params.adjust);
+ }
+ }
+
+ /*
+ * If there aren't any active_planes then DCH HUBP may be clock-gated.
+ * In that case, pageflip completion interrupts won't fire and pageflip
+ * completion events won't get delivered. Prevent this by sending
+ * pending pageflip events from here if a flip is still pending.
+ *
+ * If any planes are enabled, use dm_pflip_high_irq() instead, to
+ * avoid race conditions between flip programming and completion,
+ * which could cause too early flip completion events.
+ */
+ if (adev->family >= AMDGPU_FAMILY_RV &&
+ acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+ acrtc->dm_irq_params.active_planes == 0) {
+ if (acrtc->event) {
+ drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
+ acrtc->event = NULL;
+ drm_crtc_vblank_put(&acrtc->base);
+ }
+ acrtc->pflip_status = AMDGPU_FLIP_NONE;
+ }
+
+ spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
+}
+
+#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
+/**
+ * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
+ * DCN generation ASICs
+ * @interrupt_params: interrupt parameters
+ *
+ * Used to set crc window/read out crc value at vertical line 0 position
+ */
+static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params)
+{
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_crtc *acrtc;
+
+ acrtc = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VLINE0);
+
+ if (!acrtc)
+ return;
+
+ amdgpu_dm_crtc_handle_crc_window_irq(&acrtc->base);
+}
+#endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */
+
+static void dm_handle_hpd_work(struct work_struct *work)
+{
+ struct dmub_hpd_work *dmub_hpd_wrk;
+
+ dmub_hpd_wrk = container_of(work, struct dmub_hpd_work, handle_hpd_work);
+
+ if (!dmub_hpd_wrk->dmub_notify) {
+ drm_err(adev_to_drm(dmub_hpd_wrk->adev), "dmub_hpd_wrk dmub_notify is NULL");
+ return;
+ }
+
+ if (dmub_hpd_wrk->dmub_notify->type < ARRAY_SIZE(dmub_hpd_wrk->adev->dm.dmub_callback)) {
+ dmub_hpd_wrk->adev->dm.dmub_callback[dmub_hpd_wrk->dmub_notify->type](dmub_hpd_wrk->adev,
+ dmub_hpd_wrk->dmub_notify);
+ }
+
+ kfree(dmub_hpd_wrk->dmub_notify);
+ kfree(dmub_hpd_wrk);
+
+}
+
+STATIC_IFN_KUNIT const char *dmub_notification_type_str(enum dmub_notification_type e)
+{
+ switch (e) {
+ case DMUB_NOTIFICATION_NO_DATA:
+ return "NO_DATA";
+ case DMUB_NOTIFICATION_AUX_REPLY:
+ return "AUX_REPLY";
+ case DMUB_NOTIFICATION_HPD:
+ return "HPD";
+ case DMUB_NOTIFICATION_HPD_IRQ:
+ return "HPD_IRQ";
+ case DMUB_NOTIFICATION_SET_CONFIG_REPLY:
+ return "SET_CONFIG_REPLY";
+ case DMUB_NOTIFICATION_DPIA_NOTIFICATION:
+ return "DPIA_NOTIFICATION";
+ case DMUB_NOTIFICATION_HPD_SENSE_NOTIFY:
+ return "HPD_SENSE_NOTIFY";
+ case DMUB_NOTIFICATION_FUSED_IO:
+ return "FUSED_IO";
+ default:
+ return "<unknown>";
+ }
+}
+EXPORT_IF_KUNIT(dmub_notification_type_str);
+
+#define DMUB_TRACE_MAX_READ 64
+/**
+ * dm_dmub_outbox1_low_irq() - Handles Outbox interrupt
+ * @interrupt_params: used for determining the Outbox instance
+ *
+ * Handles the Outbox Interrupt
+ * event handler.
+ */
+static void dm_dmub_outbox1_low_irq(void *interrupt_params)
+{
+ struct dmub_notification notify = {0};
+ struct common_irq_params *irq_params = interrupt_params;
+ struct amdgpu_device *adev = irq_params->adev;
+ struct amdgpu_display_manager *dm = &adev->dm;
+ struct dmcub_trace_buf_entry entry = { 0 };
+ u32 count = 0;
+ struct dmub_hpd_work *dmub_hpd_wrk;
+
+ do {
+ if (dc_dmub_srv_get_dmub_outbox0_msg(dm->dc, &entry)) {
+ trace_amdgpu_dmub_trace_high_irq(entry.trace_code, entry.tick_count,
+ entry.param0, entry.param1);
+
+ drm_dbg_driver(adev_to_drm(adev), "trace_code:%u, tick_count:%u, param0:%u, param1:%u\n",
+ entry.trace_code, entry.tick_count, entry.param0, entry.param1);
+ } else
+ break;
+
+ count++;
+
+ } while (count <= DMUB_TRACE_MAX_READ);
+
+ if (count > DMUB_TRACE_MAX_READ)
+ drm_dbg_driver(adev_to_drm(adev), "Warning : count > DMUB_TRACE_MAX_READ");
+
+ if (dc_enable_dmub_notifications(adev->dm.dc) &&
+ irq_params->irq_src == DC_IRQ_SOURCE_DMCUB_OUTBOX) {
+
+ do {
+ dc_stat_get_dmub_notification(adev->dm.dc, &notify);
+ if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) {
+ drm_err(adev_to_drm(adev), "DM: notify type %d invalid!", notify.type);
+ continue;
+ }
+ if (!dm->dmub_callback[notify.type]) {
+ drm_warn(adev_to_drm(adev), "DMUB notification skipped due to no handler: type=%s\n",
+ dmub_notification_type_str(notify.type));
+ continue;
+ }
+ if (dm->dmub_thread_offload[notify.type]) {
+ dmub_hpd_wrk = kzalloc_obj(*dmub_hpd_wrk,
+ GFP_ATOMIC);
+ if (!dmub_hpd_wrk) {
+ drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk");
+ return;
+ }
+ dmub_hpd_wrk->dmub_notify = kmemdup(&notify, sizeof(struct dmub_notification),
+ GFP_ATOMIC);
+ if (!dmub_hpd_wrk->dmub_notify) {
+ kfree(dmub_hpd_wrk);
+ drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk->dmub_notify");
+ return;
+ }
+ INIT_WORK(&dmub_hpd_wrk->handle_hpd_work, dm_handle_hpd_work);
+ dmub_hpd_wrk->adev = adev;
+ queue_work(adev->dm.delayed_hpd_wq, &dmub_hpd_wrk->handle_hpd_work);
+ } else {
+ dm->dmub_callback[notify.type](adev, &notify);
+ }
+ } while (notify.pending_notification);
+ }
+}
+
+/* Register IRQ sources and initialize IRQ callbacks */
+int amdgpu_dm_dce110_register_irq_handlers(struct amdgpu_device *adev)
+{
+ struct dc *dc = adev->dm.dc;
+ struct common_irq_params *c_irq_params;
+ struct dc_interrupt_params int_params = {0};
+ int r;
+ int i;
+ unsigned int src_id;
+ unsigned int client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
+ /* Use different interrupts for VBLANK on DCE 6 vs. newer. */
+ const unsigned int vblank_d1 =
+ adev->dm.dc->ctx->dce_version >= DCE_VERSION_8_0
+ ? VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 : 1;
+
+ if (adev->family >= AMDGPU_FAMILY_AI)
+ client_id = SOC15_IH_CLIENTID_DCE;
+
+ int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
+ int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
+
+ /*
+ * Actions of amdgpu_irq_add_id():
+ * 1. Register a set() function with base driver.
+ * Base driver will call set() function to enable/disable an
+ * interrupt in DC hardware.
+ * 2. Register amdgpu_dm_irq_handler().
+ * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
+ * coming from DC hardware.
+ * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
+ * for acknowledging and handling.
+ */
+
+ /* Use VBLANK interrupt */
+ for (i = 0; i < adev->mode_info.num_crtc; i++) {
+ src_id = vblank_d1 + i;
+ r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->crtc_irq);
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, src_id, 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
+ int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
+ drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_crtc_high_irq, c_irq_params))
+ return -ENOMEM;
+ }
+
+ if (dc_supports_vrr(adev->dm.dc->ctx->dce_version)) {
+ /* Use VUPDATE interrupt */
+ for (i = 0; i < adev->mode_info.num_crtc; i++) {
+ src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + i * 2;
+ r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->vupdate_irq);
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, src_id, 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 ||
+ int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) {
+ drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.vupdate_params[
+ int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_vupdate_high_irq, c_irq_params))
+ return -ENOMEM;
+ }
+ }
+
+ /* Use GRPH_PFLIP interrupt */
+ for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP;
+ i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) {
+ r = amdgpu_irq_add_id(adev, client_id, i, &adev->pageflip_irq);
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, i, 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
+ int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
+ drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_pflip_high_irq, c_irq_params))
+ return -ENOMEM;
+ }
+
+ /* HPD */
+ r = amdgpu_irq_add_id(adev, client_id,
+ VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A, &adev->hpd_irq);
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n");
+ return r;
+ }
+
+ r = amdgpu_dm_register_hpd_handlers(adev);
+
+ return r;
+}
+
+/* Register IRQ sources and initialize IRQ callbacks */
+int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev)
+{
+ struct dc *dc = adev->dm.dc;
+ struct common_irq_params *c_irq_params;
+ struct dc_interrupt_params int_params = {0};
+ int r;
+ int i;
+#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
+ static const unsigned int vrtl_int_srcid[] = {
+ DCN_1_0__SRCID__OTG1_VERTICAL_INTERRUPT0_CONTROL,
+ DCN_1_0__SRCID__OTG2_VERTICAL_INTERRUPT0_CONTROL,
+ DCN_1_0__SRCID__OTG3_VERTICAL_INTERRUPT0_CONTROL,
+ DCN_1_0__SRCID__OTG4_VERTICAL_INTERRUPT0_CONTROL,
+ DCN_1_0__SRCID__OTG5_VERTICAL_INTERRUPT0_CONTROL,
+ DCN_1_0__SRCID__OTG6_VERTICAL_INTERRUPT0_CONTROL
+ };
+#endif
+
+ int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
+ int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
+
+ /*
+ * Actions of amdgpu_irq_add_id():
+ * 1. Register a set() function with base driver.
+ * Base driver will call set() function to enable/disable an
+ * interrupt in DC hardware.
+ * 2. Register amdgpu_dm_irq_handler().
+ * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
+ * coming from DC hardware.
+ * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
+ * for acknowledging and handling.
+ */
+
+ /* Use VSTARTUP interrupt */
+ for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP;
+ i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1;
+ i++) {
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq);
+
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, i, 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
+ int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
+ drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_crtc_high_irq, c_irq_params))
+ return -ENOMEM;
+ }
+
+ /* Use otg vertical line interrupt */
+#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
+ for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE,
+ vrtl_int_srcid[i], &adev->vline0_irq);
+
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add vline0 irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, vrtl_int_srcid[i], 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_DC1_VLINE0 ||
+ int_params.irq_source > DC_IRQ_SOURCE_DC6_VLINE0) {
+ drm_err(adev_to_drm(adev), "Failed to register vline0 irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.vline0_params[int_params.irq_source
+ - DC_IRQ_SOURCE_DC1_VLINE0];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_dcn_vertical_interrupt0_high_irq,
+ c_irq_params))
+ return -ENOMEM;
+ }
+#endif
+
+ /* Use VUPDATE_NO_LOCK interrupt on DCN, which seems to correspond to
+ * the regular VUPDATE interrupt on DCE. We want DC_IRQ_SOURCE_VUPDATEx
+ * to trigger at end of each vblank, regardless of state of the lock,
+ * matching DCE behaviour.
+ */
+ for (i = DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT;
+ i <= DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT + adev->mode_info.num_crtc - 1;
+ i++) {
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->vupdate_irq);
+
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, i, 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 ||
+ int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) {
+ drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.vupdate_params[int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_vupdate_high_irq, c_irq_params))
+ return -ENOMEM;
+ }
+
+ /* Use GRPH_PFLIP interrupt */
+ for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT;
+ i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1;
+ i++) {
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq);
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
+ return r;
+ }
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, i, 0);
+
+ if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
+ int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
+ int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
+ drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
+ return -EINVAL;
+ }
+
+ c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_pflip_high_irq, c_irq_params))
+ return -ENOMEM;
+ }
+
+ /* HPD */
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
+ &adev->hpd_irq);
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n");
+ return r;
+ }
+
+ r = amdgpu_dm_register_hpd_handlers(adev);
+
+ return r;
+}
+
+/* Register Outbox IRQ sources and initialize IRQ callbacks */
+int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev)
+{
+ struct dc *dc = adev->dm.dc;
+ struct common_irq_params *c_irq_params;
+ struct dc_interrupt_params int_params = {0};
+ int r, i;
+
+ int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
+ int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
+
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT,
+ &adev->dmub_outbox_irq);
+ if (r) {
+ drm_err(adev_to_drm(adev), "Failed to add outbox irq id!\n");
+ return r;
+ }
+
+ if (dc->ctx->dmub_srv) {
+ i = DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT;
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source =
+ dc_interrupt_to_irq_source(dc, i, 0);
+
+ c_irq_params = &adev->dm.dmub_outbox_params[0];
+
+ c_irq_params->adev = adev;
+ c_irq_params->irq_src = int_params.irq_source;
+
+ if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_dmub_outbox1_low_irq, c_irq_params))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h
index 4f6b58f4f90d..bccb5d354a9f 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h
@@ -27,6 +27,14 @@
#include "irq_types.h" /* DAL irq definitions */
+struct amdgpu_device;
+struct amdgpu_crtc;
+struct amdgpu_display_manager;
+struct dc_sink;
+struct hpd_rx_irq_offload_work_queue;
+struct work_struct;
+enum dmub_notification_type;
+
/*
* Display Manager IRQ-related interfaces (for use by DAL).
*/
@@ -101,4 +109,23 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev);
void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev);
void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev);
+/* HPD handling */
+struct hpd_rx_irq_offload_work_queue *amdgpu_dm_hpd_rx_irq_create_workqueue(struct amdgpu_device *adev);
+void amdgpu_dm_hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm);
+int amdgpu_dm_register_hpd_handlers(struct amdgpu_device *adev);
+void amdgpu_dm_hdmi_hpd_debounce_work(struct work_struct *work);
+
+/* IRQ handlers */
+struct amdgpu_crtc *amdgpu_dm_get_crtc_by_otg_inst(struct amdgpu_device *adev,
+ int otg_inst);
+int amdgpu_dm_dce110_register_irq_handlers(struct amdgpu_device *adev);
+int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev);
+int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev);
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type);
+bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2);
+const char *dmub_notification_type_str(enum dmub_notification_type e);
+#endif
+
#endif /* __AMDGPU_DM_IRQ_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h
index 4b2864375105..1f910a6a00c0 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h
@@ -10,6 +10,7 @@
#define STATIC_IFN_KUNIT
#define INLINE_IFN_KUNIT inline
#define EXPORT_IF_KUNIT(symbol) EXPORT_SYMBOL(symbol)
+
#else
#define STATIC_IFN_KUNIT static
#define INLINE_IFN_KUNIT
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index b3af7445b457..0546efea5de1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -34,16 +34,17 @@
#include "dm_services.h"
#include "amdgpu.h"
#include "amdgpu_dm.h"
+#include "dmub_cmd.h"
#include "amdgpu_dm_mst_types.h"
#include "amdgpu_dm_hdcp.h"
#include "dc.h"
#include "dm_helpers.h"
+#include "amdgpu_dm_kunit_helpers.h"
#include "ddc_service_types.h"
#include "dpcd_defs.h"
-#include "dmub_cmd.h"
#if defined(CONFIG_DEBUG_FS)
#include "amdgpu_dm_debugfs.h"
#endif
@@ -53,10 +54,53 @@
#define PEAK_FACTOR_X1000 1006
/*
+ * Translate a failed AUX transaction's operation result into an errno-style
+ * return value. @result is returned unchanged for AUX_RET_SUCCESS.
+ */
+STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer_result(ssize_t result,
+ enum aux_return_code_type operation_result)
+{
+ switch (operation_result) {
+ case AUX_RET_SUCCESS:
+ break;
+ case AUX_RET_ERROR_HPD_DISCON:
+ case AUX_RET_ERROR_UNKNOWN:
+ case AUX_RET_ERROR_INVALID_OPERATION:
+ case AUX_RET_ERROR_PROTOCOL_ERROR:
+ result = -EIO;
+ break;
+ case AUX_RET_ERROR_INVALID_REPLY:
+ case AUX_RET_ERROR_ENGINE_ACQUIRE:
+ result = -EBUSY;
+ break;
+ case AUX_RET_ERROR_TIMEOUT:
+ result = -ETIMEDOUT;
+ break;
+ }
+
+ return result;
+}
+EXPORT_IF_KUNIT(dm_dp_aux_transfer_result);
+
+/*
+ * Derive the AUX payload transaction flags from a DP AUX request field.
+ */
+STATIC_IFN_KUNIT void dm_dp_aux_fill_payload_flags(u8 request,
+ struct aux_payload *payload)
+{
+ payload->i2c_over_aux = (request & DP_AUX_NATIVE_WRITE) == 0;
+ payload->write = (request & DP_AUX_I2C_READ) == 0;
+ payload->mot = (request & DP_AUX_I2C_MOT) != 0;
+ payload->write_status_update =
+ (request & DP_AUX_I2C_WRITE_STATUS_UPDATE) != 0;
+}
+EXPORT_IF_KUNIT(dm_dp_aux_fill_payload_flags);
+
+/*
* This function handles both native AUX and I2C-Over-AUX transactions.
*/
-static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
- struct drm_dp_aux_msg *msg)
+STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
{
ssize_t result = 0;
struct aux_payload payload;
@@ -72,11 +116,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
payload.data = msg->buffer;
payload.length = msg->size;
payload.reply = &msg->reply;
- payload.i2c_over_aux = (msg->request & DP_AUX_NATIVE_WRITE) == 0;
- payload.write = (msg->request & DP_AUX_I2C_READ) == 0;
- payload.mot = (msg->request & DP_AUX_I2C_MOT) != 0;
- payload.write_status_update =
- (msg->request & DP_AUX_I2C_WRITE_STATUS_UPDATE) != 0;
+ dm_dp_aux_fill_payload_flags(msg->request, &payload);
payload.defer_delay = 0;
if (payload.write) {
@@ -116,23 +156,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
}
if (result < 0) {
- switch (operation_result) {
- case AUX_RET_SUCCESS:
- break;
- case AUX_RET_ERROR_HPD_DISCON:
- case AUX_RET_ERROR_UNKNOWN:
- case AUX_RET_ERROR_INVALID_OPERATION:
- case AUX_RET_ERROR_PROTOCOL_ERROR:
- result = -EIO;
- break;
- case AUX_RET_ERROR_INVALID_REPLY:
- case AUX_RET_ERROR_ENGINE_ACQUIRE:
- result = -EBUSY;
- break;
- case AUX_RET_ERROR_TIMEOUT:
- result = -ETIMEDOUT;
- break;
- }
+ result = dm_dp_aux_transfer_result(result, operation_result);
drm_dbg_dp(adev_to_drm(adev), "DP AUX transfer fail:%d\n", operation_result);
}
@@ -143,6 +167,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux,
return result;
}
+EXPORT_IF_KUNIT(dm_dp_aux_transfer);
static void
dm_dp_mst_connector_destroy(struct drm_connector *connector)
@@ -183,7 +208,7 @@ amdgpu_dm_mst_connector_late_register(struct drm_connector *connector)
}
-static inline void
+STATIC_IFN_KUNIT void
amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector)
{
aconnector->drm_edid = NULL;
@@ -192,6 +217,7 @@ amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector
aconnector->mst_local_bw = 0;
aconnector->vc_full_pbn = 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_mst_reset_mst_connector_setting);
static void
amdgpu_dm_mst_connector_early_unregister(struct drm_connector *connector)
@@ -248,6 +274,7 @@ bool needs_dsc_aux_workaround(struct dc_link *link)
return false;
}
+EXPORT_IF_KUNIT(needs_dsc_aux_workaround);
#if defined(CONFIG_DRM_AMD_DC_FP)
static bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port)
@@ -311,7 +338,7 @@ static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnecto
}
#endif
-static bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector)
+STATIC_IFN_KUNIT bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector)
{
union dp_downstream_port_present ds_port_present;
@@ -329,8 +356,9 @@ static bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnect
return true;
}
+EXPORT_IF_KUNIT(retrieve_downstream_port_device);
-static bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector)
+STATIC_IFN_KUNIT bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector)
{
struct drm_connector *connector = &aconnector->base;
struct drm_dp_mst_port *port = aconnector->mst_output_port;
@@ -357,15 +385,13 @@ static bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector
return true;
}
+EXPORT_IF_KUNIT(retrieve_branch_specific_data);
static int dm_dp_mst_get_modes(struct drm_connector *connector)
{
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
int ret = 0;
- if (!aconnector)
- return drm_add_edid_modes(connector, NULL);
-
if (!aconnector->drm_edid) {
const struct drm_edid *drm_edid;
@@ -456,7 +482,7 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
* plugged back with same display index, its hdcp properties
* will be retrieved from hdcp_work within dm_dp_mst_get_modes
*/
- if (aconnector->dc_sink && connector->state) {
+ if (connector->state) {
struct drm_device *dev = connector->dev;
struct amdgpu_device *adev = drm_to_adev(dev);
@@ -472,20 +498,18 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
}
}
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(
- connector, aconnector->drm_edid, true);
+ amdgpu_dm_update_freesync_caps(
+ connector, aconnector->drm_edid, true);
#if defined(CONFIG_DRM_AMD_DC_FP)
- if (!validate_dsc_caps_on_connector(aconnector))
- memset(&aconnector->dc_sink->dsc_caps,
- 0, sizeof(aconnector->dc_sink->dsc_caps));
+ if (!validate_dsc_caps_on_connector(aconnector))
+ memset(&aconnector->dc_sink->dsc_caps,
+ 0, sizeof(aconnector->dc_sink->dsc_caps));
#endif
- if (!retrieve_downstream_port_device(aconnector))
- memset(&aconnector->mst_downstream_port_present,
- 0, sizeof(aconnector->mst_downstream_port_present));
- }
+ if (!retrieve_downstream_port_device(aconnector))
+ memset(&aconnector->mst_downstream_port_present,
+ 0, sizeof(aconnector->mst_downstream_port_present));
}
drm_edid_connector_update(&aconnector->base, aconnector->drm_edid);
@@ -495,7 +519,7 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
return ret;
}
-static struct drm_encoder *
+STATIC_IFN_KUNIT struct drm_encoder *
dm_mst_atomic_best_encoder(struct drm_connector *connector,
struct drm_atomic_commit *state)
{
@@ -506,8 +530,9 @@ dm_mst_atomic_best_encoder(struct drm_connector *connector,
return &adev->dm.mst_encoders[acrtc->crtc_id].base;
}
+EXPORT_IF_KUNIT(dm_mst_atomic_best_encoder);
-static int
+STATIC_IFN_KUNIT int
dm_dp_mst_detect(struct drm_connector *connector,
struct drm_modeset_acquire_ctx *ctx, bool force)
{
@@ -577,9 +602,10 @@ dm_dp_mst_detect(struct drm_connector *connector,
return connection_status;
}
+EXPORT_IF_KUNIT(dm_dp_mst_detect);
-static int dm_dp_mst_atomic_check(struct drm_connector *connector,
- struct drm_atomic_commit *state)
+STATIC_IFN_KUNIT int dm_dp_mst_atomic_check(struct drm_connector *connector,
+ struct drm_atomic_commit *state)
{
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
struct drm_dp_mst_topology_mgr *mst_mgr = &aconnector->mst_root->mst_mgr;
@@ -587,6 +613,7 @@ static int dm_dp_mst_atomic_check(struct drm_connector *connector,
return drm_dp_atomic_release_time_slots(state, mst_mgr, mst_port);
}
+EXPORT_IF_KUNIT(dm_dp_mst_atomic_check);
static const struct drm_connector_helper_funcs dm_dp_mst_connector_helper_funcs = {
.get_modes = dm_dp_mst_get_modes,
@@ -627,6 +654,7 @@ dm_dp_create_fake_mst_encoders(struct amdgpu_device *adev)
drm_encoder_helper_add(encoder, &amdgpu_dm_encoder_helper_funcs);
}
}
+EXPORT_IF_KUNIT(dm_dp_create_fake_mst_encoders);
static struct drm_connector *
dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
@@ -711,6 +739,44 @@ dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
return connector;
}
+/*
+ * Select the ESI[1] mask used to filter the MST sideband ready bits for a
+ * given message-ready event type.
+ */
+STATIC_IFN_KUNIT u8 dm_mst_msg_ready_mask(enum mst_msg_ready_type msg_rdy_type)
+{
+ switch (msg_rdy_type) {
+ case DOWN_REP_MSG_RDY_EVENT:
+ /* Only handle DOWN_REP_MSG_RDY case*/
+ return DP_DOWN_REP_MSG_RDY;
+ case UP_REQ_MSG_RDY_EVENT:
+ /* Only handle UP_REQ_MSG_RDY case*/
+ return DP_UP_REQ_MSG_RDY;
+ default:
+ /* Handle both cases*/
+ return DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY;
+ }
+}
+EXPORT_IF_KUNIT(dm_mst_msg_ready_mask);
+
+/*
+ * Select the DPCD ESI address and read length based on the DPCD revision.
+ */
+STATIC_IFN_KUNIT void dm_mst_select_esi_dpcd(u8 dpcd_rev, int *dpcd_addr,
+ u8 *dpcd_bytes_to_read)
+{
+ if (dpcd_rev < 0x12) {
+ *dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT;
+ /* DPCD 0x200 - 0x201 for downstream IRQ */
+ *dpcd_addr = DP_SINK_COUNT;
+ } else {
+ *dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI;
+ /* DPCD 0x2002 - 0x2005 for downstream IRQ */
+ *dpcd_addr = DP_SINK_COUNT_ESI;
+ }
+}
+EXPORT_IF_KUNIT(dm_mst_select_esi_dpcd);
+
void dm_handle_mst_sideband_msg_ready_event(
struct drm_dp_mst_topology_mgr *mgr,
enum mst_msg_ready_type msg_rdy_type)
@@ -729,15 +795,8 @@ void dm_handle_mst_sideband_msg_ready_event(
const struct dc_link_status *link_status = dc_link_get_status(aconnector->dc_link);
- if (link_status->dpcd_caps->dpcd_rev.raw < 0x12) {
- dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT;
- /* DPCD 0x200 - 0x201 for downstream IRQ */
- dpcd_addr = DP_SINK_COUNT;
- } else {
- dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI;
- /* DPCD 0x2002 - 0x2005 for downstream IRQ */
- dpcd_addr = DP_SINK_COUNT_ESI;
- }
+ dm_mst_select_esi_dpcd(link_status->dpcd_caps->dpcd_rev.raw, &dpcd_addr,
+ &dpcd_bytes_to_read);
mutex_lock(&aconnector->handle_mst_msg_ready);
@@ -759,20 +818,7 @@ void dm_handle_mst_sideband_msg_ready_event(
DRM_DEBUG_DRIVER("ESI %02x %02x %02x\n", esi[0], esi[1], esi[2]);
- switch (msg_rdy_type) {
- case DOWN_REP_MSG_RDY_EVENT:
- /* Only handle DOWN_REP_MSG_RDY case*/
- esi[1] &= DP_DOWN_REP_MSG_RDY;
- break;
- case UP_REQ_MSG_RDY_EVENT:
- /* Only handle UP_REQ_MSG_RDY case*/
- esi[1] &= DP_UP_REQ_MSG_RDY;
- break;
- default:
- /* Handle both cases*/
- esi[1] &= (DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY);
- break;
- }
+ esi[1] &= dm_mst_msg_ready_mask(msg_rdy_type);
if (!esi[1])
break;
@@ -814,6 +860,7 @@ void dm_handle_mst_sideband_msg_ready_event(
if (process_count == max_process_count)
DRM_DEBUG_DRIVER("Loop exceeded max iterations\n");
}
+EXPORT_IF_KUNIT(dm_handle_mst_sideband_msg_ready_event);
static void dm_handle_mst_down_rep_msg_ready(struct drm_dp_mst_topology_mgr *mgr)
{
@@ -869,6 +916,7 @@ uint32_t dm_mst_get_pbn_divider(struct dc_link *link)
return dfixed_const(pbn_div_x100) / 100;
}
+EXPORT_IF_KUNIT(dm_mst_get_pbn_divider);
struct dsc_mst_fairness_params {
struct dc_crtc_timing *timing;
@@ -1787,7 +1835,7 @@ int pre_validate_dsc(struct drm_atomic_commit *state,
dm_old_crtc_state = to_dm_crtc_state(state->crtcs[ind].old_state);
local_dc_state->streams[i] =
- create_validate_stream_for_sink(connector,
+ amdgpu_dm_create_validate_stream_for_sink(connector,
&state->crtcs[ind].new_state->mode,
dm_new_conn_state,
dm_old_crtc_state->stream);
@@ -2066,3 +2114,4 @@ enum dc_status dm_dp_mst_is_port_support_mode(
#endif
return DC_OK;
}
+EXPORT_IF_KUNIT(dm_dp_mst_is_port_support_mode);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
index 0e8eef5bdb74..fecf108a9216 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
@@ -57,8 +57,15 @@ enum mst_msg_ready_type {
DOWN_OR_UP_MSG_RDY_EVENT = 3
};
+struct amdgpu_device;
struct amdgpu_display_manager;
struct amdgpu_dm_connector;
+struct aux_payload;
+struct dc_state;
+struct dc_stream_state;
+struct dm_atomic_state;
+struct drm_atomic_commit;
+struct drm_dp_mst_topology_mgr;
uint32_t dm_mst_get_pbn_divider(struct dc_link *link);
@@ -94,4 +101,22 @@ enum dc_status dm_dp_mst_is_port_support_mode(
struct amdgpu_dm_connector *aconnector,
struct dc_stream_state *stream);
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+void amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector);
+bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector);
+bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector);
+ssize_t dm_dp_aux_transfer_result(ssize_t result,
+ enum aux_return_code_type operation_result);
+void dm_dp_aux_fill_payload_flags(u8 request, struct aux_payload *payload);
+ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg);
+u8 dm_mst_msg_ready_mask(enum mst_msg_ready_type msg_rdy_type);
+void dm_mst_select_esi_dpcd(u8 dpcd_rev, int *dpcd_addr, u8 *dpcd_bytes_to_read);
+struct drm_encoder *dm_mst_atomic_best_encoder(struct drm_connector *connector,
+ struct drm_atomic_commit *state);
+int dm_dp_mst_atomic_check(struct drm_connector *connector,
+ struct drm_atomic_commit *state);
+int dm_dp_mst_detect(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx, bool force);
+#endif
+
#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
index c7f8e08feaf4..1b564cfe2120 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
@@ -37,6 +37,7 @@
#include "amdgpu_display.h"
#include "amdgpu_dm_trace.h"
#include "amdgpu_dm_plane.h"
+#include "amdgpu_dm_kunit_helpers.h"
#include "amdgpu_dm_colorop.h"
#include "gc/gc_11_0_0_offset.h"
#include "gc/gc_11_0_0_sh_mask.h"
@@ -97,6 +98,7 @@ const struct drm_format_info *amdgpu_dm_plane_get_format_info(u32 pixel_format,
{
return amdgpu_lookup_format_info(pixel_format, modifier);
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_get_format_info);
void amdgpu_dm_plane_fill_blending_from_plane_state(const struct drm_plane_state *plane_state,
bool *per_pixel_alpha, bool *pre_multiplied_alpha,
@@ -135,12 +137,24 @@ void amdgpu_dm_plane_fill_blending_from_plane_state(const struct drm_plane_state
}
if (plane_state->alpha < 0xffff) {
+ struct amdgpu_device *adev = drm_to_adev(plane_state->plane->dev);
*global_alpha = true;
- *global_alpha_value = plane_state->alpha >> 8;
+ /*
+ * DCN 4.2 uses a 12-bit MPCC_GLOBAL_ALPHA field, while
+ * other ASICs use an 8-bit field. The DRM plane alpha is
+ * 16-bit, so scale it down to the width the hardware expects.
+ */
+ if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0))
+ *global_alpha_value = plane_state->alpha >> 4;
+ else
+ *global_alpha_value = plane_state->alpha >> 8;
+
}
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_blending_from_plane_state);
-static void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size, uint64_t *cap, uint64_t mod)
+STATIC_IFN_KUNIT void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size,
+ uint64_t *cap, uint64_t mod)
{
if (!*mods)
return;
@@ -164,27 +178,29 @@ static void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size, uint64
(*mods)[*size] = mod;
*size += 1;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_add_modifier);
-static bool amdgpu_dm_plane_modifier_has_dcc(uint64_t modifier)
+STATIC_IFN_KUNIT bool amdgpu_dm_plane_modifier_has_dcc(uint64_t modifier)
{
return IS_AMD_FMT_MOD(modifier) && AMD_FMT_MOD_GET(DCC, modifier);
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_modifier_has_dcc);
-static unsigned int amdgpu_dm_plane_modifier_gfx9_swizzle_mode(uint64_t modifier)
+STATIC_IFN_KUNIT unsigned int amdgpu_dm_plane_modifier_gfx9_swizzle_mode(uint64_t modifier)
{
if (modifier == DRM_FORMAT_MOD_LINEAR)
return 0;
return AMD_FMT_MOD_GET(TILE, modifier);
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_modifier_gfx9_swizzle_mode);
-static void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_info *tiling_info,
- uint64_t tiling_flags)
+STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_info *tiling_info,
+ uint64_t tiling_flags)
{
/* Fill GFX8 params */
if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == DC_ARRAY_2D_TILED_THIN1) {
unsigned int bankw, bankh, mtaspect, tile_split, num_banks;
-
bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH);
bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT);
mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT);
@@ -210,9 +226,10 @@ static void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_in
tiling_info->gfx8.pipe_config =
AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG);
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags);
-static void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev,
- struct dc_tiling_info *tiling_info)
+STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev,
+ struct dc_tiling_info *tiling_info)
{
/* Fill GFX9 params */
tiling_info->gfx9.num_pipes =
@@ -231,10 +248,11 @@ static void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgp
if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(10, 3, 0))
tiling_info->gfx9.num_pkrs = adev->gfx.config.gb_addr_config_fields.num_pkrs;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_tiling_info_from_device);
-static void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev,
- struct dc_tiling_info *tiling_info,
- uint64_t modifier)
+STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev,
+ struct dc_tiling_info *tiling_info,
+ uint64_t modifier)
{
unsigned int mod_bank_xor_bits = AMD_FMT_MOD_GET(BANK_XOR_BITS, modifier);
unsigned int mod_pipe_xor_bits = AMD_FMT_MOD_GET(PIPE_XOR_BITS, modifier);
@@ -259,14 +277,15 @@ static void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amd
/* for DCC we know it isn't rb aligned, so rb_per_se doesn't matter. */
}
}
-
-static int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev,
- const enum surface_pixel_format format,
- const enum dc_rotation_angle rotation,
- const struct dc_tiling_info *tiling_info,
- const struct dc_plane_dcc_param *dcc,
- const struct dc_plane_address *address,
- const struct plane_size *plane_size)
+EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier);
+
+STATIC_IFN_KUNIT int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev,
+ const enum surface_pixel_format format,
+ const enum dc_rotation_angle rotation,
+ const struct dc_tiling_info *tiling_info,
+ const struct dc_plane_dcc_param *dcc,
+ const struct dc_plane_address *address,
+ const struct plane_size *plane_size)
{
struct dc *dc = adev->dm.dc;
struct dc_dcc_surface_param input;
@@ -307,15 +326,16 @@ static int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev,
return 0;
}
-
-static int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev,
- const struct amdgpu_framebuffer *afb,
- const enum surface_pixel_format format,
- const enum dc_rotation_angle rotation,
- const struct plane_size *plane_size,
- struct dc_tiling_info *tiling_info,
- struct dc_plane_dcc_param *dcc,
- struct dc_plane_address *address)
+EXPORT_IF_KUNIT(amdgpu_dm_plane_validate_dcc);
+
+STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev,
+ const struct amdgpu_framebuffer *afb,
+ const enum surface_pixel_format format,
+ const enum dc_rotation_angle rotation,
+ const struct plane_size *plane_size,
+ struct dc_tiling_info *tiling_info,
+ struct dc_plane_dcc_param *dcc,
+ struct dc_plane_address *address)
{
const uint64_t modifier = afb->base.modifier;
int ret = 0;
@@ -358,15 +378,16 @@ static int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdg
return ret;
}
-
-static int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amdgpu_device *adev,
- const struct amdgpu_framebuffer *afb,
- const enum surface_pixel_format format,
- const enum dc_rotation_angle rotation,
- const struct plane_size *plane_size,
- struct dc_tiling_info *tiling_info,
- struct dc_plane_dcc_param *dcc,
- struct dc_plane_address *address)
+EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers);
+
+STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amdgpu_device *adev,
+ const struct amdgpu_framebuffer *afb,
+ const enum surface_pixel_format format,
+ const enum dc_rotation_angle rotation,
+ const struct plane_size *plane_size,
+ struct dc_tiling_info *tiling_info,
+ struct dc_plane_dcc_param *dcc,
+ struct dc_plane_address *address)
{
const uint64_t modifier = afb->base.modifier;
int ret = 0;
@@ -398,6 +419,7 @@ static int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amd
return ret;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers);
static void amdgpu_dm_plane_add_gfx10_1_modifiers(const struct amdgpu_device *adev,
uint64_t **mods,
@@ -724,7 +746,7 @@ static void amdgpu_dm_plane_add_gfx12_modifiers(struct amdgpu_device *adev,
}
-static int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, unsigned int plane_type, uint64_t **mods)
+STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, unsigned int plane_type, uint64_t **mods)
{
uint64_t size = 0, capacity = 128;
*mods = NULL;
@@ -777,10 +799,11 @@ static int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, unsig
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_get_plane_modifiers);
-static int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane,
- const struct dc_plane_cap *plane_cap,
- uint32_t *formats, int max_formats)
+STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane,
+ const struct dc_plane_cap *plane_cap,
+ uint32_t *formats, int max_formats)
{
int i, num_formats = 0;
@@ -836,6 +859,7 @@ static int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane,
return num_formats;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_get_plane_formats);
int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev,
const struct amdgpu_framebuffer *afb,
@@ -922,6 +946,7 @@ int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev,
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_plane_buffer_attributes);
static int amdgpu_dm_plane_helper_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state)
@@ -1042,9 +1067,9 @@ static void amdgpu_dm_plane_helper_cleanup_fb(struct drm_plane *plane,
amdgpu_bo_unref(&rbo);
}
-static void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev,
- struct drm_framebuffer *fb,
- int *min_downscale, int *max_upscale)
+STATIC_IFN_KUNIT void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev,
+ struct drm_framebuffer *fb,
+ int *min_downscale, int *max_upscale)
{
struct amdgpu_device *adev = drm_to_adev(dev);
struct dc *dc = adev->dm.dc;
@@ -1088,6 +1113,7 @@ static void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev,
if (*min_downscale == 1)
*min_downscale = 1000;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_get_min_max_dc_plane_scaling);
int amdgpu_dm_plane_helper_check_state(struct drm_plane_state *state,
struct drm_crtc_state *new_crtc_state)
@@ -1142,6 +1168,7 @@ int amdgpu_dm_plane_helper_check_state(struct drm_plane_state *state,
return drm_atomic_helper_check_plane_state(
state, new_crtc_state, min_scale, max_scale, true, true);
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_helper_check_state);
int amdgpu_dm_plane_fill_dc_scaling_info(struct amdgpu_device *adev,
const struct drm_plane_state *state,
@@ -1225,6 +1252,7 @@ int amdgpu_dm_plane_fill_dc_scaling_info(struct amdgpu_device *adev,
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_dc_scaling_info);
static int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_commit *state)
@@ -1343,6 +1371,7 @@ int amdgpu_dm_plane_get_cursor_position(struct drm_plane *plane, struct drm_crtc
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_get_cursor_position);
void amdgpu_dm_plane_handle_cursor_update(struct drm_plane *plane,
struct drm_plane_state *old_plane_state)
@@ -1488,17 +1517,15 @@ static const struct drm_plane_helper_funcs dm_primary_plane_helper_funcs = {
static void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane)
{
- struct dm_plane_state *amdgpu_state = NULL;
-
- if (plane->state)
- plane->funcs->atomic_destroy_state(plane, plane->state);
+ struct dm_plane_state *amdgpu_state;
amdgpu_state = kzalloc_obj(*amdgpu_state);
- WARN_ON(amdgpu_state == NULL);
-
if (!amdgpu_state)
return;
+ if (plane->state)
+ plane->funcs->atomic_destroy_state(plane, plane->state);
+
__drm_atomic_helper_plane_reset(plane, &amdgpu_state->base);
amdgpu_state->degamma_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
amdgpu_state->hdr_mult = AMDGPU_HDR_MULT_DEFAULT;
@@ -1546,9 +1573,9 @@ static struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct
return &dm_plane_state->base;
}
-static bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane,
- uint32_t format,
- uint64_t modifier)
+STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane,
+ uint32_t format,
+ uint64_t modifier)
{
struct amdgpu_device *adev = drm_to_adev(plane->dev);
const struct drm_format_info *info = drm_format_info(format);
@@ -1607,6 +1634,7 @@ static bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane,
return true;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_format_mod_supported);
static void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
struct drm_plane_state *state)
@@ -1982,4 +2010,5 @@ bool amdgpu_dm_plane_is_video_format(uint32_t format)
return false;
}
+EXPORT_IF_KUNIT(amdgpu_dm_plane_is_video_format);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h
index ea2619b507db..911fb2d73e22 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h
@@ -28,6 +28,8 @@
#define __AMDGPU_DM_PLANE_H__
#include "dc.h"
+#include <drm/drm_plane.h>
+#include "amdgpu.h"
int amdgpu_dm_plane_get_cursor_position(struct drm_plane *plane, struct drm_crtc *crtc,
struct dc_cursor_position *position);
@@ -65,4 +67,53 @@ void amdgpu_dm_plane_fill_blending_from_plane_state(const struct drm_plane_state
bool *global_alpha, int *global_alpha_value);
bool amdgpu_dm_plane_is_video_format(uint32_t format);
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size,
+ uint64_t *cap, uint64_t mod);
+void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_info *tiling_info,
+ uint64_t tiling_flags);
+void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev,
+ struct dc_tiling_info *tiling_info);
+void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev,
+ struct dc_tiling_info *tiling_info,
+ uint64_t modifier);
+int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev,
+ const enum surface_pixel_format format,
+ const enum dc_rotation_angle rotation,
+ const struct dc_tiling_info *tiling_info,
+ const struct dc_plane_dcc_param *dcc,
+ const struct dc_plane_address *address,
+ const struct plane_size *plane_size);
+bool amdgpu_dm_plane_modifier_has_dcc(uint64_t modifier);
+unsigned int amdgpu_dm_plane_modifier_gfx9_swizzle_mode(uint64_t modifier);
+int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev,
+ unsigned int plane_type, uint64_t **mods);
+int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane,
+ const struct dc_plane_cap *plane_cap,
+ uint32_t *formats, int max_formats);
+int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev,
+ const struct amdgpu_framebuffer *afb,
+ const enum surface_pixel_format format,
+ const enum dc_rotation_angle rotation,
+ const struct plane_size *plane_size,
+ struct dc_tiling_info *tiling_info,
+ struct dc_plane_dcc_param *dcc,
+ struct dc_plane_address *address);
+int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amdgpu_device *adev,
+ const struct amdgpu_framebuffer *afb,
+ const enum surface_pixel_format format,
+ const enum dc_rotation_angle rotation,
+ const struct plane_size *plane_size,
+ struct dc_tiling_info *tiling_info,
+ struct dc_plane_dcc_param *dcc,
+ struct dc_plane_address *address);
+bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane,
+ uint32_t format,
+ uint64_t modifier);
+void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev,
+ struct drm_framebuffer *fb,
+ int *min_downscale,
+ int *max_upscale);
+#endif
#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c
index 2cdb8fea504a..0d2e5294d062 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c
@@ -33,73 +33,67 @@
#include "amdgpu_dm_irq.h"
#include "amdgpu_pm.h"
#include "dm_pp_smu.h"
+#include "amdgpu_dm_kunit_helpers.h"
+#include "amdgpu_dm_pp_smu.h"
-bool dm_pp_apply_display_requirements(
- const struct dc_context *ctx,
+STATIC_IFN_KUNIT void build_pm_display_cfg(
+ struct amd_pp_display_configuration *pm_display_cfg,
const struct dm_pp_display_configuration *pp_display_cfg)
{
- struct amdgpu_device *adev = ctx->driver_context;
int i;
- if (adev->pm.dpm_enabled) {
+ memset(pm_display_cfg, 0, sizeof(*pm_display_cfg));
- memset(&adev->pm.pm_display_cfg, 0,
- sizeof(adev->pm.pm_display_cfg));
+ pm_display_cfg->cpu_cc6_disable = pp_display_cfg->cpu_cc6_disable;
+ pm_display_cfg->cpu_pstate_disable = pp_display_cfg->cpu_pstate_disable;
+ pm_display_cfg->cpu_pstate_separation_time = pp_display_cfg->cpu_pstate_separation_time;
+ pm_display_cfg->nb_pstate_switch_disable = pp_display_cfg->nb_pstate_switch_disable;
- adev->pm.pm_display_cfg.cpu_cc6_disable =
- pp_display_cfg->cpu_cc6_disable;
+ pm_display_cfg->num_display = pp_display_cfg->display_count;
+ pm_display_cfg->num_path_including_non_display = pp_display_cfg->display_count;
- adev->pm.pm_display_cfg.cpu_pstate_disable =
- pp_display_cfg->cpu_pstate_disable;
+ pm_display_cfg->min_core_set_clock = pp_display_cfg->min_engine_clock_khz/10;
+ pm_display_cfg->min_core_set_clock_in_sr =
+ pp_display_cfg->min_engine_clock_deep_sleep_khz/10;
+ pm_display_cfg->min_mem_set_clock = pp_display_cfg->min_memory_clock_khz/10;
- adev->pm.pm_display_cfg.cpu_pstate_separation_time =
- pp_display_cfg->cpu_pstate_separation_time;
+ pm_display_cfg->min_dcef_deep_sleep_set_clk =
+ pp_display_cfg->min_engine_clock_deep_sleep_khz/10;
+ pm_display_cfg->min_dcef_set_clk = pp_display_cfg->min_dcfclock_khz/10;
- adev->pm.pm_display_cfg.nb_pstate_switch_disable =
- pp_display_cfg->nb_pstate_switch_disable;
+ pm_display_cfg->multi_monitor_in_sync = pp_display_cfg->all_displays_in_sync;
+ pm_display_cfg->min_vblank_time = pp_display_cfg->avail_mclk_switch_time_us;
- adev->pm.pm_display_cfg.num_display =
- pp_display_cfg->display_count;
- adev->pm.pm_display_cfg.num_path_including_non_display =
- pp_display_cfg->display_count;
+ pm_display_cfg->display_clk = pp_display_cfg->disp_clk_khz/10;
- adev->pm.pm_display_cfg.min_core_set_clock =
- pp_display_cfg->min_engine_clock_khz/10;
- adev->pm.pm_display_cfg.min_core_set_clock_in_sr =
- pp_display_cfg->min_engine_clock_deep_sleep_khz/10;
- adev->pm.pm_display_cfg.min_mem_set_clock =
- pp_display_cfg->min_memory_clock_khz/10;
+ pm_display_cfg->dce_tolerable_mclk_in_active_latency =
+ pp_display_cfg->avail_mclk_switch_time_in_disp_active_us;
- adev->pm.pm_display_cfg.min_dcef_deep_sleep_set_clk =
- pp_display_cfg->min_engine_clock_deep_sleep_khz/10;
- adev->pm.pm_display_cfg.min_dcef_set_clk =
- pp_display_cfg->min_dcfclock_khz/10;
+ pm_display_cfg->crtc_index = pp_display_cfg->crtc_index;
+ pm_display_cfg->line_time_in_us = pp_display_cfg->line_time_in_us;
- adev->pm.pm_display_cfg.multi_monitor_in_sync =
- pp_display_cfg->all_displays_in_sync;
- adev->pm.pm_display_cfg.min_vblank_time =
- pp_display_cfg->avail_mclk_switch_time_us;
+ pm_display_cfg->vrefresh = pp_display_cfg->disp_configs[0].v_refresh;
+ pm_display_cfg->crossfire_display_index = -1;
+ pm_display_cfg->min_bus_bandwidth = 0;
- adev->pm.pm_display_cfg.display_clk =
- pp_display_cfg->disp_clk_khz/10;
-
- adev->pm.pm_display_cfg.dce_tolerable_mclk_in_active_latency =
- pp_display_cfg->avail_mclk_switch_time_in_disp_active_us;
+ for (i = 0; i < pp_display_cfg->display_count; i++) {
+ const struct dm_pp_single_disp_config *dc_cfg =
+ &pp_display_cfg->disp_configs[i];
+ pm_display_cfg->displays[i].controller_id = dc_cfg->pipe_idx + 1;
+ pm_display_cfg->displays[i].pixel_clock = dc_cfg->pixel_clock;
+ }
+}
+EXPORT_IF_KUNIT(build_pm_display_cfg);
- adev->pm.pm_display_cfg.crtc_index = pp_display_cfg->crtc_index;
- adev->pm.pm_display_cfg.line_time_in_us =
- pp_display_cfg->line_time_in_us;
+bool dm_pp_apply_display_requirements(
+ const struct dc_context *ctx,
+ const struct dm_pp_display_configuration *pp_display_cfg)
+{
+ struct amdgpu_device *adev = ctx->driver_context;
- adev->pm.pm_display_cfg.vrefresh = pp_display_cfg->disp_configs[0].v_refresh;
- adev->pm.pm_display_cfg.crossfire_display_index = -1;
- adev->pm.pm_display_cfg.min_bus_bandwidth = 0;
+ if (adev->pm.dpm_enabled) {
- for (i = 0; i < pp_display_cfg->display_count; i++) {
- const struct dm_pp_single_disp_config *dc_cfg =
- &pp_display_cfg->disp_configs[i];
- adev->pm.pm_display_cfg.displays[i].controller_id = dc_cfg->pipe_idx + 1;
- adev->pm.pm_display_cfg.displays[i].pixel_clock = dc_cfg->pixel_clock;
- }
+ build_pm_display_cfg(&adev->pm.pm_display_cfg, pp_display_cfg);
amdgpu_dpm_display_configuration_change(adev, &adev->pm.pm_display_cfg);
@@ -108,8 +102,9 @@ bool dm_pp_apply_display_requirements(
return true;
}
+EXPORT_IF_KUNIT(dm_pp_apply_display_requirements);
-static void get_default_clock_levels(
+STATIC_IFN_KUNIT void get_default_clock_levels(
enum dm_pp_clock_type clk_type,
struct dm_pp_clock_levels *clks)
{
@@ -140,8 +135,9 @@ static void get_default_clock_levels(
break;
}
}
+EXPORT_IF_KUNIT(get_default_clock_levels);
-static enum amd_pp_clock_type dc_to_pp_clock_type(
+STATIC_IFN_KUNIT enum amd_pp_clock_type dc_to_pp_clock_type(
enum dm_pp_clock_type dm_pp_clk_type)
{
enum amd_pp_clock_type amd_pp_clk_type = 0;
@@ -182,8 +178,9 @@ static enum amd_pp_clock_type dc_to_pp_clock_type(
return amd_pp_clk_type;
}
+EXPORT_IF_KUNIT(dc_to_pp_clock_type);
-static void pp_to_dc_clock_levels(
+STATIC_IFN_KUNIT void pp_to_dc_clock_levels(
const struct amd_pp_clocks *pp_clks,
struct dm_pp_clock_levels *dc_clks,
enum dm_pp_clock_type dc_clk_type)
@@ -208,8 +205,9 @@ static void pp_to_dc_clock_levels(
dc_clks->clocks_in_khz[i] = pp_clks->clock[i];
}
}
+EXPORT_IF_KUNIT(pp_to_dc_clock_levels);
-static void pp_to_dc_clock_levels_with_latency(
+STATIC_IFN_KUNIT void pp_to_dc_clock_levels_with_latency(
const struct pp_clock_levels_with_latency *pp_clks,
struct dm_pp_clock_levels_with_latency *clk_level_info,
enum dm_pp_clock_type dc_clk_type)
@@ -235,8 +233,9 @@ static void pp_to_dc_clock_levels_with_latency(
clk_level_info->data[i].latency_in_us = pp_clks->data[i].latency_in_us;
}
}
+EXPORT_IF_KUNIT(pp_to_dc_clock_levels_with_latency);
-static void pp_to_dc_clock_levels_with_voltage(
+STATIC_IFN_KUNIT void pp_to_dc_clock_levels_with_voltage(
const struct pp_clock_levels_with_voltage *pp_clks,
struct dm_pp_clock_levels_with_voltage *clk_level_info,
enum dm_pp_clock_type dc_clk_type)
@@ -263,6 +262,41 @@ static void pp_to_dc_clock_levels_with_voltage(
clk_level_info->data[i].voltage_in_mv = pp_clks->data[i].voltage_in_mv;
}
}
+EXPORT_IF_KUNIT(pp_to_dc_clock_levels_with_voltage);
+
+STATIC_IFN_KUNIT void cap_clock_levels_to_validation(
+ struct dm_pp_clock_levels *dc_clks,
+ enum dm_pp_clock_type clk_type,
+ const struct amd_pp_simple_clock_info *validation_clks)
+{
+ uint32_t i;
+
+ /* Determine the highest non-boosted level from the Validation Clocks */
+ if (clk_type == DM_PP_CLOCK_TYPE_ENGINE_CLK) {
+ for (i = 0; i < dc_clks->num_levels; i++) {
+ if (dc_clks->clocks_in_khz[i] > validation_clks->engine_max_clock) {
+ /* This clock is higher the validation clock.
+ * Than means the previous one is the highest
+ * non-boosted one.
+ */
+ DRM_INFO("DM_PPLIB: reducing engine clock level from %d to %d\n",
+ dc_clks->num_levels, i);
+ dc_clks->num_levels = i > 0 ? i : 1;
+ break;
+ }
+ }
+ } else if (clk_type == DM_PP_CLOCK_TYPE_MEMORY_CLK) {
+ for (i = 0; i < dc_clks->num_levels; i++) {
+ if (dc_clks->clocks_in_khz[i] > validation_clks->memory_max_clock) {
+ DRM_INFO("DM_PPLIB: reducing memory clock level from %d to %d\n",
+ dc_clks->num_levels, i);
+ dc_clks->num_levels = i > 0 ? i : 1;
+ break;
+ }
+ }
+ }
+}
+EXPORT_IF_KUNIT(cap_clock_levels_to_validation);
bool dm_pp_get_clock_levels_by_type(
const struct dc_context *ctx,
@@ -272,7 +306,6 @@ bool dm_pp_get_clock_levels_by_type(
struct amdgpu_device *adev = ctx->driver_context;
struct amd_pp_clocks pp_clks = { 0 };
struct amd_pp_simple_clock_info validation_clks = { 0 };
- uint32_t i;
if (amdgpu_dpm_get_clock_by_type(adev,
dc_to_pp_clock_type(clk_type), &pp_clks)) {
@@ -300,33 +333,11 @@ bool dm_pp_get_clock_levels_by_type(
validation_clks.engine_max_clock *= 10;
validation_clks.memory_max_clock *= 10;
- /* Determine the highest non-boosted level from the Validation Clocks */
- if (clk_type == DM_PP_CLOCK_TYPE_ENGINE_CLK) {
- for (i = 0; i < dc_clks->num_levels; i++) {
- if (dc_clks->clocks_in_khz[i] > validation_clks.engine_max_clock) {
- /* This clock is higher the validation clock.
- * Than means the previous one is the highest
- * non-boosted one.
- */
- DRM_INFO("DM_PPLIB: reducing engine clock level from %d to %d\n",
- dc_clks->num_levels, i);
- dc_clks->num_levels = i > 0 ? i : 1;
- break;
- }
- }
- } else if (clk_type == DM_PP_CLOCK_TYPE_MEMORY_CLK) {
- for (i = 0; i < dc_clks->num_levels; i++) {
- if (dc_clks->clocks_in_khz[i] > validation_clks.memory_max_clock) {
- DRM_INFO("DM_PPLIB: reducing memory clock level from %d to %d\n",
- dc_clks->num_levels, i);
- dc_clks->num_levels = i > 0 ? i : 1;
- break;
- }
- }
- }
+ cap_clock_levels_to_validation(dc_clks, clk_type, &validation_clks);
return true;
}
+EXPORT_IF_KUNIT(dm_pp_get_clock_levels_by_type);
bool dm_pp_get_clock_levels_by_type_with_latency(
const struct dc_context *ctx,
@@ -347,6 +358,7 @@ bool dm_pp_get_clock_levels_by_type_with_latency(
return true;
}
+EXPORT_IF_KUNIT(dm_pp_get_clock_levels_by_type_with_latency);
bool dm_pp_get_clock_levels_by_type_with_voltage(
const struct dc_context *ctx,
@@ -367,6 +379,7 @@ bool dm_pp_get_clock_levels_by_type_with_voltage(
return true;
}
+EXPORT_IF_KUNIT(dm_pp_get_clock_levels_by_type_with_voltage);
bool dm_pp_notify_wm_clock_changes(
const struct dc_context *ctx,
@@ -386,6 +399,7 @@ bool dm_pp_notify_wm_clock_changes(
return false;
}
+EXPORT_IF_KUNIT(dm_pp_notify_wm_clock_changes);
bool dm_pp_apply_clock_for_voltage_request(
const struct dc_context *ctx,
@@ -407,26 +421,26 @@ bool dm_pp_apply_clock_for_voltage_request(
return true;
}
+EXPORT_IF_KUNIT(dm_pp_apply_clock_for_voltage_request);
-static void pp_rv_set_wm_ranges(struct pp_smu *pp,
- struct pp_smu_wm_range_sets *ranges)
+STATIC_IFN_KUNIT void build_wm_clock_ranges_soc15(
+ const struct pp_smu_wm_range_sets *ranges,
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges)
{
- const struct dc_context *ctx = pp->dm;
- struct amdgpu_device *adev = ctx->driver_context;
- struct dm_pp_wm_sets_with_clock_ranges_soc15 wm_with_clock_ranges;
- struct dm_pp_clock_range_for_dmif_wm_set_soc15 *wm_dce_clocks = wm_with_clock_ranges.wm_dmif_clocks_ranges;
- struct dm_pp_clock_range_for_mcif_wm_set_soc15 *wm_soc_clocks = wm_with_clock_ranges.wm_mcif_clocks_ranges;
+ struct dm_pp_clock_range_for_dmif_wm_set_soc15 *wm_dce_clocks =
+ wm_with_clock_ranges->wm_dmif_clocks_ranges;
+ struct dm_pp_clock_range_for_mcif_wm_set_soc15 *wm_soc_clocks =
+ wm_with_clock_ranges->wm_mcif_clocks_ranges;
int32_t i;
- wm_with_clock_ranges.num_wm_dmif_sets = ranges->num_reader_wm_sets;
- wm_with_clock_ranges.num_wm_mcif_sets = ranges->num_writer_wm_sets;
+ wm_with_clock_ranges->num_wm_dmif_sets = ranges->num_reader_wm_sets;
+ wm_with_clock_ranges->num_wm_mcif_sets = ranges->num_writer_wm_sets;
- for (i = 0; i < wm_with_clock_ranges.num_wm_dmif_sets; i++) {
+ for (i = 0; i < wm_with_clock_ranges->num_wm_dmif_sets; i++) {
if (ranges->reader_wm_sets[i].wm_inst > 3)
wm_dce_clocks[i].wm_set_id = WM_SET_A;
else
- wm_dce_clocks[i].wm_set_id =
- ranges->reader_wm_sets[i].wm_inst;
+ wm_dce_clocks[i].wm_set_id = ranges->reader_wm_sets[i].wm_inst;
wm_dce_clocks[i].wm_max_dcfclk_clk_in_khz =
ranges->reader_wm_sets[i].max_drain_clk_mhz * 1000;
wm_dce_clocks[i].wm_min_dcfclk_clk_in_khz =
@@ -437,12 +451,11 @@ static void pp_rv_set_wm_ranges(struct pp_smu *pp,
ranges->reader_wm_sets[i].min_fill_clk_mhz * 1000;
}
- for (i = 0; i < wm_with_clock_ranges.num_wm_mcif_sets; i++) {
+ for (i = 0; i < wm_with_clock_ranges->num_wm_mcif_sets; i++) {
if (ranges->writer_wm_sets[i].wm_inst > 3)
wm_soc_clocks[i].wm_set_id = WM_SET_A;
else
- wm_soc_clocks[i].wm_set_id =
- ranges->writer_wm_sets[i].wm_inst;
+ wm_soc_clocks[i].wm_set_id = ranges->writer_wm_sets[i].wm_inst;
wm_soc_clocks[i].wm_max_socclk_clk_in_khz =
ranges->writer_wm_sets[i].max_fill_clk_mhz * 1000;
wm_soc_clocks[i].wm_min_socclk_clk_in_khz =
@@ -452,52 +465,69 @@ static void pp_rv_set_wm_ranges(struct pp_smu *pp,
wm_soc_clocks[i].wm_min_mem_clk_in_khz =
ranges->writer_wm_sets[i].min_drain_clk_mhz * 1000;
}
+}
+EXPORT_IF_KUNIT(build_wm_clock_ranges_soc15);
+
+STATIC_IFN_KUNIT void pp_rv_set_wm_ranges(struct pp_smu *pp,
+ struct pp_smu_wm_range_sets *ranges)
+{
+ const struct dc_context *ctx = pp->dm;
+ struct amdgpu_device *adev = ctx->driver_context;
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 wm_with_clock_ranges;
+
+ build_wm_clock_ranges_soc15(ranges, &wm_with_clock_ranges);
amdgpu_dpm_set_watermarks_for_clocks_ranges(adev,
&wm_with_clock_ranges);
}
+EXPORT_IF_KUNIT(pp_rv_set_wm_ranges);
-static void pp_rv_set_pme_wa_enable(struct pp_smu *pp)
+STATIC_IFN_KUNIT void pp_rv_set_pme_wa_enable(struct pp_smu *pp)
{
const struct dc_context *ctx = pp->dm;
struct amdgpu_device *adev = ctx->driver_context;
amdgpu_dpm_notify_smu_enable_pwe(adev);
}
+EXPORT_IF_KUNIT(pp_rv_set_pme_wa_enable);
-static void pp_rv_set_active_display_count(struct pp_smu *pp, int count)
+STATIC_IFN_KUNIT void pp_rv_set_active_display_count(struct pp_smu *pp, int count)
{
const struct dc_context *ctx = pp->dm;
struct amdgpu_device *adev = ctx->driver_context;
amdgpu_dpm_set_active_display_count(adev, count);
}
+EXPORT_IF_KUNIT(pp_rv_set_active_display_count);
-static void pp_rv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int clock)
+STATIC_IFN_KUNIT void pp_rv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int clock)
{
const struct dc_context *ctx = pp->dm;
struct amdgpu_device *adev = ctx->driver_context;
amdgpu_dpm_set_min_deep_sleep_dcefclk(adev, clock);
}
+EXPORT_IF_KUNIT(pp_rv_set_min_deep_sleep_dcfclk);
-static void pp_rv_set_hard_min_dcefclk_by_freq(struct pp_smu *pp, int clock)
+STATIC_IFN_KUNIT void pp_rv_set_hard_min_dcefclk_by_freq(struct pp_smu *pp, int clock)
{
const struct dc_context *ctx = pp->dm;
struct amdgpu_device *adev = ctx->driver_context;
amdgpu_dpm_set_hard_min_dcefclk_by_freq(adev, clock);
}
+EXPORT_IF_KUNIT(pp_rv_set_hard_min_dcefclk_by_freq);
-static void pp_rv_set_hard_min_fclk_by_freq(struct pp_smu *pp, int mhz)
+STATIC_IFN_KUNIT void pp_rv_set_hard_min_fclk_by_freq(struct pp_smu *pp, int mhz)
{
const struct dc_context *ctx = pp->dm;
struct amdgpu_device *adev = ctx->driver_context;
amdgpu_dpm_set_hard_min_fclk_by_freq(adev, mhz);
}
+EXPORT_IF_KUNIT(pp_rv_set_hard_min_fclk_by_freq);
-static enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp,
+STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp,
struct pp_smu_wm_range_sets *ranges)
{
const struct dc_context *ctx = pp->dm;
@@ -507,8 +537,9 @@ static enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp,
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_set_wm_ranges);
-static enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count)
+STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count)
{
const struct dc_context *ctx = pp->dm;
struct amdgpu_device *adev = ctx->driver_context;
@@ -523,8 +554,9 @@ static enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count)
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_set_display_count);
-static enum pp_smu_status
+STATIC_IFN_KUNIT enum pp_smu_status
pp_nv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int mhz)
{
const struct dc_context *ctx = pp->dm;
@@ -540,8 +572,9 @@ pp_nv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int mhz)
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_set_min_deep_sleep_dcfclk);
-static enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq(
+STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq(
struct pp_smu *pp, int mhz)
{
const struct dc_context *ctx = pp->dm;
@@ -563,8 +596,9 @@ static enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq(
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_set_hard_min_dcefclk_by_freq);
-static enum pp_smu_status
+STATIC_IFN_KUNIT enum pp_smu_status
pp_nv_set_hard_min_uclk_by_freq(struct pp_smu *pp, int mhz)
{
const struct dc_context *ctx = pp->dm;
@@ -586,8 +620,9 @@ pp_nv_set_hard_min_uclk_by_freq(struct pp_smu *pp, int mhz)
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_set_hard_min_uclk_by_freq);
-static enum pp_smu_status pp_nv_set_pstate_handshake_support(
+STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_pstate_handshake_support(
struct pp_smu *pp, bool pstate_handshake_supported)
{
const struct dc_context *ctx = pp->dm;
@@ -599,28 +634,40 @@ static enum pp_smu_status pp_nv_set_pstate_handshake_support(
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_set_pstate_handshake_support);
-static enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp,
- enum pp_smu_nv_clock_id clock_id, int mhz)
+STATIC_IFN_KUNIT bool pp_smu_nv_clock_id_to_pp(enum pp_smu_nv_clock_id clock_id,
+ enum amd_pp_clock_type *clock_type)
{
- const struct dc_context *ctx = pp->dm;
- struct amdgpu_device *adev = ctx->driver_context;
- struct pp_display_clock_request clock_req;
- int ret = 0;
-
switch (clock_id) {
case PP_SMU_NV_DISPCLK:
- clock_req.clock_type = amd_pp_disp_clock;
+ *clock_type = amd_pp_disp_clock;
break;
case PP_SMU_NV_PHYCLK:
- clock_req.clock_type = amd_pp_phy_clock;
+ *clock_type = amd_pp_phy_clock;
break;
case PP_SMU_NV_PIXELCLK:
- clock_req.clock_type = amd_pp_pixel_clock;
+ *clock_type = amd_pp_pixel_clock;
break;
default:
- break;
+ return false;
}
+
+ return true;
+}
+EXPORT_IF_KUNIT(pp_smu_nv_clock_id_to_pp);
+
+STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp,
+ enum pp_smu_nv_clock_id clock_id, int mhz)
+{
+ const struct dc_context *ctx = pp->dm;
+ struct amdgpu_device *adev = ctx->driver_context;
+ struct pp_display_clock_request clock_req;
+ int ret = 0;
+
+ if (!pp_smu_nv_clock_id_to_pp(clock_id, &clock_req.clock_type))
+ return PP_SMU_RESULT_FAIL;
+
clock_req.clock_freq_in_khz = mhz * 1000;
/* 0: successful or smu.ppt_funcs->display_clock_voltage_request = NULL
@@ -634,8 +681,9 @@ static enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp,
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_set_voltage_by_freq);
-static enum pp_smu_status pp_nv_get_maximum_sustainable_clocks(
+STATIC_IFN_KUNIT enum pp_smu_status pp_nv_get_maximum_sustainable_clocks(
struct pp_smu *pp, struct pp_smu_nv_clock_table *max_clocks)
{
const struct dc_context *ctx = pp->dm;
@@ -651,8 +699,9 @@ static enum pp_smu_status pp_nv_get_maximum_sustainable_clocks(
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_get_maximum_sustainable_clocks);
-static enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp,
+STATIC_IFN_KUNIT enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp,
unsigned int *clock_values_in_khz, unsigned int *num_states)
{
const struct dc_context *ctx = pp->dm;
@@ -669,8 +718,9 @@ static enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp,
return PP_SMU_RESULT_OK;
}
+EXPORT_IF_KUNIT(pp_nv_get_uclk_dpm_states);
-static enum pp_smu_status pp_rn_get_dpm_clock_table(
+STATIC_IFN_KUNIT enum pp_smu_status pp_rn_get_dpm_clock_table(
struct pp_smu *pp, struct dpm_clocks *clock_table)
{
const struct dc_context *ctx = pp->dm;
@@ -685,17 +735,7 @@ static enum pp_smu_status pp_rn_get_dpm_clock_table(
return PP_SMU_RESULT_OK;
}
-
-static enum pp_smu_status pp_rn_set_wm_ranges(struct pp_smu *pp,
- struct pp_smu_wm_range_sets *ranges)
-{
- const struct dc_context *ctx = pp->dm;
- struct amdgpu_device *adev = ctx->driver_context;
-
- amdgpu_dpm_set_watermarks_for_clocks_ranges(adev, ranges);
-
- return PP_SMU_RESULT_OK;
-}
+EXPORT_IF_KUNIT(pp_rn_get_dpm_clock_table);
void dm_pp_get_funcs(
struct dc_context *ctx,
@@ -743,7 +783,7 @@ void dm_pp_get_funcs(
case DCN_VERSION_2_1:
funcs->ctx.ver = PP_SMU_VER_RN;
funcs->rn_funcs.pp_smu.dm = ctx;
- funcs->rn_funcs.set_wm_ranges = pp_rn_set_wm_ranges;
+ funcs->rn_funcs.set_wm_ranges = pp_nv_set_wm_ranges;
funcs->rn_funcs.get_dpm_clock_table = pp_rn_get_dpm_clock_table;
break;
default:
@@ -751,3 +791,4 @@ void dm_pp_get_funcs(
break;
}
}
+EXPORT_IF_KUNIT(dm_pp_get_funcs);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h
new file mode 100644
index 000000000000..f918eb71f0d1
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef __AMDGPU_DM_PP_SMU_H__
+#define __AMDGPU_DM_PP_SMU_H__
+
+#include "dm_pp_interface.h"
+
+struct amd_pp_display_configuration;
+struct pp_smu_wm_range_sets;
+struct dm_pp_wm_sets_with_clock_ranges_soc15;
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+void build_pm_display_cfg(struct amd_pp_display_configuration *pm_display_cfg,
+ const struct dm_pp_display_configuration *pp_display_cfg);
+void build_wm_clock_ranges_soc15(const struct pp_smu_wm_range_sets *ranges,
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges);
+void get_default_clock_levels(enum dm_pp_clock_type clk_type, struct dm_pp_clock_levels *clks);
+enum amd_pp_clock_type dc_to_pp_clock_type(enum dm_pp_clock_type dm_pp_clk_type);
+void pp_to_dc_clock_levels(const struct amd_pp_clocks *pp_clks,
+ struct dm_pp_clock_levels *dc_clks,
+ enum dm_pp_clock_type dc_clk_type);
+void pp_to_dc_clock_levels_with_latency(const struct pp_clock_levels_with_latency *pp_clks,
+ struct dm_pp_clock_levels_with_latency *clk_level_info,
+ enum dm_pp_clock_type dc_clk_type);
+void pp_to_dc_clock_levels_with_voltage(const struct pp_clock_levels_with_voltage *pp_clks,
+ struct dm_pp_clock_levels_with_voltage *clk_level_info,
+ enum dm_pp_clock_type dc_clk_type);
+void cap_clock_levels_to_validation(struct dm_pp_clock_levels *dc_clks,
+ enum dm_pp_clock_type clk_type,
+ const struct amd_pp_simple_clock_info *validation_clks);
+bool pp_smu_nv_clock_id_to_pp(enum pp_smu_nv_clock_id clock_id,
+ enum amd_pp_clock_type *clock_type);
+void pp_rv_set_wm_ranges(struct pp_smu *pp, struct pp_smu_wm_range_sets *ranges);
+void pp_rv_set_pme_wa_enable(struct pp_smu *pp);
+void pp_rv_set_active_display_count(struct pp_smu *pp, int count);
+void pp_rv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int clock);
+void pp_rv_set_hard_min_dcefclk_by_freq(struct pp_smu *pp, int clock);
+void pp_rv_set_hard_min_fclk_by_freq(struct pp_smu *pp, int mhz);
+enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp,
+ struct pp_smu_wm_range_sets *ranges);
+enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count);
+enum pp_smu_status pp_nv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int mhz);
+enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq(struct pp_smu *pp, int mhz);
+enum pp_smu_status pp_nv_set_hard_min_uclk_by_freq(struct pp_smu *pp, int mhz);
+enum pp_smu_status pp_nv_set_pstate_handshake_support(struct pp_smu *pp,
+ bool pstate_handshake_supported);
+enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp,
+ enum pp_smu_nv_clock_id clock_id, int mhz);
+enum pp_smu_status pp_nv_get_maximum_sustainable_clocks(struct pp_smu *pp,
+ struct pp_smu_nv_clock_table *max_clocks);
+enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp,
+ unsigned int *clock_values_in_khz,
+ unsigned int *num_states);
+enum pp_smu_status pp_rn_get_dpm_clock_table(struct pp_smu *pp,
+ struct dpm_clocks *clock_table);
+#endif
+
+#endif /* __AMDGPU_DM_PP_SMU_H__ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c
index 0dadc0bb214f..f87de3d18ac0 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c
@@ -32,8 +32,8 @@
#include "modules/power/power_helpers.h"
#include "amdgpu_dm_kunit_helpers.h"
-
-static bool link_supports_psrsu(struct dc_link *link)
+STATIC_IFN_KUNIT
+bool link_supports_psrsu(struct dc_link *link)
{
struct dc *dc = link->ctx->dc;
@@ -60,6 +60,7 @@ static bool link_supports_psrsu(struct dc_link *link)
/* Temporarily disable PSR-SU to avoid glitches */
return false;
}
+EXPORT_IF_KUNIT(link_supports_psrsu);
STATIC_IFN_KUNIT
void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps)
@@ -134,6 +135,7 @@ bool amdgpu_dm_set_psr_caps(struct dc_link *link, struct amdgpu_dm_connector *ac
amdgpu_dm_psr_fill_caps(link, &aconnector->psr_caps);
return true;
}
+EXPORT_IF_KUNIT(amdgpu_dm_set_psr_caps);
/*
* amdgpu_dm_psr_is_active_allowed() - check if psr is allowed on any stream
@@ -157,6 +159,7 @@ bool amdgpu_dm_psr_is_active_allowed(struct amdgpu_display_manager *dm)
}
return false;
}
+EXPORT_IF_KUNIT(amdgpu_dm_psr_is_active_allowed);
/*
* amdgpu_dm_psr_set_event() - set or clear PSR event for stream
@@ -190,3 +193,47 @@ bool amdgpu_dm_psr_set_event(struct amdgpu_display_manager *dm, struct dc_stream
set_event, event, wait_for_disable);
}
EXPORT_IF_KUNIT(amdgpu_dm_psr_set_event);
+
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+/**
+ * amdgpu_dm_psr_get_dc_feature_mask() - Get DC feature mask for KUnit tests.
+ *
+ * Return: Current value of amdgpu_dc_feature_mask.
+ */
+unsigned int amdgpu_dm_psr_get_dc_feature_mask(void)
+{
+ return amdgpu_dc_feature_mask;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_psr_get_dc_feature_mask);
+
+/**
+ * amdgpu_dm_psr_set_dc_feature_mask() - Set DC feature mask for KUnit tests.
+ * @feature_mask: DC feature mask to set while testing amdgpu_dm_psr_fill_caps().
+ */
+void amdgpu_dm_psr_set_dc_feature_mask(unsigned int feature_mask)
+{
+ amdgpu_dc_feature_mask = feature_mask;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_psr_set_dc_feature_mask);
+
+/**
+ * amdgpu_dm_psr_get_dc_debug_mask() - Get DC debug mask for KUnit tests.
+ *
+ * Return: Current value of amdgpu_dc_debug_mask.
+ */
+unsigned int amdgpu_dm_psr_get_dc_debug_mask(void)
+{
+ return amdgpu_dc_debug_mask;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_psr_get_dc_debug_mask);
+
+/**
+ * amdgpu_dm_psr_set_dc_debug_mask() - Set DC debug mask for KUnit tests.
+ * @debug_mask: DC debug mask to set while testing link_supports_psrsu().
+ */
+void amdgpu_dm_psr_set_dc_debug_mask(unsigned int debug_mask)
+{
+ amdgpu_dc_debug_mask = debug_mask;
+}
+EXPORT_IF_KUNIT(amdgpu_dm_psr_set_dc_debug_mask);
+#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h
index 40a09b5dc606..e442e7ed82ec 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h
@@ -43,7 +43,12 @@ bool amdgpu_dm_psr_set_event(struct amdgpu_display_manager *dm,
bool wait_for_disable);
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+bool link_supports_psrsu(struct dc_link *link);
void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps);
+unsigned int amdgpu_dm_psr_get_dc_feature_mask(void);
+void amdgpu_dm_psr_set_dc_feature_mask(unsigned int feature_mask);
+unsigned int amdgpu_dm_psr_get_dc_debug_mask(void);
+void amdgpu_dm_psr_set_dc_debug_mask(unsigned int debug_mask);
#endif
#endif /* AMDGPU_DM_AMDGPU_DM_PSR_H_ */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c
index 1da07ebf9217..cf28d50c3b5e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c
@@ -28,6 +28,7 @@
#include "amdgpu.h"
#include "amdgpu_dm.h"
+#include "amdgpu_dm_kunit_helpers.h"
struct amdgpu_dm_quirks {
bool aux_hpd_discon;
@@ -176,3 +177,4 @@ void retrieve_dmi_info(struct amdgpu_display_manager *dm)
drm_info(dev, "support_edp0_on_dp1 attached\n");
}
}
+EXPORT_IF_KUNIT(retrieve_dmi_info);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c
index 22aa4305d2af..42e17119461d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c
@@ -121,14 +121,14 @@ bool amdgpu_dm_set_replay_caps(struct dc_link *link, struct amdgpu_dm_connector
debug_flags = (union replay_debug_flags *)&pr_config.debug_flags;
debug_flags->u32All = 0;
- debug_flags->bitfields.visual_confirm =
- link->ctx->dc->debug.visual_confirm == VISUAL_CONFIRM_REPLAY;
+ debug_flags->bitfields.visual_confirm = dc->debug.visual_confirm == VISUAL_CONFIRM_REPLAY;
debug_flags->bitfields.skip_crtc_disabled = dc->debug.replay_skip_crtc_disabled;
init_replay_config(link, &pr_config);
return true;
}
+EXPORT_IF_KUNIT(amdgpu_dm_set_replay_caps);
/*
* amdgpu_dm_link_setup_replay() - config replay settings
@@ -144,7 +144,6 @@ bool amdgpu_dm_link_setup_replay(struct dc_stream_state *stream,
{
struct dc_link *link;
unsigned int static_coasting_vtotal;
- unsigned int nom_coasting_vtotal;
if (!stream || !stream->link || !vrr_params)
return false;
@@ -159,16 +158,16 @@ bool amdgpu_dm_link_setup_replay(struct dc_stream_state *stream,
calculate_replay_link_off_frame_count(link, stream->timing.v_total,
stream->timing.h_total);
- nom_coasting_vtotal = stream->timing.v_total;
static_coasting_vtotal = mod_freesync_calc_v_total_from_refresh(stream,
vrr_params->min_refresh_in_uhz);
set_replay_coasting_vtotal(link, PR_COASTING_TYPE_NOM,
- nom_coasting_vtotal);
+ stream->timing.v_total);
set_replay_coasting_vtotal(link, PR_COASTING_TYPE_STATIC,
static_coasting_vtotal);
return true;
}
+EXPORT_IF_KUNIT(amdgpu_dm_link_setup_replay);
/*
* amdgpu_dm_replay_set_event() - set or clear replay event for a stream
@@ -208,3 +207,4 @@ bool amdgpu_dm_replay_set_event(struct amdgpu_display_manager *dm,
return mod_power_set_replay_event(dm->power_module, stream,
set_event, event, wait_for_disable);
}
+EXPORT_IF_KUNIT(amdgpu_dm_replay_set_event);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c
index 84dcb573d98f..6c0464754ed8 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c
@@ -32,9 +32,11 @@
#include "dm_services.h"
#include "amdgpu.h"
#include "amdgpu_dm.h"
+#include "amdgpu_dm_backlight.h"
#include "amdgpu_dm_irq.h"
#include "amdgpu_pm.h"
#include "amdgpu_dm_trace.h"
+#include "amdgpu_dm_kunit_helpers.h"
unsigned long long
dm_get_elapse_time_in_ns(struct dc_context *ctx,
@@ -43,6 +45,7 @@
{
return current_time_stamp - last_time_stamp;
}
+EXPORT_IF_KUNIT(dm_get_elapse_time_in_ns);
void dm_perf_trace_timestamp(const char *func_name, unsigned int line, struct dc_context *ctx)
{
@@ -52,14 +55,17 @@ void dm_perf_trace_timestamp(const char *func_name, unsigned int line, struct dc
&ctx->perf_trace->last_entry_write,
func_name, line);
}
+EXPORT_IF_KUNIT(dm_perf_trace_timestamp);
void dm_trace_smu_enter(uint32_t msg_id, uint32_t param_in, unsigned int delay, struct dc_context *ctx)
{
}
+EXPORT_IF_KUNIT(dm_trace_smu_enter);
void dm_trace_smu_exit(bool success, uint32_t response, struct dc_context *ctx)
{
}
+EXPORT_IF_KUNIT(dm_trace_smu_exit);
/**** power component interfaces ****/
@@ -89,3 +95,4 @@ bool dm_query_extended_brightness_caps(struct dc_context *ctx,
sizeof(struct dm_bl_data_point) * pCaps->num_data_points);
return true;
}
+EXPORT_IF_KUNIT(dm_query_extended_brightness_caps);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c
index 110f0173eee6..0bf82e46f773 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c
@@ -29,6 +29,7 @@
#include "amdgpu.h"
#include "amdgpu_dm.h"
#include "amdgpu_dm_wb.h"
+#include "amdgpu_dm_kunit_helpers.h"
#include "amdgpu_display.h"
#include "dc.h"
@@ -40,7 +41,7 @@ static const u32 amdgpu_dm_wb_formats[] = {
DRM_FORMAT_XRGB2101010,
};
-static int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder,
+STATIC_IFN_KUNIT int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
@@ -59,9 +60,11 @@ static int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder,
return -EINVAL;
}
- for (i = 0; i < sizeof(amdgpu_dm_wb_formats) / sizeof(u32); i++) {
- if (fb->format->format == amdgpu_dm_wb_formats[i])
+ for (i = 0; i < ARRAY_SIZE(amdgpu_dm_wb_formats); i++) {
+ if (fb->format->format == amdgpu_dm_wb_formats[i]) {
found = true;
+ break;
+ }
}
if (!found) {
@@ -72,13 +75,15 @@ static int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder,
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_wb_encoder_atomic_check);
-static int amdgpu_dm_wb_connector_get_modes(struct drm_connector *connector)
+STATIC_IFN_KUNIT int amdgpu_dm_wb_connector_get_modes(struct drm_connector *connector)
{
/* Maximum resolution supported by DWB */
return drm_add_modes_noedid(connector, 3840, 2160);
}
+EXPORT_IF_KUNIT(amdgpu_dm_wb_connector_get_modes);
static int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector,
struct drm_writeback_job *job)
@@ -187,7 +192,7 @@ int amdgpu_dm_wb_connector_init(struct amdgpu_display_manager *dm,
{
struct dc *dc = dm->dc;
struct dc_link *link = dc_get_link_at_index(dc, link_index);
- int res = 0;
+ int res;
wbcon->link = link;
@@ -211,3 +216,4 @@ int amdgpu_dm_wb_connector_init(struct amdgpu_display_manager *dm,
return 0;
}
+EXPORT_IF_KUNIT(amdgpu_dm_wb_connector_init);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h
index 13d31c857dee..7e9fd7a036fa 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h
@@ -29,8 +29,21 @@
#include <drm/drm_writeback.h>
+struct amdgpu_display_manager;
+struct amdgpu_dm_wb_connector;
+
int amdgpu_dm_wb_connector_init(struct amdgpu_display_manager *dm,
struct amdgpu_dm_wb_connector *dm_wbcon,
uint32_t link_index);
+#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+
+int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state);
+int amdgpu_dm_wb_connector_get_modes(struct drm_connector *connector);
+#endif
+
#endif
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig
index bd1bf8d959f9..c7c8527dbb10 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig
@@ -15,6 +15,12 @@ CONFIG_I2C=y
CONFIG_POWER_SUPPLY=y
CONFIG_CRC16=y
+# Limit stack size to 1280
+CONFIG_FRAME_WARN=1280
+
+# Treat warnings as errors
+CONFIG_WERROR=y
+
# GCOV Coverage - see tools/testing/kunit/configs/coverage_uml.config
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile
index 768f9bbc50e1..1592e8dae1a9 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile
@@ -8,11 +8,29 @@ ccflags-y += -I$(src)/../../include
ccflags-y += -I$(src)/../../modules/inc
ccflags-y += -I$(src)/../../dc
ccflags-y += -I$(src)/../../../amdgpu
+ccflags-y += -I$(src)/../../../amdkfd
+ccflags-y += -I$(src)/../../../include
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_kunit_helpers.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crc_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_hdcp_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_audio_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_color_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_colorop_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_connector_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_backlight_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_dmub_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_psr_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_replay_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_ism_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_irq_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_wb_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_plane_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_mst_types_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_pp_smu_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crtc_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_services_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_helpers_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_quirks_test.o
+obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_plane_test.o
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c
new file mode 100644
index 000000000000..79ff5d9b3fa5
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c
@@ -0,0 +1,490 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_audio.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include <drm/drm_audio_component.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_audio.h"
+
+/* Tests for amdgpu_dm_audio_init() */
+
+/**
+ * dm_test_audio_init_disabled - Test audio init exits when audio is disabled
+ * @test: The KUnit test context
+ */
+static void dm_test_audio_init_disabled(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ int saved_audio = amdgpu_dm_audio_get_param();
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ amdgpu_dm_audio_set_param(0);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_audio_init(adev), 0);
+ KUNIT_EXPECT_FALSE(test, adev->mode_info.audio.enabled);
+ KUNIT_EXPECT_FALSE(test, adev->dm.audio_registered);
+
+ amdgpu_dm_audio_set_param(saved_audio);
+}
+
+/* Tests for amdgpu_dm_audio_fini() */
+
+/**
+ * dm_test_audio_fini_without_enabled_audio - Test fini exits when audio is not enabled
+ * @test: The KUnit test context
+ */
+static void dm_test_audio_fini_without_enabled_audio(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ int saved_audio = amdgpu_dm_audio_get_param();
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ amdgpu_dm_audio_set_param(1);
+ adev->mode_info.audio.enabled = false;
+ adev->dm.audio_registered = true;
+
+ amdgpu_dm_audio_fini(adev);
+
+ KUNIT_EXPECT_FALSE(test, adev->mode_info.audio.enabled);
+ KUNIT_EXPECT_TRUE(test, adev->dm.audio_registered);
+
+ amdgpu_dm_audio_set_param(saved_audio);
+}
+
+/* Tests for amdgpu_dm_fill_audio_info() */
+
+/**
+ * dm_test_fill_audio_info_ids_name_flags - Test Fill audio info ids name flags
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_ids_name_flags(struct kunit *test)
+{
+ struct audio_info *audio_info;
+ struct drm_connector *connector;
+ struct dc_sink *dc_sink;
+ const char *name = "DM-AUDIO-PANEL";
+
+ audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+ dc_sink->edid_caps.manufacturer_id = 0x1234;
+ dc_sink->edid_caps.product_id = 0xABCD;
+ dc_sink->edid_caps.speaker_flags = 0x5;
+ strscpy(dc_sink->edid_caps.display_name, name,
+ AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
+
+ connector->display_info.cea_rev = 1;
+
+ amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+ KUNIT_EXPECT_EQ(test, audio_info->manufacture_id, 0x1234U);
+ KUNIT_EXPECT_EQ(test, audio_info->product_id, 0xABCDU);
+ KUNIT_EXPECT_EQ(test, audio_info->flags.all, 0x5U);
+ KUNIT_EXPECT_STREQ(test, audio_info->display_name, name);
+}
+
+/**
+ * dm_test_fill_audio_info_cea_lt_3_skips_modes - Test Fill audio info cea lt 3 skips modes
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_cea_lt_3_skips_modes(struct kunit *test)
+{
+ struct audio_info *audio_info;
+ struct drm_connector *connector;
+ struct dc_sink *dc_sink;
+
+ audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+ connector->display_info.cea_rev = 2;
+ dc_sink->edid_caps.audio_mode_count = 2;
+ dc_sink->edid_caps.audio_modes[0].format_code = 1;
+ dc_sink->edid_caps.audio_modes[0].channel_count = 2;
+ dc_sink->edid_caps.audio_modes[0].sample_rate = 0x07;
+ dc_sink->edid_caps.audio_modes[0].sample_size = 16;
+
+ amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+ KUNIT_EXPECT_EQ(test, audio_info->mode_count, 0U);
+}
+
+/**
+ * dm_test_fill_audio_info_cea_ge_3_copies_modes - Test Fill audio info cea ge 3 copies modes
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_cea_ge_3_copies_modes(struct kunit *test)
+{
+ struct audio_info *audio_info;
+ struct drm_connector *connector;
+ struct dc_sink *dc_sink;
+
+ audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+ connector->display_info.cea_rev = 3;
+ dc_sink->edid_caps.audio_mode_count = 2;
+
+ dc_sink->edid_caps.audio_modes[0].format_code = 1;
+ dc_sink->edid_caps.audio_modes[0].channel_count = 2;
+ dc_sink->edid_caps.audio_modes[0].sample_rate = 0x07;
+ dc_sink->edid_caps.audio_modes[0].sample_size = 16;
+
+ dc_sink->edid_caps.audio_modes[1].format_code = 11;
+ dc_sink->edid_caps.audio_modes[1].channel_count = 6;
+ dc_sink->edid_caps.audio_modes[1].sample_rate = 0x1F;
+ dc_sink->edid_caps.audio_modes[1].sample_size = 24;
+
+ amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+ KUNIT_EXPECT_EQ(test, audio_info->mode_count, 2U);
+
+ KUNIT_EXPECT_EQ(test, (int)audio_info->modes[0].format_code, 1);
+ KUNIT_EXPECT_EQ(test, audio_info->modes[0].channel_count, 2);
+ KUNIT_EXPECT_EQ(test, audio_info->modes[0].sample_rates.all, 0x07U);
+ KUNIT_EXPECT_EQ(test, audio_info->modes[0].sample_size, 16);
+
+ KUNIT_EXPECT_EQ(test, (int)audio_info->modes[1].format_code, 11);
+ KUNIT_EXPECT_EQ(test, audio_info->modes[1].channel_count, 6);
+ KUNIT_EXPECT_EQ(test, audio_info->modes[1].sample_rates.all, 0x1FU);
+ KUNIT_EXPECT_EQ(test, audio_info->modes[1].sample_size, 24);
+}
+
+/**
+ * dm_test_fill_audio_info_latency_present - Test Fill audio info latency present
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_latency_present(struct kunit *test)
+{
+ struct audio_info *audio_info;
+ struct drm_connector *connector;
+ struct dc_sink *dc_sink;
+
+ audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+ connector->display_info.cea_rev = 3;
+ connector->latency_present[0] = true;
+ connector->video_latency[0] = 11;
+ connector->audio_latency[0] = 22;
+
+ amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+ KUNIT_EXPECT_EQ(test, audio_info->video_latency, 11U);
+ KUNIT_EXPECT_EQ(test, audio_info->audio_latency, 22U);
+}
+
+/**
+ * dm_test_fill_audio_info_latency_absent_keeps_zero - Test Fill audio info latency absent keeps zero
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_audio_info_latency_absent_keeps_zero(struct kunit *test)
+{
+ struct audio_info *audio_info;
+ struct drm_connector *connector;
+ struct dc_sink *dc_sink;
+
+ audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+ connector->display_info.cea_rev = 3;
+ connector->latency_present[0] = false;
+ connector->video_latency[0] = 99;
+ connector->audio_latency[0] = 88;
+
+ amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+ KUNIT_EXPECT_EQ(test, audio_info->video_latency, 0U);
+ KUNIT_EXPECT_EQ(test, audio_info->audio_latency, 0U);
+}
+
+/**
+ * dm_test_fill_audio_info_cea_ge_3_zero_modes - Test cea >= 3 with zero modes
+ * @test: The KUnit test context
+ *
+ * When cea_rev >= 3 but the sink reports no audio modes, mode_count must be
+ * copied as 0 and no mode entries should be populated.
+ */
+static void dm_test_fill_audio_info_cea_ge_3_zero_modes(struct kunit *test)
+{
+ struct audio_info *audio_info;
+ struct drm_connector *connector;
+ struct dc_sink *dc_sink;
+
+ audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL);
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink);
+
+ connector->display_info.cea_rev = 3;
+ dc_sink->edid_caps.audio_mode_count = 0;
+
+ amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink);
+
+ KUNIT_EXPECT_EQ(test, audio_info->mode_count, 0U);
+ KUNIT_EXPECT_EQ(test, (int)audio_info->modes[0].format_code, 0);
+}
+
+/* Tests for amdgpu_dm_audio_component_bind()/unbind() */
+
+/**
+ * dm_test_audio_component_bind_sets_fields - Test bind wires up audio component
+ * @test: The KUnit test context
+ *
+ * Binding must publish the DRM audio component ops, record the kernel device,
+ * and store the component pointer in the display manager.
+ */
+static void dm_test_audio_component_bind_sets_fields(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct device *kdev;
+ struct drm_audio_component *acomp;
+ int ret;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ kdev = kunit_kzalloc(test, sizeof(*kdev), GFP_KERNEL);
+ acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, kdev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+
+ dev_set_drvdata(kdev, &adev->ddev);
+
+ ret = amdgpu_dm_audio_component_bind(kdev, NULL, acomp);
+
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_NOT_NULL(test, acomp->ops);
+ KUNIT_EXPECT_PTR_EQ(test, acomp->dev, kdev);
+ KUNIT_EXPECT_PTR_EQ(test, adev->dm.audio_component, acomp);
+}
+
+/**
+ * dm_test_audio_component_unbind_clears_fields - Test unbind tears down component
+ * @test: The KUnit test context
+ *
+ * Unbinding must clear the component ops, the kernel device, and the display
+ * manager's stored component pointer.
+ */
+static void dm_test_audio_component_unbind_clears_fields(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct device *kdev;
+ struct drm_audio_component *acomp;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ kdev = kunit_kzalloc(test, sizeof(*kdev), GFP_KERNEL);
+ acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, kdev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+
+ dev_set_drvdata(kdev, &adev->ddev);
+
+ /* Pretend a prior bind already happened. */
+ acomp->dev = kdev;
+ adev->dm.audio_component = acomp;
+
+ amdgpu_dm_audio_component_unbind(kdev, NULL, acomp);
+
+ KUNIT_EXPECT_NULL(test, acomp->ops);
+ KUNIT_EXPECT_NULL(test, acomp->dev);
+ KUNIT_EXPECT_NULL(test, adev->dm.audio_component);
+}
+
+/* Tests for amdgpu_dm_audio_eld_notify() */
+
+static int dm_test_eld_notify_count;
+static int dm_test_eld_notify_port;
+static void *dm_test_eld_notify_ptr;
+
+static void dm_test_pin_eld_notify(void *audio_ptr, int port, int pipe)
+{
+ dm_test_eld_notify_count++;
+ dm_test_eld_notify_port = port;
+ dm_test_eld_notify_ptr = audio_ptr;
+}
+
+/**
+ * dm_test_eld_notify_invokes_callback - Test ELD notify forwards to hda driver
+ * @test: The KUnit test context
+ *
+ * When a component with a pin_eld_notify callback is registered, the notify
+ * helper must invoke it with the audio pointer and the requested pin.
+ */
+static void dm_test_eld_notify_invokes_callback(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_audio_component *acomp;
+ struct drm_audio_component_audio_ops *audio_ops;
+ int marker = 0;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+ audio_ops = kunit_kzalloc(test, sizeof(*audio_ops), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_ops);
+
+ audio_ops->audio_ptr = &marker;
+ audio_ops->pin_eld_notify = dm_test_pin_eld_notify;
+ acomp->audio_ops = audio_ops;
+ adev->dm.audio_component = acomp;
+
+ dm_test_eld_notify_count = 0;
+ dm_test_eld_notify_port = -100;
+ dm_test_eld_notify_ptr = NULL;
+
+ amdgpu_dm_audio_eld_notify(adev, 7);
+
+ KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1);
+ KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 7);
+ KUNIT_EXPECT_PTR_EQ(test, dm_test_eld_notify_ptr, (void *)&marker);
+}
+
+/**
+ * dm_test_eld_notify_no_component - Test ELD notify is a no-op without component
+ * @test: The KUnit test context
+ *
+ * With no registered audio component, the notify helper must return without
+ * invoking any callback.
+ */
+static void dm_test_eld_notify_no_component(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->dm.audio_component = NULL;
+
+ dm_test_eld_notify_count = 0;
+
+ amdgpu_dm_audio_eld_notify(adev, 3);
+
+ KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+/**
+ * dm_test_eld_notify_null_audio_ops - Test ELD notify is a no-op without audio_ops
+ * @test: The KUnit test context
+ *
+ * A component without audio_ops must not trigger any callback.
+ */
+static void dm_test_eld_notify_null_audio_ops(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_audio_component *acomp;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+
+ acomp->audio_ops = NULL;
+ adev->dm.audio_component = acomp;
+
+ dm_test_eld_notify_count = 0;
+
+ amdgpu_dm_audio_eld_notify(adev, 3);
+
+ KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+/**
+ * dm_test_eld_notify_null_callback - Test ELD notify is a no-op without callback
+ * @test: The KUnit test context
+ *
+ * audio_ops present but with a NULL pin_eld_notify must not crash or call
+ * anything.
+ */
+static void dm_test_eld_notify_null_callback(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_audio_component *acomp;
+ struct drm_audio_component_audio_ops *audio_ops;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL);
+ audio_ops = kunit_kzalloc(test, sizeof(*audio_ops), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_ops);
+
+ audio_ops->pin_eld_notify = NULL;
+ acomp->audio_ops = audio_ops;
+ adev->dm.audio_component = acomp;
+
+ dm_test_eld_notify_count = 0;
+
+ amdgpu_dm_audio_eld_notify(adev, 3);
+
+ KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
+}
+
+static struct kunit_case dm_audio_test_cases[] = {
+ /* amdgpu_dm_audio_init */
+ KUNIT_CASE(dm_test_audio_init_disabled),
+ /* amdgpu_dm_audio_fini */
+ KUNIT_CASE(dm_test_audio_fini_without_enabled_audio),
+ /* amdgpu_dm_fill_audio_info */
+ KUNIT_CASE(dm_test_fill_audio_info_ids_name_flags),
+ KUNIT_CASE(dm_test_fill_audio_info_cea_lt_3_skips_modes),
+ KUNIT_CASE(dm_test_fill_audio_info_cea_ge_3_copies_modes),
+ KUNIT_CASE(dm_test_fill_audio_info_cea_ge_3_zero_modes),
+ KUNIT_CASE(dm_test_fill_audio_info_latency_present),
+ KUNIT_CASE(dm_test_fill_audio_info_latency_absent_keeps_zero),
+ /* amdgpu_dm_audio_component_bind/unbind */
+ KUNIT_CASE(dm_test_audio_component_bind_sets_fields),
+ KUNIT_CASE(dm_test_audio_component_unbind_clears_fields),
+ /* amdgpu_dm_audio_eld_notify */
+ KUNIT_CASE(dm_test_eld_notify_invokes_callback),
+ KUNIT_CASE(dm_test_eld_notify_no_component),
+ KUNIT_CASE(dm_test_eld_notify_null_audio_ops),
+ KUNIT_CASE(dm_test_eld_notify_null_callback),
+ {}
+};
+
+static struct kunit_suite dm_audio_test_suite = {
+ .name = "amdgpu_dm_audio",
+ .test_cases = dm_audio_test_cases,
+};
+
+kunit_test_suite(dm_audio_test_suite);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_audio");
+MODULE_AUTHOR("AMD");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
new file mode 100644
index 000000000000..fff50c1325c6
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c
@@ -0,0 +1,1242 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_backlight.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+#include <linux/backlight.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_backlight.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+#include "amd_shared.h"
+#include "dc/inc/hw/panel_cntl.h"
+
+struct dm_backlight_connector_fixture {
+ struct amdgpu_device *adev;
+ struct amdgpu_dm_connector *aconnector;
+ struct dc_link *link;
+};
+
+static void setup_test_connector(struct kunit *test,
+ struct dm_backlight_connector_fixture *fixture,
+ int bl_idx, enum signal_type signal)
+{
+ fixture->adev = kunit_kzalloc(test, sizeof(*fixture->adev), GFP_KERNEL);
+ fixture->aconnector = kunit_kzalloc(test, sizeof(*fixture->aconnector), GFP_KERNEL);
+ fixture->link = kunit_kzalloc(test, sizeof(*fixture->link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->aconnector);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->link);
+
+ fixture->aconnector->bl_idx = bl_idx;
+ fixture->aconnector->dc_link = fixture->link;
+ fixture->aconnector->base.dev = &fixture->adev->ddev;
+ fixture->link->connector_signal = signal;
+}
+
+/* Tests for amdgpu_dm_backlight_get_device_index() */
+
+/**
+ * dm_test_backlight_device_index_matches_second - Test matching second backlight device
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_device_index_matches_second(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct backlight_device *bd0;
+ struct backlight_device *bd1;
+
+ bd0 = kunit_kzalloc(test, sizeof(*bd0), GFP_KERNEL);
+ bd1 = kunit_kzalloc(test, sizeof(*bd1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, bd0);
+ KUNIT_ASSERT_NOT_NULL(test, bd1);
+
+ dm->num_of_edps = 2;
+ dm->backlight_dev[0] = bd0;
+ dm->backlight_dev[1] = bd1;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_device_index(dm, bd1), 1);
+}
+
+/**
+ * dm_test_backlight_device_index_missing_fallback - Test missing backlight device fallback
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_device_index_missing_fallback(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct backlight_device *known_bd;
+ struct backlight_device *unknown_bd;
+
+ known_bd = kunit_kzalloc(test, sizeof(*known_bd), GFP_KERNEL);
+ unknown_bd = kunit_kzalloc(test, sizeof(*unknown_bd), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, known_bd);
+ KUNIT_ASSERT_NOT_NULL(test, unknown_bd);
+
+ dm->num_of_edps = 1;
+ dm->backlight_dev[0] = known_bd;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_device_index(dm, unknown_bd), 0);
+}
+
+/* Tests for amdgpu_dm_update_backlight_caps() */
+
+/**
+ * dm_test_backlight_caps_valid_short_circuit - Test Backlight caps valid short circuit
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_caps_valid_short_circuit(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0];
+
+ caps->caps_valid = true;
+ caps->aux_support = false;
+ caps->min_input_signal = 42;
+ caps->max_input_signal = 199;
+
+ amdgpu_dm_update_backlight_caps(dm, 0);
+
+ KUNIT_EXPECT_TRUE(test, caps->caps_valid);
+ KUNIT_EXPECT_EQ(test, caps->min_input_signal, 42);
+ KUNIT_EXPECT_EQ(test, caps->max_input_signal, 199);
+}
+
+#if !defined(CONFIG_ACPI)
+
+/**
+ * dm_test_backlight_caps_aux_support_noop - Test Backlight caps aux support noop
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_caps_aux_support_noop(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0];
+
+ caps->caps_valid = false;
+ caps->aux_support = true;
+ caps->min_input_signal = 11;
+ caps->max_input_signal = 222;
+
+ amdgpu_dm_update_backlight_caps(dm, 0);
+
+ KUNIT_EXPECT_FALSE(test, caps->caps_valid);
+ KUNIT_EXPECT_EQ(test, caps->min_input_signal, 11);
+ KUNIT_EXPECT_EQ(test, caps->max_input_signal, 222);
+}
+
+/**
+ * dm_test_backlight_caps_non_aux_sets_defaults - Test Backlight caps non aux sets defaults
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_caps_non_aux_sets_defaults(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0];
+
+ caps->caps_valid = false;
+ caps->aux_support = false;
+ caps->min_input_signal = 0;
+ caps->max_input_signal = 0;
+
+ amdgpu_dm_update_backlight_caps(dm, 0);
+
+ KUNIT_EXPECT_TRUE(test, caps->caps_valid);
+ KUNIT_EXPECT_EQ(test, caps->min_input_signal, AMDGPU_DM_DEFAULT_MIN_BACKLIGHT);
+ KUNIT_EXPECT_EQ(test, caps->max_input_signal, AMDGPU_DM_DEFAULT_MAX_BACKLIGHT);
+}
+#endif
+
+/* Tests for get_brightness_range() */
+
+/**
+ * dm_test_brightness_range_null_caps - Test Brightness range null caps
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_range_null_caps(struct kunit *test)
+{
+ unsigned int min = 99, max = 99;
+
+ KUNIT_EXPECT_EQ(test, get_brightness_range(NULL, &min, &max), 0);
+ /* min/max should remain untouched */
+ KUNIT_EXPECT_EQ(test, min, 99U);
+ KUNIT_EXPECT_EQ(test, max, 99U);
+}
+
+/**
+ * dm_test_brightness_range_pwm - Test Brightness range pwm
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_range_pwm(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+
+ KUNIT_EXPECT_EQ(test, get_brightness_range(&caps, &min, &max), 1);
+ /* 0x101 * AMDGPU_DM_DEFAULT_MIN_BACKLIGHT, 0x101 * AMDGPU_DM_DEFAULT_MAX_BACKLIGHT */
+ KUNIT_EXPECT_EQ(test, min, 0x101U * AMDGPU_DM_DEFAULT_MIN_BACKLIGHT);
+ KUNIT_EXPECT_EQ(test, max, 0x101U * AMDGPU_DM_DEFAULT_MAX_BACKLIGHT);
+}
+
+/**
+ * dm_test_brightness_range_aux - Test Brightness range aux
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_range_aux(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.aux_support = true;
+ caps.aux_min_input_signal = 1;
+ caps.aux_max_input_signal = 512;
+
+ KUNIT_EXPECT_EQ(test, get_brightness_range(&caps, &min, &max), 1);
+ /* millinits: 1000 * value */
+ KUNIT_EXPECT_EQ(test, min, 1000U);
+ KUNIT_EXPECT_EQ(test, max, 512000U);
+}
+
+/* Tests for convert_brightness_to_user() */
+
+/**
+ * dm_test_brightness_to_user_null_caps - Test Brightness to user null caps
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_to_user_null_caps(struct kunit *test)
+{
+ /*
+ * With NULL caps, get_brightness_range fails → passthrough.
+ * We simulate this by passing a zeroed caps struct where
+ * max_input_signal=0 makes max=0 and the function hits
+ * get_brightness_range returning 0 since caps is NULL.
+ */
+ KUNIT_EXPECT_EQ(test, convert_brightness_to_user(NULL, 42), 42U);
+}
+
+/**
+ * dm_test_brightness_to_user_below_min - Test Brightness to user below min
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_to_user_below_min(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+
+ /* brightness < min (0x101*AMDGPU_DM_DEFAULT_MIN_BACKLIGHT), should return 0 */
+ KUNIT_EXPECT_EQ(test, convert_brightness_to_user(&caps, 100), 0U);
+}
+
+/**
+ * dm_test_brightness_to_user_at_max - Test Brightness to user at max
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_to_user_at_max(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /* At max → should return max */
+ KUNIT_EXPECT_EQ(test, convert_brightness_to_user(&caps, max), max);
+}
+
+/**
+ * dm_test_brightness_to_user_at_min - Test Brightness to user at min
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_to_user_at_min(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /* At min → should return 0 */
+ KUNIT_EXPECT_EQ(test, convert_brightness_to_user(&caps, min), 0U);
+}
+
+/**
+ * dm_test_brightness_to_user_midpoint_pwm - Test Brightness to user midpoint pwm
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_to_user_midpoint_pwm(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max, mid_hw, result;
+ u64 expected;
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /* midpoint of hw range */
+ mid_hw = min + (max - min) / 2;
+ /* expected = DIV_ROUND_CLOSEST_ULL((u64)max * (mid_hw - min), max - min) */
+ expected = DIV_ROUND_CLOSEST_ULL((u64)max * (mid_hw - min), max - min);
+ result = convert_brightness_to_user(&caps, mid_hw);
+
+ KUNIT_EXPECT_EQ(test, result, (u32)expected);
+}
+
+/* Tests for convert_brightness_from_user() — no custom curve */
+
+/**
+ * dm_test_brightness_from_user_null_caps - Test Brightness from user null caps
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_from_user_null_caps(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(NULL, 100), 100U);
+}
+
+/**
+ * dm_test_brightness_from_user_zero - Test Brightness from user zero
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_from_user_zero(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ /* no custom curve */
+ caps.data_points = 0;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /* brightness=0 → min + 0 = min */
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 0), (u32)min);
+}
+
+/**
+ * dm_test_brightness_from_user_max - Test Brightness from user max
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_from_user_max(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 0;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /*
+ * brightness=max → min + DIV_ROUND_CLOSEST((max-min)*max, max)
+ * = min + (max - min) = max
+ */
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, max), (u32)max);
+}
+
+/**
+ * dm_test_brightness_from_user_aux - Test Brightness from user aux
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_from_user_aux(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.aux_support = true;
+ caps.aux_min_input_signal = 1;
+ caps.aux_max_input_signal = 512;
+ caps.data_points = 0;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /* brightness=0 → min */
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 0), (u32)min);
+ /* brightness=max → max */
+ KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, max), (u32)max);
+}
+
+/* Tests for convert_custom_brightness() */
+
+/**
+ * dm_test_custom_brightness_no_data_points - Test Custom brightness no data points
+ * @test: The KUnit test context
+ */
+static void dm_test_custom_brightness_no_data_points(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ uint32_t brightness = 128;
+ uint32_t saved = brightness;
+
+ caps.data_points = 0;
+
+ convert_custom_brightness(&caps, 3084, 65535, &brightness);
+
+ /* No data points → no-op */
+ KUNIT_EXPECT_EQ(test, brightness, saved);
+}
+
+/**
+ * dm_test_custom_brightness_debug_mask_disables - Test Custom brightness debug mask disables
+ * @test: The KUnit test context
+ */
+static void dm_test_custom_brightness_debug_mask_disables(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ uint32_t brightness = 128;
+ uint32_t saved = brightness;
+ uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+ caps.data_points = 3;
+ caps.luminance_data[0].input_signal = 50;
+ caps.luminance_data[0].luminance = 10;
+
+ /* Set the disable flag */
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() | DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+
+ convert_custom_brightness(&caps, 3084, 65535, &brightness);
+
+ /* Should be no-op due to debug mask */
+ KUNIT_EXPECT_EQ(test, brightness, saved);
+
+ amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_custom_brightness_exact_match - Test Custom brightness exact match
+ * @test: The KUnit test context
+ */
+static void dm_test_custom_brightness_exact_match(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ uint32_t brightness;
+ unsigned int min, max;
+ uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+
+ caps.aux_support = false;
+ caps.min_input_signal = 0;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 3;
+ caps.luminance_data[0].input_signal = 50;
+ caps.luminance_data[0].luminance = 20;
+ caps.luminance_data[1].input_signal = 128;
+ caps.luminance_data[1].luminance = 50;
+ caps.luminance_data[2].input_signal = 200;
+ caps.luminance_data[2].luminance = 90;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /*
+ * Set brightness so that scale_input_to_fw yields exactly 128.
+ * scale_input_to_fw(min, max, x) = DIV_ROUND_CLOSEST(x * 255, max - min)
+ * With min=0, max=0x101*255=65535:
+ * We need x such that DIV_ROUND_CLOSEST(x * 255, 65535) = 128
+ * → x = 128 * 65535 / 255 = 32896
+ */
+ brightness = 32896;
+
+ convert_custom_brightness(&caps, min, max, &brightness);
+
+ /*
+ * Exact match: lum=50, brightness_scaled=128
+ * result = scale_fw_to_input(min, max, DIV_ROUND_CLOSEST(50*128, 101))
+ * = scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(6400, 101))
+ * = scale_fw_to_input(0, 65535, 63)
+ * = 0 + DIV_ROUND_CLOSEST(63 * 65535, 255) = 16191 (approx)
+ */
+ KUNIT_EXPECT_TRUE(test, brightness != 32896);
+ KUNIT_EXPECT_TRUE(test, brightness < 32896);
+
+ amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_custom_brightness_below_first - Test Custom brightness below first
+ * @test: The KUnit test context
+ */
+static void dm_test_custom_brightness_below_first(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ uint32_t brightness;
+ unsigned int min, max;
+ uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+
+ caps.aux_support = false;
+ caps.min_input_signal = 0;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 2;
+ caps.luminance_data[0].input_signal = 100;
+ caps.luminance_data[0].luminance = 40;
+ caps.luminance_data[1].input_signal = 200;
+ caps.luminance_data[1].luminance = 80;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /*
+ * Set brightness low enough that scaled value < 100.
+ * scale_input_to_fw(0, 65535, x) = DIV_ROUND_CLOSEST(x*255, 65535)
+ * For result=50: x = 50*65535/255 = 12850
+ */
+ brightness = 12850;
+
+ convert_custom_brightness(&caps, min, max, &brightness);
+
+ /*
+ * Below first data point: lum = DIV_ROUND_CLOSEST(40 * 50, 100) = 20
+ * Then: scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(20 * 50, 101))
+ * = scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(1000, 101))
+ * = scale_fw_to_input(0, 65535, 10)
+ * The output should be significantly less than input.
+ */
+ KUNIT_EXPECT_TRUE(test, brightness < 12850);
+
+ amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_custom_brightness_interpolation - Test Custom brightness interpolation
+ * @test: The KUnit test context
+ */
+static void dm_test_custom_brightness_interpolation(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ uint32_t brightness;
+ unsigned int min, max;
+ uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+
+ caps.aux_support = false;
+ caps.min_input_signal = 0;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 2;
+ caps.luminance_data[0].input_signal = 50;
+ caps.luminance_data[0].luminance = 20;
+ caps.luminance_data[1].input_signal = 200;
+ caps.luminance_data[1].luminance = 80;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /*
+ * Choose a value between data points 50 and 200.
+ * scale_input_to_fw(0, 65535, x) = 125 when x = 125*65535/255 = 32125
+ */
+ brightness = 32125;
+
+ convert_custom_brightness(&caps, min, max, &brightness);
+
+ /*
+ * The function should interpolate between data points and produce
+ * a remapped value different from the input.
+ */
+ KUNIT_EXPECT_TRUE(test, brightness != 32125);
+
+ amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_custom_brightness_above_last - Test Custom brightness above last data point
+ * @test: The KUnit test context
+ */
+static void dm_test_custom_brightness_above_last(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ uint32_t brightness;
+ unsigned int min, max;
+ uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+
+ caps.aux_support = false;
+ caps.min_input_signal = 0;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 2;
+ caps.luminance_data[0].input_signal = 50;
+ caps.luminance_data[0].luminance = 20;
+ caps.luminance_data[1].input_signal = 150;
+ caps.luminance_data[1].luminance = 60;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /*
+ * Choose brightness above the last data point (150).
+ * scale_input_to_fw(0, 65535, x) = 220 when x = 220*65535/255 = 56533
+ * After binary search, left >= data_points, clamped → right==left,
+ * so lum = upper_lum = 60.
+ */
+ brightness = 56533;
+
+ convert_custom_brightness(&caps, min, max, &brightness);
+
+ /* Output should differ from input (remapped via curve) */
+ KUNIT_EXPECT_TRUE(test, brightness != 56533);
+ KUNIT_EXPECT_TRUE(test, brightness < 56533);
+
+ amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_custom_brightness_single_data_point - Test Custom brightness with single data point
+ * @test: The KUnit test context
+ */
+static void dm_test_custom_brightness_single_data_point(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ uint32_t brightness;
+ unsigned int min, max;
+ uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+
+ caps.aux_support = false;
+ caps.min_input_signal = 0;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 1;
+ caps.luminance_data[0].input_signal = 128;
+ caps.luminance_data[0].luminance = 50;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /*
+ * Brightness below the single data point triggers the
+ * "below first" path: lum = DIV_ROUND_CLOSEST(50 * scaled, 128).
+ * scale_input_to_fw(0, 65535, x) = 64 when x = 64*65535/255 = 16448
+ */
+ brightness = 16448;
+
+ convert_custom_brightness(&caps, min, max, &brightness);
+
+ KUNIT_EXPECT_TRUE(test, brightness < 16448);
+
+ amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_custom_brightness_lower_lum_zero - Test Custom brightness with zero lower luminance
+ * @test: The KUnit test context
+ */
+static void dm_test_custom_brightness_lower_lum_zero(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ uint32_t brightness;
+ unsigned int min, max;
+ uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+
+ caps.aux_support = false;
+ caps.min_input_signal = 0;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 2;
+ caps.luminance_data[0].input_signal = 50;
+ caps.luminance_data[0].luminance = 0; /* zero lower luminance */
+ caps.luminance_data[1].input_signal = 200;
+ caps.luminance_data[1].luminance = 80;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /*
+ * Choose brightness between data points to trigger interpolation.
+ * scale_input_to_fw(0, 65535, x) = 125 when x = 125*65535/255 = 32125
+ * With lower_lum == 0, code takes shortcut: lum = upper_lum = 80.
+ */
+ brightness = 32125;
+
+ convert_custom_brightness(&caps, min, max, &brightness);
+
+ /* Should remap; result should differ from input */
+ KUNIT_EXPECT_TRUE(test, brightness != 32125);
+
+ amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_brightness_to_user_above_max - Test Brightness to user above max
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_to_user_above_max(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max, result;
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /* brightness above max → result > max (linear extrapolation) */
+ result = convert_brightness_to_user(&caps, max + 1000);
+
+ KUNIT_EXPECT_GT(test, result, max);
+}
+
+/**
+ * dm_test_brightness_from_user_midrange - Test Brightness from user mid-range value
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_from_user_midrange(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+ u32 result;
+
+ caps.aux_support = false;
+ caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 0;
+
+ get_brightness_range(&caps, &min, &max);
+
+ /* Mid-range brightness should map to between min and max */
+ result = convert_brightness_from_user(&caps, max / 2);
+
+ KUNIT_EXPECT_GE(test, result, min);
+ KUNIT_EXPECT_LE(test, result, max);
+}
+
+/**
+ * dm_test_brightness_from_user_with_curve - Test Brightness from user with custom curve active
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_from_user_with_curve(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+ u32 with_curve, without_curve;
+ uint saved_mask = amdgpu_dm_get_dc_debug_mask();
+
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+
+ caps.aux_support = false;
+ caps.min_input_signal = 0;
+ caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ caps.data_points = 2;
+ caps.luminance_data[0].input_signal = 50;
+ caps.luminance_data[0].luminance = 20;
+ caps.luminance_data[1].input_signal = 200;
+ caps.luminance_data[1].luminance = 80;
+
+ get_brightness_range(&caps, &min, &max);
+
+ with_curve = convert_brightness_from_user(&caps, max / 2);
+
+ /* Now disable the curve and compare */
+ amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() | DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE);
+ without_curve = convert_brightness_from_user(&caps, max / 2);
+
+ /* Custom curve should produce a different mapping */
+ KUNIT_EXPECT_NE(test, with_curve, without_curve);
+
+ amdgpu_dm_set_dc_debug_mask(saved_mask);
+}
+
+/**
+ * dm_test_brightness_range_zero_signals - Test Brightness range with zero min and max signals
+ * @test: The KUnit test context
+ */
+static void dm_test_brightness_range_zero_signals(struct kunit *test)
+{
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min = 99, max = 99;
+
+ caps.aux_support = false;
+ caps.min_input_signal = 0;
+ caps.max_input_signal = 0;
+
+ /* Both signals zero → min=max=0 */
+ KUNIT_EXPECT_EQ(test, get_brightness_range(&caps, &min, &max), 1);
+ KUNIT_EXPECT_EQ(test, min, 0U);
+ KUNIT_EXPECT_EQ(test, max, 0U);
+}
+
+/* Tests for amdgpu_dm_backlight_fill_props() */
+
+/**
+ * dm_test_backlight_fill_props_ac_linear - Test AC brightness and linear scale
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_fill_props_ac_linear(struct kunit *test)
+{
+ struct backlight_properties props = {};
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.min_input_signal = 12;
+ caps.max_input_signal = 255;
+ caps.ac_level = 40;
+ caps.dc_level = 20;
+
+ get_brightness_range(&caps, &min, &max);
+ amdgpu_dm_backlight_fill_props(&caps, true, false, &props);
+
+ KUNIT_EXPECT_EQ(test, props.brightness,
+ DIV_ROUND_CLOSEST((max - min) * caps.ac_level, 100));
+ KUNIT_EXPECT_EQ(test, props.max_brightness, max - min);
+ KUNIT_EXPECT_EQ(test, props.scale, BACKLIGHT_SCALE_LINEAR);
+ KUNIT_EXPECT_EQ(test, props.type, BACKLIGHT_RAW);
+}
+
+/**
+ * dm_test_backlight_fill_props_dc_nonlinear - Test DC brightness and non-linear scale
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_fill_props_dc_nonlinear(struct kunit *test)
+{
+ struct backlight_properties props = {};
+ struct amdgpu_dm_backlight_caps caps = {};
+ unsigned int min, max;
+
+ caps.min_input_signal = 12;
+ caps.max_input_signal = 255;
+ caps.ac_level = 40;
+ caps.dc_level = 20;
+ caps.data_points = 2;
+
+ get_brightness_range(&caps, &min, &max);
+ amdgpu_dm_backlight_fill_props(&caps, false, true, &props);
+
+ KUNIT_EXPECT_EQ(test, props.brightness,
+ DIV_ROUND_CLOSEST((max - min) * caps.dc_level, 100));
+ KUNIT_EXPECT_EQ(test, props.max_brightness, max - min);
+ KUNIT_EXPECT_EQ(test, props.scale, BACKLIGHT_SCALE_NON_LINEAR);
+ KUNIT_EXPECT_EQ(test, props.type, BACKLIGHT_RAW);
+}
+
+/**
+ * dm_test_backlight_fill_props_default_range - Test default properties without caps
+ * @test: The KUnit test context
+ */
+static void dm_test_backlight_fill_props_default_range(struct kunit *test)
+{
+ struct backlight_properties props = {};
+
+ amdgpu_dm_backlight_fill_props(NULL, false, true, &props);
+
+ KUNIT_EXPECT_EQ(test, props.brightness, MAX_BACKLIGHT_LEVEL);
+ KUNIT_EXPECT_EQ(test, props.max_brightness, MAX_BACKLIGHT_LEVEL);
+ KUNIT_EXPECT_EQ(test, props.scale, BACKLIGHT_SCALE_LINEAR);
+ KUNIT_EXPECT_EQ(test, props.type, BACKLIGHT_RAW);
+}
+
+/* Tests for amdgpu_dm_update_connector_ext_caps() */
+
+/**
+ * dm_test_update_connector_ext_caps_negative_bl_idx - Test negative backlight index early return
+ * @test: The KUnit test context
+ */
+static void dm_test_update_connector_ext_caps_negative_bl_idx(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector);
+
+ aconnector->bl_idx = -1;
+
+ amdgpu_dm_update_connector_ext_caps(aconnector);
+
+ KUNIT_SUCCEED(test);
+}
+
+/**
+ * dm_test_update_connector_ext_caps_non_edp - Test non-eDP connector early return
+ * @test: The KUnit test context
+ */
+static void dm_test_update_connector_ext_caps_non_edp(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_HDMI_TYPE_A);
+ fixture.adev->dm.backlight_caps[0].aux_support = true;
+
+ amdgpu_dm_update_connector_ext_caps(fixture.aconnector);
+
+ KUNIT_EXPECT_TRUE(test, fixture.adev->dm.backlight_caps[0].aux_support);
+ KUNIT_EXPECT_PTR_EQ(test, fixture.adev->dm.backlight_caps[0].ext_caps, NULL);
+}
+
+/**
+ * dm_test_update_connector_ext_caps_oled_defaults - Test OLED eDP defaults to AUX backlight
+ * @test: The KUnit test context
+ */
+static void dm_test_update_connector_ext_caps_oled_defaults(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_backlight = amdgpu_dm_get_backlight_param();
+
+ amdgpu_dm_set_backlight_param(-1);
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
+ fixture.link->dpcd_sink_ext_caps.bits.oled = 1;
+
+ amdgpu_dm_update_connector_ext_caps(fixture.aconnector);
+
+ KUNIT_EXPECT_PTR_EQ(test, fixture.adev->dm.backlight_caps[0].ext_caps,
+ &fixture.link->dpcd_sink_ext_caps);
+ KUNIT_EXPECT_TRUE(test, fixture.adev->dm.backlight_caps[0].aux_support);
+ KUNIT_EXPECT_EQ(test, fixture.link->backlight_control_type,
+ BACKLIGHT_CONTROL_AMD_AUX);
+ KUNIT_EXPECT_EQ(test, fixture.adev->dm.backlight_caps[0].aux_max_input_signal, 512);
+ KUNIT_EXPECT_EQ(test, fixture.adev->dm.backlight_caps[0].aux_min_input_signal, 1);
+
+ amdgpu_dm_set_backlight_param(saved_backlight);
+}
+
+/**
+ * dm_test_update_connector_ext_caps_luminance_values - Test luminance range copy
+ * @test: The KUnit test context
+ */
+static void dm_test_update_connector_ext_caps_luminance_values(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_backlight = amdgpu_dm_get_backlight_param();
+
+ amdgpu_dm_set_backlight_param(-1);
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
+ fixture.aconnector->base.display_info.luminance_range.min_luminance = 2;
+ fixture.aconnector->base.display_info.luminance_range.max_luminance = 400;
+
+ amdgpu_dm_update_connector_ext_caps(fixture.aconnector);
+
+ KUNIT_EXPECT_FALSE(test, fixture.adev->dm.backlight_caps[0].aux_support);
+ KUNIT_EXPECT_EQ(test, fixture.adev->dm.backlight_caps[0].aux_max_input_signal, 400);
+ KUNIT_EXPECT_EQ(test, fixture.adev->dm.backlight_caps[0].aux_min_input_signal, 2);
+
+ amdgpu_dm_set_backlight_param(saved_backlight);
+}
+
+/**
+ * dm_test_update_connector_ext_caps_force_aux - Test module parameter forces AUX backlight
+ * @test: The KUnit test context
+ */
+static void dm_test_update_connector_ext_caps_force_aux(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_backlight = amdgpu_dm_get_backlight_param();
+
+ amdgpu_dm_set_backlight_param(1);
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
+
+ amdgpu_dm_update_connector_ext_caps(fixture.aconnector);
+
+ KUNIT_EXPECT_TRUE(test, fixture.adev->dm.backlight_caps[0].aux_support);
+ KUNIT_EXPECT_EQ(test, fixture.link->backlight_control_type,
+ BACKLIGHT_CONTROL_AMD_AUX);
+
+ amdgpu_dm_set_backlight_param(saved_backlight);
+}
+
+/**
+ * dm_test_update_connector_ext_caps_force_pwm - Test module parameter forces PWM backlight
+ * @test: The KUnit test context
+ */
+static void dm_test_update_connector_ext_caps_force_pwm(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_backlight = amdgpu_dm_get_backlight_param();
+
+ amdgpu_dm_set_backlight_param(0);
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
+ fixture.link->dpcd_sink_ext_caps.bits.oled = 1;
+
+ amdgpu_dm_update_connector_ext_caps(fixture.aconnector);
+
+ KUNIT_EXPECT_FALSE(test, fixture.adev->dm.backlight_caps[0].aux_support);
+ KUNIT_EXPECT_NE(test, fixture.link->backlight_control_type,
+ BACKLIGHT_CONTROL_AMD_AUX);
+
+ amdgpu_dm_set_backlight_param(saved_backlight);
+}
+
+/* Tests for amdgpu_dm_should_create_sysfs() */
+
+/**
+ * dm_test_should_create_sysfs_abm_forced - Test forced ABM disables sysfs
+ * @test: The KUnit test context
+ */
+static void dm_test_should_create_sysfs_abm_forced(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_abm_level = amdgpu_dm_get_abm_level_param();
+
+ amdgpu_dm_set_abm_level_param(1);
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
+ fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
+
+ amdgpu_dm_set_abm_level_param(saved_abm_level);
+}
+
+/**
+ * dm_test_should_create_sysfs_non_edp - Test non-eDP connector disables sysfs
+ * @test: The KUnit test context
+ */
+static void dm_test_should_create_sysfs_non_edp(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_abm_level = amdgpu_dm_get_abm_level_param();
+
+ amdgpu_dm_set_abm_level_param(-1);
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_HDMI_TYPE_A);
+ fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
+
+ amdgpu_dm_set_abm_level_param(saved_abm_level);
+}
+
+/**
+ * dm_test_should_create_sysfs_no_backlight_index - Test eDP without backlight index enables sysfs
+ * @test: The KUnit test context
+ */
+static void dm_test_should_create_sysfs_no_backlight_index(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_abm_level = amdgpu_dm_get_abm_level_param();
+
+ amdgpu_dm_set_abm_level_param(-1);
+ setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP);
+ fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
+
+ amdgpu_dm_set_abm_level_param(saved_abm_level);
+}
+
+/**
+ * dm_test_should_create_sysfs_aux_backlight - Test AUX backlight disables sysfs
+ * @test: The KUnit test context
+ */
+static void dm_test_should_create_sysfs_aux_backlight(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_abm_level = amdgpu_dm_get_abm_level_param();
+
+ amdgpu_dm_set_abm_level_param(-1);
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
+ fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
+ fixture.adev->dm.backlight_caps[0].aux_support = true;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
+
+ amdgpu_dm_set_abm_level_param(saved_abm_level);
+}
+
+/**
+ * dm_test_should_create_sysfs_pwm_backlight - Test PWM backlight enables sysfs
+ * @test: The KUnit test context
+ */
+static void dm_test_should_create_sysfs_pwm_backlight(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ int saved_abm_level = amdgpu_dm_get_abm_level_param();
+
+ amdgpu_dm_set_abm_level_param(-1);
+ setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
+ fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
+ fixture.adev->dm.backlight_caps[0].aux_support = false;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
+
+ amdgpu_dm_set_abm_level_param(saved_abm_level);
+}
+
+/* Tests for amdgpu_dm_setup_backlight_device() */
+
+/**
+ * dm_test_setup_backlight_device_non_edp - Test non-eDP/LVDS link is skipped
+ * @test: The KUnit test context
+ */
+static void dm_test_setup_backlight_device_non_edp(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ struct amdgpu_display_manager *dm;
+
+ setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_HDMI_TYPE_A);
+ fixture.link->type = dc_connection_single;
+ dm = &fixture.adev->dm;
+ dm->adev = fixture.adev;
+ dm->num_of_edps = 0;
+
+ amdgpu_dm_setup_backlight_device(dm, fixture.aconnector);
+
+ /* Non-eDP/LVDS signal → no backlight setup */
+ KUNIT_EXPECT_EQ(test, dm->num_of_edps, 0);
+ KUNIT_EXPECT_EQ(test, fixture.aconnector->bl_idx, -1);
+}
+
+/**
+ * dm_test_setup_backlight_device_connection_none - Test disconnected link is skipped
+ * @test: The KUnit test context
+ */
+static void dm_test_setup_backlight_device_connection_none(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ struct amdgpu_display_manager *dm;
+
+ setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP);
+ fixture.link->type = dc_connection_none;
+ dm = &fixture.adev->dm;
+ dm->adev = fixture.adev;
+ dm->num_of_edps = 0;
+
+ amdgpu_dm_setup_backlight_device(dm, fixture.aconnector);
+
+ /* Disconnected link → no backlight setup */
+ KUNIT_EXPECT_EQ(test, dm->num_of_edps, 0);
+ KUNIT_EXPECT_EQ(test, fixture.aconnector->bl_idx, -1);
+}
+
+/**
+ * dm_test_setup_backlight_device_max_edps - Test setup is skipped when at eDP limit
+ * @test: The KUnit test context
+ */
+static void dm_test_setup_backlight_device_max_edps(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ struct amdgpu_display_manager *dm;
+
+ setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP);
+ fixture.link->type = dc_connection_single;
+ dm = &fixture.adev->dm;
+ dm->adev = fixture.adev;
+ dm->num_of_edps = AMDGPU_DM_MAX_NUM_EDP;
+
+ amdgpu_dm_setup_backlight_device(dm, fixture.aconnector);
+
+ /* Already at the eDP limit → no additional setup */
+ KUNIT_EXPECT_EQ(test, dm->num_of_edps, AMDGPU_DM_MAX_NUM_EDP);
+ KUNIT_EXPECT_EQ(test, fixture.aconnector->bl_idx, -1);
+}
+
+/**
+ * dm_test_setup_backlight_device_oled_success - Test successful eDP backlight setup
+ * @test: The KUnit test context
+ */
+static void dm_test_setup_backlight_device_oled_success(struct kunit *test)
+{
+ struct dm_backlight_connector_fixture fixture = {};
+ struct amdgpu_display_manager *dm;
+ int saved_backlight = amdgpu_dm_get_backlight_param();
+
+ amdgpu_dm_set_backlight_param(-1);
+ setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP);
+ fixture.link->type = dc_connection_single;
+ /* OLED panel avoids the ABM property attach path */
+ fixture.link->dpcd_sink_ext_caps.bits.oled = 1;
+ dm = &fixture.adev->dm;
+ dm->adev = fixture.adev;
+ dm->num_of_edps = 0;
+
+ amdgpu_dm_setup_backlight_device(dm, fixture.aconnector);
+
+ KUNIT_EXPECT_EQ(test, dm->num_of_edps, 1);
+ KUNIT_EXPECT_EQ(test, fixture.aconnector->bl_idx, 0);
+ KUNIT_EXPECT_PTR_EQ(test, (void *)dm->backlight_link[0],
+ (void *)fixture.link);
+ KUNIT_EXPECT_TRUE(test, dm->backlight_caps[0].aux_support);
+
+ amdgpu_dm_set_backlight_param(saved_backlight);
+}
+
+static struct kunit_case dm_backlight_test_cases[] = {
+ /* amdgpu_dm_backlight_get_device_index */
+ KUNIT_CASE(dm_test_backlight_device_index_matches_second),
+ KUNIT_CASE(dm_test_backlight_device_index_missing_fallback),
+ KUNIT_CASE(dm_test_backlight_caps_valid_short_circuit),
+#if !defined(CONFIG_ACPI)
+ KUNIT_CASE(dm_test_backlight_caps_aux_support_noop),
+ KUNIT_CASE(dm_test_backlight_caps_non_aux_sets_defaults),
+#endif
+ /* get_brightness_range */
+ KUNIT_CASE(dm_test_brightness_range_null_caps),
+ KUNIT_CASE(dm_test_brightness_range_pwm),
+ KUNIT_CASE(dm_test_brightness_range_aux),
+ /* convert_brightness_to_user */
+ KUNIT_CASE(dm_test_brightness_to_user_null_caps),
+ KUNIT_CASE(dm_test_brightness_to_user_below_min),
+ KUNIT_CASE(dm_test_brightness_to_user_at_max),
+ KUNIT_CASE(dm_test_brightness_to_user_at_min),
+ KUNIT_CASE(dm_test_brightness_to_user_midpoint_pwm),
+ /* convert_brightness_from_user */
+ KUNIT_CASE(dm_test_brightness_from_user_null_caps),
+ KUNIT_CASE(dm_test_brightness_from_user_zero),
+ KUNIT_CASE(dm_test_brightness_from_user_max),
+ KUNIT_CASE(dm_test_brightness_from_user_aux),
+ /* convert_custom_brightness */
+ KUNIT_CASE(dm_test_custom_brightness_no_data_points),
+ KUNIT_CASE(dm_test_custom_brightness_debug_mask_disables),
+ KUNIT_CASE(dm_test_custom_brightness_exact_match),
+ KUNIT_CASE(dm_test_custom_brightness_below_first),
+ KUNIT_CASE(dm_test_custom_brightness_interpolation),
+ KUNIT_CASE(dm_test_custom_brightness_above_last),
+ KUNIT_CASE(dm_test_custom_brightness_single_data_point),
+ KUNIT_CASE(dm_test_custom_brightness_lower_lum_zero),
+ KUNIT_CASE(dm_test_brightness_to_user_above_max),
+ KUNIT_CASE(dm_test_brightness_from_user_midrange),
+ KUNIT_CASE(dm_test_brightness_from_user_with_curve),
+ KUNIT_CASE(dm_test_brightness_range_zero_signals),
+ /* amdgpu_dm_backlight_fill_props */
+ KUNIT_CASE(dm_test_backlight_fill_props_ac_linear),
+ KUNIT_CASE(dm_test_backlight_fill_props_dc_nonlinear),
+ KUNIT_CASE(dm_test_backlight_fill_props_default_range),
+ /* amdgpu_dm_update_connector_ext_caps */
+ KUNIT_CASE(dm_test_update_connector_ext_caps_negative_bl_idx),
+ KUNIT_CASE(dm_test_update_connector_ext_caps_non_edp),
+ KUNIT_CASE(dm_test_update_connector_ext_caps_oled_defaults),
+ KUNIT_CASE(dm_test_update_connector_ext_caps_luminance_values),
+ KUNIT_CASE(dm_test_update_connector_ext_caps_force_aux),
+ KUNIT_CASE(dm_test_update_connector_ext_caps_force_pwm),
+ /* amdgpu_dm_should_create_sysfs */
+ KUNIT_CASE(dm_test_should_create_sysfs_abm_forced),
+ KUNIT_CASE(dm_test_should_create_sysfs_non_edp),
+ KUNIT_CASE(dm_test_should_create_sysfs_no_backlight_index),
+ KUNIT_CASE(dm_test_should_create_sysfs_aux_backlight),
+ KUNIT_CASE(dm_test_should_create_sysfs_pwm_backlight),
+ /* amdgpu_dm_setup_backlight_device */
+ KUNIT_CASE(dm_test_setup_backlight_device_non_edp),
+ KUNIT_CASE(dm_test_setup_backlight_device_connection_none),
+ KUNIT_CASE(dm_test_setup_backlight_device_max_edps),
+ KUNIT_CASE(dm_test_setup_backlight_device_oled_success),
+ {}
+};
+
+static struct kunit_suite dm_backlight_test_suite = {
+ .name = "amdgpu_dm_backlight",
+ .test_cases = dm_backlight_test_cases,
+};
+
+kunit_test_suite(dm_backlight_test_suite);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_backlight");
+MODULE_AUTHOR("AMD");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c
index f943361b70e8..d64c7da20f2c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c
@@ -1159,19 +1159,19 @@ static void dm_test_verify_lut_sizes_invalid_degamma_valid_gamma(struct kunit *t
*/
static void dm_test_atomic_lut3d_zero_size(struct kunit *test)
{
- struct dc_3dlut *lut;
+ struct dc_plane_cm *cm;
u32 initialized;
- lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, lut);
+ cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cm);
/* Pre-set initialized so we can confirm it is cleared */
- lut->state.bits.initialized = 1;
+ cm->lut3d_func.state.bits.initialized = 1;
- amdgpu_dm_atomic_lut3d(NULL, 0, lut);
+ amdgpu_dm_atomic_lut3d(NULL, 0, cm);
/* Copy bit-field: typeof cannot be applied to a bit-field */
- initialized = lut->state.bits.initialized;
+ initialized = cm->lut3d_func.state.bits.initialized;
KUNIT_EXPECT_EQ(test, initialized, 0U);
}
@@ -1183,22 +1183,22 @@ static void dm_test_atomic_lut3d_nonzero_state_bits(struct kunit *test)
{
const uint32_t lut3d_size = 5;
struct drm_color_lut *lut_data;
- struct dc_3dlut *lut;
+ struct dc_plane_cm *cm;
u32 initialized;
lut_data = kunit_kcalloc(test, lut3d_size, sizeof(*lut_data), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, lut_data);
- lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, lut);
+ cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cm);
- amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, lut);
+ amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, cm);
/* Copy bit-field: typeof cannot be applied to a bit-field */
- initialized = lut->state.bits.initialized;
+ initialized = cm->lut3d_func.state.bits.initialized;
KUNIT_EXPECT_EQ(test, initialized, 1U);
- KUNIT_EXPECT_FALSE(test, lut->lut_3d.use_tetrahedral_9);
- KUNIT_EXPECT_TRUE(test, lut->lut_3d.use_12bits);
+ KUNIT_EXPECT_FALSE(test, cm->lut3d_func.lut_3d.use_tetrahedral_9);
+ KUNIT_EXPECT_TRUE(test, cm->lut3d_func.lut_3d.use_12bits);
}
/**
@@ -1209,29 +1209,29 @@ static void dm_test_atomic_lut3d_data_forwarded(struct kunit *test)
{
const uint32_t lut3d_size = 5;
struct drm_color_lut *lut_data;
- struct dc_3dlut *lut;
+ struct dc_plane_cm *cm;
lut_data = kunit_kcalloc(test, lut3d_size, sizeof(*lut_data), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, lut_data);
- lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, lut);
+ cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cm);
lut_data[0].red = 0xFFFF;
lut_data[0].green = 0x8000;
lut_data[0].blue = 0x4000;
- amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, lut);
+ amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, cm);
/*
* use_tetrahedral_9 == false → data goes into tetrahedral_17.
* lut[0] maps to lut0[0] (first element of the first group).
*/
- KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].red,
+ KUNIT_EXPECT_EQ(test, cm->lut3d_func.lut_3d.tetrahedral_17.lut0[0].red,
drm_color_lut_extract(0xFFFF, MAX_COLOR_3DLUT_BITDEPTH));
- KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].green,
+ KUNIT_EXPECT_EQ(test, cm->lut3d_func.lut_3d.tetrahedral_17.lut0[0].green,
drm_color_lut_extract(0x8000, MAX_COLOR_3DLUT_BITDEPTH));
- KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].blue,
+ KUNIT_EXPECT_EQ(test, cm->lut3d_func.lut_3d.tetrahedral_17.lut0[0].blue,
drm_color_lut_extract(0x4000, MAX_COLOR_3DLUT_BITDEPTH));
}
@@ -1398,19 +1398,19 @@ static void dm_test_set_atomic_regamma_bypass(struct kunit *test)
*/
static void dm_test_atomic_shaper_lut_bypass(struct kunit *test)
{
- struct dc_transfer_func *func_shaper;
+ struct dc_plane_cm *cm;
- func_shaper = kunit_kzalloc(test, sizeof(*func_shaper), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, func_shaper);
+ cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cm);
/* size=0 and tf=LINEAR: must take the bypass branch */
KUNIT_EXPECT_EQ(test,
amdgpu_dm_atomic_shaper_lut(NULL, false,
TRANSFER_FUNCTION_LINEAR,
- 0, func_shaper),
+ 0, cm),
0);
- KUNIT_EXPECT_EQ(test, (int)func_shaper->type, (int)TF_TYPE_BYPASS);
- KUNIT_EXPECT_EQ(test, (int)func_shaper->tf, (int)TRANSFER_FUNCTION_LINEAR);
+ KUNIT_EXPECT_EQ(test, (int)cm->shaper_func.type, (int)TF_TYPE_BYPASS);
+ KUNIT_EXPECT_EQ(test, (int)cm->shaper_func.tf, (int)TRANSFER_FUNCTION_LINEAR);
}
/**
@@ -1419,19 +1419,19 @@ static void dm_test_atomic_shaper_lut_bypass(struct kunit *test)
*/
static void dm_test_atomic_blend_lut_bypass(struct kunit *test)
{
- struct dc_transfer_func *func_blend;
+ struct dc_plane_cm *cm;
- func_blend = kunit_kzalloc(test, sizeof(*func_blend), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, func_blend);
+ cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, cm);
/* size=0 and tf=LINEAR: must take the bypass branch */
KUNIT_EXPECT_EQ(test,
amdgpu_dm_atomic_blend_lut(NULL, false,
TRANSFER_FUNCTION_LINEAR,
- 0, func_blend),
+ 0, cm),
0);
- KUNIT_EXPECT_EQ(test, (int)func_blend->type, (int)TF_TYPE_BYPASS);
- KUNIT_EXPECT_EQ(test, (int)func_blend->tf, (int)TRANSFER_FUNCTION_LINEAR);
+ KUNIT_EXPECT_EQ(test, (int)cm->blend_func.type, (int)TF_TYPE_BYPASS);
+ KUNIT_EXPECT_EQ(test, (int)cm->blend_func.tf, (int)TRANSFER_FUNCTION_LINEAR);
}
/* ---- Tests for __set_colorop_in_tf_1d_curve ---- */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c
index fa270ff28c6a..2e557ff66818 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c
@@ -9,7 +9,10 @@
#include <drm/drm_colorop.h>
#include <drm/drm_kunit_helpers.h>
+#include "dc.h"
+#include "amdgpu.h"
#include "amdgpu_dm_colorop.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
/* Tests for amdgpu_dm_supported_degam_tfs */
@@ -133,6 +136,30 @@ static void kunit_colorop_pipeline_destroy(void *drm)
drm_colorop_pipeline_destroy((struct drm_device *)drm);
}
+static void dm_expect_colorop_pipeline(struct kunit *test, struct drm_device *drm,
+ const struct drm_prop_enum_list *list,
+ const enum drm_colorop_type *expected,
+ int expected_count)
+{
+ struct drm_colorop *op, *first = NULL;
+ int i = 0;
+
+ drm_for_each_colorop(op, drm) {
+ if (op->base.id == (uint32_t)list->type) {
+ first = op;
+ break;
+ }
+ }
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, first);
+
+ for (op = first; op; op = op->next, i++) {
+ KUNIT_ASSERT_LT(test, i, expected_count);
+ KUNIT_EXPECT_EQ(test, op->type, expected[i]);
+ KUNIT_EXPECT_NOT_NULL(test, op->bypass_property);
+ }
+ KUNIT_EXPECT_EQ(test, i, expected_count);
+}
+
/**
* dm_test_initialize_default_pipeline() - Verify amdgpu_dm_build_default_pipeline()
* produces the expected colorop chain with all ops bypassable.
@@ -154,8 +181,6 @@ static void dm_test_initialize_default_pipeline(struct kunit *test)
struct drm_device *drm;
struct drm_plane *plane;
struct drm_prop_enum_list list = {};
- struct drm_colorop *op, *first = NULL;
- int i = 0;
int ret;
dev = drm_kunit_helper_alloc_device(test);
@@ -185,20 +210,102 @@ static void dm_test_initialize_default_pipeline(struct kunit *test)
KUNIT_ASSERT_EQ(test, ret, 0);
kfree(list.name);
- drm_for_each_colorop(op, drm) {
- if (op->base.id == (uint32_t)list.type) {
- first = op;
- break;
- }
- }
- KUNIT_ASSERT_NOT_ERR_OR_NULL(test, first);
+ dm_expect_colorop_pipeline(test, drm, &list, expected, ARRAY_SIZE(expected));
+}
- for (op = first; op; op = op->next, i++) {
- KUNIT_ASSERT_LT(test, i, (int)ARRAY_SIZE(expected));
- KUNIT_EXPECT_EQ(test, op->type, expected[i]);
- KUNIT_EXPECT_NOT_NULL(test, op->bypass_property);
- }
- KUNIT_EXPECT_EQ(test, i, (int)ARRAY_SIZE(expected));
+static void dm_test_initialize_default_pipeline_caps(struct kunit *test,
+ bool dpp_hw_3d_lut,
+ bool mpc_preblend,
+ const enum drm_colorop_type *expected,
+ int expected_count)
+{
+ struct drm_prop_enum_list list = {};
+ struct amdgpu_device *adev;
+ struct drm_device *drm;
+ struct drm_plane *plane;
+ struct dc *dc;
+ int ret;
+
+ adev = dm_kunit_alloc_adev(test);
+ drm = &adev->ddev;
+
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
+ adev->dm.dc = dc;
+ adev->dm.dc->caps.color.dpp.hw_3d_lut = dpp_hw_3d_lut;
+ adev->dm.dc->caps.color.mpc.preblend = mpc_preblend;
+
+ plane = drm_kunit_helper_create_primary_plane(test, drm,
+ NULL, NULL, NULL, 0, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
+
+ kunit_add_action(test, kunit_colorop_pipeline_destroy, drm);
+
+ ret = amdgpu_dm_initialize_default_pipeline(plane, &list);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ kfree(list.name);
+
+ dm_expect_colorop_pipeline(test, drm, &list, expected, expected_count);
+}
+
+/**
+ * dm_test_initialize_default_pipeline_dpp_3d_lut() - Test DPP 3D LUT cap.
+ * @test: KUnit test context.
+ */
+static void dm_test_initialize_default_pipeline_dpp_3d_lut(struct kunit *test)
+{
+ static const enum drm_colorop_type expected[] = {
+ DRM_COLOROP_1D_CURVE,
+ DRM_COLOROP_MULTIPLIER,
+ DRM_COLOROP_CTM_3X4,
+ DRM_COLOROP_1D_CURVE,
+ DRM_COLOROP_1D_LUT,
+ DRM_COLOROP_3D_LUT,
+ DRM_COLOROP_1D_CURVE,
+ DRM_COLOROP_1D_LUT,
+ };
+
+ dm_test_initialize_default_pipeline_caps(test, true, false,
+ expected, ARRAY_SIZE(expected));
+}
+
+/**
+ * dm_test_initialize_default_pipeline_mpc_preblend() - Test MPC preblend cap.
+ * @test: KUnit test context.
+ */
+static void dm_test_initialize_default_pipeline_mpc_preblend(struct kunit *test)
+{
+ static const enum drm_colorop_type expected[] = {
+ DRM_COLOROP_1D_CURVE,
+ DRM_COLOROP_MULTIPLIER,
+ DRM_COLOROP_CTM_3X4,
+ DRM_COLOROP_1D_CURVE,
+ DRM_COLOROP_1D_LUT,
+ DRM_COLOROP_3D_LUT,
+ DRM_COLOROP_1D_CURVE,
+ DRM_COLOROP_1D_LUT,
+ };
+
+ dm_test_initialize_default_pipeline_caps(test, false, true,
+ expected, ARRAY_SIZE(expected));
+}
+
+/**
+ * dm_test_initialize_default_pipeline_no_3d_lut() - Test no 3D LUT caps.
+ * @test: KUnit test context.
+ */
+static void dm_test_initialize_default_pipeline_no_3d_lut(struct kunit *test)
+{
+ static const enum drm_colorop_type expected[] = {
+ DRM_COLOROP_1D_CURVE,
+ DRM_COLOROP_MULTIPLIER,
+ DRM_COLOROP_CTM_3X4,
+ DRM_COLOROP_1D_CURVE,
+ DRM_COLOROP_1D_LUT,
+ };
+
+ dm_test_initialize_default_pipeline_caps(test, false, false,
+ expected, ARRAY_SIZE(expected));
}
static struct kunit_case dm_colorop_test_cases[] = {
@@ -224,6 +331,9 @@ static struct kunit_case dm_colorop_test_cases[] = {
KUNIT_CASE(dm_test_degam_and_blnd_tfs_match),
/* amdgpu_dm_initialize_default_pipeline */
KUNIT_CASE(dm_test_initialize_default_pipeline),
+ KUNIT_CASE(dm_test_initialize_default_pipeline_dpp_3d_lut),
+ KUNIT_CASE(dm_test_initialize_default_pipeline_mpc_preblend),
+ KUNIT_CASE(dm_test_initialize_default_pipeline_no_3d_lut),
{}
};
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
new file mode 100644
index 000000000000..aa451064b30c
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
@@ -0,0 +1,2158 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_connector.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_kunit_helpers.h>
+#include <linux/hdmi.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_display.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_connector.h"
+#include "amdgpu_dm_backlight.h"
+#include "include/grph_object_id.h"
+
+/* Tests for get_subconnector_type() */
+
+/**
+ * dm_test_subconnector_type_none - Test Subconnector type none
+ * @test: The KUnit test context
+ */
+static void dm_test_subconnector_type_none(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_NONE;
+ KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_Native);
+}
+
+/**
+ * dm_test_subconnector_type_vga - Test Subconnector type vga
+ * @test: The KUnit test context
+ */
+static void dm_test_subconnector_type_vga(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_VGA_CONVERTER;
+ KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_VGA);
+}
+
+/**
+ * dm_test_subconnector_type_dvi_converter - Test Subconnector type dvi converter
+ * @test: The KUnit test context
+ */
+static void dm_test_subconnector_type_dvi_converter(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_DVI_CONVERTER;
+ KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_DVID);
+}
+
+/**
+ * dm_test_subconnector_type_dvi_dongle - Test Subconnector type dvi dongle
+ * @test: The KUnit test context
+ */
+static void dm_test_subconnector_type_dvi_dongle(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_DVI_DONGLE;
+ KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_DVID);
+}
+
+/**
+ * dm_test_subconnector_type_hdmi_converter - Test Subconnector type hdmi converter
+ * @test: The KUnit test context
+ */
+static void dm_test_subconnector_type_hdmi_converter(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER;
+ KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_HDMIA);
+}
+
+/**
+ * dm_test_subconnector_type_hdmi_dongle - Test Subconnector type hdmi dongle
+ * @test: The KUnit test context
+ */
+static void dm_test_subconnector_type_hdmi_dongle(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_DONGLE;
+ KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_HDMIA);
+}
+
+/**
+ * dm_test_subconnector_type_mismatched - Test Subconnector type mismatched
+ * @test: The KUnit test context
+ */
+static void dm_test_subconnector_type_mismatched(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE;
+ KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_Unknown);
+}
+
+/**
+ * dm_test_subconnector_type_default_unknown - Test Subconnector type default unknown
+ * @test: The KUnit test context
+ */
+static void dm_test_subconnector_type_default_unknown(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.dongle_type = (typeof(link->dpcd_caps.dongle_type))0x7f;
+ KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_Unknown);
+}
+
+/* Tests for get_output_content_type() */
+
+/**
+ * dm_test_content_type_no_data - Test Content type no data
+ * @test: The KUnit test context
+ */
+static void dm_test_content_type_no_data(struct kunit *test)
+{
+ struct drm_connector_state state = {};
+
+ state.content_type = DRM_MODE_CONTENT_TYPE_NO_DATA;
+ KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_NO_DATA);
+}
+
+/**
+ * dm_test_content_type_graphics - Test Content type graphics
+ * @test: The KUnit test context
+ */
+static void dm_test_content_type_graphics(struct kunit *test)
+{
+ struct drm_connector_state state = {};
+
+ state.content_type = DRM_MODE_CONTENT_TYPE_GRAPHICS;
+ KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_GRAPHICS);
+}
+
+/**
+ * dm_test_content_type_photo - Test Content type photo
+ * @test: The KUnit test context
+ */
+static void dm_test_content_type_photo(struct kunit *test)
+{
+ struct drm_connector_state state = {};
+
+ state.content_type = DRM_MODE_CONTENT_TYPE_PHOTO;
+ KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_PHOTO);
+}
+
+/**
+ * dm_test_content_type_cinema - Test Content type cinema
+ * @test: The KUnit test context
+ */
+static void dm_test_content_type_cinema(struct kunit *test)
+{
+ struct drm_connector_state state = {};
+
+ state.content_type = DRM_MODE_CONTENT_TYPE_CINEMA;
+ KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_CINEMA);
+}
+
+/**
+ * dm_test_content_type_game - Test Content type game
+ * @test: The KUnit test context
+ */
+static void dm_test_content_type_game(struct kunit *test)
+{
+ struct drm_connector_state state = {};
+
+ state.content_type = DRM_MODE_CONTENT_TYPE_GAME;
+ KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_GAME);
+}
+
+/**
+ * dm_test_content_type_unknown_defaults_no_data - Test unknown content type defaults to no data
+ * @test: The KUnit test context
+ */
+static void dm_test_content_type_unknown_defaults_no_data(struct kunit *test)
+{
+ struct drm_connector_state state = {};
+
+ state.content_type = 0x7f;
+ KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state),
+ (int)DISPLAY_CONTENT_TYPE_NO_DATA);
+}
+
+/* Tests for adjust_colour_depth_from_display_info() */
+
+/**
+ * dm_test_adjust_colour_depth_fits_at_888 - Test Adjust colour depth fits at 888
+ * @test: The KUnit test context
+ */
+static void dm_test_adjust_colour_depth_fits_at_888(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_display_info info = {};
+
+ /* 1080p @ 148500 KHz = 1485000 in 100Hz units */
+ timing.pix_clk_100hz = 1485000;
+ timing.display_color_depth = COLOR_DEPTH_888;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ info.max_tmds_clock = 150000; /* 150 MHz */
+
+ KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info));
+ KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_888);
+}
+
+/**
+ * dm_test_adjust_colour_depth_reduces_to_888 - Test Adjust colour depth reduces to 888
+ * @test: The KUnit test context
+ */
+static void dm_test_adjust_colour_depth_reduces_to_888(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_display_info info = {};
+
+ /* Request 10bpc but TMDS limit only allows 8bpc */
+ timing.pix_clk_100hz = 1485000;
+ timing.display_color_depth = COLOR_DEPTH_101010;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ /* 10bpc would need 148500*30/24 = 185625 KHz, exceeds limit */
+ info.max_tmds_clock = 160000;
+
+ KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info));
+ KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_888);
+}
+
+/**
+ * dm_test_adjust_colour_depth_10bpc_passes - Test Adjust colour depth 10bpc passes
+ * @test: The KUnit test context
+ */
+static void dm_test_adjust_colour_depth_10bpc_passes(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_display_info info = {};
+
+ timing.pix_clk_100hz = 1485000;
+ timing.display_color_depth = COLOR_DEPTH_101010;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ /* 10bpc needs 185625 KHz, allow it */
+ info.max_tmds_clock = 200000;
+
+ KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info));
+ KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_101010);
+}
+
+/**
+ * dm_test_adjust_colour_depth_420_halves_clk - Test Adjust colour depth 420 halves clk
+ * @test: The KUnit test context
+ */
+static void dm_test_adjust_colour_depth_420_halves_clk(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_display_info info = {};
+
+ /* 4K @ 594000 KHz = 5940000 in 100Hz units */
+ timing.pix_clk_100hz = 5940000;
+ timing.display_color_depth = COLOR_DEPTH_101010;
+ timing.pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ /* With 420: effective = 594000/2 = 297000, 10bpc = 297000*30/24 = 371250 */
+ info.max_tmds_clock = 400000;
+
+ KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info));
+ KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_101010);
+}
+
+/**
+ * dm_test_adjust_colour_depth_reduces_12bpc_to_10bpc - Test Adjust colour
+ * depth reduces 12bpc to 10bpc
+ * @test: The KUnit test context
+ */
+static void dm_test_adjust_colour_depth_reduces_12bpc_to_10bpc(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_display_info info = {};
+
+ timing.pix_clk_100hz = 1485000;
+ timing.display_color_depth = COLOR_DEPTH_121212;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ info.max_tmds_clock = 190000;
+
+ KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info));
+ KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_101010);
+}
+
+/**
+ * dm_test_adjust_colour_depth_16bpc_no_fallback - Test Adjust colour depth
+ * 16bpc cannot fall back
+ * @test: The KUnit test context
+ */
+static void dm_test_adjust_colour_depth_16bpc_no_fallback(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_display_info info = {};
+
+ /* 16bpc that exceeds limit cannot reduce because the next enum
+ * value (COLOR_DEPTH_141414) is not a valid HDMI depth.
+ */
+ timing.pix_clk_100hz = 1485000;
+ timing.display_color_depth = COLOR_DEPTH_161616;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ info.max_tmds_clock = 230000;
+
+ KUNIT_EXPECT_FALSE(test, adjust_colour_depth_from_display_info(&timing, &info));
+}
+
+/**
+ * dm_test_adjust_colour_depth_none_fits - Test Adjust colour depth none fits
+ * @test: The KUnit test context
+ */
+static void dm_test_adjust_colour_depth_none_fits(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_display_info info = {};
+
+ /* Even 8bpc doesn't fit */
+ timing.pix_clk_100hz = 1485000;
+ timing.display_color_depth = COLOR_DEPTH_888;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ info.max_tmds_clock = 100000; /* Too low */
+
+ KUNIT_EXPECT_FALSE(test, adjust_colour_depth_from_display_info(&timing, &info));
+}
+
+/**
+ * dm_test_adjust_colour_depth_invalid_depth - Test Adjust colour depth invalid depth
+ * @test: The KUnit test context
+ */
+static void dm_test_adjust_colour_depth_invalid_depth(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_display_info info = {};
+
+ timing.pix_clk_100hz = 1485000;
+ timing.display_color_depth = COLOR_DEPTH_141414;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ info.max_tmds_clock = 400000;
+
+ KUNIT_EXPECT_FALSE(test, adjust_colour_depth_from_display_info(&timing, &info));
+ KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_141414);
+}
+
+/* Tests for amdgpu_dm_get_output_color_space() */
+
+/**
+ * dm_test_output_color_space_default_rgb_full - Test Output color space default rgb full
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_default_rgb_full(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
+ state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_SRGB);
+}
+
+/**
+ * dm_test_output_color_space_default_rgb_limited - Test Output color space default rgb limited
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_default_rgb_limited(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
+ state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_SRGB_LIMITED);
+}
+
+/**
+ * dm_test_output_color_space_default_ycbcr709 - Test Output color space default ycbcr709
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_default_ycbcr709(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.pixel_encoding = PIXEL_ENCODING_YCBCR444;
+ timing.pix_clk_100hz = 300000;
+ timing.flags.Y_ONLY = 0;
+ state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_YCBCR709);
+}
+
+/**
+ * dm_test_output_color_space_default_ycbcr601_limited - Test Output color space
+ * default ycbcr601 limited
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_default_ycbcr601_limited(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.pixel_encoding = PIXEL_ENCODING_YCBCR444;
+ timing.pix_clk_100hz = 270300;
+ timing.flags.Y_ONLY = 1;
+ state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_YCBCR601_LIMITED);
+}
+
+/**
+ * dm_test_output_color_space_bt601_y_only - Test Output color space bt601 y only
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_bt601_y_only(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.flags.Y_ONLY = 1;
+ state.colorspace = DRM_MODE_COLORIMETRY_BT601_YCC;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_YCBCR601_LIMITED);
+}
+
+/**
+ * dm_test_output_color_space_bt601 - Test Output color space bt601
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_bt601(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.flags.Y_ONLY = 0;
+ state.colorspace = DRM_MODE_COLORIMETRY_BT601_YCC;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_YCBCR601);
+}
+
+/**
+ * dm_test_output_color_space_bt709 - Test Output color space bt709
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_bt709(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.flags.Y_ONLY = 0;
+ state.colorspace = DRM_MODE_COLORIMETRY_BT709_YCC;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_YCBCR709);
+}
+
+/**
+ * dm_test_output_color_space_bt709_y_only - Test Output color space bt709 y only
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_bt709_y_only(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.flags.Y_ONLY = 1;
+ state.colorspace = DRM_MODE_COLORIMETRY_BT709_YCC;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_YCBCR709_LIMITED);
+}
+
+/**
+ * dm_test_output_color_space_oprgb - Test Output color space oprgb
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_oprgb(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ state.colorspace = DRM_MODE_COLORIMETRY_OPRGB;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_ADOBERGB);
+}
+
+/**
+ * dm_test_output_color_space_bt2020_rgb - Test Output color space bt2020 rgb
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_bt2020_rgb(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ state.colorspace = DRM_MODE_COLORIMETRY_BT2020_RGB;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_2020_RGB_FULLRANGE);
+}
+
+/**
+ * dm_test_output_color_space_bt2020_ycc - Test Output color space bt2020 ycc
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_bt2020_ycc(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+
+ timing.pixel_encoding = PIXEL_ENCODING_YCBCR422;
+ state.colorspace = DRM_MODE_COLORIMETRY_BT2020_YCC;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_2020_YCBCR_LIMITED);
+}
+
+/* Tests for amdgpu_dm_convert_dc_color_depth_into_bpc() */
+
+/**
+ * dm_test_convert_color_depth_bpc_mappings - Test Convert color depth bpc mappings
+ * @test: The KUnit test context
+ */
+static void dm_test_convert_color_depth_bpc_mappings(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_666), 6);
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_888), 8);
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_101010), 10);
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_121212), 12);
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_141414), 14);
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_161616), 16);
+}
+
+/**
+ * dm_test_convert_color_depth_bpc_unknown - Test Convert color depth bpc unknown
+ * @test: The KUnit test context
+ */
+static void dm_test_convert_color_depth_bpc_unknown(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_UNDEFINED), 0);
+}
+
+/* Tests for amdgpu_dm_convert_color_depth_from_display_info() */
+
+/**
+ * dm_test_color_depth_from_info_bpc8 - Test Color depth from info bpc8
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_bpc8(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ connector->display_info.bpc = 8;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 0),
+ (int)COLOR_DEPTH_888);
+}
+
+/**
+ * dm_test_color_depth_from_info_bpc10 - Test Color depth from info bpc10
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_bpc10(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ connector->display_info.bpc = 10;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 0),
+ (int)COLOR_DEPTH_101010);
+}
+
+/**
+ * dm_test_color_depth_from_info_zero_bpc_defaults_888 - Test Color depth from
+ * info zero bpc defaults 888
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_zero_bpc_defaults_888(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ connector->display_info.bpc = 0;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 0),
+ (int)COLOR_DEPTH_888);
+}
+
+/**
+ * dm_test_color_depth_from_info_requested_bpc_caps - Test Color depth from info requested bpc caps
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_requested_bpc_caps(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ /* Display supports 12bpc but user requests max 10 */
+ connector->display_info.bpc = 12;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 10),
+ (int)COLOR_DEPTH_101010);
+}
+
+/**
+ * dm_test_color_depth_from_info_y420_default - Test Color depth from info y420 default
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_y420_default(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ /* No Y420 DC modes set → 8bpc */
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, true, 0),
+ (int)COLOR_DEPTH_888);
+}
+
+/**
+ * dm_test_color_depth_from_info_y420_10bpc - Test Color depth from info y420 10bpc
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_y420_10bpc(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ connector->display_info.hdmi.y420_dc_modes = DRM_EDID_YCBCR420_DC_30;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, true, 0),
+ (int)COLOR_DEPTH_101010);
+}
+
+/**
+ * dm_test_color_depth_from_info_y420_12bpc - Test Color depth from info y420 12bpc
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_y420_12bpc(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ connector->display_info.hdmi.y420_dc_modes = DRM_EDID_YCBCR420_DC_36;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, true, 0),
+ (int)COLOR_DEPTH_121212);
+}
+
+/**
+ * dm_test_color_depth_from_info_y420_16bpc - Test Color depth from info y420 16bpc
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_y420_16bpc(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ connector->display_info.hdmi.y420_dc_modes = DRM_EDID_YCBCR420_DC_48;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, true, 0),
+ (int)COLOR_DEPTH_161616);
+}
+
+/**
+ * dm_test_color_depth_from_info_requested_odd_bpc - Test Color depth from info requested odd bpc
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_requested_odd_bpc(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ connector->display_info.bpc = 12;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 11),
+ (int)COLOR_DEPTH_101010);
+}
+
+/**
+ * dm_test_color_depth_from_info_unsupported_bpc - Test Color depth from info unsupported bpc
+ * @test: The KUnit test context
+ */
+static void dm_test_color_depth_from_info_unsupported_bpc(struct kunit *test)
+{
+ struct drm_connector *connector;
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
+
+ connector->display_info.bpc = 9;
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 0),
+ (int)COLOR_DEPTH_UNDEFINED);
+}
+
+/* Tests for to_drm_connector_type() */
+
+/**
+ * dm_test_to_connector_type_hdmi - Test To connector type hdmi
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_hdmi(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_HDMI_TYPE_A, 0),
+ DRM_MODE_CONNECTOR_HDMIA);
+}
+
+/**
+ * dm_test_to_connector_type_edp - Test To connector type edp
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_edp(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_EDP, 0),
+ DRM_MODE_CONNECTOR_eDP);
+}
+
+/**
+ * dm_test_to_connector_type_lvds - Test To connector type lvds
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_lvds(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_LVDS, 0),
+ DRM_MODE_CONNECTOR_LVDS);
+}
+
+/**
+ * dm_test_to_connector_type_rgb - Test To connector type rgb
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_rgb(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_RGB, 0),
+ DRM_MODE_CONNECTOR_VGA);
+}
+
+/**
+ * dm_test_to_connector_type_dp - Test To connector type dp
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_dp(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_DISPLAY_PORT, 0),
+ DRM_MODE_CONNECTOR_DisplayPort);
+}
+
+/**
+ * dm_test_to_connector_type_dp_mst - Test To connector type dp mst
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_dp_mst(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_DISPLAY_PORT_MST, 0),
+ DRM_MODE_CONNECTOR_DisplayPort);
+}
+
+/**
+ * dm_test_to_connector_type_dvi_dvii - Test To connector type dvi dvii
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_dvi_dvii(struct kunit *test)
+{
+ int type = to_drm_connector_type(SIGNAL_TYPE_DVI_SINGLE_LINK, CONNECTOR_ID_SINGLE_LINK_DVII);
+
+ KUNIT_EXPECT_EQ(test, type, DRM_MODE_CONNECTOR_DVII);
+}
+
+/**
+ * dm_test_to_connector_type_dual_link_dvii - Test To connector type dual link dvii
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_dual_link_dvii(struct kunit *test)
+{
+ int type = to_drm_connector_type(SIGNAL_TYPE_DVI_DUAL_LINK, CONNECTOR_ID_DUAL_LINK_DVII);
+
+ KUNIT_EXPECT_EQ(test, type, DRM_MODE_CONNECTOR_DVII);
+}
+
+/**
+ * dm_test_to_connector_type_dvi_dvid - Test To connector type dvi dvid
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_dvi_dvid(struct kunit *test)
+{
+ int type = to_drm_connector_type(SIGNAL_TYPE_DVI_SINGLE_LINK, CONNECTOR_ID_SINGLE_LINK_DVID);
+
+ KUNIT_EXPECT_EQ(test, type, DRM_MODE_CONNECTOR_DVID);
+}
+
+/**
+ * dm_test_to_connector_type_virtual - Test To connector type virtual
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_virtual(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_VIRTUAL, 0),
+ DRM_MODE_CONNECTOR_VIRTUAL);
+}
+
+/**
+ * dm_test_to_connector_type_unknown - Test To connector type unknown
+ * @test: The KUnit test context
+ */
+static void dm_test_to_connector_type_unknown(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_NONE, 0),
+ DRM_MODE_CONNECTOR_Unknown);
+}
+
+/* Tests for is_duplicate_mode() */
+
+/**
+ * dm_test_is_duplicate_mode_empty_list - Test Is duplicate mode empty list
+ * @test: The KUnit test context
+ */
+static void dm_test_is_duplicate_mode_empty_list(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_display_mode mode = {};
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector);
+
+ INIT_LIST_HEAD(&aconnector->base.probed_modes);
+ mode.hdisplay = 1920;
+ mode.vdisplay = 1080;
+
+ KUNIT_EXPECT_FALSE(test, is_duplicate_mode(aconnector, &mode));
+}
+
+/**
+ * dm_test_is_duplicate_mode_match - Test Is duplicate mode match
+ * @test: The KUnit test context
+ */
+static void dm_test_is_duplicate_mode_match(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_display_mode existing = {};
+ struct drm_display_mode candidate = {};
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector);
+
+ INIT_LIST_HEAD(&aconnector->base.probed_modes);
+ existing.hdisplay = 1920;
+ existing.vdisplay = 1080;
+ existing.clock = 148500;
+ list_add_tail(&existing.head, &aconnector->base.probed_modes);
+
+ candidate.hdisplay = 1920;
+ candidate.vdisplay = 1080;
+ candidate.clock = 148500;
+
+ KUNIT_EXPECT_TRUE(test, is_duplicate_mode(aconnector, &candidate));
+}
+
+/**
+ * dm_test_is_duplicate_mode_no_match - Test Is duplicate mode no match
+ * @test: The KUnit test context
+ */
+static void dm_test_is_duplicate_mode_no_match(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_display_mode existing = {};
+ struct drm_display_mode candidate = {};
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector);
+
+ INIT_LIST_HEAD(&aconnector->base.probed_modes);
+ existing.hdisplay = 1920;
+ existing.vdisplay = 1080;
+ existing.clock = 148500;
+ list_add_tail(&existing.head, &aconnector->base.probed_modes);
+
+ candidate.hdisplay = 2560;
+ candidate.vdisplay = 1440;
+ candidate.clock = 241500;
+
+ KUNIT_EXPECT_FALSE(test, is_duplicate_mode(aconnector, &candidate));
+}
+
+/**
+ * dm_test_is_duplicate_mode_same_size_different_clock - Test Is duplicate mode
+ * same size different clock
+ * @test: The KUnit test context
+ */
+static void dm_test_is_duplicate_mode_same_size_different_clock(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_display_mode existing = {};
+ struct drm_display_mode candidate = {};
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector);
+
+ INIT_LIST_HEAD(&aconnector->base.probed_modes);
+ existing.hdisplay = 1920;
+ existing.vdisplay = 1080;
+ existing.clock = 148500;
+ list_add_tail(&existing.head, &aconnector->base.probed_modes);
+
+ candidate.hdisplay = 1920;
+ candidate.vdisplay = 1080;
+ candidate.clock = 74250;
+
+ KUNIT_EXPECT_FALSE(test, is_duplicate_mode(aconnector, &candidate));
+}
+
+/* Tests for amdgpu_dm_get_encoder_crtc_mask() */
+
+/**
+ * dm_test_encoder_crtc_mask_1 - Test Encoder crtc mask 1
+ * @test: The KUnit test context
+ */
+static void dm_test_encoder_crtc_mask_1(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->mode_info.num_crtc = 1;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x1);
+}
+
+/**
+ * dm_test_encoder_crtc_mask_2 - Test Encoder crtc mask 2
+ * @test: The KUnit test context
+ */
+static void dm_test_encoder_crtc_mask_2(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->mode_info.num_crtc = 2;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x3);
+}
+
+/**
+ * dm_test_encoder_crtc_mask_3 - Test Encoder crtc mask 3
+ * @test: The KUnit test context
+ */
+static void dm_test_encoder_crtc_mask_3(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->mode_info.num_crtc = 3;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x7);
+}
+
+/**
+ * dm_test_encoder_crtc_mask_4 - Test Encoder crtc mask 4
+ * @test: The KUnit test context
+ */
+static void dm_test_encoder_crtc_mask_4(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->mode_info.num_crtc = 4;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0xf);
+}
+
+/**
+ * dm_test_encoder_crtc_mask_5 - Test Encoder crtc mask 5
+ * @test: The KUnit test context
+ */
+static void dm_test_encoder_crtc_mask_5(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->mode_info.num_crtc = 5;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x1f);
+}
+
+/**
+ * dm_test_encoder_crtc_mask_6 - Test Encoder crtc mask 6
+ * @test: The KUnit test context
+ */
+static void dm_test_encoder_crtc_mask_6(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->mode_info.num_crtc = 6;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x3f);
+}
+
+/**
+ * dm_test_encoder_crtc_mask_default - Test Encoder crtc mask default
+ * @test: The KUnit test context
+ */
+static void dm_test_encoder_crtc_mask_default(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ /* Values > 6 use the default case */
+ adev->mode_info.num_crtc = 8;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x3f);
+}
+
+/* Tests for get_aspect_ratio() */
+
+/**
+ * dm_test_aspect_ratio_no_data - Test Aspect ratio no data
+ * @test: The KUnit test context
+ */
+static void dm_test_aspect_ratio_no_data(struct kunit *test)
+{
+ struct drm_display_mode mode = {};
+
+ mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+ KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_NO_DATA);
+}
+
+/**
+ * dm_test_aspect_ratio_4_3 - Test Aspect ratio 4 3
+ * @test: The KUnit test context
+ */
+static void dm_test_aspect_ratio_4_3(struct kunit *test)
+{
+ struct drm_display_mode mode = {};
+
+ mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
+ KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_4_3);
+}
+
+/**
+ * dm_test_aspect_ratio_16_9 - Test Aspect ratio 16 9
+ * @test: The KUnit test context
+ */
+static void dm_test_aspect_ratio_16_9(struct kunit *test)
+{
+ struct drm_display_mode mode = {};
+
+ mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
+ KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_16_9);
+}
+
+/**
+ * dm_test_aspect_ratio_64_27 - Test Aspect ratio 64 27
+ * @test: The KUnit test context
+ */
+static void dm_test_aspect_ratio_64_27(struct kunit *test)
+{
+ struct drm_display_mode mode = {};
+
+ mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27;
+ KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_64_27);
+}
+
+/**
+ * dm_test_aspect_ratio_256_135 - Test Aspect ratio 256 135
+ * @test: The KUnit test context
+ */
+static void dm_test_aspect_ratio_256_135(struct kunit *test)
+{
+ struct drm_display_mode mode = {};
+
+ mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135;
+ KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_256_135);
+}
+
+/* Tests for decide_crtc_timing_for_drm_display_mode() */
+
+/**
+ * dm_test_decide_crtc_timing_scale_enabled - Test Decide crtc timing scale enabled
+ * @test: The KUnit test context
+ */
+static void dm_test_decide_crtc_timing_scale_enabled(struct kunit *test)
+{
+ struct drm_display_mode drm_mode = {};
+ struct drm_display_mode native_mode = {};
+
+ native_mode.crtc_clock = 148500;
+ native_mode.crtc_hdisplay = 1920;
+ native_mode.crtc_vdisplay = 1080;
+ native_mode.crtc_htotal = 2200;
+ native_mode.crtc_vtotal = 1125;
+ native_mode.crtc_hsync_start = 2008;
+ native_mode.crtc_hsync_end = 2052;
+ native_mode.crtc_vsync_start = 1084;
+ native_mode.crtc_vsync_end = 1089;
+
+ /* Different clock/htotal/vtotal, but scale_enabled forces copy */
+ drm_mode.clock = 74250;
+ drm_mode.htotal = 1650;
+ drm_mode.vtotal = 750;
+
+ decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, true);
+
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_clock, 148500);
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 1920);
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_vdisplay, 1080);
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_htotal, 2200);
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_vtotal, 1125);
+}
+
+/**
+ * dm_test_decide_crtc_timing_matching_mode - Test Decide crtc timing matching mode
+ * @test: The KUnit test context
+ */
+static void dm_test_decide_crtc_timing_matching_mode(struct kunit *test)
+{
+ struct drm_display_mode drm_mode = {};
+ struct drm_display_mode native_mode = {};
+
+ native_mode.clock = 148500;
+ native_mode.htotal = 2200;
+ native_mode.vtotal = 1125;
+ native_mode.crtc_clock = 148500;
+ native_mode.crtc_hdisplay = 1920;
+ native_mode.crtc_vdisplay = 1080;
+ native_mode.crtc_htotal = 2200;
+ native_mode.crtc_vtotal = 1125;
+
+ /* Matching clock/htotal/vtotal triggers copy */
+ drm_mode.clock = 148500;
+ drm_mode.htotal = 2200;
+ drm_mode.vtotal = 1125;
+
+ decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, false);
+
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_clock, 148500);
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 1920);
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_vtotal, 1125);
+}
+
+/**
+ * dm_test_decide_crtc_timing_no_copy - Test Decide crtc timing no copy
+ * @test: The KUnit test context
+ */
+static void dm_test_decide_crtc_timing_no_copy(struct kunit *test)
+{
+ struct drm_display_mode drm_mode = {};
+ struct drm_display_mode native_mode = {};
+
+ native_mode.clock = 148500;
+ native_mode.htotal = 2200;
+ native_mode.vtotal = 1125;
+ native_mode.crtc_clock = 148500;
+ native_mode.crtc_hdisplay = 1920;
+
+ /* Different timings, no scaling → no copy */
+ drm_mode.clock = 74250;
+ drm_mode.htotal = 1650;
+ drm_mode.vtotal = 750;
+
+ decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, false);
+
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_clock, 0);
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 0);
+}
+
+/**
+ * dm_test_decide_crtc_timing_no_crtc_clock - Test Decide crtc timing no crtc clock
+ * @test: The KUnit test context
+ */
+static void dm_test_decide_crtc_timing_no_crtc_clock(struct kunit *test)
+{
+ struct drm_display_mode drm_mode = {};
+ struct drm_display_mode native_mode = {};
+
+ /* Matching timings but native crtc_clock is 0 → no copy */
+ native_mode.clock = 148500;
+ native_mode.htotal = 2200;
+ native_mode.vtotal = 1125;
+ native_mode.crtc_clock = 0;
+ native_mode.crtc_hdisplay = 1920;
+
+ drm_mode.clock = 148500;
+ drm_mode.htotal = 2200;
+ drm_mode.vtotal = 1125;
+
+ decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, false);
+
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_clock, 0);
+ KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 0);
+}
+
+/* Tests for amdgpu_dm_connector_funcs_reset() */
+
+static const struct drm_connector_funcs dm_test_connector_funcs = {
+ .reset = amdgpu_dm_connector_funcs_reset,
+ .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/**
+ * dm_test_funcs_reset_sets_defaults - Test funcs_reset sets defaults
+ * @test: The KUnit test context
+ */
+static void dm_test_funcs_reset_sets_defaults(struct kunit *test)
+{
+ struct device *dev;
+ struct drm_device *drm;
+ struct drm_connector *connector;
+ struct dm_connector_state *dm_state;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+ sizeof(*drm), 0,
+ DRIVER_MODESET);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, connector);
+
+ drmm_connector_init(drm, connector, &dm_test_connector_funcs,
+ DRM_MODE_CONNECTOR_DisplayPort, NULL);
+
+ amdgpu_dm_connector_funcs_reset(connector);
+
+ KUNIT_ASSERT_NOT_NULL(test, connector->state);
+ dm_state = to_dm_connector_state(connector->state);
+ KUNIT_EXPECT_EQ(test, (int)dm_state->scaling, (int)RMX_OFF);
+ KUNIT_EXPECT_FALSE(test, dm_state->underscan_enable);
+ KUNIT_EXPECT_EQ(test, (int)dm_state->underscan_hborder, 0);
+ KUNIT_EXPECT_EQ(test, (int)dm_state->underscan_vborder, 0);
+ KUNIT_EXPECT_EQ(test, (int)dm_state->base.max_requested_bpc, 8);
+ KUNIT_EXPECT_EQ(test, dm_state->vcpi_slots, 0);
+ KUNIT_EXPECT_EQ(test, (int)dm_state->pbn, 0);
+}
+
+/**
+ * dm_test_funcs_reset_edp_abm_level - Test funcs_reset eDP sets ABM
+ * @test: The KUnit test context
+ */
+static void dm_test_funcs_reset_edp_abm_level(struct kunit *test)
+{
+ struct device *dev;
+ struct drm_device *drm;
+ struct drm_connector *connector;
+ struct dm_connector_state *dm_state;
+ int saved_abm_level = amdgpu_dm_get_abm_level_param();
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+ sizeof(*drm), 0,
+ DRIVER_MODESET);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, connector);
+
+ drmm_connector_init(drm, connector, &dm_test_connector_funcs,
+ DRM_MODE_CONNECTOR_eDP, NULL);
+
+ /* Test with abm_level > 0 */
+ amdgpu_dm_set_abm_level_param(3);
+ amdgpu_dm_connector_funcs_reset(connector);
+
+ KUNIT_ASSERT_NOT_NULL(test, connector->state);
+ dm_state = to_dm_connector_state(connector->state);
+ KUNIT_EXPECT_EQ(test, (int)dm_state->abm_level, 3);
+
+ amdgpu_dm_set_abm_level_param(saved_abm_level);
+}
+
+/**
+ * dm_test_funcs_reset_edp_abm_disabled - Test funcs_reset eDP ABM
+ * disabled
+ * @test: The KUnit test context
+ */
+static void dm_test_funcs_reset_edp_abm_disabled(struct kunit *test)
+{
+ struct device *dev;
+ struct drm_device *drm;
+ struct drm_connector *connector;
+ struct dm_connector_state *dm_state;
+ int saved_abm_level = amdgpu_dm_get_abm_level_param();
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+ sizeof(*drm), 0,
+ DRIVER_MODESET);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, connector);
+
+ drmm_connector_init(drm, connector, &dm_test_connector_funcs,
+ DRM_MODE_CONNECTOR_eDP, NULL);
+
+ /* Test with abm_level <= 0 → immediate disable */
+ amdgpu_dm_set_abm_level_param(-1);
+ amdgpu_dm_connector_funcs_reset(connector);
+
+ KUNIT_ASSERT_NOT_NULL(test, connector->state);
+ dm_state = to_dm_connector_state(connector->state);
+ KUNIT_EXPECT_EQ(test, (int)dm_state->abm_level,
+ (int)ABM_LEVEL_IMMEDIATE_DISABLE);
+
+ amdgpu_dm_set_abm_level_param(saved_abm_level);
+}
+
+/* Tests for amdgpu_dm_connector_atomic_duplicate_state() */
+
+/**
+ * dm_test_atomic_dup_state_copies_fields - Test atomic_duplicate copies
+ * fields
+ * @test: The KUnit test context
+ */
+static void dm_test_atomic_dup_state_copies_fields(struct kunit *test)
+{
+ struct device *dev;
+ struct drm_device *drm;
+ struct drm_connector *connector;
+ struct dm_connector_state *dm_state;
+ struct dm_connector_state *new_dm_state;
+ struct drm_connector_state *new_state;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+ sizeof(*drm), 0,
+ DRIVER_MODESET);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, connector);
+
+ drmm_connector_init(drm, connector, &dm_test_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA, NULL);
+
+ amdgpu_dm_connector_funcs_reset(connector);
+ KUNIT_ASSERT_NOT_NULL(test, connector->state);
+
+ /* Modify original state fields */
+ dm_state = to_dm_connector_state(connector->state);
+ dm_state->scaling = RMX_CENTER;
+ dm_state->underscan_enable = true;
+ dm_state->underscan_hborder = 10;
+ dm_state->underscan_vborder = 20;
+ dm_state->freesync_capable = true;
+ dm_state->abm_level = 2;
+ dm_state->vcpi_slots = 4;
+ dm_state->pbn = 1234;
+
+ /* Duplicate */
+ new_state = amdgpu_dm_connector_atomic_duplicate_state(connector);
+ KUNIT_ASSERT_NOT_NULL(test, new_state);
+ new_dm_state = to_dm_connector_state(new_state);
+
+ /* Verify all fields copied */
+ KUNIT_EXPECT_EQ(test, (int)new_dm_state->scaling, (int)RMX_CENTER);
+ KUNIT_EXPECT_TRUE(test, new_dm_state->underscan_enable);
+ KUNIT_EXPECT_EQ(test, (int)new_dm_state->underscan_hborder, 10);
+ KUNIT_EXPECT_EQ(test, (int)new_dm_state->underscan_vborder, 20);
+ KUNIT_EXPECT_TRUE(test, new_dm_state->freesync_capable);
+ KUNIT_EXPECT_EQ(test, (int)new_dm_state->abm_level, 2);
+ KUNIT_EXPECT_EQ(test, new_dm_state->vcpi_slots, 4);
+ KUNIT_EXPECT_EQ(test, (int)new_dm_state->pbn, 1234);
+
+ kfree(new_dm_state);
+}
+
+/* Tests for amdgpu_dm_fill_hdr_info_packet() */
+
+/**
+ * dm_test_fill_hdr_null_metadata - Test fill_hdr returns 0 with no
+ * metadata
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_hdr_null_metadata(struct kunit *test)
+{
+ struct drm_connector_state state = {};
+ struct dc_info_packet out = {};
+
+ /* No hdr_output_metadata → early return 0, out stays zeroed */
+ state.hdr_output_metadata = NULL;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_fill_hdr_info_packet(&state, &out), 0);
+ KUNIT_EXPECT_FALSE(test, out.valid);
+}
+
+/**
+ * dm_test_fill_hdr_zeroes_output - Test fill_hdr zeroes output with no
+ * metadata
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_hdr_zeroes_output(struct kunit *test)
+{
+ struct drm_connector_state state = {};
+ struct dc_info_packet out;
+
+ /* Pre-fill out with nonzero to verify memset(0) */
+ memset(&out, 0xAA, sizeof(out));
+
+ state.hdr_output_metadata = NULL;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_fill_hdr_info_packet(&state, &out), 0);
+ KUNIT_EXPECT_FALSE(test, out.valid);
+ KUNIT_EXPECT_EQ(test, (int)out.hb0, 0);
+ KUNIT_EXPECT_EQ(test, (int)out.hb1, 0);
+ KUNIT_EXPECT_EQ(test, (int)out.hb2, 0);
+ KUNIT_EXPECT_EQ(test, (int)out.hb3, 0);
+}
+
+/* Tests for amdgpu_dm_connector_atomic_set_property() */
+
+/*
+ * Build a connector wired to a kunit-allocated amdgpu_device so that
+ * drm_to_adev() resolves correctly, together with old/new dm states and
+ * the set of properties used by the get/set property handlers.
+ */
+struct dm_test_prop_ctx {
+ struct amdgpu_device *adev;
+ struct drm_connector *connector;
+ struct dm_connector_state *old_state;
+ struct dm_connector_state *new_state;
+ struct drm_property *scaling_prop;
+ struct drm_property *hborder_prop;
+ struct drm_property *vborder_prop;
+ struct drm_property *underscan_prop;
+ struct drm_property *abm_prop;
+};
+
+static struct dm_test_prop_ctx *dm_test_prop_ctx_alloc(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ ctx->adev = kunit_kzalloc(test, sizeof(*ctx->adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->adev);
+ ctx->connector = kunit_kzalloc(test, sizeof(*ctx->connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->connector);
+ ctx->old_state = kunit_kzalloc(test, sizeof(*ctx->old_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->old_state);
+ ctx->new_state = kunit_kzalloc(test, sizeof(*ctx->new_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->new_state);
+ ctx->scaling_prop = kunit_kzalloc(test, sizeof(*ctx->scaling_prop), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->scaling_prop);
+ ctx->hborder_prop = kunit_kzalloc(test, sizeof(*ctx->hborder_prop), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->hborder_prop);
+ ctx->vborder_prop = kunit_kzalloc(test, sizeof(*ctx->vborder_prop), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->vborder_prop);
+ ctx->underscan_prop = kunit_kzalloc(test, sizeof(*ctx->underscan_prop), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->underscan_prop);
+ ctx->abm_prop = kunit_kzalloc(test, sizeof(*ctx->abm_prop), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->abm_prop);
+
+ ctx->connector->dev = &ctx->adev->ddev;
+ ctx->connector->state = &ctx->old_state->base;
+
+ ctx->adev->ddev.mode_config.scaling_mode_property = ctx->scaling_prop;
+ ctx->adev->mode_info.underscan_hborder_property = ctx->hborder_prop;
+ ctx->adev->mode_info.underscan_vborder_property = ctx->vborder_prop;
+ ctx->adev->mode_info.underscan_property = ctx->underscan_prop;
+ ctx->adev->mode_info.abm_level_property = ctx->abm_prop;
+
+ return ctx;
+}
+
+/**
+ * dm_test_set_property_scaling_center - Test set scaling property to center
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_scaling_center(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, DRM_MODE_SCALE_CENTER), 0);
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_CENTER);
+}
+
+/**
+ * dm_test_set_property_scaling_aspect - Test set scaling property to aspect
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_scaling_aspect(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, DRM_MODE_SCALE_ASPECT), 0);
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_ASPECT);
+}
+
+/**
+ * dm_test_set_property_scaling_fullscreen - Test set scaling property to full
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_scaling_fullscreen(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, DRM_MODE_SCALE_FULLSCREEN), 0);
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_FULL);
+}
+
+/**
+ * dm_test_set_property_scaling_none - Test set scaling property to none
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_scaling_none(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ /* old scaling is RMX_CENTER so RMX_OFF is a real change */
+ ctx->old_state->scaling = RMX_CENTER;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, DRM_MODE_SCALE_NONE), 0);
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_OFF);
+}
+
+/**
+ * dm_test_set_property_scaling_unchanged - Test set scaling property unchanged
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_scaling_unchanged(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ /* old already RMX_OFF, requesting NONE/OFF returns 0 without write */
+ ctx->old_state->scaling = RMX_OFF;
+ ctx->new_state->scaling = RMX_CENTER;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, DRM_MODE_SCALE_NONE), 0);
+ /* new_state untouched because of early return */
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_CENTER);
+}
+
+/**
+ * dm_test_set_property_underscan_hborder - Test set underscan hborder
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_underscan_hborder(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->hborder_prop, 42), 0);
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->underscan_hborder, 42);
+}
+
+/**
+ * dm_test_set_property_underscan_vborder - Test set underscan vborder
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_underscan_vborder(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->vborder_prop, 24), 0);
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->underscan_vborder, 24);
+}
+
+/**
+ * dm_test_set_property_underscan_enable - Test set underscan enable
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_underscan_enable(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->underscan_prop, 1), 0);
+ KUNIT_EXPECT_TRUE(test, ctx->new_state->underscan_enable);
+}
+
+/**
+ * dm_test_set_property_abm_sysfs_control - Test set abm sysfs control
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_abm_sysfs_control(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ ctx->new_state->abm_sysfs_forbidden = true;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->abm_prop, ABM_SYSFS_CONTROL), 0);
+ KUNIT_EXPECT_FALSE(test, ctx->new_state->abm_sysfs_forbidden);
+}
+
+/**
+ * dm_test_set_property_abm_level_off - Test set abm level off
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_abm_level_off(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->abm_prop, ABM_LEVEL_OFF), 0);
+ KUNIT_EXPECT_TRUE(test, ctx->new_state->abm_sysfs_forbidden);
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->abm_level,
+ (int)ABM_LEVEL_IMMEDIATE_DISABLE);
+}
+
+/**
+ * dm_test_set_property_abm_level_value - Test set abm level to a value
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_abm_level_value(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->abm_prop, 3), 0);
+ KUNIT_EXPECT_TRUE(test, ctx->new_state->abm_sysfs_forbidden);
+ KUNIT_EXPECT_EQ(test, (int)ctx->new_state->abm_level, 3);
+}
+
+/**
+ * dm_test_set_property_unknown - Test set unknown property returns -EINVAL
+ * @test: The KUnit test context
+ */
+static void dm_test_set_property_unknown(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ struct drm_property *other;
+
+ other = kunit_kzalloc(test, sizeof(*other), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, other);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property(
+ ctx->connector, &ctx->new_state->base,
+ other, 0), -EINVAL);
+}
+
+/* Tests for amdgpu_dm_connector_atomic_get_property() */
+
+/**
+ * dm_test_get_property_scaling_center - Test get scaling property center
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_scaling_center(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ uint64_t val = 0;
+
+ ctx->new_state->scaling = RMX_CENTER;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, (int)DRM_MODE_SCALE_CENTER);
+}
+
+/**
+ * dm_test_get_property_scaling_aspect - Test get scaling property aspect
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_scaling_aspect(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ uint64_t val = 0;
+
+ ctx->new_state->scaling = RMX_ASPECT;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, (int)DRM_MODE_SCALE_ASPECT);
+}
+
+/**
+ * dm_test_get_property_scaling_full - Test get scaling property fullscreen
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_scaling_full(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ uint64_t val = 0;
+
+ ctx->new_state->scaling = RMX_FULL;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, (int)DRM_MODE_SCALE_FULLSCREEN);
+}
+
+/**
+ * dm_test_get_property_scaling_off - Test get scaling property off/none
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_scaling_off(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ uint64_t val = 0;
+
+ ctx->new_state->scaling = RMX_OFF;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->scaling_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, (int)DRM_MODE_SCALE_NONE);
+}
+
+/**
+ * dm_test_get_property_underscan_borders - Test get underscan borders/enable
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_underscan_borders(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ uint64_t val = 0;
+
+ ctx->new_state->underscan_hborder = 12;
+ ctx->new_state->underscan_vborder = 34;
+ ctx->new_state->underscan_enable = true;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->hborder_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, 12);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->vborder_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, 34);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->underscan_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, 1);
+}
+
+/**
+ * dm_test_get_property_abm_sysfs_allowed - Test get abm returns sysfs control
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_abm_sysfs_allowed(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ uint64_t val = 0;
+
+ ctx->new_state->abm_sysfs_forbidden = false;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->abm_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, (int)ABM_SYSFS_CONTROL);
+}
+
+/**
+ * dm_test_get_property_abm_level - Test get abm returns level when forbidden
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_abm_level(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ uint64_t val = 0;
+
+ ctx->new_state->abm_sysfs_forbidden = true;
+ ctx->new_state->abm_level = 2;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->abm_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, 2);
+}
+
+/**
+ * dm_test_get_property_abm_disabled_zero - Test get abm returns 0 when disabled
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_abm_disabled_zero(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ uint64_t val = 0xdead;
+
+ ctx->new_state->abm_sysfs_forbidden = true;
+ ctx->new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ ctx->abm_prop, &val), 0);
+ KUNIT_EXPECT_EQ(test, (int)val, 0);
+}
+
+/**
+ * dm_test_get_property_unknown - Test get unknown property returns -EINVAL
+ * @test: The KUnit test context
+ */
+static void dm_test_get_property_unknown(struct kunit *test)
+{
+ struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test);
+ struct drm_property *other;
+ uint64_t val = 0;
+
+ other = kunit_kzalloc(test, sizeof(*other), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, other);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property(
+ ctx->connector, &ctx->new_state->base,
+ other, &val), -EINVAL);
+}
+
+/* Tests for amdgpu_dm_get_highest_refresh_rate_mode() */
+
+/**
+ * dm_test_highest_refresh_writeback_null - Test writeback connector returns NULL
+ * @test: The KUnit test context
+ */
+static void dm_test_highest_refresh_writeback_null(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK;
+ KUNIT_EXPECT_NULL(test, amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false));
+}
+
+/**
+ * dm_test_highest_refresh_cached_base - Test cached freesync_vid_base is returned
+ * @test: The KUnit test context
+ */
+static void dm_test_highest_refresh_cached_base(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+ aconnector->freesync_vid_base.clock = 148500;
+
+ KUNIT_EXPECT_PTR_EQ(test, amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false),
+ &aconnector->freesync_vid_base);
+}
+
+/**
+ * dm_test_highest_refresh_preferred_mode - Test preferred mode is selected
+ * @test: The KUnit test context
+ */
+static void dm_test_highest_refresh_preferred_mode(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_display_mode *mode;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ mode = kunit_kzalloc(test, sizeof(*mode), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, mode);
+
+ aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+ INIT_LIST_HEAD(&aconnector->base.modes);
+
+ mode->type = DRM_MODE_TYPE_PREFERRED;
+ mode->clock = 148500;
+ mode->hdisplay = 1920;
+ mode->vdisplay = 1080;
+ mode->htotal = 2200;
+ mode->vtotal = 1125;
+ list_add_tail(&mode->head, &aconnector->base.modes);
+
+ KUNIT_EXPECT_PTR_EQ(test, amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false),
+ mode);
+}
+
+/* Tests for amdgpu_dm_is_freesync_video_mode() */
+
+/**
+ * dm_test_is_freesync_video_mode_null_mode - Test NULL mode returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_is_freesync_video_mode_null_mode(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+ aconnector->freesync_vid_base.clock = 148500;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_freesync_video_mode(NULL, aconnector));
+}
+
+/**
+ * dm_test_is_freesync_video_mode_match - Test matching mode returns true
+ * @test: The KUnit test context
+ */
+static void dm_test_is_freesync_video_mode_match(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_display_mode candidate = {};
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ /* Cached high mode acts as reference */
+ aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+ aconnector->freesync_vid_base.clock = 148500;
+ aconnector->freesync_vid_base.hdisplay = 1920;
+ aconnector->freesync_vid_base.vdisplay = 1080;
+ aconnector->freesync_vid_base.hsync_start = 2008;
+ aconnector->freesync_vid_base.hsync_end = 2052;
+ aconnector->freesync_vid_base.htotal = 2200;
+ aconnector->freesync_vid_base.vsync_start = 1084;
+ aconnector->freesync_vid_base.vsync_end = 1089;
+ aconnector->freesync_vid_base.vtotal = 1125;
+
+ candidate.clock = 148500;
+ candidate.hdisplay = 1920;
+ candidate.vdisplay = 1080;
+ candidate.hsync_start = 2008;
+ candidate.hsync_end = 2052;
+ candidate.htotal = 2200;
+ candidate.vsync_start = 1084;
+ candidate.vsync_end = 1089;
+ candidate.vtotal = 1125;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_freesync_video_mode(&candidate, aconnector));
+}
+
+/**
+ * dm_test_is_freesync_video_mode_no_match - Test mismatched mode returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_is_freesync_video_mode_no_match(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_display_mode candidate = {};
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA;
+ aconnector->freesync_vid_base.clock = 148500;
+ aconnector->freesync_vid_base.hdisplay = 1920;
+ aconnector->freesync_vid_base.vdisplay = 1080;
+ aconnector->freesync_vid_base.htotal = 2200;
+ aconnector->freesync_vid_base.vtotal = 1125;
+
+ /* Different resolution → not a freesync video mode */
+ candidate.clock = 148500;
+ candidate.hdisplay = 1280;
+ candidate.vdisplay = 720;
+ candidate.htotal = 1650;
+ candidate.vtotal = 750;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_freesync_video_mode(&candidate, aconnector));
+}
+
+static struct kunit_case amdgpu_dm_connector_tests[] = {
+ /* get_subconnector_type */
+ KUNIT_CASE(dm_test_subconnector_type_none),
+ KUNIT_CASE(dm_test_subconnector_type_vga),
+ KUNIT_CASE(dm_test_subconnector_type_dvi_converter),
+ KUNIT_CASE(dm_test_subconnector_type_dvi_dongle),
+ KUNIT_CASE(dm_test_subconnector_type_hdmi_converter),
+ KUNIT_CASE(dm_test_subconnector_type_hdmi_dongle),
+ KUNIT_CASE(dm_test_subconnector_type_mismatched),
+ KUNIT_CASE(dm_test_subconnector_type_default_unknown),
+ /* get_output_content_type */
+ KUNIT_CASE(dm_test_content_type_no_data),
+ KUNIT_CASE(dm_test_content_type_graphics),
+ KUNIT_CASE(dm_test_content_type_photo),
+ KUNIT_CASE(dm_test_content_type_cinema),
+ KUNIT_CASE(dm_test_content_type_game),
+ KUNIT_CASE(dm_test_content_type_unknown_defaults_no_data),
+ /* adjust_colour_depth_from_display_info */
+ KUNIT_CASE(dm_test_adjust_colour_depth_fits_at_888),
+ KUNIT_CASE(dm_test_adjust_colour_depth_reduces_to_888),
+ KUNIT_CASE(dm_test_adjust_colour_depth_10bpc_passes),
+ KUNIT_CASE(dm_test_adjust_colour_depth_420_halves_clk),
+ KUNIT_CASE(dm_test_adjust_colour_depth_reduces_12bpc_to_10bpc),
+ KUNIT_CASE(dm_test_adjust_colour_depth_16bpc_no_fallback),
+ KUNIT_CASE(dm_test_adjust_colour_depth_none_fits),
+ KUNIT_CASE(dm_test_adjust_colour_depth_invalid_depth),
+ /* amdgpu_dm_get_output_color_space */
+ KUNIT_CASE(dm_test_output_color_space_default_rgb_full),
+ KUNIT_CASE(dm_test_output_color_space_default_rgb_limited),
+ KUNIT_CASE(dm_test_output_color_space_default_ycbcr709),
+ KUNIT_CASE(dm_test_output_color_space_default_ycbcr601_limited),
+ KUNIT_CASE(dm_test_output_color_space_bt601_y_only),
+ KUNIT_CASE(dm_test_output_color_space_bt601),
+ KUNIT_CASE(dm_test_output_color_space_bt709),
+ KUNIT_CASE(dm_test_output_color_space_bt709_y_only),
+ KUNIT_CASE(dm_test_output_color_space_oprgb),
+ KUNIT_CASE(dm_test_output_color_space_bt2020_rgb),
+ KUNIT_CASE(dm_test_output_color_space_bt2020_ycc),
+ /* Tests for amdgpu_dm_convert_dc_color_depth_into_bpc */
+ KUNIT_CASE(dm_test_convert_color_depth_bpc_mappings),
+ KUNIT_CASE(dm_test_convert_color_depth_bpc_unknown),
+ /* amdgpu_dm_convert_color_depth_from_display_info */
+ KUNIT_CASE(dm_test_color_depth_from_info_bpc8),
+ KUNIT_CASE(dm_test_color_depth_from_info_bpc10),
+ KUNIT_CASE(dm_test_color_depth_from_info_zero_bpc_defaults_888),
+ KUNIT_CASE(dm_test_color_depth_from_info_requested_bpc_caps),
+ KUNIT_CASE(dm_test_color_depth_from_info_y420_default),
+ KUNIT_CASE(dm_test_color_depth_from_info_y420_10bpc),
+ KUNIT_CASE(dm_test_color_depth_from_info_y420_12bpc),
+ KUNIT_CASE(dm_test_color_depth_from_info_y420_16bpc),
+ KUNIT_CASE(dm_test_color_depth_from_info_requested_odd_bpc),
+ KUNIT_CASE(dm_test_color_depth_from_info_unsupported_bpc),
+ /* to_drm_connector_type */
+ KUNIT_CASE(dm_test_to_connector_type_hdmi),
+ KUNIT_CASE(dm_test_to_connector_type_edp),
+ KUNIT_CASE(dm_test_to_connector_type_lvds),
+ KUNIT_CASE(dm_test_to_connector_type_rgb),
+ KUNIT_CASE(dm_test_to_connector_type_dp),
+ KUNIT_CASE(dm_test_to_connector_type_dp_mst),
+ KUNIT_CASE(dm_test_to_connector_type_dvi_dvii),
+ KUNIT_CASE(dm_test_to_connector_type_dual_link_dvii),
+ KUNIT_CASE(dm_test_to_connector_type_dvi_dvid),
+ KUNIT_CASE(dm_test_to_connector_type_virtual),
+ KUNIT_CASE(dm_test_to_connector_type_unknown),
+ /* is_duplicate_mode */
+ KUNIT_CASE(dm_test_is_duplicate_mode_empty_list),
+ KUNIT_CASE(dm_test_is_duplicate_mode_match),
+ KUNIT_CASE(dm_test_is_duplicate_mode_no_match),
+ KUNIT_CASE(dm_test_is_duplicate_mode_same_size_different_clock),
+ /* amdgpu_dm_get_encoder_crtc_mask */
+ KUNIT_CASE(dm_test_encoder_crtc_mask_1),
+ KUNIT_CASE(dm_test_encoder_crtc_mask_2),
+ KUNIT_CASE(dm_test_encoder_crtc_mask_3),
+ KUNIT_CASE(dm_test_encoder_crtc_mask_4),
+ KUNIT_CASE(dm_test_encoder_crtc_mask_5),
+ KUNIT_CASE(dm_test_encoder_crtc_mask_6),
+ KUNIT_CASE(dm_test_encoder_crtc_mask_default),
+ /* get_aspect_ratio */
+ KUNIT_CASE(dm_test_aspect_ratio_no_data),
+ KUNIT_CASE(dm_test_aspect_ratio_4_3),
+ KUNIT_CASE(dm_test_aspect_ratio_16_9),
+ KUNIT_CASE(dm_test_aspect_ratio_64_27),
+ KUNIT_CASE(dm_test_aspect_ratio_256_135),
+ /* decide_crtc_timing_for_drm_display_mode */
+ KUNIT_CASE(dm_test_decide_crtc_timing_scale_enabled),
+ KUNIT_CASE(dm_test_decide_crtc_timing_matching_mode),
+ KUNIT_CASE(dm_test_decide_crtc_timing_no_copy),
+ KUNIT_CASE(dm_test_decide_crtc_timing_no_crtc_clock),
+ /* amdgpu_dm_connector_funcs_reset */
+ KUNIT_CASE(dm_test_funcs_reset_sets_defaults),
+ KUNIT_CASE(dm_test_funcs_reset_edp_abm_level),
+ KUNIT_CASE(dm_test_funcs_reset_edp_abm_disabled),
+ /* amdgpu_dm_connector_atomic_duplicate_state */
+ KUNIT_CASE(dm_test_atomic_dup_state_copies_fields),
+ /* amdgpu_dm_fill_hdr_info_packet */
+ KUNIT_CASE(dm_test_fill_hdr_null_metadata),
+ KUNIT_CASE(dm_test_fill_hdr_zeroes_output),
+ /* amdgpu_dm_connector_atomic_set_property */
+ KUNIT_CASE(dm_test_set_property_scaling_center),
+ KUNIT_CASE(dm_test_set_property_scaling_aspect),
+ KUNIT_CASE(dm_test_set_property_scaling_fullscreen),
+ KUNIT_CASE(dm_test_set_property_scaling_none),
+ KUNIT_CASE(dm_test_set_property_scaling_unchanged),
+ KUNIT_CASE(dm_test_set_property_underscan_hborder),
+ KUNIT_CASE(dm_test_set_property_underscan_vborder),
+ KUNIT_CASE(dm_test_set_property_underscan_enable),
+ KUNIT_CASE(dm_test_set_property_abm_sysfs_control),
+ KUNIT_CASE(dm_test_set_property_abm_level_off),
+ KUNIT_CASE(dm_test_set_property_abm_level_value),
+ KUNIT_CASE(dm_test_set_property_unknown),
+ /* amdgpu_dm_connector_atomic_get_property */
+ KUNIT_CASE(dm_test_get_property_scaling_center),
+ KUNIT_CASE(dm_test_get_property_scaling_aspect),
+ KUNIT_CASE(dm_test_get_property_scaling_full),
+ KUNIT_CASE(dm_test_get_property_scaling_off),
+ KUNIT_CASE(dm_test_get_property_underscan_borders),
+ KUNIT_CASE(dm_test_get_property_abm_sysfs_allowed),
+ KUNIT_CASE(dm_test_get_property_abm_level),
+ KUNIT_CASE(dm_test_get_property_abm_disabled_zero),
+ KUNIT_CASE(dm_test_get_property_unknown),
+ /* amdgpu_dm_get_highest_refresh_rate_mode */
+ KUNIT_CASE(dm_test_highest_refresh_writeback_null),
+ KUNIT_CASE(dm_test_highest_refresh_cached_base),
+ KUNIT_CASE(dm_test_highest_refresh_preferred_mode),
+ /* amdgpu_dm_is_freesync_video_mode */
+ KUNIT_CASE(dm_test_is_freesync_video_mode_null_mode),
+ KUNIT_CASE(dm_test_is_freesync_video_mode_match),
+ KUNIT_CASE(dm_test_is_freesync_video_mode_no_match),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_connector_test_suite = {
+ .name = "amdgpu_dm_connector",
+ .test_cases = amdgpu_dm_connector_tests,
+};
+
+kunit_test_suite(amdgpu_dm_connector_test_suite);
+
+MODULE_AUTHOR("AMD");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_connector");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c
index bba8b1a8fa1c..a6fd3a6fd803 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c
@@ -95,17 +95,139 @@ static void dm_test_is_valid_crc_source(struct kunit *test)
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
+/**
+ * dm_test_need_dp_aux() - Test dm_need_dp_aux().
+ * @test: KUnit test context.
+ *
+ * Verifies that dm_need_dp_aux() returns true when the transition starts or
+ * stops a DPRX CRC source (requiring the DP AUX handle), and false for
+ * non-DPRX transitions such as CRTC or NONE→NONE.
+ */
+static void dm_test_need_dp_aux(struct kunit *test)
+{
+ /* Starting a DPRX source always needs AUX, regardless of current source */
+ KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+ KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX,
+ AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
+ KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+
+ /* Stopping a DPRX source (NONE requested, DPRX was active) needs AUX */
+ KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
+ KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
+
+ /* CRTC transitions do not need AUX */
+ KUNIT_EXPECT_FALSE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+ KUNIT_EXPECT_FALSE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
+ KUNIT_EXPECT_FALSE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+}
+
+/**
+ * dm_test_crc_source_should_start_dprx() - Test dm_crc_source_should_start_dprx().
+ * @test: KUnit test context.
+ *
+ * Verifies that dm_crc_source_should_start_dprx() returns true only when CRC
+ * is transitioning from off (!enabled) to a DPRX source (enable &&
+ * is_dprx(source)), and false for all other combinations including
+ * already-enabled or non-DPRX targets.
+ */
+static void dm_test_crc_source_should_start_dprx(struct kunit *test)
+{
+ /* CRC off → DPRX: should start */
+ KUNIT_EXPECT_TRUE(test,
+ dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+ KUNIT_EXPECT_TRUE(test,
+ dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+
+ /* CRC already on (any source) → DPRX: should NOT start (already enabled) */
+ KUNIT_EXPECT_FALSE(test,
+ dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX,
+ AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
+ KUNIT_EXPECT_FALSE(test,
+ dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX,
+ AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
+
+ /* CRC off → CRTC: not a DPRX start */
+ KUNIT_EXPECT_FALSE(test,
+ dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+
+ /* Disabling: should not start */
+ KUNIT_EXPECT_FALSE(test,
+ dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
+}
+
+/**
+ * dm_test_crc_source_should_stop_dprx() - Test dm_crc_source_should_stop_dprx().
+ * @test: KUnit test context.
+ *
+ * Verifies that dm_crc_source_should_stop_dprx() returns true only when CRC
+ * is transitioning from a DPRX source (enabled && is_dprx(cur_crc_src)) to
+ * off (!enable), and false for non-DPRX disables, DPRX starts, and no-op
+ * transitions.
+ */
+static void dm_test_crc_source_should_stop_dprx(struct kunit *test)
+{
+ /* DPRX → off: should stop */
+ KUNIT_EXPECT_TRUE(test,
+ dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
+ KUNIT_EXPECT_TRUE(test,
+ dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
+
+ /* CRTC → off: not a DPRX stop */
+ KUNIT_EXPECT_FALSE(test,
+ dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
+
+ /* off → DPRX: not a stop */
+ KUNIT_EXPECT_FALSE(test,
+ dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+
+ /* DPRX → DPRX: no transition, not a stop */
+ KUNIT_EXPECT_FALSE(test,
+ dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX,
+ AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
+
+ /* off → off: not a stop */
+ KUNIT_EXPECT_FALSE(test,
+ dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE,
+ AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
+}
+
static struct kunit_case dm_crc_test_cases[] = {
+ /* dm_parse_crc_source() */
KUNIT_CASE(dm_test_parse_crc_source_none),
KUNIT_CASE(dm_test_parse_crc_source_crtc),
KUNIT_CASE(dm_test_parse_crc_source_dprx),
KUNIT_CASE(dm_test_parse_crc_source_crtc_dither),
KUNIT_CASE(dm_test_parse_crc_source_dprx_dither),
KUNIT_CASE(dm_test_parse_crc_source_invalid),
+ /* dm_is_crc_source_crtc() */
KUNIT_CASE(dm_test_is_crc_source_crtc),
+ /* dm_is_crc_source_dprx() */
KUNIT_CASE(dm_test_is_crc_source_dprx),
+ /* dm_need_crc_dither() */
KUNIT_CASE(dm_test_need_crc_dither),
+ /* amdgpu_dm_is_valid_crc_source() */
KUNIT_CASE(dm_test_is_valid_crc_source),
+ /* dm_need_dp_aux() */
+ KUNIT_CASE(dm_test_need_dp_aux),
+ /* dm_crc_source_should_start_dprx() */
+ KUNIT_CASE(dm_test_crc_source_should_start_dprx),
+ /* dm_crc_source_should_stop_dprx() */
+ KUNIT_CASE(dm_test_crc_source_should_stop_dprx),
{}
};
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c
new file mode 100644
index 000000000000..0edaf969f16b
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_crtc.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_kunit_helpers.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_crtc.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+#include "amdgpu_dm_irq_params.h"
+
+/* Tests for amdgpu_dm_crtc_modeset_required() */
+
+/**
+ * dm_test_crtc_modeset_required_active_mode_changed - Test Crtc modeset required active mode changed
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_modeset_required_active_mode_changed(struct kunit *test)
+{
+ struct drm_crtc_state state = {};
+
+ state.active = true;
+ state.mode_changed = true;
+
+ KUNIT_EXPECT_TRUE(test,
+ amdgpu_dm_crtc_modeset_required(&state, NULL, NULL));
+}
+
+/**
+ * dm_test_crtc_modeset_required_active_active_changed - Test Crtc modeset required active active changed
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_modeset_required_active_active_changed(struct kunit *test)
+{
+ struct drm_crtc_state state = {};
+
+ state.active = true;
+ state.active_changed = true;
+
+ KUNIT_EXPECT_TRUE(test,
+ amdgpu_dm_crtc_modeset_required(&state, NULL, NULL));
+}
+
+/**
+ * dm_test_crtc_modeset_required_active_connectors_changed - Test Crtc modeset required active connectors changed
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_modeset_required_active_connectors_changed(struct kunit *test)
+{
+ struct drm_crtc_state state = {};
+
+ state.active = true;
+ state.connectors_changed = true;
+
+ KUNIT_EXPECT_TRUE(test,
+ amdgpu_dm_crtc_modeset_required(&state, NULL, NULL));
+}
+
+/**
+ * dm_test_crtc_modeset_required_inactive - Test Crtc modeset required inactive
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_modeset_required_inactive(struct kunit *test)
+{
+ struct drm_crtc_state state = {};
+
+ state.active = false;
+ state.mode_changed = true;
+
+ KUNIT_EXPECT_FALSE(test,
+ amdgpu_dm_crtc_modeset_required(&state, NULL, NULL));
+}
+
+/**
+ * dm_test_crtc_modeset_required_no_changes - Test Crtc modeset required no changes
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_modeset_required_no_changes(struct kunit *test)
+{
+ struct drm_crtc_state state = {};
+
+ state.active = true;
+ state.mode_changed = false;
+ state.active_changed = false;
+ state.connectors_changed = false;
+
+ KUNIT_EXPECT_FALSE(test,
+ amdgpu_dm_crtc_modeset_required(&state, NULL, NULL));
+}
+
+/* Tests for amdgpu_dm_crtc_vrr_active_irq() */
+
+/**
+ * dm_test_crtc_vrr_active_irq_variable - Test Crtc vrr active irq variable
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_irq_variable(struct kunit *test)
+{
+ struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+
+ acrtc->dm_irq_params.freesync_config.state = VRR_STATE_ACTIVE_VARIABLE;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc));
+}
+
+/**
+ * dm_test_crtc_vrr_active_irq_fixed - Test Crtc vrr active irq fixed
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_irq_fixed(struct kunit *test)
+{
+ struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+
+ acrtc->dm_irq_params.freesync_config.state = VRR_STATE_ACTIVE_FIXED;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc));
+}
+
+/**
+ * dm_test_crtc_vrr_active_irq_inactive - Test Crtc vrr active irq inactive
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_irq_inactive(struct kunit *test)
+{
+ struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+
+ acrtc->dm_irq_params.freesync_config.state = VRR_STATE_INACTIVE;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc));
+}
+
+/**
+ * dm_test_crtc_vrr_active_irq_disabled - Test Crtc vrr active irq disabled
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_irq_disabled(struct kunit *test)
+{
+ struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+
+ acrtc->dm_irq_params.freesync_config.state = VRR_STATE_DISABLED;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc));
+}
+
+/**
+ * dm_test_crtc_vrr_active_irq_unsupported - Test Crtc vrr active irq unsupported
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_irq_unsupported(struct kunit *test)
+{
+ struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+
+ acrtc->dm_irq_params.freesync_config.state = VRR_STATE_UNSUPPORTED;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc));
+}
+
+/* Tests for amdgpu_dm_crtc_vrr_active() */
+
+/**
+ * dm_test_crtc_vrr_active_variable - Test Crtc vrr active variable
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_variable(struct kunit *test)
+{
+ struct dm_crtc_state *dm_state = kunit_kzalloc(test,
+ sizeof(*dm_state),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state);
+
+ dm_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_crtc_vrr_active(dm_state));
+}
+
+/**
+ * dm_test_crtc_vrr_active_fixed - Test Crtc vrr active fixed
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_fixed(struct kunit *test)
+{
+ struct dm_crtc_state *dm_state = kunit_kzalloc(test,
+ sizeof(*dm_state),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state);
+
+ dm_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_crtc_vrr_active(dm_state));
+}
+
+/**
+ * dm_test_crtc_vrr_active_inactive - Test Crtc vrr active inactive
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_inactive(struct kunit *test)
+{
+ struct dm_crtc_state *dm_state = kunit_kzalloc(test,
+ sizeof(*dm_state),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state);
+
+ dm_state->freesync_config.state = VRR_STATE_INACTIVE;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active(dm_state));
+}
+
+/**
+ * dm_test_crtc_vrr_active_disabled - Test Crtc vrr active disabled
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_disabled(struct kunit *test)
+{
+ struct dm_crtc_state *dm_state = kunit_kzalloc(test,
+ sizeof(*dm_state),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state);
+
+ dm_state->freesync_config.state = VRR_STATE_DISABLED;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active(dm_state));
+}
+
+/**
+ * dm_test_crtc_vrr_active_unsupported - Test Crtc vrr active unsupported
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_vrr_active_unsupported(struct kunit *test)
+{
+ struct dm_crtc_state *dm_state = kunit_kzalloc(test,
+ sizeof(*dm_state),
+ GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state);
+
+ dm_state->freesync_config.state = VRR_STATE_UNSUPPORTED;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active(dm_state));
+}
+
+/* Tests for amdgpu_dm_is_headless() */
+
+static void dm_test_add_connector(struct drm_device *dev,
+ struct drm_connector *connector,
+ int connector_type,
+ enum drm_connector_status status)
+{
+ INIT_LIST_HEAD(&connector->head);
+ kref_init(&connector->base.refcount);
+ connector->connector_type = connector_type;
+ connector->status = status;
+ list_add_tail(&connector->head, &dev->mode_config.connector_list);
+}
+
+/**
+ * dm_test_crtc_is_headless_null_adev - Test Crtc is headless null adev
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_is_headless_null_adev(struct kunit *test)
+{
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(NULL));
+}
+
+/**
+ * dm_test_crtc_is_headless_no_connectors - Test Crtc is headless no connectors
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_is_headless_no_connectors(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ spin_lock_init(&dev->mode_config.connector_list_lock);
+ adev->dm.ddev = dev;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(adev));
+}
+
+/**
+ * dm_test_crtc_is_headless_writeback_only - Test Crtc is headless writeback only
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_is_headless_writeback_only(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
+ struct drm_connector *wb = kunit_kzalloc(test, sizeof(*wb), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wb);
+
+ INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ spin_lock_init(&dev->mode_config.connector_list_lock);
+ adev->dm.ddev = dev;
+
+ dm_test_add_connector(dev, wb, DRM_MODE_CONNECTOR_WRITEBACK,
+ connector_status_connected);
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(adev));
+}
+
+/**
+ * dm_test_crtc_is_headless_disconnected_display - Test Crtc is headless disconnected display
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_is_headless_disconnected_display(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
+ struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display);
+
+ INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ spin_lock_init(&dev->mode_config.connector_list_lock);
+ adev->dm.ddev = dev;
+
+ dm_test_add_connector(dev, display, DRM_MODE_CONNECTOR_HDMIA,
+ connector_status_disconnected);
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(adev));
+}
+
+/**
+ * dm_test_crtc_is_headless_connected_display - Test Crtc is headless connected display
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_is_headless_connected_display(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
+ struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display);
+
+ INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ spin_lock_init(&dev->mode_config.connector_list_lock);
+ adev->dm.ddev = dev;
+
+ dm_test_add_connector(dev, display, DRM_MODE_CONNECTOR_HDMIA,
+ connector_status_connected);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_headless(adev));
+}
+
+/**
+ * dm_test_crtc_is_headless_mixed_connectors - Test headless skips WB and finds display
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_is_headless_mixed_connectors(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
+ struct drm_connector *wb = kunit_kzalloc(test, sizeof(*wb), GFP_KERNEL);
+ struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wb);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display);
+
+ INIT_LIST_HEAD(&dev->mode_config.connector_list);
+ spin_lock_init(&dev->mode_config.connector_list_lock);
+ adev->dm.ddev = dev;
+
+ dm_test_add_connector(dev, wb, DRM_MODE_CONNECTOR_WRITEBACK,
+ connector_status_connected);
+ dm_test_add_connector(dev, display, DRM_MODE_CONNECTOR_DisplayPort,
+ connector_status_connected);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_headless(adev));
+}
+
+/* Tests for amdgpu_dm_crtc_helper_mode_fixup() */
+
+/**
+ * dm_test_crtc_helper_mode_fixup_returns_true - Test mode_fixup accepts mode
+ * @test: The KUnit test context
+ */
+static void dm_test_crtc_helper_mode_fixup_returns_true(struct kunit *test)
+{
+ struct drm_display_mode mode = { 0 };
+ struct drm_display_mode adjusted_mode = { 0 };
+
+ KUNIT_EXPECT_TRUE(test,
+ amdgpu_dm_crtc_helper_mode_fixup(NULL, &mode, &adjusted_mode));
+}
+
+/* Tests for amdgpu_dm_crtc_set_vupdate_irq() */
+
+/**
+ * dm_test_crtc_set_vupdate_irq_no_otg - Test vupdate irq with unassigned OTG
+ * @test: The KUnit test context
+ *
+ * When the CRTC has no OTG instance assigned (otg_inst == -1) the function
+ * must return 0 immediately without touching the DC interrupt state.
+ */
+static void dm_test_crtc_set_vupdate_irq_no_otg(struct kunit *test)
+{
+ struct amdgpu_crtc *acrtc;
+ struct amdgpu_device *adev;
+
+ adev = dm_kunit_alloc_adev(test);
+
+ acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+
+ acrtc->base.dev = &adev->ddev;
+ acrtc->otg_inst = -1;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, true), 0);
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, false), 0);
+}
+
+/* Tests for idle_create_workqueue() */
+
+/**
+ * dm_test_idle_create_workqueue - Test idle workqueue creation
+ * @test: The KUnit test context
+ *
+ * Verify that idle_create_workqueue() allocates an idle workqueue tied to the
+ * device's display manager and initializes it in a disabled, non-running state.
+ */
+static void dm_test_idle_create_workqueue(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct idle_workqueue *idle_work;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ idle_work = idle_create_workqueue(adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, idle_work);
+
+ KUNIT_EXPECT_PTR_EQ(test, idle_work->dm, &adev->dm);
+ KUNIT_EXPECT_FALSE(test, idle_work->enable);
+ KUNIT_EXPECT_FALSE(test, idle_work->running);
+
+ kfree(idle_work);
+}
+
+static struct kunit_case amdgpu_dm_crtc_tests[] = {
+ /* amdgpu_dm_crtc_modeset_required */
+ KUNIT_CASE(dm_test_crtc_modeset_required_active_mode_changed),
+ KUNIT_CASE(dm_test_crtc_modeset_required_active_active_changed),
+ KUNIT_CASE(dm_test_crtc_modeset_required_active_connectors_changed),
+ KUNIT_CASE(dm_test_crtc_modeset_required_inactive),
+ KUNIT_CASE(dm_test_crtc_modeset_required_no_changes),
+ /* amdgpu_dm_crtc_vrr_active_irq */
+ KUNIT_CASE(dm_test_crtc_vrr_active_irq_variable),
+ KUNIT_CASE(dm_test_crtc_vrr_active_irq_fixed),
+ KUNIT_CASE(dm_test_crtc_vrr_active_irq_inactive),
+ KUNIT_CASE(dm_test_crtc_vrr_active_irq_disabled),
+ KUNIT_CASE(dm_test_crtc_vrr_active_irq_unsupported),
+ /* amdgpu_dm_crtc_vrr_active */
+ KUNIT_CASE(dm_test_crtc_vrr_active_variable),
+ KUNIT_CASE(dm_test_crtc_vrr_active_fixed),
+ KUNIT_CASE(dm_test_crtc_vrr_active_inactive),
+ KUNIT_CASE(dm_test_crtc_vrr_active_disabled),
+ KUNIT_CASE(dm_test_crtc_vrr_active_unsupported),
+ /* amdgpu_dm_is_headless */
+ KUNIT_CASE(dm_test_crtc_is_headless_null_adev),
+ KUNIT_CASE(dm_test_crtc_is_headless_no_connectors),
+ KUNIT_CASE(dm_test_crtc_is_headless_writeback_only),
+ KUNIT_CASE(dm_test_crtc_is_headless_disconnected_display),
+ KUNIT_CASE(dm_test_crtc_is_headless_connected_display),
+ KUNIT_CASE(dm_test_crtc_is_headless_mixed_connectors),
+ /* amdgpu_dm_crtc_helper_mode_fixup */
+ KUNIT_CASE(dm_test_crtc_helper_mode_fixup_returns_true),
+ /* amdgpu_dm_crtc_set_vupdate_irq */
+ KUNIT_CASE(dm_test_crtc_set_vupdate_irq_no_otg),
+ /* idle_create_workqueue */
+ KUNIT_CASE(dm_test_idle_create_workqueue),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_crtc_test_suite = {
+ .name = "amdgpu_dm_crtc",
+ .test_cases = amdgpu_dm_crtc_tests,
+};
+
+kunit_test_suite(amdgpu_dm_crtc_test_suite);
+
+MODULE_AUTHOR("AMD");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_crtc");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
new file mode 100644
index 000000000000..bf90ccfbf431
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
@@ -0,0 +1,582 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_dmub.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include "dc.h"
+#include "dc/inc/core_types.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "dmub/dmub_srv.h"
+#include "amdgpu_dm_dmub.h"
+
+/* Tests for dm_register_dmub_notify_callback() */
+
+static void dummy_callback(struct amdgpu_device *adev,
+ struct dmub_notification *notify)
+{
+}
+
+/**
+ * dm_test_register_dmub_notify_callback_null_callback - Test null callback is rejected
+ * @test: The KUnit test context
+ */
+static void dm_test_register_dmub_notify_callback_null_callback(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ KUNIT_EXPECT_FALSE(test, dm_register_dmub_notify_callback(adev,
+ DMUB_NOTIFICATION_AUX_REPLY, NULL, false));
+}
+
+/**
+ * dm_test_register_dmub_notify_callback_type_out_of_range - Test out-of-range type is rejected
+ * @test: The KUnit test context
+ */
+static void dm_test_register_dmub_notify_callback_type_out_of_range(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ KUNIT_EXPECT_FALSE(test, dm_register_dmub_notify_callback(adev,
+ AMDGPU_DMUB_NOTIFICATION_MAX, dummy_callback, false));
+}
+
+/**
+ * dm_test_register_dmub_notify_callback_valid - Test Register dmub notify callback valid
+ * @test: The KUnit test context
+ */
+static void dm_test_register_dmub_notify_callback_valid(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ KUNIT_EXPECT_TRUE(test, dm_register_dmub_notify_callback(adev,
+ DMUB_NOTIFICATION_AUX_REPLY, dummy_callback, true));
+
+ KUNIT_EXPECT_TRUE(test,
+ adev->dm.dmub_callback[DMUB_NOTIFICATION_AUX_REPLY] == dummy_callback);
+ KUNIT_EXPECT_TRUE(test,
+ adev->dm.dmub_thread_offload[DMUB_NOTIFICATION_AUX_REPLY]);
+}
+
+/**
+ * dm_test_register_dmub_notify_callback_offload_false - Test registration with offload disabled
+ * @test: The KUnit test context
+ */
+static void dm_test_register_dmub_notify_callback_offload_false(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ KUNIT_EXPECT_TRUE(test, dm_register_dmub_notify_callback(adev,
+ DMUB_NOTIFICATION_HPD, dummy_callback, false));
+
+ KUNIT_EXPECT_TRUE(test,
+ adev->dm.dmub_callback[DMUB_NOTIFICATION_HPD] == dummy_callback);
+ KUNIT_EXPECT_FALSE(test,
+ adev->dm.dmub_thread_offload[DMUB_NOTIFICATION_HPD]);
+}
+
+/* Tests for dm_dmub_aux_setconfig_callback() */
+
+/**
+ * dm_test_dmub_aux_setconfig_callback_copies_and_completes - Test copy and complete on AUX reply
+ * @test: The KUnit test context
+ */
+static void dm_test_dmub_aux_setconfig_callback_copies_and_completes(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dmub_notification *dm_notify;
+ struct dmub_notification notify = {};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ dm_notify = kunit_kzalloc(test, sizeof(*dm_notify), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_notify);
+
+ init_completion(&adev->dm.dmub_aux_transfer_done);
+ adev->dm.dmub_notify = dm_notify;
+
+ notify.type = DMUB_NOTIFICATION_AUX_REPLY;
+ notify.result = AUX_RET_SUCCESS;
+ notify.aux_reply.command = 0xA5;
+ notify.aux_reply.length = 3;
+ notify.aux_reply.data[0] = 0x11;
+ notify.aux_reply.data[1] = 0x22;
+ notify.aux_reply.data[2] = 0x33;
+
+ dm_dmub_aux_setconfig_callback(adev, &notify);
+
+ KUNIT_EXPECT_EQ(test, dm_notify->type, notify.type);
+ KUNIT_EXPECT_EQ(test, dm_notify->result, notify.result);
+ KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.command, notify.aux_reply.command);
+ KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.length, notify.aux_reply.length);
+ KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.data[0], notify.aux_reply.data[0]);
+ KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.data[1], notify.aux_reply.data[1]);
+ KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.data[2], notify.aux_reply.data[2]);
+ KUNIT_EXPECT_TRUE(test, completion_done(&adev->dm.dmub_aux_transfer_done));
+}
+
+/**
+ * dm_test_dmub_aux_setconfig_callback_non_aux_no_complete - Test non-AUX type skips completion
+ * @test: The KUnit test context
+ */
+static void dm_test_dmub_aux_setconfig_callback_non_aux_no_complete(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dmub_notification *dm_notify;
+ struct dmub_notification notify = {};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ dm_notify = kunit_kzalloc(test, sizeof(*dm_notify), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_notify);
+
+ init_completion(&adev->dm.dmub_aux_transfer_done);
+ adev->dm.dmub_notify = dm_notify;
+
+ notify.type = DMUB_NOTIFICATION_HPD;
+ notify.result = AUX_RET_ERROR_TIMEOUT;
+
+ dm_dmub_aux_setconfig_callback(adev, &notify);
+
+ KUNIT_EXPECT_EQ(test, dm_notify->type, notify.type);
+ KUNIT_EXPECT_FALSE(test, completion_done(&adev->dm.dmub_aux_transfer_done));
+}
+
+/**
+ * dm_test_dmub_aux_setconfig_callback_aux_with_null_dm_notify - Test AUX with NULL dm_notify
+ * @test: The KUnit test context
+ */
+static void dm_test_dmub_aux_setconfig_callback_aux_with_null_dm_notify(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dmub_notification notify = {};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ init_completion(&adev->dm.dmub_aux_transfer_done);
+ adev->dm.dmub_notify = NULL;
+
+ notify.type = DMUB_NOTIFICATION_AUX_REPLY;
+
+ dm_dmub_aux_setconfig_callback(adev, &notify);
+
+ KUNIT_EXPECT_TRUE(test, completion_done(&adev->dm.dmub_aux_transfer_done));
+}
+
+/**
+ * dm_test_dmub_aux_setconfig_callback_set_config_reply - Test SET_CONFIG reply copies status
+ * @test: The KUnit test context
+ */
+static void dm_test_dmub_aux_setconfig_callback_set_config_reply(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dmub_notification *dm_notify;
+ struct dmub_notification notify = {};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ dm_notify = kunit_kzalloc(test, sizeof(*dm_notify), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_notify);
+
+ init_completion(&adev->dm.dmub_aux_transfer_done);
+ adev->dm.dmub_notify = dm_notify;
+
+ notify.type = DMUB_NOTIFICATION_SET_CONFIG_REPLY;
+ notify.sc_status = SET_CONFIG_RX_TIMEOUT;
+
+ dm_dmub_aux_setconfig_callback(adev, &notify);
+
+ KUNIT_EXPECT_EQ(test, dm_notify->type, notify.type);
+ KUNIT_EXPECT_EQ(test, dm_notify->sc_status, notify.sc_status);
+ KUNIT_EXPECT_FALSE(test, completion_done(&adev->dm.dmub_aux_transfer_done));
+}
+
+/* Tests for dm_dmub_aux_fused_io_callback() */
+
+/**
+ * dm_test_dmub_aux_fused_io_callback_copies_reply_and_completes - Test copy and complete
+ * @test: The KUnit test context
+ */
+static void dm_test_dmub_aux_fused_io_callback_copies_reply_and_completes(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dmub_notification notify = {};
+ struct dmub_cmd_fused_request *reply;
+ u32 reply_ddc_line;
+ u32 notify_ddc_line;
+ u32 reply_address;
+ u32 notify_address;
+ u32 reply_length;
+ u32 notify_length;
+ uint8_t ddc_line = 2;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ init_completion(&adev->dm.fused_io[ddc_line].replied);
+
+ notify.fused_request.identifier = 0x34;
+ notify.fused_request.status = FUSED_REQUEST_STATUS_SUCCESS;
+ notify.fused_request.u.aux.ddc_line = ddc_line;
+ notify.fused_request.u.aux.address = 0x50;
+ notify.fused_request.u.aux.length = 4;
+
+ dm_dmub_aux_fused_io_callback(adev, &notify);
+
+ KUNIT_EXPECT_TRUE(test, completion_done(&adev->dm.fused_io[ddc_line].replied));
+
+ reply = (struct dmub_cmd_fused_request *)adev->dm.fused_io[ddc_line].reply_data;
+ reply_ddc_line = reply->u.aux.ddc_line;
+ notify_ddc_line = notify.fused_request.u.aux.ddc_line;
+ reply_address = reply->u.aux.address;
+ notify_address = notify.fused_request.u.aux.address;
+ reply_length = reply->u.aux.length;
+ notify_length = notify.fused_request.u.aux.length;
+
+ KUNIT_EXPECT_EQ(test, reply->identifier, notify.fused_request.identifier);
+ KUNIT_EXPECT_EQ(test, reply->status, notify.fused_request.status);
+ KUNIT_EXPECT_EQ(test, reply_ddc_line, notify_ddc_line);
+ KUNIT_EXPECT_EQ(test, reply_address, notify_address);
+ KUNIT_EXPECT_EQ(test, reply_length, notify_length);
+}
+
+/**
+ * dm_test_dmub_aux_fused_io_callback_max_ddc_line - Test Dmub aux fused io callback max ddc line
+ * @test: The KUnit test context
+ */
+static void dm_test_dmub_aux_fused_io_callback_max_ddc_line(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dmub_notification notify = {};
+ struct dmub_cmd_fused_request *reply;
+ u32 reply_ddc_line;
+ u32 notify_ddc_line;
+ uint8_t ddc_line;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ ddc_line = ARRAY_SIZE(adev->dm.fused_io) - 1;
+ init_completion(&adev->dm.fused_io[ddc_line].replied);
+
+ notify.fused_request.identifier = 0x56;
+ notify.fused_request.status = FUSED_REQUEST_STATUS_SUCCESS;
+ notify.fused_request.u.aux.ddc_line = ddc_line;
+ notify.fused_request.u.aux.address = 0x50;
+ notify.fused_request.u.aux.length = 1;
+
+ dm_dmub_aux_fused_io_callback(adev, &notify);
+
+ KUNIT_EXPECT_TRUE(test, completion_done(&adev->dm.fused_io[ddc_line].replied));
+
+ reply = (struct dmub_cmd_fused_request *)adev->dm.fused_io[ddc_line].reply_data;
+ reply_ddc_line = reply->u.aux.ddc_line;
+ notify_ddc_line = notify.fused_request.u.aux.ddc_line;
+
+ KUNIT_EXPECT_EQ(test, reply->identifier, notify.fused_request.identifier);
+ KUNIT_EXPECT_EQ(test, reply_ddc_line, notify_ddc_line);
+}
+
+/* Tests for dm_get_default_ips_mode() */
+
+/**
+ * dm_test_get_default_ips_mode_dcn35 - Test Get default ips mode dcn35
+ * @test: The KUnit test context
+ */
+static void dm_test_get_default_ips_mode_dcn35(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 0);
+
+ KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev),
+ DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF);
+}
+
+/**
+ * dm_test_get_default_ips_mode_dcn351 - Test Get default ips mode dcn351
+ * @test: The KUnit test context
+ */
+static void dm_test_get_default_ips_mode_dcn351(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 1);
+
+ KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev),
+ DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF);
+}
+
+/**
+ * dm_test_get_default_ips_mode_dcn36 - Test Get default ips mode dcn36
+ * @test: The KUnit test context
+ */
+static void dm_test_get_default_ips_mode_dcn36(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 6, 0);
+
+ KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev),
+ DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF);
+}
+
+/**
+ * dm_test_get_default_ips_mode_older_than_dcn35 - Test Get default ips mode older than dcn35
+ * @test: The KUnit test context
+ */
+static void dm_test_get_default_ips_mode_older_than_dcn35(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 2, 0);
+
+ KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev),
+ DMUB_IPS_DISABLE_ALL);
+}
+
+/**
+ * dm_test_get_default_ips_mode_newer_default - Test Get default ips mode newer default
+ * @test: The KUnit test context
+ */
+static void dm_test_get_default_ips_mode_newer_default(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ /* DCN 4.0.1 is >= 3.5 but has no explicit case, returns ENABLE */
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(4, 0, 1);
+
+ KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev),
+ DMUB_IPS_ENABLE);
+}
+
+/* Tests for dm_dmub_hw_init() */
+
+/*
+ * Build an amdgpu_device with the minimal dc/res_pool pointers that
+ * dm_dmub_hw_init() and dm_dmub_hw_resume() dereference before their
+ * early-return checks.
+ */
+static struct amdgpu_device *dm_test_alloc_adev_with_dc(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct resource_pool *res_pool;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
+
+ res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res_pool);
+
+ dc->res_pool = res_pool;
+ adev->dm.dc = dc;
+
+ return adev;
+}
+
+/**
+ * dm_test_dmub_hw_init_no_dmub_srv - Test hw init returns 0 when DMUB unsupported
+ * @test: The KUnit test context
+ *
+ * When adev->dm.dmub_srv is NULL the ASIC does not support DMUB and
+ * dm_dmub_hw_init() should return 0 without touching the hardware.
+ */
+static void dm_test_dmub_hw_init_no_dmub_srv(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test);
+
+ adev->dm.dmub_srv = NULL;
+
+ KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+}
+
+/**
+ * dm_test_dmub_hw_init_no_fb_info - Test hw init fails without framebuffer info
+ * @test: The KUnit test context
+ *
+ * With a DMUB service present but no framebuffer info, dm_dmub_hw_init()
+ * should return -EINVAL.
+ */
+static void dm_test_dmub_hw_init_no_fb_info(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test);
+ struct dmub_srv *dmub_srv;
+
+ dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv);
+
+ adev->dm.dmub_srv = dmub_srv;
+ adev->dm.dmub_fb_info = NULL;
+
+ KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), -EINVAL);
+}
+
+/**
+ * dm_test_dmub_hw_init_no_firmware - Test hw init fails without firmware
+ * @test: The KUnit test context
+ *
+ * With a DMUB service and framebuffer info present but no firmware,
+ * dm_dmub_hw_init() should return -EINVAL.
+ */
+static void dm_test_dmub_hw_init_no_firmware(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test);
+ struct dmub_srv *dmub_srv;
+ struct dmub_srv_fb_info *fb_info;
+
+ dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv);
+
+ fb_info = kunit_kzalloc(test, sizeof(*fb_info), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fb_info);
+
+ adev->dm.dmub_srv = dmub_srv;
+ adev->dm.dmub_fb_info = fb_info;
+ adev->dm.dmub_fw = NULL;
+
+ KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), -EINVAL);
+}
+
+/* Tests for dm_dmub_hw_resume() */
+
+/**
+ * dm_test_dmub_hw_resume_no_dmub_srv - Test hw resume is a no-op when DMUB unsupported
+ * @test: The KUnit test context
+ *
+ * When adev->dm.dmub_srv is NULL, dm_dmub_hw_resume() should return early
+ * without dereferencing the (absent) DMUB service.
+ */
+static void dm_test_dmub_hw_resume_no_dmub_srv(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test);
+
+ adev->dm.dmub_srv = NULL;
+
+ /* Must not crash. */
+ dm_dmub_hw_resume(adev);
+}
+
+/* Tests for dm_dmub_sw_init() */
+
+/**
+ * dm_test_dmub_sw_init_unsupported_asic - Test sw init returns 0 for unsupported ASIC
+ * @test: The KUnit test context
+ *
+ * For an IP version with no DMUB support, dm_dmub_sw_init() should return 0
+ * before attempting to access the firmware.
+ */
+static void dm_test_dmub_sw_init_unsupported_asic(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(1, 0, 0);
+
+ KUNIT_EXPECT_EQ(test, dm_dmub_sw_init(adev), 0);
+}
+
+/* Tests for dm_init_microcode() */
+
+/**
+ * dm_test_init_microcode_unsupported_asic - Test microcode init returns 0 for unsupported ASIC
+ * @test: The KUnit test context
+ *
+ * For an IP version with no DMUB support, dm_init_microcode() should return 0
+ * without requesting any firmware.
+ */
+static void dm_test_init_microcode_unsupported_asic(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(1, 0, 0);
+
+ KUNIT_EXPECT_EQ(test, dm_init_microcode(adev), 0);
+}
+
+static struct kunit_case amdgpu_dm_dmub_tests[] = {
+ /* dm_register_dmub_notify_callback() */
+ KUNIT_CASE(dm_test_register_dmub_notify_callback_null_callback),
+ KUNIT_CASE(dm_test_register_dmub_notify_callback_type_out_of_range),
+ KUNIT_CASE(dm_test_register_dmub_notify_callback_valid),
+ KUNIT_CASE(dm_test_register_dmub_notify_callback_offload_false),
+ /* dm_dmub_aux_setconfig_callback() */
+ KUNIT_CASE(dm_test_dmub_aux_setconfig_callback_copies_and_completes),
+ KUNIT_CASE(dm_test_dmub_aux_setconfig_callback_non_aux_no_complete),
+ KUNIT_CASE(dm_test_dmub_aux_setconfig_callback_aux_with_null_dm_notify),
+ KUNIT_CASE(dm_test_dmub_aux_setconfig_callback_set_config_reply),
+ /* dm_dmub_aux_fused_io_callback() */
+ KUNIT_CASE(dm_test_dmub_aux_fused_io_callback_copies_reply_and_completes),
+ KUNIT_CASE(dm_test_dmub_aux_fused_io_callback_max_ddc_line),
+ /* dm_get_default_ips_mode() */
+ KUNIT_CASE(dm_test_get_default_ips_mode_dcn35),
+ KUNIT_CASE(dm_test_get_default_ips_mode_dcn351),
+ KUNIT_CASE(dm_test_get_default_ips_mode_dcn36),
+ KUNIT_CASE(dm_test_get_default_ips_mode_older_than_dcn35),
+ KUNIT_CASE(dm_test_get_default_ips_mode_newer_default),
+ /* dm_dmub_hw_init() */
+ KUNIT_CASE(dm_test_dmub_hw_init_no_dmub_srv),
+ KUNIT_CASE(dm_test_dmub_hw_init_no_fb_info),
+ KUNIT_CASE(dm_test_dmub_hw_init_no_firmware),
+ /* dm_dmub_hw_resume() */
+ KUNIT_CASE(dm_test_dmub_hw_resume_no_dmub_srv),
+ /* dm_dmub_sw_init() */
+ KUNIT_CASE(dm_test_dmub_sw_init_unsupported_asic),
+ /* dm_init_microcode() */
+ KUNIT_CASE(dm_test_init_microcode_unsupported_asic),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_dmub_test_suite = {
+ .name = "amdgpu_dm_dmub",
+ .test_cases = amdgpu_dm_dmub_tests,
+};
+
+kunit_test_suite(amdgpu_dm_dmub_test_suite);
+
+MODULE_AUTHOR("AMD");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_dmub");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c
index d03b606d27bc..619b4a80c82b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c
@@ -12,11 +12,241 @@
static void dummy_work_fn(struct work_struct *work) {}
+/* Tests for hdcp_get_content_protection_from_status() */
+
+/**
+ * dm_test_hdcp_get_cp_disabled_returns_desired - HDCP off maps to DESIRED
+ * @test: KUnit test context
+ *
+ * When encryption status is HDCP_OFF, content_protection should be set
+ * to DESIRED and the function should return true to indicate an update.
+ */
+static void dm_test_hdcp_get_cp_disabled_returns_desired(struct kunit *test)
+{
+ unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+ bool update;
+
+ update = hdcp_get_content_protection_from_status(
+ DRM_MODE_HDCP_CONTENT_TYPE0,
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF,
+ &content_protection);
+
+ KUNIT_EXPECT_TRUE(test, update);
+ KUNIT_EXPECT_EQ(test, content_protection,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED);
+}
+
+/**
+ * dm_test_hdcp_get_cp_type0_returns_enabled - TYPE0 with TYPE0_ON maps to ENABLED
+ * @test: KUnit test context
+ *
+ * When content type is TYPE0 and encryption status is at or below
+ * HDCP2_TYPE0_ON, content_protection should be set to ENABLED.
+ */
+static void dm_test_hdcp_get_cp_type0_returns_enabled(struct kunit *test)
+{
+ unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+ bool update;
+
+ update = hdcp_get_content_protection_from_status(
+ DRM_MODE_HDCP_CONTENT_TYPE0,
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON,
+ &content_protection);
+
+ KUNIT_EXPECT_TRUE(test, update);
+ KUNIT_EXPECT_EQ(test, content_protection,
+ DRM_MODE_CONTENT_PROTECTION_ENABLED);
+}
+
+/**
+ * dm_test_hdcp_get_cp_type1_returns_enabled - TYPE1 with TYPE1_ON maps to ENABLED
+ * @test: KUnit test context
+ *
+ * When content type is TYPE1 and encryption status is exactly
+ * HDCP2_TYPE1_ON, content_protection should be set to ENABLED.
+ */
+static void dm_test_hdcp_get_cp_type1_returns_enabled(struct kunit *test)
+{
+ unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+ bool update;
+
+ update = hdcp_get_content_protection_from_status(
+ DRM_MODE_HDCP_CONTENT_TYPE1,
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON,
+ &content_protection);
+
+ KUNIT_EXPECT_TRUE(test, update);
+ KUNIT_EXPECT_EQ(test, content_protection,
+ DRM_MODE_CONTENT_PROTECTION_ENABLED);
+}
+
+/**
+ * dm_test_hdcp_get_cp_type1_rejects_type0_status - TYPE1 rejects TYPE0_ON
+ * @test: KUnit test context
+ *
+ * When content type is TYPE1 but encryption status is only TYPE0_ON,
+ * the function should return false and leave content_protection unchanged.
+ */
+static void dm_test_hdcp_get_cp_type1_rejects_type0_status(struct kunit *test)
+{
+ unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+ bool update;
+
+ update = hdcp_get_content_protection_from_status(
+ DRM_MODE_HDCP_CONTENT_TYPE1,
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON,
+ &content_protection);
+
+ KUNIT_EXPECT_FALSE(test, update);
+ KUNIT_EXPECT_EQ(test, content_protection,
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED);
+}
+
+/**
+ * dm_test_hdcp_get_cp_type0_rejects_type1_status - TYPE0 rejects TYPE1_ON
+ * @test: KUnit test context
+ *
+ * When content type is TYPE0 but encryption status exceeds the TYPE0_ON
+ * boundary (TYPE1_ON), the function should return false.
+ */
+static void dm_test_hdcp_get_cp_type0_rejects_type1_status(struct kunit *test)
+{
+ unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
+ bool update;
+
+ update = hdcp_get_content_protection_from_status(
+ DRM_MODE_HDCP_CONTENT_TYPE0,
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON,
+ &content_protection);
+
+ KUNIT_EXPECT_FALSE(test, update);
+ KUNIT_EXPECT_EQ(test, content_protection,
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED);
+}
+
+/* Tests for hdcp_get_link_display_adjustments() */
+
+/**
+ * dm_test_hdcp_get_adjustments_disable_authentication - disable path zeroes adjustments
+ * @test: KUnit test context
+ *
+ * When enable_encryption is false, display_adjust should disable
+ * authentication and all link_adjust fields should remain zeroed.
+ */
+static void dm_test_hdcp_get_adjustments_disable_authentication(struct kunit *test)
+{
+ struct mod_hdcp_link_adjustment link_adjust;
+ struct mod_hdcp_display_adjustment display_adjust;
+ unsigned int disable;
+ unsigned int hdcp1_disable;
+ unsigned int force_type;
+
+ hdcp_get_link_display_adjustments(false, DRM_MODE_HDCP_CONTENT_TYPE0,
+ false, false, false, &link_adjust, &display_adjust);
+ disable = display_adjust.disable;
+ hdcp1_disable = link_adjust.hdcp1.disable;
+ force_type = link_adjust.hdcp2.force_type;
+
+ KUNIT_EXPECT_EQ(test, disable,
+ MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION);
+ KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 0);
+ KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, 0);
+ KUNIT_EXPECT_EQ(test, hdcp1_disable, 0);
+ KUNIT_EXPECT_EQ(test, force_type, 0);
+}
+
+/**
+ * dm_test_hdcp_get_adjustments_type0_policy - TYPE0 enables HDCP1 and forces TYPE0
+ * @test: KUnit test context
+ *
+ * When encryption is enabled with content TYPE0, hdcp1 should remain
+ * enabled, force_type should be TYPE_0, and sw_locality_fallback should
+ * be propagated from the input parameter.
+ */
+static void dm_test_hdcp_get_adjustments_type0_policy(struct kunit *test)
+{
+ struct mod_hdcp_link_adjustment link_adjust;
+ struct mod_hdcp_display_adjustment display_adjust;
+ unsigned int disable;
+ unsigned int hdcp1_disable;
+ unsigned int force_type;
+
+ hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE0,
+ false, false, true, &link_adjust, &display_adjust);
+ disable = display_adjust.disable;
+ hdcp1_disable = link_adjust.hdcp1.disable;
+ force_type = link_adjust.hdcp2.force_type;
+
+ KUNIT_EXPECT_EQ(test, disable,
+ MOD_HDCP_DISPLAY_NOT_DISABLE);
+ KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 2);
+ KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, MAX_NUM_OF_ATTEMPTS);
+ KUNIT_EXPECT_EQ(test, hdcp1_disable, 0);
+ KUNIT_EXPECT_EQ(test, force_type,
+ MOD_HDCP_FORCE_TYPE_0);
+ KUNIT_EXPECT_FALSE(test, link_adjust.hdcp2.use_fw_locality_check);
+ KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_sw_locality_fallback);
+}
+
+/**
+ * dm_test_hdcp_get_adjustments_type1_policy - TYPE1 disables HDCP1 and forces TYPE1
+ * @test: KUnit test context
+ *
+ * When encryption is enabled with content TYPE1, hdcp1 should be
+ * disabled, force_type should be TYPE_1, and fw_locality_check should
+ * be enabled when hdcp_lc_force_fw_enable is set.
+ */
+static void dm_test_hdcp_get_adjustments_type1_policy(struct kunit *test)
+{
+ struct mod_hdcp_link_adjustment link_adjust;
+ struct mod_hdcp_display_adjustment display_adjust;
+ unsigned int disable;
+ unsigned int hdcp1_disable;
+ unsigned int force_type;
+
+ hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE1,
+ false, true, false, &link_adjust, &display_adjust);
+ disable = display_adjust.disable;
+ hdcp1_disable = link_adjust.hdcp1.disable;
+ force_type = link_adjust.hdcp2.force_type;
+
+ KUNIT_EXPECT_EQ(test, disable,
+ MOD_HDCP_DISPLAY_NOT_DISABLE);
+ KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 2);
+ KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, MAX_NUM_OF_ATTEMPTS);
+ KUNIT_EXPECT_EQ(test, hdcp1_disable, 1);
+ KUNIT_EXPECT_EQ(test, force_type,
+ MOD_HDCP_FORCE_TYPE_1);
+ KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_fw_locality_check);
+ KUNIT_EXPECT_FALSE(test, link_adjust.hdcp2.use_sw_locality_fallback);
+}
+
+/**
+ * dm_test_hdcp_get_adjustments_fused_io_enables_fw_check - fused_io enables FW locality check
+ * @test: KUnit test context
+ *
+ * When fused_io_supported is true, use_fw_locality_check should be
+ * enabled regardless of hdcp_lc_force_fw_enable.
+ */
+static void dm_test_hdcp_get_adjustments_fused_io_enables_fw_check(struct kunit *test)
+{
+ struct mod_hdcp_link_adjustment link_adjust;
+ struct mod_hdcp_display_adjustment display_adjust;
+
+ hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE0,
+ true, false, false, &link_adjust, &display_adjust);
+
+ KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_fw_locality_check);
+}
+
/* Tests for process_output() */
-/*
- * Helper: allocate and initialise a minimal hdcp_workqueue sufficient for
- * process_output() testing. Only the three delayed works accessed by
+/**
+ * alloc_test_workqueue - allocate a minimal hdcp_workqueue for testing
+ * @test: KUnit test context for managed allocation
+ *
+ * Allocates and initialises a minimal hdcp_workqueue sufficient for
+ * process_output() testing. Only the three delayed works accessed by
* process_output() are initialised; everything else is zeroed.
*/
static struct hdcp_workqueue *alloc_test_workqueue(struct kunit *test)
@@ -33,9 +263,12 @@ static struct hdcp_workqueue *alloc_test_workqueue(struct kunit *test)
return work;
}
-/*
+/**
+ * dm_test_process_output_property_validate_always_scheduled - validate_dwork always queued
+ * @test: KUnit test context
+ *
* process_output() always schedules property_validate_dwork with delay=0,
- * which queues the work item directly (bypassing the timer). Use
+ * which queues the work item directly (bypassing the timer). Uses
* work_pending() rather than delayed_work_pending() to detect this.
*/
static void dm_test_process_output_property_validate_always_scheduled(struct kunit *test)
@@ -52,8 +285,12 @@ static void dm_test_process_output_property_validate_always_scheduled(struct kun
cancel_delayed_work_sync(&work->property_validate_dwork);
}
-/*
- * output.callback_needed=true must schedule callback_dwork.
+/**
+ * dm_test_process_output_callback_needed - callback_needed schedules callback_dwork
+ * @test: KUnit test context
+ *
+ * When output.callback_needed is true, process_output() must schedule
+ * callback_dwork with the specified delay.
*/
static void dm_test_process_output_callback_needed(struct kunit *test)
{
@@ -70,8 +307,12 @@ static void dm_test_process_output_callback_needed(struct kunit *test)
cancel_delayed_work_sync(&work->property_validate_dwork);
}
-/*
- * output.callback_stop=true must cancel a previously scheduled callback_dwork.
+/**
+ * dm_test_process_output_callback_stop - callback_stop cancels callback_dwork
+ * @test: KUnit test context
+ *
+ * When output.callback_stop is true, process_output() must cancel a
+ * previously scheduled callback_dwork.
*/
static void dm_test_process_output_callback_stop(struct kunit *test)
{
@@ -90,8 +331,12 @@ static void dm_test_process_output_callback_stop(struct kunit *test)
cancel_delayed_work_sync(&work->property_validate_dwork);
}
-/*
- * output.watchdog_timer_needed=true must schedule watchdog_timer_dwork.
+/**
+ * dm_test_process_output_watchdog_needed - watchdog_needed schedules watchdog_dwork
+ * @test: KUnit test context
+ *
+ * When output.watchdog_timer_needed is true, process_output() must
+ * schedule watchdog_timer_dwork with the specified delay.
*/
static void dm_test_process_output_watchdog_needed(struct kunit *test)
{
@@ -108,9 +353,12 @@ static void dm_test_process_output_watchdog_needed(struct kunit *test)
cancel_delayed_work_sync(&work->property_validate_dwork);
}
-/*
- * output.watchdog_timer_stop=true must cancel a previously scheduled
- * watchdog_timer_dwork.
+/**
+ * dm_test_process_output_watchdog_stop - watchdog_stop cancels watchdog_dwork
+ * @test: KUnit test context
+ *
+ * When output.watchdog_timer_stop is true, process_output() must cancel
+ * a previously scheduled watchdog_timer_dwork.
*/
static void dm_test_process_output_watchdog_stop(struct kunit *test)
{
@@ -129,9 +377,12 @@ static void dm_test_process_output_watchdog_stop(struct kunit *test)
cancel_delayed_work_sync(&work->property_validate_dwork);
}
-/*
- * Both callback_needed and watchdog_timer_needed set: both dworks are
- * scheduled independently.
+/**
+ * dm_test_process_output_callback_and_watchdog_needed - both dworks scheduled independently
+ * @test: KUnit test context
+ *
+ * When both callback_needed and watchdog_timer_needed are set,
+ * process_output() must schedule both dworks independently.
*/
static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *test)
{
@@ -154,6 +405,18 @@ static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *te
/* End of tests for process_output() */
static struct kunit_case dm_hdcp_test_cases[] = {
+ /* hdcp_get_content_protection_from_status() */
+ KUNIT_CASE(dm_test_hdcp_get_cp_disabled_returns_desired),
+ KUNIT_CASE(dm_test_hdcp_get_cp_type0_returns_enabled),
+ KUNIT_CASE(dm_test_hdcp_get_cp_type1_returns_enabled),
+ KUNIT_CASE(dm_test_hdcp_get_cp_type1_rejects_type0_status),
+ KUNIT_CASE(dm_test_hdcp_get_cp_type0_rejects_type1_status),
+ /* hdcp_get_link_display_adjustments() */
+ KUNIT_CASE(dm_test_hdcp_get_adjustments_disable_authentication),
+ KUNIT_CASE(dm_test_hdcp_get_adjustments_type0_policy),
+ KUNIT_CASE(dm_test_hdcp_get_adjustments_type1_policy),
+ KUNIT_CASE(dm_test_hdcp_get_adjustments_fused_io_enables_fw_check),
+ /* process_output() */
KUNIT_CASE(dm_test_process_output_property_validate_always_scheduled),
KUNIT_CASE(dm_test_process_output_callback_needed),
KUNIT_CASE(dm_test_process_output_callback_stop),
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c
new file mode 100644
index 000000000000..33014a2d2222
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c
@@ -0,0 +1,634 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_helpers.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_kunit_helpers.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "dm_helpers.h"
+#include "ddc_service_types.h"
+#include "amdgpu_dm_helpers.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+
+/* Tests for edid_extract_panel_id() */
+
+/**
+ * dm_test_edid_extract_panel_id_basic - Test Edid extract panel id basic
+ * @test: The KUnit test context
+ */
+static void dm_test_edid_extract_panel_id_basic(struct kunit *test)
+{
+ struct edid *edid;
+ u32 panel_id;
+
+ edid = kunit_kzalloc(test, sizeof(*edid), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, edid);
+
+ edid->mfg_id[0] = 0x12;
+ edid->mfg_id[1] = 0x34;
+ edid->prod_code[0] = 0xAB;
+ edid->prod_code[1] = 0xCD;
+
+ panel_id = edid_extract_panel_id(edid);
+
+ /*
+ * Expected: (0x12 << 24) | (0x34 << 16) | EDID_PRODUCT_ID(edid)
+ * EDID_PRODUCT_ID = prod_code[0] | (prod_code[1] << 8) = 0xAB | 0xCD00 = 0xCDAB
+ * Result: 0x12340000 | 0x0000CDAB = 0x1234CDAB
+ */
+ KUNIT_EXPECT_EQ(test, panel_id, (u32)0x1234CDAB);
+}
+
+/**
+ * dm_test_edid_extract_panel_id_zeros - Test Edid extract panel id zeros
+ * @test: The KUnit test context
+ */
+static void dm_test_edid_extract_panel_id_zeros(struct kunit *test)
+{
+ struct edid *edid;
+
+ edid = kunit_kzalloc(test, sizeof(*edid), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, edid);
+
+ KUNIT_EXPECT_EQ(test, edid_extract_panel_id(edid), 0U);
+}
+
+/* Tests for dm_is_freesync_pcon_whitelist() */
+
+/**
+ * dm_test_freesync_pcon_whitelist_all_known - Test all known Freesync Pcon whitelist entries
+ * @test: The KUnit test context
+ *
+ * Iterates over the driver's whitelist table directly so that any ID added
+ * to dm_freesync_pcon_whitelist[] is automatically covered by this test.
+ */
+static void dm_test_freesync_pcon_whitelist_all_known(struct kunit *test)
+{
+ u32 i;
+
+ for (i = 0; i < dm_freesync_pcon_whitelist_count(); i++)
+ KUNIT_EXPECT_TRUE(test,
+ dm_is_freesync_pcon_whitelist(dm_freesync_pcon_whitelist[i]));
+}
+
+/**
+ * dm_test_freesync_pcon_whitelist_not_in_list - Test Freesync pcon whitelist not in list
+ * @test: The KUnit test context
+ */
+static void dm_test_freesync_pcon_whitelist_not_in_list(struct kunit *test)
+{
+ /* 0xFFFFFF is not a known whitelist device */
+ KUNIT_EXPECT_FALSE(test, dm_is_freesync_pcon_whitelist(0xFFFFFF));
+}
+
+/**
+ * dm_test_freesync_pcon_whitelist_zero - Test Freesync pcon whitelist zero
+ * @test: The KUnit test context
+ */
+static void dm_test_freesync_pcon_whitelist_zero(struct kunit *test)
+{
+ KUNIT_EXPECT_FALSE(test, dm_is_freesync_pcon_whitelist(0));
+}
+
+/* Tests for populate_hdmi_info_from_connector() */
+
+/**
+ * dm_test_populate_hdmi_scdc_present_true - Test Populate hdmi scdc present true
+ * @test: The KUnit test context
+ */
+static void dm_test_populate_hdmi_scdc_present_true(struct kunit *test)
+{
+ struct drm_hdmi_info *hdmi;
+ struct dc_edid_caps *caps;
+
+ hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, hdmi);
+ caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, caps);
+
+ hdmi->scdc.supported = true;
+
+ populate_hdmi_info_from_connector(true, hdmi, caps);
+
+ KUNIT_EXPECT_TRUE(test, caps->scdc_present);
+}
+
+/**
+ * dm_test_populate_hdmi_scdc_present_false - Test Populate hdmi scdc present false
+ * @test: The KUnit test context
+ */
+static void dm_test_populate_hdmi_scdc_present_false(struct kunit *test)
+{
+ struct drm_hdmi_info *hdmi;
+ struct dc_edid_caps *caps;
+
+ hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, hdmi);
+ caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, caps);
+
+ hdmi->scdc.supported = false;
+ caps->scdc_present = true; /* pre-set to confirm it gets cleared */
+
+ populate_hdmi_info_from_connector(true, hdmi, caps);
+
+ KUNIT_EXPECT_FALSE(test, caps->scdc_present);
+}
+
+/**
+ * dm_test_populate_hdmi_frl_dsc_10bpc - Test HDMI FRL DSC 10 bpc caps
+ * @test: The KUnit test context
+ */
+static void dm_test_populate_hdmi_frl_dsc_10bpc(struct kunit *test)
+{
+ struct drm_hdmi_info *hdmi;
+ struct dc_edid_caps *caps;
+
+ hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, hdmi);
+ caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, caps);
+
+ hdmi->max_lanes = 4;
+ hdmi->max_frl_rate_per_lane = 12;
+ hdmi->dsc_cap.v_1p2 = true;
+ hdmi->dsc_cap.bpc_supported = 10;
+ hdmi->dsc_cap.all_bpp = true;
+ hdmi->dsc_cap.native_420 = true;
+ hdmi->dsc_cap.max_slices = 8;
+ hdmi->dsc_cap.clk_per_slice = 400;
+ hdmi->dsc_cap.max_lanes = 4;
+ hdmi->dsc_cap.max_frl_rate_per_lane = 10;
+ hdmi->dsc_cap.total_chunk_kbytes = 7;
+
+ populate_hdmi_info_from_connector(true, hdmi, caps);
+
+ KUNIT_EXPECT_EQ(test, caps->max_frl_rate, 6);
+ KUNIT_EXPECT_TRUE(test, caps->frl_dsc_support);
+ KUNIT_EXPECT_TRUE(test, caps->frl_dsc_10bpc);
+ KUNIT_EXPECT_FALSE(test, caps->frl_dsc_12bpc);
+ KUNIT_EXPECT_TRUE(test, caps->frl_dsc_all_bpp);
+ KUNIT_EXPECT_TRUE(test, caps->frl_dsc_native_420);
+ KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_slices, 5);
+ KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_frl_rate, 5);
+ KUNIT_EXPECT_EQ(test, caps->frl_dsc_total_chunk_kbytes, 7);
+}
+
+/**
+ * dm_test_populate_hdmi_frl_dsc_12bpc - Test HDMI FRL DSC 12 bpc caps
+ * @test: The KUnit test context
+ */
+static void dm_test_populate_hdmi_frl_dsc_12bpc(struct kunit *test)
+{
+ struct drm_hdmi_info *hdmi;
+ struct dc_edid_caps *caps;
+
+ hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, hdmi);
+ caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, caps);
+
+ hdmi->max_lanes = 3;
+ hdmi->max_frl_rate_per_lane = 6;
+ hdmi->dsc_cap.v_1p2 = true;
+ hdmi->dsc_cap.bpc_supported = 12;
+ hdmi->dsc_cap.max_slices = 16;
+ hdmi->dsc_cap.clk_per_slice = 400;
+ hdmi->dsc_cap.max_lanes = 3;
+ hdmi->dsc_cap.max_frl_rate_per_lane = 3;
+
+ populate_hdmi_info_from_connector(true, hdmi, caps);
+
+ KUNIT_EXPECT_EQ(test, caps->max_frl_rate, 2);
+ KUNIT_EXPECT_TRUE(test, caps->frl_dsc_support);
+ KUNIT_EXPECT_FALSE(test, caps->frl_dsc_10bpc);
+ KUNIT_EXPECT_TRUE(test, caps->frl_dsc_12bpc);
+ KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_slices, 7);
+ KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_frl_rate, 1);
+}
+
+/**
+ * dm_test_populate_hdmi_frl_dsc_unknown_values - Test HDMI FRL DSC unknown values
+ * @test: The KUnit test context
+ */
+static void dm_test_populate_hdmi_frl_dsc_unknown_values(struct kunit *test)
+{
+ struct drm_hdmi_info *hdmi;
+ struct dc_edid_caps *caps;
+
+ hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, hdmi);
+ caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, caps);
+
+ hdmi->max_lanes = 2;
+ hdmi->max_frl_rate_per_lane = 3;
+ hdmi->dsc_cap.v_1p2 = true;
+ hdmi->dsc_cap.bpc_supported = 8;
+ hdmi->dsc_cap.max_slices = 3;
+ hdmi->dsc_cap.clk_per_slice = 340;
+ hdmi->dsc_cap.max_lanes = 2;
+ hdmi->dsc_cap.max_frl_rate_per_lane = 12;
+
+ populate_hdmi_info_from_connector(true, hdmi, caps);
+
+ KUNIT_EXPECT_EQ(test, caps->max_frl_rate, 0);
+ KUNIT_EXPECT_TRUE(test, caps->frl_dsc_support);
+ KUNIT_EXPECT_FALSE(test, caps->frl_dsc_10bpc);
+ KUNIT_EXPECT_FALSE(test, caps->frl_dsc_12bpc);
+ KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_slices, 0);
+ KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_frl_rate, 0);
+}
+
+/* Tests for dm_get_adaptive_sync_support_type() */
+
+/**
+ * dm_test_adaptive_sync_type_none_default - Test Adaptive sync type none default
+ * @test: The KUnit test context
+ */
+static void dm_test_adaptive_sync_type_none_default(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ /* dongle_type = 0 (DISPLAY_DONGLE_NONE) → default case → TYPE_NONE */
+ KUNIT_EXPECT_EQ(test,
+ (int)dm_get_adaptive_sync_support_type(link),
+ (int)ADAPTIVE_SYNC_TYPE_NONE);
+}
+
+/**
+ * dm_test_adaptive_sync_type_converter_no_conditions - Converter without caps
+ * @test: The KUnit test context
+ */
+static void dm_test_adaptive_sync_type_converter_no_conditions(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ /* HDMI converter but no adaptive sync cap → still NONE */
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)dm_get_adaptive_sync_support_type(link),
+ (int)ADAPTIVE_SYNC_TYPE_NONE);
+}
+
+/**
+ * dm_test_adaptive_sync_type_converter_partial_conditions - Partial caps
+ * @test: The KUnit test context
+ */
+static void dm_test_adaptive_sync_type_converter_partial_conditions(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ /* Cap set and whitelist ID, but allow_invalid_MSA_timing_param = false */
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER;
+ link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1;
+ link->dpcd_caps.allow_invalid_MSA_timing_param = false;
+ link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_0060AD;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)dm_get_adaptive_sync_support_type(link),
+ (int)ADAPTIVE_SYNC_TYPE_NONE);
+}
+
+/**
+ * dm_test_adaptive_sync_type_pcon_whitelist - Test Adaptive sync type pcon whitelist
+ * @test: The KUnit test context
+ */
+static void dm_test_adaptive_sync_type_pcon_whitelist(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ /* All conditions met → FREESYNC_TYPE_PCON_IN_WHITELIST */
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER;
+ link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1;
+ link->dpcd_caps.allow_invalid_MSA_timing_param = true;
+ link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_0060AD;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)dm_get_adaptive_sync_support_type(link),
+ (int)FREESYNC_TYPE_PCON_IN_WHITELIST);
+}
+
+/**
+ * dm_test_adaptive_sync_type_converter_nonwhitelist - Converter not whitelisted
+ * @test: The KUnit test context
+ */
+static void dm_test_adaptive_sync_type_converter_nonwhitelist(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ /* All conditions met but branch_dev_id not in whitelist → NONE */
+ link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER;
+ link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1;
+ link->dpcd_caps.allow_invalid_MSA_timing_param = true;
+ link->dpcd_caps.branch_dev_id = 0xFFFFFF;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)dm_get_adaptive_sync_support_type(link),
+ (int)ADAPTIVE_SYNC_TYPE_NONE);
+}
+
+/* Tests for dm_helpers_is_fullscreen() and dm_helpers_is_hdr_on() */
+
+/**
+ * dm_test_helpers_is_fullscreen_returns_false - Test Helpers is fullscreen returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_helpers_is_fullscreen_returns_false(struct kunit *test)
+{
+ /* Stub — always returns false */
+ KUNIT_EXPECT_FALSE(test, dm_helpers_is_fullscreen(NULL, NULL));
+}
+
+/**
+ * dm_test_helpers_is_hdr_on_returns_false - Test Helpers is hdr on returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_helpers_is_hdr_on_returns_false(struct kunit *test)
+{
+ /* Stub — always returns false */
+ KUNIT_EXPECT_FALSE(test, dm_helpers_is_hdr_on(NULL, NULL));
+}
+
+/* Tests for get_max_frl_rate() */
+
+/**
+ * dm_test_get_max_frl_rate_3lanes_3gbps - Test Get max frl rate 3lanes 3gbps
+ * @test: The KUnit test context
+ */
+static void dm_test_get_max_frl_rate_3lanes_3gbps(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, get_max_frl_rate(3, 3), 1);
+}
+
+/**
+ * dm_test_get_max_frl_rate_3lanes_6gbps - Test Get max frl rate 3lanes 6gbps
+ * @test: The KUnit test context
+ */
+static void dm_test_get_max_frl_rate_3lanes_6gbps(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, get_max_frl_rate(3, 6), 2);
+}
+
+/**
+ * dm_test_get_max_frl_rate_4lanes_6gbps - Test Get max frl rate 4lanes 6gbps
+ * @test: The KUnit test context
+ */
+static void dm_test_get_max_frl_rate_4lanes_6gbps(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, get_max_frl_rate(4, 6), 3);
+}
+
+/**
+ * dm_test_get_max_frl_rate_4lanes_8gbps - Test Get max frl rate 4lanes 8gbps
+ * @test: The KUnit test context
+ */
+static void dm_test_get_max_frl_rate_4lanes_8gbps(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, get_max_frl_rate(4, 8), 4);
+}
+
+/**
+ * dm_test_get_max_frl_rate_4lanes_10gbps - Test Get max frl rate 4lanes 10gbps
+ * @test: The KUnit test context
+ */
+static void dm_test_get_max_frl_rate_4lanes_10gbps(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, get_max_frl_rate(4, 10), 5);
+}
+
+/**
+ * dm_test_get_max_frl_rate_4lanes_12gbps - Test Get max frl rate 4lanes 12gbps
+ * @test: The KUnit test context
+ */
+static void dm_test_get_max_frl_rate_4lanes_12gbps(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, get_max_frl_rate(4, 12), 6);
+}
+
+/**
+ * dm_test_get_max_frl_rate_unknown - Test Get max frl rate unknown
+ * @test: The KUnit test context
+ */
+static void dm_test_get_max_frl_rate_unknown(struct kunit *test)
+{
+ /* Unknown lane/rate combination → 0 */
+ KUNIT_EXPECT_EQ(test, get_max_frl_rate(2, 3), 0);
+}
+
+/* Tests for dm_dtn_log_begin() / dm_dtn_log_append_v() / dm_dtn_log_end() */
+
+/**
+ * dm_test_dtn_log_buffer_accumulates - Test DTN log buffer accumulation
+ * @test: The KUnit test context
+ */
+static void dm_test_dtn_log_buffer_accumulates(struct kunit *test)
+{
+ struct dc_log_buffer_ctx log_ctx = {0};
+
+ dm_dtn_log_begin(NULL, &log_ctx);
+ dm_dtn_log_append_v(NULL, &log_ctx, "x=%d\n", 7);
+ dm_dtn_log_end(NULL, &log_ctx);
+
+ KUNIT_ASSERT_NOT_NULL(test, log_ctx.buf);
+ KUNIT_EXPECT_STREQ(test, log_ctx.buf, "[dtn begin]\nx=7\n[dtn end]\n");
+ KUNIT_EXPECT_EQ(test, log_ctx.pos, strlen("[dtn begin]\nx=7\n[dtn end]\n"));
+
+ kvfree(log_ctx.buf);
+}
+
+/**
+ * dm_test_dtn_log_null_ctx_no_crash - Test DTN log helpers with NULL log buffer
+ * @test: The KUnit test context
+ */
+static void dm_test_dtn_log_null_ctx_no_crash(struct kunit *test)
+{
+ /* NULL log_ctx redirects to dmesg and must not dereference a buffer */
+ dm_dtn_log_begin(NULL, NULL);
+ dm_dtn_log_append_v(NULL, NULL, "value %d\n", 1);
+ dm_dtn_log_end(NULL, NULL);
+
+ KUNIT_EXPECT_TRUE(test, true);
+}
+
+/* Tests for dm_helpers_dp_read_dpcd() / dm_helpers_dp_write_dpcd() */
+
+/**
+ * dm_test_dp_read_dpcd_null_priv - Test DPCD read returns false without connector
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_read_dpcd_null_priv(struct kunit *test)
+{
+ struct dc_link *link;
+ uint8_t data = 0;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ /* link->priv (aconnector) is NULL → early return false */
+ KUNIT_EXPECT_FALSE(test,
+ dm_helpers_dp_read_dpcd(NULL, link, 0, &data, sizeof(data)));
+}
+
+/**
+ * dm_test_dp_write_dpcd_null_priv - Test DPCD write returns false without connector
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_dpcd_null_priv(struct kunit *test)
+{
+ struct dc_link *link;
+ uint8_t data = 0;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ /* link->priv (aconnector) is NULL → early return false */
+ KUNIT_EXPECT_FALSE(test,
+ dm_helpers_dp_write_dpcd(NULL, link, 0, &data, sizeof(data)));
+}
+
+/* Tests for dm_helpers_dp_mst_start_top_mgr() / dm_helpers_dp_mst_stop_top_mgr() */
+
+/**
+ * dm_test_mst_start_top_mgr_null_priv - Test MST start returns false without connector
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_start_top_mgr_null_priv(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ KUNIT_EXPECT_FALSE(test, dm_helpers_dp_mst_start_top_mgr(NULL, link, false));
+}
+
+/**
+ * dm_test_mst_stop_top_mgr_null_priv - Test MST stop returns false without connector
+ * @test: The KUnit test context
+ */
+static void dm_test_mst_stop_top_mgr_null_priv(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ KUNIT_EXPECT_FALSE(test, dm_helpers_dp_mst_stop_top_mgr(NULL, link));
+}
+
+/**
+ * dm_test_mst_start_top_mgr_boot - Test MST start boot path on a connector-backed link
+ * @test: The KUnit test context
+ *
+ * Uses the DRM KUnit mock device to back the connector so the link is a
+ * realistic connector-backed link. The boot path short-circuits and returns
+ * true without touching the MST topology manager.
+ */
+static void dm_test_mst_start_top_mgr_boot(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct amdgpu_device *adev;
+ struct dc_link *link;
+
+ adev = dm_kunit_alloc_adev(test);
+
+ link = dm_kunit_alloc_link(test);
+
+ aconnector = dm_kunit_alloc_connector(test, adev, NULL);
+
+ link->priv = aconnector;
+
+ KUNIT_EXPECT_TRUE(test, dm_helpers_dp_mst_start_top_mgr(NULL, link, true));
+}
+
+/* Tests for dm_helpers_dp_write_hblank_reduction() */
+
+/**
+ * dm_test_dp_write_hblank_reduction_false - Test hblank reduction stub returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_dp_write_hblank_reduction_false(struct kunit *test)
+{
+ KUNIT_EXPECT_FALSE(test, dm_helpers_dp_write_hblank_reduction(NULL, NULL));
+}
+
+static struct kunit_case amdgpu_dm_helpers_test_cases[] = {
+ /* edid_extract_panel_id */
+ KUNIT_CASE(dm_test_edid_extract_panel_id_basic),
+ KUNIT_CASE(dm_test_edid_extract_panel_id_zeros),
+ /* dm_is_freesync_pcon_whitelist */
+ KUNIT_CASE(dm_test_freesync_pcon_whitelist_all_known),
+ KUNIT_CASE(dm_test_freesync_pcon_whitelist_not_in_list),
+ KUNIT_CASE(dm_test_freesync_pcon_whitelist_zero),
+ /* populate_hdmi_info_from_connector */
+ KUNIT_CASE(dm_test_populate_hdmi_scdc_present_true),
+ KUNIT_CASE(dm_test_populate_hdmi_scdc_present_false),
+ KUNIT_CASE(dm_test_populate_hdmi_frl_dsc_10bpc),
+ KUNIT_CASE(dm_test_populate_hdmi_frl_dsc_12bpc),
+ KUNIT_CASE(dm_test_populate_hdmi_frl_dsc_unknown_values),
+ /* dm_get_adaptive_sync_support_type */
+ KUNIT_CASE(dm_test_adaptive_sync_type_none_default),
+ KUNIT_CASE(dm_test_adaptive_sync_type_converter_no_conditions),
+ KUNIT_CASE(dm_test_adaptive_sync_type_converter_partial_conditions),
+ KUNIT_CASE(dm_test_adaptive_sync_type_pcon_whitelist),
+ KUNIT_CASE(dm_test_adaptive_sync_type_converter_nonwhitelist),
+ /* dm_helpers_is_fullscreen / dm_helpers_is_hdr_on */
+ KUNIT_CASE(dm_test_helpers_is_fullscreen_returns_false),
+ KUNIT_CASE(dm_test_helpers_is_hdr_on_returns_false),
+ /* get_max_frl_rate */
+ KUNIT_CASE(dm_test_get_max_frl_rate_3lanes_3gbps),
+ KUNIT_CASE(dm_test_get_max_frl_rate_3lanes_6gbps),
+ KUNIT_CASE(dm_test_get_max_frl_rate_4lanes_6gbps),
+ KUNIT_CASE(dm_test_get_max_frl_rate_4lanes_8gbps),
+ KUNIT_CASE(dm_test_get_max_frl_rate_4lanes_10gbps),
+ KUNIT_CASE(dm_test_get_max_frl_rate_4lanes_12gbps),
+ KUNIT_CASE(dm_test_get_max_frl_rate_unknown),
+ /* dm_dtn_log_begin / dm_dtn_log_append_v / dm_dtn_log_end */
+ KUNIT_CASE(dm_test_dtn_log_buffer_accumulates),
+ KUNIT_CASE(dm_test_dtn_log_null_ctx_no_crash),
+ /* dm_helpers_dp_read_dpcd / dm_helpers_dp_write_dpcd */
+ KUNIT_CASE(dm_test_dp_read_dpcd_null_priv),
+ KUNIT_CASE(dm_test_dp_write_dpcd_null_priv),
+ /* dm_helpers_dp_mst_start_top_mgr / dm_helpers_dp_mst_stop_top_mgr */
+ KUNIT_CASE(dm_test_mst_start_top_mgr_null_priv),
+ KUNIT_CASE(dm_test_mst_stop_top_mgr_null_priv),
+ KUNIT_CASE(dm_test_mst_start_top_mgr_boot),
+ /* dm_helpers_dp_write_hblank_reduction */
+ KUNIT_CASE(dm_test_dp_write_hblank_reduction_false),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_helpers_test_suite = {
+ .name = "amdgpu_dm_helpers",
+ .test_cases = amdgpu_dm_helpers_test_cases,
+};
+
+kunit_test_suite(amdgpu_dm_helpers_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_helpers");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c
new file mode 100644
index 000000000000..a73a6dd146d6
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c
@@ -0,0 +1,909 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_irq.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+#include <drm/drm_kunit_helpers.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_irq.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+#include "dmub/dmub_srv.h"
+
+static void dm_test_irq_handler(void *arg)
+{
+}
+
+static void dm_test_irq_handler_alt(void *arg)
+{
+}
+
+static void dm_test_crtc_list_del(void *data)
+{
+ struct amdgpu_crtc *acrtc = data;
+
+ list_del_init(&acrtc->base.head);
+}
+
+/* Tests for amdgpu_dm_hpd_to_dal_irq_source() */
+
+/**
+ * dm_test_hpd_to_dal_irq_source_hpd1 - Test Hpd to dal irq source hpd1
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_to_dal_irq_source_hpd1(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_1),
+ (int)DC_IRQ_SOURCE_HPD1);
+}
+
+/**
+ * dm_test_hpd_to_dal_irq_source_hpd2 - Test Hpd to dal irq source hpd2
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_to_dal_irq_source_hpd2(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_2),
+ (int)DC_IRQ_SOURCE_HPD2);
+}
+
+/**
+ * dm_test_hpd_to_dal_irq_source_hpd3 - Test Hpd to dal irq source hpd3
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_to_dal_irq_source_hpd3(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_3),
+ (int)DC_IRQ_SOURCE_HPD3);
+}
+
+/**
+ * dm_test_hpd_to_dal_irq_source_hpd4 - Test Hpd to dal irq source hpd4
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_to_dal_irq_source_hpd4(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_4),
+ (int)DC_IRQ_SOURCE_HPD4);
+}
+
+/**
+ * dm_test_hpd_to_dal_irq_source_hpd5 - Test Hpd to dal irq source hpd5
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_to_dal_irq_source_hpd5(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_5),
+ (int)DC_IRQ_SOURCE_HPD5);
+}
+
+/**
+ * dm_test_hpd_to_dal_irq_source_hpd6 - Test Hpd to dal irq source hpd6
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_to_dal_irq_source_hpd6(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_6),
+ (int)DC_IRQ_SOURCE_HPD6);
+}
+
+/**
+ * dm_test_hpd_to_dal_irq_source_invalid - Test Hpd to dal irq source invalid
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_to_dal_irq_source_invalid(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_NONE),
+ (int)DC_IRQ_SOURCE_INVALID);
+}
+
+/**
+ * dm_test_hpd_to_dal_irq_source_out_of_range - Test Hpd to dal irq source out of range
+ * @test: The KUnit test context
+ */
+static void dm_test_hpd_to_dal_irq_source_out_of_range(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(99),
+ (int)DC_IRQ_SOURCE_INVALID);
+}
+
+/* Tests for are_sinks_equal() */
+
+/**
+ * dm_test_are_sinks_equal_both_null - Test Are sinks equal both null
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_both_null(struct kunit *test)
+{
+ KUNIT_EXPECT_FALSE(test, are_sinks_equal(NULL, NULL));
+}
+
+/**
+ * dm_test_are_sinks_equal_first_null - Test Are sinks equal first null
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_first_null(struct kunit *test)
+{
+ struct dc_sink *sink2;
+
+ sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2);
+
+ KUNIT_EXPECT_FALSE(test, are_sinks_equal(NULL, sink2));
+}
+
+/**
+ * dm_test_are_sinks_equal_second_null - Test Are sinks equal second null
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_second_null(struct kunit *test)
+{
+ struct dc_sink *sink1;
+
+ sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1);
+
+ KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, NULL));
+}
+
+/**
+ * dm_test_are_sinks_equal_different_signal - Test Are sinks equal different signal
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_different_signal(struct kunit *test)
+{
+ struct dc_sink *sink1, *sink2;
+
+ sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1);
+ sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2);
+
+ sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink2->sink_signal = SIGNAL_TYPE_DISPLAY_PORT;
+
+ KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, sink2));
+}
+
+/**
+ * dm_test_are_sinks_equal_different_edid_length - Test Are sinks equal different edid length
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_different_edid_length(struct kunit *test)
+{
+ struct dc_sink *sink1, *sink2;
+
+ sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1);
+ sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2);
+
+ sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink1->dc_edid.length = 128;
+ sink2->dc_edid.length = 256;
+
+ KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, sink2));
+}
+
+/**
+ * dm_test_are_sinks_equal_different_edid_data - Test Are sinks equal different edid data
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_different_edid_data(struct kunit *test)
+{
+ struct dc_sink *sink1, *sink2;
+
+ sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1);
+ sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2);
+
+ sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink1->dc_edid.length = 4;
+ sink2->dc_edid.length = 4;
+ memset(sink1->dc_edid.raw_edid, 0xAA, 4);
+ memset(sink2->dc_edid.raw_edid, 0xBB, 4);
+
+ KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, sink2));
+}
+
+/**
+ * dm_test_are_sinks_equal_identical - Test Are sinks equal identical
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_identical(struct kunit *test)
+{
+ struct dc_sink *sink1, *sink2;
+
+ sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1);
+ sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2);
+
+ sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink1->dc_edid.length = 4;
+ sink2->dc_edid.length = 4;
+ memset(sink1->dc_edid.raw_edid, 0xAA, 4);
+ memset(sink2->dc_edid.raw_edid, 0xAA, 4);
+
+ KUNIT_EXPECT_TRUE(test, are_sinks_equal(sink1, sink2));
+}
+
+/**
+ * dm_test_are_sinks_equal_zero_length - Test Are sinks equal zero length
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_zero_length(struct kunit *test)
+{
+ struct dc_sink *sink1, *sink2;
+
+ sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1);
+ sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2);
+
+ sink1->sink_signal = SIGNAL_TYPE_DISPLAY_PORT;
+ sink2->sink_signal = SIGNAL_TYPE_DISPLAY_PORT;
+ sink1->dc_edid.length = 0;
+ sink2->dc_edid.length = 0;
+
+ KUNIT_EXPECT_TRUE(test, are_sinks_equal(sink1, sink2));
+}
+
+/**
+ * dm_test_are_sinks_equal_full_edid_identical - Test Are sinks equal full edid identical
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_full_edid_identical(struct kunit *test)
+{
+ struct dc_sink *sink1, *sink2;
+
+ sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1);
+ sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2);
+
+ sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink1->dc_edid.length = 128;
+ sink2->dc_edid.length = 128;
+ memset(sink1->dc_edid.raw_edid, 0x5A, 128);
+ memset(sink2->dc_edid.raw_edid, 0x5A, 128);
+
+ KUNIT_EXPECT_TRUE(test, are_sinks_equal(sink1, sink2));
+}
+
+/**
+ * dm_test_are_sinks_equal_full_edid_last_byte_differs - Test Are sinks equal last byte differs
+ * @test: The KUnit test context
+ */
+static void dm_test_are_sinks_equal_full_edid_last_byte_differs(struct kunit *test)
+{
+ struct dc_sink *sink1, *sink2;
+
+ sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1);
+ sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2);
+
+ sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
+ sink1->dc_edid.length = 128;
+ sink2->dc_edid.length = 128;
+ memset(sink1->dc_edid.raw_edid, 0x5A, 128);
+ memset(sink2->dc_edid.raw_edid, 0x5A, 128);
+ sink2->dc_edid.raw_edid[127] = 0x5B;
+
+ KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, sink2));
+}
+
+/* Tests for dmub_notification_type_str() */
+
+/**
+ * dm_test_notification_str_no_data - Test Notification str no data
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_no_data(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_NO_DATA), "NO_DATA");
+}
+
+/**
+ * dm_test_notification_str_aux_reply - Test Notification str aux reply
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_aux_reply(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_AUX_REPLY), "AUX_REPLY");
+}
+
+/**
+ * dm_test_notification_str_hpd - Test Notification str hpd
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_hpd(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_HPD), "HPD");
+}
+
+/**
+ * dm_test_notification_str_hpd_irq - Test Notification str hpd irq
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_hpd_irq(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_HPD_IRQ), "HPD_IRQ");
+}
+
+/**
+ * dm_test_notification_str_set_config - Test Notification str set config
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_set_config(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_SET_CONFIG_REPLY),
+ "SET_CONFIG_REPLY");
+}
+
+/**
+ * dm_test_notification_str_dpia - Test Notification str dpia
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_dpia(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_DPIA_NOTIFICATION),
+ "DPIA_NOTIFICATION");
+}
+
+/**
+ * dm_test_notification_str_hpd_sense - Test Notification str hpd sense
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_hpd_sense(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_HPD_SENSE_NOTIFY),
+ "HPD_SENSE_NOTIFY");
+}
+
+/**
+ * dm_test_notification_str_fused_io - Test Notification str fused io
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_fused_io(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_FUSED_IO),
+ "FUSED_IO");
+}
+
+/**
+ * dm_test_notification_str_unknown - Test Notification str unknown
+ * @test: The KUnit test context
+ */
+static void dm_test_notification_str_unknown(struct kunit *test)
+{
+ KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_MAX), "<unknown>");
+}
+
+/* Tests for amdgpu_dm_irq_init() */
+
+/**
+ * dm_test_irq_init_initializes_lists - Test irq init initializes list heads
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_init_initializes_lists(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ int src;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[src]));
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[src]));
+ }
+}
+
+/* Tests for amdgpu_dm_irq_register_interrupt() */
+
+/**
+ * dm_test_irq_register_rejects_null_params - Test register rejects null params
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_register_rejects_null_params(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source = DC_IRQ_SOURCE_HPD1;
+
+ KUNIT_EXPECT_NULL(test,
+ amdgpu_dm_irq_register_interrupt(adev, NULL,
+ dm_test_irq_handler, NULL));
+ KUNIT_EXPECT_NULL(test,
+ amdgpu_dm_irq_register_interrupt(adev, &int_params, NULL, NULL));
+}
+
+/**
+ * dm_test_irq_register_rejects_invalid_context - Test register rejects context
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_register_rejects_invalid_context(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ int_params.int_context = INTERRUPT_CONTEXT_NUMBER;
+ int_params.irq_source = DC_IRQ_SOURCE_HPD1;
+
+ KUNIT_EXPECT_NULL(test,
+ amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, NULL));
+}
+
+/**
+ * dm_test_irq_register_rejects_invalid_source - Test register rejects source
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_register_rejects_invalid_source(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source = DC_IRQ_SOURCE_INVALID;
+
+ KUNIT_EXPECT_NULL(test,
+ amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, NULL));
+}
+
+/**
+ * dm_test_irq_register_adds_low_context_handler - Test register adds low handler
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_register_adds_low_context_handler(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+ void *handler;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source = DC_IRQ_SOURCE_HPD1;
+
+ handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, adev);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
+ KUNIT_EXPECT_FALSE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1]));
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD1]));
+
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1,
+ dm_test_irq_handler);
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1]));
+}
+
+/**
+ * dm_test_irq_register_adds_high_context_handler - Test register adds high handler
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_register_adds_high_context_handler(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+ void *handler;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source = DC_IRQ_SOURCE_HPD2;
+
+ handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, adev);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
+ KUNIT_EXPECT_FALSE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD2]));
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD2]));
+
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD2,
+ dm_test_irq_handler);
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD2]));
+}
+
+/**
+ * dm_test_irq_register_multiple_handlers - Test register keeps multiple handlers
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_register_multiple_handlers(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+ struct list_head *hnd_list;
+ void *handler1, *handler2;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source = DC_IRQ_SOURCE_HPD1;
+
+ handler1 = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler1);
+ handler2 = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler_alt, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler2);
+
+ hnd_list = &adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1];
+ KUNIT_EXPECT_EQ(test, list_count_nodes(hnd_list), 2);
+
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1,
+ dm_test_irq_handler);
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1,
+ dm_test_irq_handler_alt);
+ KUNIT_EXPECT_TRUE(test, list_empty(hnd_list));
+}
+
+/**
+ * dm_test_irq_register_separate_contexts - Test register same source in two contexts
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_register_separate_contexts(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+ void *handler;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ int_params.irq_source = DC_IRQ_SOURCE_HPD5;
+
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
+
+ KUNIT_EXPECT_FALSE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD5]));
+ KUNIT_EXPECT_FALSE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD5]));
+
+ /*
+ * A single unregister call stops at the first context where the handler
+ * is found (low context), leaving the high context handler in place.
+ */
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD5,
+ dm_test_irq_handler);
+
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD5]));
+ KUNIT_EXPECT_FALSE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD5]));
+
+ /* A second call removes the remaining high context handler. */
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD5,
+ dm_test_irq_handler);
+
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD5]));
+}
+
+/* Tests for amdgpu_dm_irq_unregister_interrupt() */
+
+/**
+ * dm_test_irq_unregister_rejects_invalid_source - Test unregister rejects source
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_unregister_rejects_invalid_source(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_INVALID,
+ dm_test_irq_handler);
+
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1]));
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD1]));
+}
+
+/**
+ * dm_test_irq_unregister_rejects_null_handler - Test unregister rejects handler
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_unregister_rejects_null_handler(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1,
+ DAL_INVALID_IRQ_HANDLER_IDX);
+
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1]));
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD1]));
+}
+
+/**
+ * dm_test_irq_unregister_handler_not_found - Test unregister keeps unmatched handler
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_unregister_handler_not_found(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+ void *handler;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source = DC_IRQ_SOURCE_HPD1;
+ handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
+
+ /* Unregister a handler that was never registered for this source. */
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1,
+ dm_test_irq_handler_alt);
+
+ /* The originally registered handler must still be present. */
+ KUNIT_EXPECT_FALSE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1]));
+
+ amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1,
+ dm_test_irq_handler);
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1]));
+}
+
+/* Tests for amdgpu_dm_irq_fini() */
+
+/**
+ * dm_test_irq_fini_removes_registered_handlers - Test fini removes handlers
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_fini_removes_registered_handlers(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_interrupt_params int_params = { 0 };
+ void *handler;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
+ int_params.irq_source = DC_IRQ_SOURCE_HPD3;
+ handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
+
+ int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
+ int_params.irq_source = DC_IRQ_SOURCE_HPD4;
+ handler = amdgpu_dm_irq_register_interrupt(adev, &int_params,
+ dm_test_irq_handler, adev);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler);
+
+ amdgpu_dm_irq_fini(adev);
+
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD3]));
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD4]));
+}
+
+/**
+ * dm_test_irq_fini_on_empty_tables - Test fini on tables with no handlers
+ * @test: The KUnit test context
+ */
+static void dm_test_irq_fini_on_empty_tables(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ int src;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
+ KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
+
+ amdgpu_dm_irq_fini(adev);
+
+ for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_low_tab[src]));
+ KUNIT_EXPECT_TRUE(test,
+ list_empty(&adev->dm.irq_handler_list_high_tab[src]));
+ }
+}
+
+/* Tests for amdgpu_dm_get_crtc_by_otg_inst() */
+
+/**
+ * dm_test_get_crtc_by_otg_inst_returns_match - Test CRTC lookup by OTG instance
+ * @test: The KUnit test context
+ */
+static void dm_test_get_crtc_by_otg_inst_returns_match(struct kunit *test)
+{
+ struct amdgpu_crtc *acrtc_a, *acrtc_b;
+ struct amdgpu_device *adev;
+ struct drm_device *drm;
+
+ adev = dm_kunit_alloc_adev(test);
+ drm = &adev->ddev;
+
+ acrtc_a = kunit_kzalloc(test, sizeof(*acrtc_a), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc_a);
+ acrtc_b = kunit_kzalloc(test, sizeof(*acrtc_b), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc_b);
+
+ INIT_LIST_HEAD(&acrtc_a->base.head);
+ INIT_LIST_HEAD(&acrtc_b->base.head);
+ acrtc_a->otg_inst = 1;
+ acrtc_b->otg_inst = 3;
+
+ list_add_tail(&acrtc_a->base.head, &drm->mode_config.crtc_list);
+ KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_crtc_list_del,
+ acrtc_a), 0);
+ list_add_tail(&acrtc_b->base.head, &drm->mode_config.crtc_list);
+ KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_crtc_list_del,
+ acrtc_b), 0);
+
+ KUNIT_EXPECT_PTR_EQ(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 3), acrtc_b);
+}
+
+/**
+ * dm_test_get_crtc_by_otg_inst_returns_null - Test CRTC lookup misses unknown OTG
+ * @test: The KUnit test context
+ */
+static void dm_test_get_crtc_by_otg_inst_returns_null(struct kunit *test)
+{
+ struct amdgpu_crtc *acrtc;
+ struct amdgpu_device *adev;
+ struct drm_device *drm;
+
+ adev = dm_kunit_alloc_adev(test);
+ drm = &adev->ddev;
+
+ acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc);
+
+ INIT_LIST_HEAD(&acrtc->base.head);
+ acrtc->otg_inst = 2;
+
+ list_add_tail(&acrtc->base.head, &drm->mode_config.crtc_list);
+ KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_crtc_list_del,
+ acrtc), 0);
+
+ KUNIT_EXPECT_NULL(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 5));
+}
+
+/**
+ * dm_test_get_crtc_by_otg_inst_empty_list - Test CRTC lookup on empty CRTC list
+ * @test: The KUnit test context
+ */
+static void dm_test_get_crtc_by_otg_inst_empty_list(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+
+ adev = dm_kunit_alloc_adev(test);
+
+ KUNIT_EXPECT_NULL(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 0));
+}
+
+static struct kunit_case amdgpu_dm_irq_tests[] = {
+ /* amdgpu_dm_hpd_to_dal_irq_source */
+ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd1),
+ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd2),
+ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd3),
+ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd4),
+ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd5),
+ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd6),
+ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_invalid),
+ KUNIT_CASE(dm_test_hpd_to_dal_irq_source_out_of_range),
+ /* are_sinks_equal */
+ KUNIT_CASE(dm_test_are_sinks_equal_both_null),
+ KUNIT_CASE(dm_test_are_sinks_equal_first_null),
+ KUNIT_CASE(dm_test_are_sinks_equal_second_null),
+ KUNIT_CASE(dm_test_are_sinks_equal_different_signal),
+ KUNIT_CASE(dm_test_are_sinks_equal_different_edid_length),
+ KUNIT_CASE(dm_test_are_sinks_equal_different_edid_data),
+ KUNIT_CASE(dm_test_are_sinks_equal_identical),
+ KUNIT_CASE(dm_test_are_sinks_equal_zero_length),
+ KUNIT_CASE(dm_test_are_sinks_equal_full_edid_identical),
+ KUNIT_CASE(dm_test_are_sinks_equal_full_edid_last_byte_differs),
+ /* dmub_notification_type_str */
+ KUNIT_CASE(dm_test_notification_str_no_data),
+ KUNIT_CASE(dm_test_notification_str_aux_reply),
+ KUNIT_CASE(dm_test_notification_str_hpd),
+ KUNIT_CASE(dm_test_notification_str_hpd_irq),
+ KUNIT_CASE(dm_test_notification_str_set_config),
+ KUNIT_CASE(dm_test_notification_str_dpia),
+ KUNIT_CASE(dm_test_notification_str_hpd_sense),
+ KUNIT_CASE(dm_test_notification_str_fused_io),
+ KUNIT_CASE(dm_test_notification_str_unknown),
+ /* amdgpu_dm_irq_init */
+ KUNIT_CASE(dm_test_irq_init_initializes_lists),
+ /* amdgpu_dm_irq_register_interrupt */
+ KUNIT_CASE(dm_test_irq_register_rejects_null_params),
+ KUNIT_CASE(dm_test_irq_register_rejects_invalid_context),
+ KUNIT_CASE(dm_test_irq_register_rejects_invalid_source),
+ KUNIT_CASE(dm_test_irq_register_adds_low_context_handler),
+ KUNIT_CASE(dm_test_irq_register_adds_high_context_handler),
+ KUNIT_CASE(dm_test_irq_register_multiple_handlers),
+ KUNIT_CASE(dm_test_irq_register_separate_contexts),
+ /* amdgpu_dm_irq_unregister_interrupt */
+ KUNIT_CASE(dm_test_irq_unregister_rejects_invalid_source),
+ KUNIT_CASE(dm_test_irq_unregister_rejects_null_handler),
+ KUNIT_CASE(dm_test_irq_unregister_handler_not_found),
+ /* amdgpu_dm_irq_fini */
+ KUNIT_CASE(dm_test_irq_fini_removes_registered_handlers),
+ KUNIT_CASE(dm_test_irq_fini_on_empty_tables),
+ /* amdgpu_dm_get_crtc_by_otg_inst */
+ KUNIT_CASE(dm_test_get_crtc_by_otg_inst_returns_match),
+ KUNIT_CASE(dm_test_get_crtc_by_otg_inst_returns_null),
+ KUNIT_CASE(dm_test_get_crtc_by_otg_inst_empty_list),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_irq_test_suite = {
+ .name = "amdgpu_dm_irq",
+ .test_cases = amdgpu_dm_irq_tests,
+};
+
+kunit_test_suite(amdgpu_dm_irq_test_suite);
+
+MODULE_AUTHOR("AMD");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_irq");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c
index f3b3f77aafd5..7dfb3b351d20 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c
@@ -9,20 +9,7 @@
#include "dc.h"
#include "amdgpu_dm_ism.h"
-
-/*
- * Helper: allocate and zero-initialise a dc_stream_state for timing tests.
- * Only the timing sub-struct is accessed by the functions under test.
- */
-static struct dc_stream_state *alloc_test_stream(struct kunit *test)
-{
- struct dc_stream_state *stream;
-
- stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, stream);
-
- return stream;
-}
+#include "amdgpu_dm_kunit_test_helpers.h"
/*
* Helper: allocate and zero-initialise an ISM instance.
@@ -275,7 +262,7 @@ static void dm_test_ism_sso_delay_null_stream(struct kunit *test)
static void dm_test_ism_sso_delay_zero_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
@@ -288,7 +275,7 @@ static void dm_test_ism_sso_delay_zero_frames(struct kunit *test)
static void dm_test_ism_sso_delay_1080p60_3frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
uint64_t expected_one_frame_ns, expected;
/*
@@ -311,7 +298,7 @@ static void dm_test_ism_sso_delay_1080p60_3frames(struct kunit *test)
static void dm_test_ism_sso_delay_4k60_1frame(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
uint64_t expected_one_frame_ns;
/*
@@ -347,7 +334,7 @@ static void dm_test_ism_idle_delay_null_stream(struct kunit *test)
static void dm_test_ism_idle_delay_zero_filter_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
@@ -361,7 +348,7 @@ static void dm_test_ism_idle_delay_zero_filter_frames(struct kunit *test)
static void dm_test_ism_idle_delay_zero_entry_count(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
@@ -376,7 +363,7 @@ static void dm_test_ism_idle_delay_zero_entry_count(struct kunit *test)
static void dm_test_ism_idle_delay_zero_delay_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
@@ -392,7 +379,7 @@ static void dm_test_ism_idle_delay_zero_delay_frames(struct kunit *test)
static void dm_test_ism_idle_delay_no_short_idles(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
uint64_t one_frame_ns;
/*
@@ -426,7 +413,7 @@ static void dm_test_ism_idle_delay_no_short_idles(struct kunit *test)
static void dm_test_ism_idle_delay_enough_short_idles(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
uint64_t one_frame_ns, expected;
/*
@@ -461,7 +448,7 @@ static void dm_test_ism_idle_delay_enough_short_idles(struct kunit *test)
static void dm_test_ism_idle_delay_wraps_around_buffer(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
uint64_t one_frame_ns, expected;
/*
@@ -497,7 +484,7 @@ static void dm_test_ism_idle_delay_wraps_around_buffer(struct kunit *test)
static void dm_test_ism_idle_delay_old_history_cutoff(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
uint64_t one_frame_ns;
/*
@@ -545,7 +532,7 @@ static void dm_test_ism_idle_delay_old_history_cutoff(struct kunit *test)
static void dm_test_ism_idle_delay_mixed_durations(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
uint64_t one_frame_ns;
/*
@@ -586,7 +573,7 @@ static void dm_test_ism_idle_delay_mixed_durations(struct kunit *test)
static void dm_test_ism_idle_delay_entry_count_exceeds_history_size(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
- struct dc_stream_state *stream = alloc_test_stream(test);
+ struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL);
uint64_t one_frame_ns, expected;
/*
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c
new file mode 100644
index 000000000000..58615cdbe854
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit test helpers for amdgpu_dm tests.
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+#include <linux/module.h>
+#include <drm/drm_kunit_helpers.h>
+
+#include "dc.h"
+#include "core_types.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+
+struct amdgpu_device *dm_kunit_alloc_adev(struct kunit *test)
+{
+ struct drm_device *drm;
+ struct device *dev;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+ sizeof(struct amdgpu_device),
+ offsetof(struct amdgpu_device, ddev),
+ DRIVER_MODESET | DRIVER_ATOMIC);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+
+ return drm_to_adev(drm);
+}
+EXPORT_SYMBOL(dm_kunit_alloc_adev);
+
+struct dc_link *dm_kunit_alloc_link(struct kunit *test)
+{
+ struct dc_link *link;
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ return link;
+}
+EXPORT_SYMBOL(dm_kunit_alloc_link);
+
+struct dc_link *dm_kunit_alloc_link_with_ctx(struct kunit *test)
+{
+ struct dc_link *link;
+ struct dc_context *ctx;
+ struct dc *dc;
+
+ link = dm_kunit_alloc_link(test);
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+
+ link->ctx = ctx;
+ ctx->dc = dc;
+ dc->ctx = ctx;
+
+ return link;
+}
+EXPORT_SYMBOL(dm_kunit_alloc_link_with_ctx);
+
+struct amdgpu_display_manager *dm_kunit_alloc_dm(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+ struct dc *dc;
+ struct dc_state *state;
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm);
+
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, state);
+
+ dm->dc = dc;
+ dc->current_state = state;
+
+ return dm;
+}
+EXPORT_SYMBOL(dm_kunit_alloc_dm);
+
+struct dc_stream_state *dm_kunit_alloc_stream(struct kunit *test,
+ struct dc_link *link)
+{
+ struct dc_stream_state *stream;
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+
+ stream->link = link;
+ kref_init(&stream->refcount);
+
+ return stream;
+}
+EXPORT_SYMBOL(dm_kunit_alloc_stream);
+
+void dm_kunit_add_stream_to_state(struct kunit *test, struct dc_state *state,
+ unsigned int index, struct dc_link *link)
+{
+ struct dc_stream_state *stream;
+
+ KUNIT_ASSERT_LT(test, index, (unsigned int)MAX_PIPES);
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stream);
+
+ stream->link = link;
+ state->streams[index] = stream;
+ if (state->stream_count <= index)
+ state->stream_count = index + 1;
+}
+EXPORT_SYMBOL(dm_kunit_add_stream_to_state);
+
+struct amdgpu_dm_connector *dm_kunit_alloc_connector(struct kunit *test,
+ struct amdgpu_device *adev,
+ struct dc_link *link)
+{
+ struct amdgpu_dm_connector *aconnector;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ if (adev)
+ aconnector->base.dev = &adev->ddev;
+ aconnector->dc_link = link;
+
+ return aconnector;
+}
+EXPORT_SYMBOL(dm_kunit_alloc_connector);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("KUnit test helpers for amdgpu_dm tests");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h
new file mode 100644
index 000000000000..0f1c48fa2128
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * KUnit test helpers for amdgpu_dm tests.
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#ifndef AMDGPU_DM_KUNIT_TEST_HELPERS_H
+#define AMDGPU_DM_KUNIT_TEST_HELPERS_H
+
+#include <kunit/test.h>
+
+struct amdgpu_device;
+struct amdgpu_display_manager;
+struct amdgpu_dm_connector;
+struct dc_link;
+struct dc_state;
+struct dc_stream_state;
+
+struct amdgpu_device *dm_kunit_alloc_adev(struct kunit *test);
+struct dc_link *dm_kunit_alloc_link(struct kunit *test);
+struct dc_link *dm_kunit_alloc_link_with_ctx(struct kunit *test);
+struct amdgpu_display_manager *dm_kunit_alloc_dm(struct kunit *test);
+struct dc_stream_state *dm_kunit_alloc_stream(struct kunit *test,
+ struct dc_link *link);
+void dm_kunit_add_stream_to_state(struct kunit *test, struct dc_state *state,
+ unsigned int index, struct dc_link *link);
+struct amdgpu_dm_connector *dm_kunit_alloc_connector(struct kunit *test,
+ struct amdgpu_device *adev,
+ struct dc_link *link);
+
+#endif /* AMDGPU_DM_KUNIT_TEST_HELPERS_H */
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
new file mode 100644
index 000000000000..3a663ee0ca2b
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c
@@ -0,0 +1,1092 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_mst_types.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_kunit_helpers.h>
+#include <drm/display/drm_dp.h>
+#include <drm/display/drm_dp_helper.h>
+#include <drm/display/drm_dp_mst_helper.h>
+
+#include "dc.h"
+#include "dpcd_defs.h"
+#include "dmub_cmd.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_mst_types.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+#include "inc/link_service.h"
+
+/*
+ * Minimal mock DPCD backing store and AUX transfer callback used to exercise
+ * the DPCD read paths without real hardware.
+ */
+static u8 dm_mst_test_dpcd[0x10];
+static u8 dm_mst_test_desc_dpcd[0x10];
+static struct aux_payload dm_mst_test_last_payload;
+static int dm_mst_test_aux_transfer_raw_result;
+static enum aux_return_code_type dm_mst_test_aux_transfer_raw_operation_result;
+
+static int dm_mst_test_aux_transfer_raw(struct ddc_service *ddc,
+ struct aux_payload *payload,
+ enum aux_return_code_type *operation_result)
+{
+ size_t i;
+
+ dm_mst_test_last_payload = *payload;
+ *operation_result = dm_mst_test_aux_transfer_raw_operation_result;
+
+ if (dm_mst_test_aux_transfer_raw_result)
+ return dm_mst_test_aux_transfer_raw_result;
+
+ if (payload->write)
+ return 0;
+
+ for (i = 0; i < payload->length; i++)
+ payload->data[i] = dm_mst_test_dpcd[(payload->address + i) & 0xf];
+
+ return payload->length;
+}
+
+static void dm_mst_test_setup_dm_aux(struct amdgpu_dm_dp_aux *dm_aux,
+ struct ddc_service *ddc,
+ struct dc_link *link,
+ struct dc *dc,
+ struct link_service *link_srv,
+ struct dc_context *ctx,
+ struct amdgpu_device *adev)
+{
+ memset(&dm_mst_test_last_payload, 0, sizeof(dm_mst_test_last_payload));
+ dm_mst_test_aux_transfer_raw_result = 0;
+ dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_SUCCESS;
+ link_srv->aux_transfer_raw = dm_mst_test_aux_transfer_raw;
+ dc->link_srv = link_srv;
+ link->dc = dc;
+ ctx->driver_context = adev;
+ ddc->link = link;
+ ddc->ctx = ctx;
+ dm_aux->ddc_service = ddc;
+ dm_aux->aux.name = "dm_mst_test_dm_aux";
+ dm_aux->aux.transfer = dm_dp_aux_transfer;
+ drm_dp_aux_init(&dm_aux->aux);
+ drm_dp_dpcd_set_probe(&dm_aux->aux, false);
+}
+
+static const struct dc_link_status *dm_mst_test_get_status(const struct dc_link *link)
+{
+ return &link->link_status;
+}
+
+static ssize_t dm_mst_test_aux_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ size_t i;
+
+ switch (msg->request & ~DP_AUX_I2C_MOT) {
+ case DP_AUX_NATIVE_READ:
+ for (i = 0; i < msg->size; i++)
+ ((u8 *)msg->buffer)[i] =
+ dm_mst_test_dpcd[(msg->address + i) & 0xf];
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+ return msg->size;
+ case DP_AUX_NATIVE_WRITE:
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+ return msg->size;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t dm_mst_test_desc_aux_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ size_t i;
+
+ if ((msg->request & ~DP_AUX_I2C_MOT) != DP_AUX_NATIVE_READ)
+ return -EINVAL;
+
+ for (i = 0; i < msg->size; i++)
+ ((u8 *)msg->buffer)[i] = dm_mst_test_desc_dpcd[msg->address + i - DP_BRANCH_OUI];
+
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+ return msg->size;
+}
+
+/* Tests for needs_dsc_aux_workaround */
+
+/**
+ * dm_mst_test_needs_dsc_aux_workaround_match - Test workaround triggers for matching device
+ * @test: KUnit test context
+ *
+ * Verify that needs_dsc_aux_workaround() returns true when the link has
+ * the specific branch device ID, DPCD rev 1.4, and sink count >= 2.
+ */
+static void dm_mst_test_needs_dsc_aux_workaround_match(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24;
+ link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
+ link->dpcd_caps.sink_count.bits.SINK_COUNT = 2;
+
+ KUNIT_EXPECT_TRUE(test, needs_dsc_aux_workaround(link));
+}
+
+/**
+ * dm_mst_test_needs_dsc_aux_workaround_rev12 - Test workaround triggers for DPCD rev 1.2
+ * @test: KUnit test context
+ *
+ * Verify that needs_dsc_aux_workaround() returns true when the link has
+ * the specific branch device ID, DPCD rev 1.2, and sink count >= 2.
+ */
+static void dm_mst_test_needs_dsc_aux_workaround_rev12(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24;
+ link->dpcd_caps.dpcd_rev.raw = DPCD_REV_12;
+ link->dpcd_caps.sink_count.bits.SINK_COUNT = 3;
+
+ KUNIT_EXPECT_TRUE(test, needs_dsc_aux_workaround(link));
+}
+
+/**
+ * dm_mst_test_needs_dsc_aux_workaround_wrong_dev_id - Test workaround skipped for wrong device
+ * @test: KUnit test context
+ *
+ * Verify that needs_dsc_aux_workaround() returns false when the branch
+ * device ID does not match DP_BRANCH_DEVICE_ID_90CC24.
+ */
+static void dm_mst_test_needs_dsc_aux_workaround_wrong_dev_id(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.branch_dev_id = 0x123456;
+ link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
+ link->dpcd_caps.sink_count.bits.SINK_COUNT = 2;
+
+ KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(link));
+}
+
+/**
+ * dm_mst_test_needs_dsc_aux_workaround_wrong_rev - Test workaround skipped for unsupported rev
+ * @test: KUnit test context
+ *
+ * Verify that needs_dsc_aux_workaround() returns false when the DPCD
+ * revision is neither 1.2 nor 1.4.
+ */
+static void dm_mst_test_needs_dsc_aux_workaround_wrong_rev(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24;
+ link->dpcd_caps.dpcd_rev.raw = 0x11; /* DPCD 1.1 */
+ link->dpcd_caps.sink_count.bits.SINK_COUNT = 2;
+
+ KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(link));
+}
+
+/**
+ * dm_mst_test_needs_dsc_aux_workaround_low_sink_count - Test workaround skipped for single sink
+ * @test: KUnit test context
+ *
+ * Verify that needs_dsc_aux_workaround() returns false when the sink
+ * count is less than 2, even if device ID and DPCD rev match.
+ */
+static void dm_mst_test_needs_dsc_aux_workaround_low_sink_count(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24;
+ link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
+ link->dpcd_caps.sink_count.bits.SINK_COUNT = 1;
+
+ KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(link));
+}
+
+/**
+ * dm_mst_test_needs_dsc_aux_workaround_zero_sink_count - Test workaround skipped for zero sinks
+ * @test: KUnit test context
+ *
+ * Verify that needs_dsc_aux_workaround() returns false when the sink
+ * count is zero, even if device ID and DPCD rev match.
+ */
+static void dm_mst_test_needs_dsc_aux_workaround_zero_sink_count(struct kunit *test)
+{
+ struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link);
+
+ link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24;
+ link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
+ link->dpcd_caps.sink_count.bits.SINK_COUNT = 0;
+
+ KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(link));
+}
+
+/* Tests for dm_mst_get_pbn_divider */
+
+/**
+ * dm_mst_test_pbn_divider_null_link - Test pbn_divider with NULL link
+ * @test: KUnit test context
+ *
+ * Verify that dm_mst_get_pbn_divider() returns 0 when passed a NULL
+ * link pointer without crashing.
+ */
+static void dm_mst_test_pbn_divider_null_link(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, dm_mst_get_pbn_divider(NULL), 0U);
+}
+
+/* Tests for amdgpu_dm_mst_reset_mst_connector_setting */
+
+/**
+ * dm_mst_test_reset_connector_setting - Test MST connector setting reset
+ * @test: KUnit test context
+ *
+ * Verify that amdgpu_dm_mst_reset_mst_connector_setting() clears the cached
+ * EDID, DSC AUX, passthrough AUX, local bandwidth, and VC PBN state.
+ */
+static void dm_mst_test_reset_connector_setting(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_dp_mst_port *port;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ KUNIT_ASSERT_NOT_NULL(test, port);
+
+ aconnector->drm_edid = (const struct drm_edid *)test;
+ aconnector->dsc_aux = (struct drm_dp_aux *)test;
+ aconnector->mst_output_port = port;
+ aconnector->mst_output_port->passthrough_aux = (struct drm_dp_aux *)test;
+ aconnector->mst_local_bw = 12345;
+ aconnector->vc_full_pbn = 678;
+
+ amdgpu_dm_mst_reset_mst_connector_setting(aconnector);
+
+ KUNIT_EXPECT_TRUE(test, aconnector->drm_edid == NULL);
+ KUNIT_EXPECT_TRUE(test, aconnector->dsc_aux == NULL);
+ KUNIT_EXPECT_TRUE(test, aconnector->mst_output_port->passthrough_aux == NULL);
+ KUNIT_EXPECT_EQ(test, aconnector->mst_local_bw, 0U);
+ KUNIT_EXPECT_EQ(test, aconnector->vc_full_pbn, 0U);
+}
+
+/* Tests for retrieve_downstream_port_device */
+
+/**
+ * dm_mst_test_retrieve_downstream_no_aux - Test retrieval bails out without AUX
+ * @test: KUnit test context
+ *
+ * Verify that retrieve_downstream_port_device() returns false when the
+ * connector has no DSC AUX channel and therefore cannot read DPCD.
+ */
+static void dm_mst_test_retrieve_downstream_no_aux(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ aconnector->dsc_aux = NULL;
+
+ KUNIT_EXPECT_FALSE(test, retrieve_downstream_port_device(aconnector));
+}
+
+/**
+ * dm_mst_test_retrieve_downstream_present - Test retrieval parses DPCD 0x05
+ * @test: KUnit test context
+ *
+ * Verify that retrieve_downstream_port_device() reads DP_DOWNSTREAMPORT_PRESENT
+ * over a mock AUX channel and caches the parsed downstream port fields.
+ */
+static void dm_mst_test_retrieve_downstream_present(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_dp_aux *aux;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ KUNIT_ASSERT_NOT_NULL(test, aux);
+
+ memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
+ /* PORT_PRESENT = 1, PORT_TYPE = 2 (0b101) */
+ dm_mst_test_dpcd[DP_DOWNSTREAMPORT_PRESENT] = 0x05;
+
+ aux->name = "dm_mst_test_aux";
+ aux->transfer = dm_mst_test_aux_transfer;
+ drm_dp_aux_init(aux);
+ drm_dp_dpcd_set_probe(aux, false);
+ aconnector->dsc_aux = aux;
+
+ KUNIT_EXPECT_TRUE(test, retrieve_downstream_port_device(aconnector));
+ KUNIT_EXPECT_EQ(test,
+ (int)aconnector->mst_downstream_port_present.fields.PORT_PRESENT, 1);
+ KUNIT_EXPECT_EQ(test,
+ (int)aconnector->mst_downstream_port_present.fields.PORT_TYPE, 2);
+}
+
+/* Tests for retrieve_branch_specific_data */
+
+/**
+ * dm_mst_test_retrieve_branch_no_parent - Test branch lookup needs a parent port
+ * @test: KUnit test context
+ *
+ * Verify that retrieve_branch_specific_data() returns false when the MST
+ * output port has no parent branch device to query.
+ */
+static void dm_mst_test_retrieve_branch_no_parent(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_dp_mst_port *port;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ KUNIT_ASSERT_NOT_NULL(test, port);
+
+ port->parent = NULL;
+ aconnector->mst_output_port = port;
+
+ KUNIT_EXPECT_FALSE(test, retrieve_branch_specific_data(aconnector));
+}
+
+/**
+ * dm_mst_test_retrieve_branch_reads_oui - Test branch OUI parsing
+ * @test: KUnit test context
+ *
+ * Verify that retrieve_branch_specific_data() reads the immediate upstream
+ * branch descriptor and caches its IEEE OUI value on the connector.
+ */
+static void dm_mst_test_retrieve_branch_reads_oui(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_dp_mst_topology_mgr *mgr;
+ struct drm_dp_mst_branch *branch;
+ struct drm_dp_mst_port *port;
+ struct drm_dp_aux *aux;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ mgr = kunit_kzalloc(test, sizeof(*mgr), GFP_KERNEL);
+ branch = kunit_kzalloc(test, sizeof(*branch), GFP_KERNEL);
+ port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+ aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ KUNIT_ASSERT_NOT_NULL(test, mgr);
+ KUNIT_ASSERT_NOT_NULL(test, branch);
+ KUNIT_ASSERT_NOT_NULL(test, port);
+ KUNIT_ASSERT_NOT_NULL(test, aux);
+
+ memset(dm_mst_test_desc_dpcd, 0, sizeof(dm_mst_test_desc_dpcd));
+ dm_mst_test_desc_dpcd[0] = 0x12;
+ dm_mst_test_desc_dpcd[1] = 0x34;
+ dm_mst_test_desc_dpcd[2] = 0x56;
+
+ aux->name = "dm_mst_test_desc_aux";
+ aux->transfer = dm_mst_test_desc_aux_transfer;
+ drm_dp_aux_init(aux);
+ drm_dp_dpcd_set_probe(aux, false);
+ mgr->aux = aux;
+ port->parent = branch;
+ port->mgr = mgr;
+ port->aux.drm_dev = NULL;
+ aconnector->mst_output_port = port;
+
+ KUNIT_EXPECT_TRUE(test, retrieve_branch_specific_data(aconnector));
+ KUNIT_EXPECT_EQ(test, aconnector->branch_ieee_oui, 0x123456U);
+}
+
+/**
+ * dm_mst_test_aux_result_success - AUX_RET_SUCCESS preserves the input result.
+ * @test: KUnit test context.
+ *
+ * On success the original (negative) transfer result must be returned unchanged.
+ */
+static void dm_mst_test_aux_result_success(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-5, AUX_RET_SUCCESS), (ssize_t)-5);
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(3, AUX_RET_SUCCESS), (ssize_t)3);
+}
+
+/**
+ * dm_mst_test_aux_result_eio - HPD/unknown/protocol errors map to -EIO.
+ * @test: KUnit test context.
+ *
+ * AUX_RET_ERROR_HPD_DISCON, AUX_RET_ERROR_UNKNOWN,
+ * AUX_RET_ERROR_INVALID_OPERATION and AUX_RET_ERROR_PROTOCOL_ERROR all map to -EIO.
+ */
+static void dm_mst_test_aux_result_eio(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_HPD_DISCON),
+ (ssize_t)-EIO);
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_UNKNOWN),
+ (ssize_t)-EIO);
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_INVALID_OPERATION),
+ (ssize_t)-EIO);
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_PROTOCOL_ERROR),
+ (ssize_t)-EIO);
+}
+
+/**
+ * dm_mst_test_aux_result_ebusy - invalid reply / engine acquire map to -EBUSY.
+ * @test: KUnit test context.
+ *
+ * AUX_RET_ERROR_INVALID_REPLY and AUX_RET_ERROR_ENGINE_ACQUIRE map to -EBUSY.
+ */
+static void dm_mst_test_aux_result_ebusy(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_INVALID_REPLY),
+ (ssize_t)-EBUSY);
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_ENGINE_ACQUIRE),
+ (ssize_t)-EBUSY);
+}
+
+/**
+ * dm_mst_test_aux_result_timeout - AUX_RET_ERROR_TIMEOUT maps to -ETIMEDOUT.
+ * @test: KUnit test context.
+ */
+static void dm_mst_test_aux_result_timeout(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_TIMEOUT),
+ (ssize_t)-ETIMEDOUT);
+}
+
+/**
+ * dm_mst_test_aux_transfer_native_read - native AUX read through DM callback.
+ * @test: KUnit test context.
+ *
+ * The DM AUX transfer callback should build a read payload, call the DC link
+ * service, and return the number of bytes provided by the fake backend.
+ */
+static void dm_mst_test_aux_transfer_native_read(struct kunit *test)
+{
+ struct amdgpu_dm_dp_aux *dm_aux;
+ struct amdgpu_device *adev;
+ struct ddc_service *ddc;
+ struct dc_link *link;
+ struct dc *dc;
+ struct link_service *link_srv;
+ struct dc_context *ctx;
+ u8 buffer[3] = { 0 };
+ ssize_t ret;
+
+ dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ddc);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+ KUNIT_ASSERT_NOT_NULL(test, link_srv);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
+ dm_mst_test_dpcd[4] = 0xaa;
+ dm_mst_test_dpcd[5] = 0xbb;
+ dm_mst_test_dpcd[6] = 0xcc;
+ dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+
+ ret = drm_dp_dpcd_read(&dm_aux->aux, 4, buffer, sizeof(buffer));
+
+ KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer));
+ KUNIT_EXPECT_EQ(test, buffer[0], (u8)0xaa);
+ KUNIT_EXPECT_EQ(test, buffer[1], (u8)0xbb);
+ KUNIT_EXPECT_EQ(test, buffer[2], (u8)0xcc);
+ KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.write);
+ KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.i2c_over_aux);
+ KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 4U);
+}
+
+/**
+ * dm_mst_test_aux_transfer_native_write - native AUX write through DM callback.
+ * @test: KUnit test context.
+ *
+ * A successful write with an ACK reply should report the requested write size
+ * and pass a write payload into the fake DC link service.
+ */
+static void dm_mst_test_aux_transfer_native_write(struct kunit *test)
+{
+ struct amdgpu_dm_dp_aux *dm_aux;
+ struct amdgpu_device *adev;
+ struct ddc_service *ddc;
+ struct dc_link *link;
+ struct dc *dc;
+ struct link_service *link_srv;
+ struct dc_context *ctx;
+ u8 buffer[2] = { 0x11, 0x22 };
+ ssize_t ret;
+
+ dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ddc);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+ KUNIT_ASSERT_NOT_NULL(test, link_srv);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+
+ ret = drm_dp_dpcd_write(&dm_aux->aux, 7, buffer, sizeof(buffer));
+
+ KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer));
+ KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write);
+ KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.i2c_over_aux);
+ KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 7U);
+ KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.length,
+ (u32)sizeof(buffer));
+}
+
+/**
+ * dm_mst_test_aux_transfer_partial_write - partial write reports byte count.
+ * @test: KUnit test context.
+ *
+ * A positive write result from the DC link service should be interpreted as a
+ * partial write and replaced with the first payload byte.
+ */
+static void dm_mst_test_aux_transfer_partial_write(struct kunit *test)
+{
+ struct amdgpu_dm_dp_aux *dm_aux;
+ struct amdgpu_device *adev;
+ struct ddc_service *ddc;
+ struct dc_link *link;
+ struct dc *dc;
+ struct link_service *link_srv;
+ struct dc_context *ctx;
+ u8 buffer[2] = { 1, 0xaa };
+ struct drm_dp_aux_msg msg = {
+ .address = 7,
+ .request = DP_AUX_NATIVE_WRITE,
+ .buffer = buffer,
+ .size = sizeof(buffer),
+ };
+ ssize_t ret;
+
+ dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ddc);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+ KUNIT_ASSERT_NOT_NULL(test, link_srv);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+ dm_mst_test_aux_transfer_raw_result = 1;
+
+ ret = dm_dp_aux_transfer(&dm_aux->aux, &msg);
+
+ KUNIT_EXPECT_EQ(test, ret, (ssize_t)buffer[0]);
+ KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write);
+ KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 7U);
+}
+
+/**
+ * dm_mst_test_aux_transfer_error_result - transfer errors are remapped.
+ * @test: KUnit test context.
+ *
+ * A negative DC link service result should be converted through
+ * dm_dp_aux_transfer_result() using the returned AUX operation result.
+ */
+static void dm_mst_test_aux_transfer_error_result(struct kunit *test)
+{
+ struct amdgpu_dm_dp_aux *dm_aux;
+ struct amdgpu_device *adev;
+ struct ddc_service *ddc;
+ struct dc_link *link;
+ struct dc *dc;
+ struct link_service *link_srv;
+ struct dc_context *ctx;
+ u8 buffer[2] = { 0 };
+ ssize_t ret;
+
+ dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ddc);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+ KUNIT_ASSERT_NOT_NULL(test, link_srv);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+ dm_mst_test_aux_transfer_raw_result = -EIO;
+ dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_ERROR_TIMEOUT;
+
+ ret = drm_dp_dpcd_read(&dm_aux->aux, 4, buffer, sizeof(buffer));
+
+ KUNIT_EXPECT_EQ(test, ret, (ssize_t)-ETIMEDOUT);
+ KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.write);
+ KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 4U);
+}
+
+/**
+ * dm_mst_test_aux_transfer_hpd_discon_quirk - HPD disconnect quirk succeeds.
+ * @test: KUnit test context.
+ *
+ * AUX_RET_ERROR_HPD_DISCON on the sideband down request address should be
+ * treated as a successful transfer when the platform quirk is enabled.
+ */
+static void dm_mst_test_aux_transfer_hpd_discon_quirk(struct kunit *test)
+{
+ struct amdgpu_dm_dp_aux *dm_aux;
+ struct amdgpu_device *adev;
+ struct ddc_service *ddc;
+ struct dc_link *link;
+ struct dc *dc;
+ struct link_service *link_srv;
+ struct dc_context *ctx;
+ u8 buffer[2] = { 2, 0 };
+ ssize_t ret;
+
+ dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm_aux);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ddc);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+ KUNIT_ASSERT_NOT_NULL(test, link_srv);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
+ adev->dm.aux_hpd_discon_quirk = true;
+ dm_mst_test_aux_transfer_raw_result = -EIO;
+ dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_ERROR_HPD_DISCON;
+
+ ret = drm_dp_dpcd_write(&dm_aux->aux, DP_SIDEBAND_MSG_DOWN_REQ_BASE,
+ buffer, sizeof(buffer));
+
+ KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer));
+ KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write);
+ KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address,
+ DP_SIDEBAND_MSG_DOWN_REQ_BASE);
+}
+
+/**
+ * dm_mst_test_fill_payload_flags_native_write - native write request decode.
+ * @test: KUnit test context.
+ *
+ * DP_AUX_NATIVE_WRITE clears i2c_over_aux and sets write; no I2C bits set.
+ */
+static void dm_mst_test_fill_payload_flags_native_write(struct kunit *test)
+{
+ struct aux_payload payload = { 0 };
+
+ dm_dp_aux_fill_payload_flags(DP_AUX_NATIVE_WRITE, &payload);
+
+ KUNIT_EXPECT_FALSE(test, payload.i2c_over_aux);
+ KUNIT_EXPECT_TRUE(test, payload.write);
+ KUNIT_EXPECT_FALSE(test, payload.mot);
+ KUNIT_EXPECT_FALSE(test, payload.write_status_update);
+}
+
+/**
+ * dm_mst_test_fill_payload_flags_native_read - native read request decode.
+ * @test: KUnit test context.
+ *
+ * DP_AUX_NATIVE_READ keeps i2c_over_aux clear; the I2C_READ bit clears write.
+ */
+static void dm_mst_test_fill_payload_flags_native_read(struct kunit *test)
+{
+ struct aux_payload payload = { 0 };
+
+ dm_dp_aux_fill_payload_flags(DP_AUX_NATIVE_READ, &payload);
+
+ KUNIT_EXPECT_FALSE(test, payload.i2c_over_aux);
+ KUNIT_EXPECT_FALSE(test, payload.write);
+ KUNIT_EXPECT_FALSE(test, payload.mot);
+}
+
+/**
+ * dm_mst_test_fill_payload_flags_i2c_read_mot - I2C read with MOT request decode.
+ * @test: KUnit test context.
+ *
+ * DP_AUX_I2C_READ sets i2c_over_aux and clears write; DP_AUX_I2C_MOT sets mot.
+ */
+static void dm_mst_test_fill_payload_flags_i2c_read_mot(struct kunit *test)
+{
+ struct aux_payload payload = { 0 };
+
+ dm_dp_aux_fill_payload_flags(DP_AUX_I2C_READ | DP_AUX_I2C_MOT, &payload);
+
+ KUNIT_EXPECT_TRUE(test, payload.i2c_over_aux);
+ KUNIT_EXPECT_FALSE(test, payload.write);
+ KUNIT_EXPECT_TRUE(test, payload.mot);
+}
+
+/**
+ * dm_mst_test_fill_payload_flags_write_status - write status update decode.
+ * @test: KUnit test context.
+ *
+ * DP_AUX_I2C_WRITE_STATUS_UPDATE sets write_status_update.
+ */
+static void dm_mst_test_fill_payload_flags_write_status(struct kunit *test)
+{
+ struct aux_payload payload = { 0 };
+
+ dm_dp_aux_fill_payload_flags(DP_AUX_I2C_WRITE | DP_AUX_I2C_WRITE_STATUS_UPDATE,
+ &payload);
+
+ KUNIT_EXPECT_TRUE(test, payload.i2c_over_aux);
+ KUNIT_EXPECT_TRUE(test, payload.write_status_update);
+}
+
+/**
+ * dm_mst_test_msg_ready_mask - ESI mask selection per message-ready type.
+ * @test: KUnit test context.
+ *
+ * DOWN_REP and UP_REQ each select their single bit; other types select both.
+ */
+static void dm_mst_test_msg_ready_mask(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(DOWN_REP_MSG_RDY_EVENT),
+ (u8)DP_DOWN_REP_MSG_RDY);
+ KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(UP_REQ_MSG_RDY_EVENT),
+ (u8)DP_UP_REQ_MSG_RDY);
+ KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(DOWN_OR_UP_MSG_RDY_EVENT),
+ (u8)(DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY));
+ KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(NONE_MSG_RDY_EVENT),
+ (u8)(DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY));
+}
+
+/**
+ * dm_mst_test_select_esi_dpcd_legacy - pre-1.2 DPCD ESI address/length.
+ * @test: KUnit test context.
+ *
+ * For DPCD rev < 0x12 the legacy DP_SINK_COUNT address/length pair is selected.
+ */
+static void dm_mst_test_select_esi_dpcd_legacy(struct kunit *test)
+{
+ int dpcd_addr = -1;
+ u8 dpcd_bytes_to_read = 0;
+
+ dm_mst_select_esi_dpcd(0x11, &dpcd_addr, &dpcd_bytes_to_read);
+
+ KUNIT_EXPECT_EQ(test, dpcd_addr, DP_SINK_COUNT);
+ KUNIT_EXPECT_EQ(test, (int)dpcd_bytes_to_read,
+ (int)(DP_LANE0_1_STATUS - DP_SINK_COUNT));
+}
+
+/**
+ * dm_mst_test_select_esi_dpcd_esi - 1.2+ DPCD ESI address/length.
+ * @test: KUnit test context.
+ *
+ * For DPCD rev >= 0x12 the ESI DP_SINK_COUNT_ESI address/length pair is selected.
+ */
+static void dm_mst_test_select_esi_dpcd_esi(struct kunit *test)
+{
+ int dpcd_addr = -1;
+ u8 dpcd_bytes_to_read = 0;
+
+ dm_mst_select_esi_dpcd(0x14, &dpcd_addr, &dpcd_bytes_to_read);
+
+ KUNIT_EXPECT_EQ(test, dpcd_addr, DP_SINK_COUNT_ESI);
+ KUNIT_EXPECT_EQ(test, (int)dpcd_bytes_to_read,
+ (int)(DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI));
+}
+
+/**
+ * dm_mst_test_sideband_msg_ready_no_ready_bits - Test idle sideband event
+ * @test: KUnit test context
+ *
+ * Verify that dm_handle_mst_sideband_msg_ready_event() returns cleanly when
+ * the ESI read succeeds but no DOWN_REP/UP_REQ ready bits are set.
+ */
+static void dm_mst_test_sideband_msg_ready_no_ready_bits(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+ struct link_service *link_srv;
+ struct dc_link *link;
+ struct dc *dc;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ KUNIT_ASSERT_NOT_NULL(test, link_srv);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+
+ mutex_init(&aconnector->handle_mst_msg_ready);
+ link_srv->get_status = dm_mst_test_get_status;
+ dc->link_srv = link_srv;
+ link->dc = dc;
+ link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
+ link->link_status.dpcd_caps = &link->dpcd_caps;
+ aconnector->dc_link = link;
+ aconnector->dm_dp_aux.aux.name = "dm_mst_test_sideband_aux";
+ aconnector->dm_dp_aux.aux.transfer = dm_mst_test_aux_transfer;
+ drm_dp_aux_init(&aconnector->dm_dp_aux.aux);
+ drm_dp_dpcd_set_probe(&aconnector->dm_dp_aux.aux, false);
+ memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
+
+ dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr,
+ DOWN_REP_MSG_RDY_EVENT);
+
+ KUNIT_EXPECT_EQ(test, dm_mst_test_dpcd[1], (u8)0);
+}
+
+/**
+ * dm_mst_test_atomic_best_encoder - Test MST encoder selection
+ * @test: KUnit test context
+ *
+ * Verify that dm_mst_atomic_best_encoder() selects the MST encoder indexed by
+ * the CRTC ID in the connector's new atomic state. This uses structural DRM
+ * mocks only; registering connector/CRTC objects is unnecessary for this helper.
+ */
+static void dm_mst_test_atomic_best_encoder(struct kunit *test)
+{
+ struct drm_connector_state connector_state = { 0 };
+ struct drm_atomic_commit state = { 0 };
+ struct amdgpu_dm_connector *aconnector;
+ struct amdgpu_device *adev;
+ struct amdgpu_crtc *acrtc;
+ unsigned int connector_index = 3;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ KUNIT_ASSERT_NOT_NULL(test, acrtc);
+
+ aconnector->base.dev = &adev->ddev;
+ aconnector->base.index = connector_index;
+ acrtc->crtc_id = 2;
+ connector_state.connector = &aconnector->base;
+ connector_state.crtc = &acrtc->base;
+ state.num_connector = connector_index + 1;
+ state.connectors = kunit_kzalloc(test,
+ sizeof(*state.connectors) * state.num_connector,
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, state.connectors);
+ state.connectors[connector_index].ptr = &aconnector->base;
+ state.connectors[connector_index].new_state = &connector_state;
+
+ KUNIT_EXPECT_PTR_EQ(test, dm_mst_atomic_best_encoder(&aconnector->base, &state),
+ &adev->dm.mst_encoders[2].base);
+}
+
+/**
+ * dm_mst_test_create_fake_mst_encoders - Test fake MST encoder setup
+ * @test: KUnit test context
+ *
+ * Verify that dm_dp_create_fake_mst_encoders() initializes the requested MST
+ * encoders as DPMST encoders with the CRTC mask derived from the device state.
+ */
+static void dm_mst_test_create_fake_mst_encoders(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_device *drm;
+ int i;
+
+ adev = dm_kunit_alloc_adev(test);
+ drm = &adev->ddev;
+ adev->dm.display_indexes_num = 3;
+ adev->mode_info.num_crtc = 3;
+
+ dm_dp_create_fake_mst_encoders(adev);
+
+ for (i = 0; i < adev->dm.display_indexes_num; i++) {
+ struct drm_encoder *encoder = &adev->dm.mst_encoders[i].base;
+
+ KUNIT_EXPECT_PTR_EQ(test, encoder->dev, drm);
+ KUNIT_EXPECT_EQ(test, encoder->encoder_type, DRM_MODE_ENCODER_DPMST);
+ KUNIT_EXPECT_EQ(test, encoder->possible_crtcs, 0x7U);
+ KUNIT_EXPECT_TRUE(test, encoder->helper_private != NULL);
+ }
+}
+
+/**
+ * dm_mst_test_atomic_check_no_old_crtc - Test atomic check no-op path
+ * @test: KUnit test context
+ *
+ * Verify that dm_dp_mst_atomic_check() returns success when the MST port's old
+ * connector state has no CRTC, before MST topology state is required.
+ */
+static void dm_mst_test_atomic_check_no_old_crtc(struct kunit *test)
+{
+ struct drm_connector_state *old_conn_state;
+ struct drm_connector_state *new_conn_state;
+ struct drm_atomic_commit *state;
+ struct amdgpu_dm_connector *aconnector;
+ struct amdgpu_dm_connector *root;
+ struct drm_dp_mst_port *port;
+ unsigned int connector_index = 2;
+
+ old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL);
+ new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL);
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ root = kunit_kzalloc(test, sizeof(*root), GFP_KERNEL);
+ port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, old_conn_state);
+ KUNIT_ASSERT_NOT_NULL(test, new_conn_state);
+ KUNIT_ASSERT_NOT_NULL(test, state);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+ KUNIT_ASSERT_NOT_NULL(test, root);
+ KUNIT_ASSERT_NOT_NULL(test, port);
+
+ aconnector->base.index = connector_index;
+ aconnector->mst_root = root;
+ aconnector->mst_output_port = port;
+ port->connector = &aconnector->base;
+ old_conn_state->connector = &aconnector->base;
+ new_conn_state->connector = &aconnector->base;
+ state->num_connector = connector_index + 1;
+ state->connectors = kunit_kzalloc(test,
+ sizeof(*state->connectors) * state->num_connector,
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, state->connectors);
+ state->connectors[connector_index].ptr = &aconnector->base;
+ state->connectors[connector_index].old_state = old_conn_state;
+ state->connectors[connector_index].new_state = new_conn_state;
+
+ KUNIT_EXPECT_EQ(test, dm_dp_mst_atomic_check(&aconnector->base, state), 0);
+}
+
+/**
+ * dm_mst_test_detect_unregistered - Test detect skips unregistered connector
+ * @test: KUnit test context
+ *
+ * Verify that dm_dp_mst_detect() returns disconnected for an unregistered
+ * connector before calling into the MST topology helper.
+ */
+static void dm_mst_test_detect_unregistered(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ aconnector->base.registration_state = DRM_CONNECTOR_UNREGISTERED;
+
+ KUNIT_EXPECT_EQ(test,
+ dm_dp_mst_detect(&aconnector->base, NULL, false),
+ (int)connector_status_disconnected);
+}
+
+/**
+ * dm_mst_test_fp_guarded_public_stubs - Test FP-off public fallbacks
+ * @test: KUnit test context
+ *
+ * When CONFIG_DRM_AMD_DC_FP is disabled, the public DSC validation helper
+ * has no FP body and must return DC_OK without touching its arguments.
+ */
+static void dm_mst_test_fp_guarded_public_stubs(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(NULL, NULL),
+ (enum dc_status)DC_OK);
+}
+
+static struct kunit_case dm_mst_types_test_cases[] = {
+ /* needs_dsc_aux_workaround tests */
+ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match),
+ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_rev12),
+ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_wrong_dev_id),
+ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_wrong_rev),
+ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_low_sink_count),
+ KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_zero_sink_count),
+ /* dm_mst_get_pbn_divider tests */
+ KUNIT_CASE(dm_mst_test_pbn_divider_null_link),
+ /* amdgpu_dm_mst_reset_mst_connector_setting tests */
+ KUNIT_CASE(dm_mst_test_reset_connector_setting),
+ /* retrieve_downstream_port_device tests */
+ KUNIT_CASE(dm_mst_test_retrieve_downstream_no_aux),
+ KUNIT_CASE(dm_mst_test_retrieve_downstream_present),
+ /* retrieve_branch_specific_data tests */
+ KUNIT_CASE(dm_mst_test_retrieve_branch_no_parent),
+ KUNIT_CASE(dm_mst_test_retrieve_branch_reads_oui),
+ /* dm_dp_aux_transfer_result tests */
+ KUNIT_CASE(dm_mst_test_aux_result_success),
+ KUNIT_CASE(dm_mst_test_aux_result_eio),
+ KUNIT_CASE(dm_mst_test_aux_result_ebusy),
+ KUNIT_CASE(dm_mst_test_aux_result_timeout),
+ KUNIT_CASE(dm_mst_test_aux_transfer_native_read),
+ KUNIT_CASE(dm_mst_test_aux_transfer_native_write),
+ KUNIT_CASE(dm_mst_test_aux_transfer_partial_write),
+ KUNIT_CASE(dm_mst_test_aux_transfer_error_result),
+ KUNIT_CASE(dm_mst_test_aux_transfer_hpd_discon_quirk),
+ /* dm_dp_aux_fill_payload_flags tests */
+ KUNIT_CASE(dm_mst_test_fill_payload_flags_native_write),
+ KUNIT_CASE(dm_mst_test_fill_payload_flags_native_read),
+ KUNIT_CASE(dm_mst_test_fill_payload_flags_i2c_read_mot),
+ KUNIT_CASE(dm_mst_test_fill_payload_flags_write_status),
+ /* dm_mst_msg_ready_mask tests */
+ KUNIT_CASE(dm_mst_test_msg_ready_mask),
+ /* dm_mst_select_esi_dpcd tests */
+ KUNIT_CASE(dm_mst_test_select_esi_dpcd_legacy),
+ KUNIT_CASE(dm_mst_test_select_esi_dpcd_esi),
+ /* dm_handle_mst_sideband_msg_ready_event tests */
+ KUNIT_CASE(dm_mst_test_sideband_msg_ready_no_ready_bits),
+ /* dm_mst_atomic_best_encoder tests */
+ KUNIT_CASE(dm_mst_test_atomic_best_encoder),
+ /* dm_dp_create_fake_mst_encoders tests */
+ KUNIT_CASE(dm_mst_test_create_fake_mst_encoders),
+ /* dm_dp_mst_atomic_check tests */
+ KUNIT_CASE(dm_mst_test_atomic_check_no_old_crtc),
+ /* dm_dp_mst_detect tests */
+ KUNIT_CASE(dm_mst_test_detect_unregistered),
+ /* CONFIG_DRM_AMD_DC_FP disabled public paths */
+ KUNIT_CASE(dm_mst_test_fp_guarded_public_stubs),
+ {}
+};
+
+static struct kunit_suite dm_mst_types_test_suite = {
+ .name = "amdgpu_dm_mst_types",
+ .test_cases = dm_mst_types_test_cases,
+};
+
+kunit_test_suite(dm_mst_types_test_suite);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_mst_types");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c
new file mode 100644
index 000000000000..46c9af432e37
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c
@@ -0,0 +1,1228 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_plane.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+ #include <kunit/test.h>
+ #include <drm/drm_blend.h>
+ #include "link_enc_cfg.h"
+ #include "amdgpu_dm_plane.h"
+ #include <drm/amdgpu_drm.h>
+ #include <drm/drm_plane.h>
+
+
+struct dm_test_dcc_cap_ctx {
+ bool callback_ret;
+ bool capable;
+ bool output_independent_64b_blks;
+ bool called;
+ struct dc_dcc_surface_param captured_input;
+};
+
+static struct dm_test_dcc_cap_ctx *dm_test_dcc_ctx;
+
+static bool dm_test_get_dcc_compression_cap(const struct dc *dc,
+ const struct dc_dcc_surface_param *input,
+ struct dc_surface_dcc_cap *output)
+{
+ if (!dm_test_dcc_ctx)
+ return false;
+
+ dm_test_dcc_ctx->called = true;
+ dm_test_dcc_ctx->captured_input = *input;
+ output->capable = dm_test_dcc_ctx->capable;
+ output->grph.rgb.independent_64b_blks = dm_test_dcc_ctx->output_independent_64b_blks;
+
+ return dm_test_dcc_ctx->callback_ret;
+}
+
+static void dm_test_init_validate_dcc_inputs(struct amdgpu_device **adev,
+ struct dc **dc,
+ struct dc_tiling_info *tiling_info,
+ struct dc_plane_dcc_param *dcc,
+ struct dc_plane_address *address,
+ struct plane_size *plane_size,
+ struct kunit *test)
+{
+ *adev = kunit_kzalloc(test, sizeof(**adev), GFP_KERNEL);
+ *dc = kunit_kzalloc(test, sizeof(**dc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, *adev);
+ KUNIT_ASSERT_NOT_NULL(test, *dc);
+
+ (*adev)->dm.dc = *dc;
+ (*adev)->family = AMDGPU_FAMILY_NV;
+
+ tiling_info->gfx9.swizzle = 9;
+ dcc->enable = 1;
+ dcc->independent_64b_blks = 1;
+ plane_size->surface_size.width = 1920;
+ plane_size->surface_size.height = 1080;
+
+ (void)address;
+}
+
+
+/**
+ * dm_test_plane_is_video_format_known_video() - Verify known video formats.
+ * @test: KUnit test context.
+ *
+ * Verify if NV12, NV21, and P010 are treated as video formats.
+ */
+static void dm_test_plane_is_video_format_known_video(struct kunit *test)
+{
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_plane_is_video_format(DRM_FORMAT_NV12));
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_plane_is_video_format(DRM_FORMAT_NV21));
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_plane_is_video_format(DRM_FORMAT_P010));
+}
+
+/**
+ * dm_test_fill_blending_defaults() - Verify default blending output values.
+ * @test: KUnit test context.
+ *
+ * Verify if default blending output values are used for opaque alpha and no
+ * per-pixel blending.
+ */
+static void dm_test_fill_blending_defaults(struct kunit *test)
+{
+ struct drm_plane_state state = { 0 };
+ bool per_pixel_alpha;
+ bool pre_multiplied_alpha;
+ bool global_alpha;
+ int global_alpha_value;
+
+ state.pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
+ state.alpha = 0xffff;
+
+ amdgpu_dm_plane_fill_blending_from_plane_state(&state,
+ &per_pixel_alpha,
+ &pre_multiplied_alpha,
+ &global_alpha,
+ &global_alpha_value);
+
+ KUNIT_EXPECT_FALSE(test, per_pixel_alpha);
+ KUNIT_EXPECT_TRUE(test, pre_multiplied_alpha);
+ KUNIT_EXPECT_FALSE(test, global_alpha);
+ KUNIT_EXPECT_EQ(test, global_alpha_value, 0xff);
+}
+
+/**
+ * dm_test_fill_blending_premulti_alpha_format() - Verify premultiplied alpha path.
+ * @test: KUnit test context.
+ *
+ * Verify if premultiplied mode enables per-pixel alpha for ARGB8888.
+ */
+static void dm_test_fill_blending_premulti_alpha_format(struct kunit *test)
+{
+ struct drm_plane_state state = { 0 };
+ struct drm_framebuffer fb = { 0 };
+ bool per_pixel_alpha;
+ bool pre_multiplied_alpha;
+ bool global_alpha;
+ int global_alpha_value;
+
+ fb.format = drm_format_info(DRM_FORMAT_ARGB8888);
+ KUNIT_ASSERT_NOT_NULL(test, fb.format);
+
+ state.fb = &fb;
+ state.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
+ state.alpha = 0xffff;
+
+ amdgpu_dm_plane_fill_blending_from_plane_state(&state,
+ &per_pixel_alpha,
+ &pre_multiplied_alpha,
+ &global_alpha,
+ &global_alpha_value);
+
+ KUNIT_EXPECT_TRUE(test, per_pixel_alpha);
+ KUNIT_EXPECT_TRUE(test, pre_multiplied_alpha);
+ KUNIT_EXPECT_FALSE(test, global_alpha);
+ KUNIT_EXPECT_EQ(test, global_alpha_value, 0xff);
+}
+
+/**
+ * dm_test_fill_blending_coverage_alpha_format() - Verify coverage mode behavior.
+ * @test: KUnit test context.
+ *
+ * Verify if coverage mode sets per-pixel alpha and disables
+ * pre_multiplied_alpha for ARGB8888.
+ */
+static void dm_test_fill_blending_coverage_alpha_format(struct kunit *test)
+{
+ struct drm_plane_state state = { 0 };
+ struct drm_framebuffer fb = { 0 };
+ bool per_pixel_alpha;
+ bool pre_multiplied_alpha;
+ bool global_alpha;
+ int global_alpha_value;
+
+ fb.format = drm_format_info(DRM_FORMAT_ARGB8888);
+ KUNIT_ASSERT_NOT_NULL(test, fb.format);
+
+ state.fb = &fb;
+ state.pixel_blend_mode = DRM_MODE_BLEND_COVERAGE;
+ state.alpha = 0xffff;
+
+ amdgpu_dm_plane_fill_blending_from_plane_state(&state,
+ &per_pixel_alpha,
+ &pre_multiplied_alpha,
+ &global_alpha,
+ &global_alpha_value);
+
+ KUNIT_EXPECT_TRUE(test, per_pixel_alpha);
+ KUNIT_EXPECT_FALSE(test, pre_multiplied_alpha);
+ KUNIT_EXPECT_FALSE(test, global_alpha);
+ KUNIT_EXPECT_EQ(test, global_alpha_value, 0xff);
+}
+
+/**
+ * dm_test_fill_blending_global_alpha() - Verify global alpha conversion to 8 bits.
+ * @test: KUnit test context.
+ *
+ * Verify if global alpha is enabled and converted from 16-bit to 8-bit.
+ */
+static void dm_test_fill_blending_global_alpha(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_plane *plane;
+ struct drm_plane_state *state;
+ bool per_pixel_alpha;
+ bool pre_multiplied_alpha;
+ bool global_alpha;
+ int global_alpha_value;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, state);
+
+ plane->dev = &adev->ddev;
+ state->plane = plane;
+ state->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE;
+ state->alpha = 0x8000;
+
+ amdgpu_dm_plane_fill_blending_from_plane_state(state,
+ &per_pixel_alpha,
+ &pre_multiplied_alpha,
+ &global_alpha,
+ &global_alpha_value);
+
+ KUNIT_EXPECT_FALSE(test, per_pixel_alpha);
+ KUNIT_EXPECT_TRUE(test, pre_multiplied_alpha);
+ KUNIT_EXPECT_TRUE(test, global_alpha);
+ KUNIT_EXPECT_EQ(test, global_alpha_value, 0x80);
+}
+
+/**
+ * dm_test_modifier_has_dcc() - Verify helper detects AMD DCC modifiers.
+ * @test: KUnit test context.
+ *
+ * Verify if DCC detection works for linear and AMD DCC modifiers.
+ */
+static void dm_test_modifier_has_dcc(struct kunit *test)
+{
+ uint64_t dcc_mod = AMD_FMT_MOD | AMD_FMT_MOD_SET(DCC, 1);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_plane_modifier_has_dcc(DRM_FORMAT_MOD_LINEAR));
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_plane_modifier_has_dcc(dcc_mod));
+}
+
+/**
+ * dm_test_modifier_gfx9_swizzle_mode() - Verify swizzle helper for linear and AMD modifiers.
+ * @test: KUnit test context.
+ *
+ * Verify if swizzle mode decoding works for linear and AMD tiled modifiers.
+ */
+static void dm_test_modifier_gfx9_swizzle_mode(struct kunit *test)
+{
+ uint64_t mod = AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X);
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_modifier_gfx9_swizzle_mode(DRM_FORMAT_MOD_LINEAR), 0U);
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_modifier_gfx9_swizzle_mode(mod),
+ (unsigned int)AMD_FMT_MOD_TILE_GFX9_64K_S_X);
+}
+
+/**
+ * dm_test_get_plane_formats() - Verify plane format counts for key plane types.
+ * @test: KUnit test context.
+ *
+ * Verify if returned format counts match primary, overlay, and cursor planes.
+ */
+static void dm_test_get_plane_formats(struct kunit *test)
+{
+ struct drm_plane *plane;
+ struct dc_plane_cap *cap;
+ uint32_t formats[32] = {0};
+
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ cap = kunit_kzalloc(test, sizeof(*cap), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, cap);
+
+ plane->type = DRM_PLANE_TYPE_PRIMARY;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_get_plane_formats(plane, NULL, formats, 32), 14);
+
+ cap->pixel_format_support.nv12 = true;
+ cap->pixel_format_support.p010 = true;
+ cap->pixel_format_support.fp16 = true;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_get_plane_formats(plane, cap, formats, 32), 20);
+
+ plane->type = DRM_PLANE_TYPE_OVERLAY;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_get_plane_formats(plane, NULL, formats, 32), 9);
+
+ plane->type = DRM_PLANE_TYPE_CURSOR;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_get_plane_formats(plane, NULL, formats, 32), 1);
+}
+
+/**
+ * dm_test_get_plane_modifiers() - Verify early-return and cursor modifier list.
+ * @test: KUnit test context.
+ *
+ * Verify if modifier list handling works for unsupported families and cursor planes.
+ */
+static void dm_test_get_plane_modifiers(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ uint64_t *mods = NULL;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ adev->family = AMDGPU_FAMILY_SI;
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_get_plane_modifiers(adev, DRM_PLANE_TYPE_PRIMARY, &mods),
+ 0);
+ KUNIT_EXPECT_PTR_EQ(test, mods, NULL);
+
+ adev->family = AMDGPU_FAMILY_NV;
+ KUNIT_ASSERT_EQ(test,
+ amdgpu_dm_plane_get_plane_modifiers(adev, DRM_PLANE_TYPE_CURSOR, &mods),
+ 0);
+ KUNIT_ASSERT_NOT_NULL(test, mods);
+ KUNIT_EXPECT_EQ(test, mods[0], DRM_FORMAT_MOD_LINEAR);
+ KUNIT_EXPECT_EQ(test, mods[1], DRM_FORMAT_MOD_INVALID);
+ kfree(mods);
+}
+
+/**
+ * dm_test_fill_dc_scaling_info() - Verify basic error and success paths.
+ * @test: KUnit test context.
+ *
+ * Verify if scaling info rejects invalid sizes and accepts valid sizes.
+ */
+static void dm_test_fill_dc_scaling_info(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_plane_state state = {0};
+ struct dc_scaling_info info = {0};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ state.src_w = 0;
+ state.src_h = 100 << 16;
+ state.crtc_w = 100;
+ state.crtc_h = 100;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_fill_dc_scaling_info(adev, &state, &info), -EINVAL);
+
+ state.src_w = 100 << 16;
+ state.src_h = 100 << 16;
+ state.crtc_w = 100;
+ state.crtc_h = 100;
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_fill_dc_scaling_info(adev, &state, &info), 0);
+}
+
+/**
+ * dm_test_get_min_max_dc_plane_scaling() - Verify format-specific cap selection and 1->1000 conversion.
+ * @test: KUnit test context.
+ *
+ * Verify if min/max scaling values are correct for NV12 and XRGB8888 formats.
+ */
+static void dm_test_get_min_max_dc_plane_scaling(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct drm_framebuffer *fb;
+ int min_downscale = 0;
+ int max_upscale = 0;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+ KUNIT_ASSERT_NOT_NULL(test, fb);
+
+ adev->dm.dc = dc;
+ dc->caps.planes[0].max_upscale_factor.nv12 = 1;
+ dc->caps.planes[0].max_downscale_factor.nv12 = 1;
+ dc->caps.planes[0].max_upscale_factor.argb8888 = 1600;
+ dc->caps.planes[0].max_downscale_factor.argb8888 = 250;
+
+ fb->format = drm_format_info(DRM_FORMAT_NV12);
+ KUNIT_ASSERT_NOT_NULL(test, fb->format);
+ amdgpu_dm_plane_get_min_max_dc_plane_scaling(&adev->ddev, fb, &min_downscale, &max_upscale);
+ KUNIT_EXPECT_EQ(test, min_downscale, 1000);
+ KUNIT_EXPECT_EQ(test, max_upscale, 1000);
+
+ fb->format = drm_format_info(DRM_FORMAT_XRGB8888);
+ KUNIT_ASSERT_NOT_NULL(test, fb->format);
+ amdgpu_dm_plane_get_min_max_dc_plane_scaling(&adev->ddev, fb, &min_downscale, &max_upscale);
+ KUNIT_EXPECT_EQ(test, min_downscale, 250);
+ KUNIT_EXPECT_EQ(test, max_upscale, 1600);
+}
+
+/**
+ * dm_test_fill_plane_buffer_attributes_gfx8() - Verify graphics path and GFX8 tiling fill.
+ * @test: KUnit test context.
+ *
+ * Verify if GFX8 plane buffer attributes and tiling fields are filled correctly.
+ */
+static void dm_test_fill_plane_buffer_attributes_gfx8(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct amdgpu_framebuffer *afb;
+ struct dc_tiling_info *tiling_info;
+ struct plane_size *plane_size;
+ struct dc_plane_dcc_param *dcc;
+ struct dc_plane_address *address;
+ uint64_t tiling_flags = 0;
+ int ret;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL);
+ tiling_info = kunit_kzalloc(test, sizeof(*tiling_info), GFP_KERNEL);
+ plane_size = kunit_kzalloc(test, sizeof(*plane_size), GFP_KERNEL);
+ dcc = kunit_kzalloc(test, sizeof(*dcc), GFP_KERNEL);
+ address = kunit_kzalloc(test, sizeof(*address), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, afb);
+ KUNIT_ASSERT_NOT_NULL(test, tiling_info);
+ KUNIT_ASSERT_NOT_NULL(test, plane_size);
+ KUNIT_ASSERT_NOT_NULL(test, dcc);
+ KUNIT_ASSERT_NOT_NULL(test, address);
+
+ adev->family = AMDGPU_FAMILY_SI;
+ afb->address = 0x12345000ULL;
+ afb->base.width = 1920;
+ afb->base.height = 1080;
+ afb->base.offsets[0] = 0x1000;
+ afb->base.pitches[0] = 7680;
+ afb->base.format = drm_format_info(DRM_FORMAT_XRGB8888);
+ KUNIT_ASSERT_NOT_NULL(test, afb->base.format);
+
+ tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_1D_TILED_THIN1);
+ tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 5);
+
+ ret = amdgpu_dm_plane_fill_plane_buffer_attributes(adev, afb,
+ SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, ROTATION_ANGLE_0,
+ tiling_flags, tiling_info, plane_size, dcc, address, true);
+
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, plane_size->surface_size.width, 1920);
+ KUNIT_EXPECT_EQ(test, plane_size->surface_size.height, 1080);
+ KUNIT_EXPECT_EQ(test, plane_size->surface_pitch, 1920);
+ KUNIT_EXPECT_EQ(test, address->type, (int)PLN_ADDR_TYPE_GRAPHICS);
+ KUNIT_EXPECT_TRUE(test, address->tmz_surface);
+ KUNIT_EXPECT_EQ(test, (int)tiling_info->gfx8.array_mode, (int)DC_ARRAY_1D_TILED_THIN1);
+ KUNIT_EXPECT_EQ(test, tiling_info->gfx8.pipe_config, 5U);
+}
+
+/**
+ * dm_test_get_cursor_position() - Verify cursor clipping and off-screen handling.
+ * @test: KUnit test context.
+ *
+ * Verify if cursor clipping, hotspot adjustment, and off-screen disable behavior work.
+ */
+static void dm_test_get_cursor_position(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct amdgpu_crtc *amdgpu_crtc;
+ struct drm_plane *plane;
+ struct drm_plane_state *state;
+ struct drm_framebuffer *fb;
+ struct dc_cursor_position position = {0};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ amdgpu_crtc = kunit_kzalloc(test, sizeof(*amdgpu_crtc), GFP_KERNEL);
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, amdgpu_crtc);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, state);
+ KUNIT_ASSERT_NOT_NULL(test, fb);
+
+ adev->ip_versions[DCE_HWIP][0] = IP_VERSION(4, 0, 0);
+ amdgpu_crtc->max_cursor_width = 64;
+ amdgpu_crtc->max_cursor_height = 64;
+
+ plane->dev = &adev->ddev;
+ plane->state = state;
+ state->fb = fb;
+ state->crtc_x = -5;
+ state->crtc_y = -7;
+ state->crtc_w = 32;
+ state->crtc_h = 32;
+
+ KUNIT_ASSERT_EQ(test,
+ amdgpu_dm_plane_get_cursor_position(plane, &amdgpu_crtc->base, &position),
+ 0);
+ KUNIT_EXPECT_TRUE(test, position.enable);
+ KUNIT_EXPECT_EQ(test, position.x, 0);
+ KUNIT_EXPECT_EQ(test, position.y, 0);
+ KUNIT_EXPECT_EQ(test, position.x_hotspot, 5);
+ KUNIT_EXPECT_EQ(test, position.y_hotspot, 7);
+ KUNIT_EXPECT_TRUE(test, position.translate_by_source);
+
+ memset(&position, 0, sizeof(position));
+ state->crtc_x = -64;
+ state->crtc_y = 0;
+ KUNIT_ASSERT_EQ(test,
+ amdgpu_dm_plane_get_cursor_position(plane, &amdgpu_crtc->base, &position),
+ 0);
+ KUNIT_EXPECT_FALSE(test, position.enable);
+}
+
+/**
+ * dm_test_format_mod_supported() - Verify key format/modifier acceptance and rejection paths.
+ * @test: KUnit test context.
+ *
+ * Verify if format-modifier support checks match accepted and rejected cases.
+ */
+static void dm_test_format_mod_supported(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct drm_plane *plane;
+ uint64_t listed_mod;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+
+ adev->family = AMDGPU_FAMILY_NV;
+ plane->dev = &adev->ddev;
+
+ KUNIT_EXPECT_TRUE(test,
+ amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_MOD_LINEAR));
+ KUNIT_EXPECT_TRUE(test,
+ amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_MOD_INVALID));
+
+ KUNIT_EXPECT_FALSE(test,
+ amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_MOD_VENDOR_AMD));
+
+ listed_mod = AMD_FMT_MOD |
+ AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) |
+ AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9) |
+ AMD_FMT_MOD_SET(DCC, 1);
+ plane->modifiers = &listed_mod;
+ plane->modifier_count = 1;
+
+ KUNIT_EXPECT_FALSE(test,
+ amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_NV12, listed_mod));
+}
+
+/**
+ * dm_test_fill_gfx12_plane_attributes_from_modifiers() - Verify GFX12 DCC mapping path.
+ * @test: KUnit test context.
+ *
+ * Verify if GFX12 modifier parsing enables DCC and sets expected DCC block mode.
+ */
+static void dm_test_fill_gfx12_plane_attributes_from_modifiers(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct amdgpu_framebuffer *afb;
+ struct plane_size plane_size = {0};
+ struct dc_tiling_info tiling_info = {0};
+ struct dc_plane_dcc_param dcc = {0};
+ struct dc_plane_address address = {0};
+ struct dm_test_dcc_cap_ctx ctx = {
+ .callback_ret = true,
+ .capable = true,
+ .output_independent_64b_blks = false,
+ };
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+ KUNIT_ASSERT_NOT_NULL(test, afb);
+
+ adev->family = AMDGPU_FAMILY_GC_12_0_0;
+ adev->dm.dc = dc;
+ adev->gfx.config.gb_addr_config_fields.num_pipes = 2;
+ adev->gfx.config.gb_addr_config_fields.num_banks = 4;
+ adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256;
+ adev->gfx.config.gb_addr_config_fields.num_se = 1;
+ adev->gfx.config.gb_addr_config_fields.max_compress_frags = 2;
+ adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 1;
+ dc->cap_funcs.get_dcc_compression_cap = dm_test_get_dcc_compression_cap;
+ dm_test_dcc_ctx = &ctx;
+
+ afb->base.modifier = AMD_FMT_MOD |
+ AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX12_64K_2D) |
+ AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX12) |
+ AMD_FMT_MOD_SET(DCC, 1) |
+ AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, 1);
+ plane_size.surface_size.width = 1920;
+ plane_size.surface_size.height = 1080;
+
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(
+ adev, afb, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
+ ROTATION_ANGLE_0, &plane_size, &tiling_info, &dcc, &address),
+ 0);
+ KUNIT_EXPECT_EQ(test, (int)tiling_info.gfxversion, (int)DcGfxAddr3);
+ KUNIT_EXPECT_TRUE(test, dcc.enable);
+ KUNIT_EXPECT_EQ(test, (int)dcc.dcc_ind_blk, (int)hubp_ind_block_128b);
+
+ dm_test_dcc_ctx = NULL;
+}
+
+/**
+ * dm_test_fill_gfx9_plane_attributes_from_modifiers() - Verify basic GFX9 linear modifier path.
+ * @test: KUnit test context.
+ *
+ * Verify if GFX9 linear modifier handling keeps DCC disabled.
+ */
+static void dm_test_fill_gfx9_plane_attributes_from_modifiers(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct amdgpu_framebuffer *afb;
+ struct plane_size plane_size = {0};
+ struct dc_tiling_info tiling_info = {0};
+ struct dc_plane_dcc_param dcc = {0};
+ struct dc_plane_address address = {0};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, afb);
+
+ adev->family = AMDGPU_FAMILY_NV;
+ adev->gfx.config.gb_addr_config_fields.num_pipes = 2;
+ adev->gfx.config.gb_addr_config_fields.num_banks = 4;
+ adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256;
+ adev->gfx.config.gb_addr_config_fields.num_se = 1;
+ adev->gfx.config.gb_addr_config_fields.max_compress_frags = 2;
+ adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 1;
+ adev->gfx.config.gb_addr_config_fields.num_pkrs = 2;
+ adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 3, 0);
+
+ afb->base.modifier = DRM_FORMAT_MOD_LINEAR;
+
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(
+ adev, afb, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
+ ROTATION_ANGLE_0, &plane_size, &tiling_info, &dcc, &address),
+ 0);
+ KUNIT_EXPECT_EQ(test, (int)tiling_info.gfxversion, (int)DcGfxVersion9);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.swizzle, 0U);
+ KUNIT_EXPECT_FALSE(test, dcc.enable);
+}
+
+/**
+ * dm_test_helper_check_state_viewport_reject() - Verify viewport outside screen rejects state.
+ * @test: KUnit test context.
+ *
+ * Verify if plane state is rejected when the viewport is outside display bounds.
+ */
+static void dm_test_helper_check_state_viewport_reject(struct kunit *test)
+{
+ struct drm_plane *plane;
+ struct drm_plane_state *state;
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_framebuffer *fb;
+
+ plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL);
+ state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
+ crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL);
+ new_crtc_state = kunit_kzalloc(test, sizeof(*new_crtc_state), GFP_KERNEL);
+ fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, plane);
+ KUNIT_ASSERT_NOT_NULL(test, state);
+ KUNIT_ASSERT_NOT_NULL(test, crtc);
+ KUNIT_ASSERT_NOT_NULL(test, new_crtc_state);
+ KUNIT_ASSERT_NOT_NULL(test, fb);
+
+ plane->type = DRM_PLANE_TYPE_OVERLAY;
+ state->plane = plane;
+ state->fb = fb;
+ state->crtc = crtc;
+ state->crtc_x = 200;
+ state->crtc_y = 0;
+ state->crtc_w = 100;
+ state->crtc_h = 100;
+ new_crtc_state->mode.crtc_hdisplay = 100;
+ new_crtc_state->mode.crtc_vdisplay = 100;
+
+ KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_helper_check_state(state, new_crtc_state), -EINVAL);
+}
+
+/**
+ * dm_test_validate_dcc_disabled_returns_success() - Verify disabled DCC is accepted.
+ * @test: KUnit test context.
+ *
+ * Verify if DCC validation succeeds when DCC is disabled.
+ */
+static void dm_test_validate_dcc_disabled_returns_success(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct dc_tiling_info tiling_info = {0};
+ struct dc_plane_dcc_param dcc = {0};
+ struct dc_plane_address address = {0};
+ struct plane_size plane_size = {0};
+
+ dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address,
+ &plane_size, test);
+ dcc.enable = 0;
+
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
+ ROTATION_ANGLE_0, &tiling_info, &dcc,
+ &address, &plane_size),
+ 0);
+}
+
+/**
+ * dm_test_validate_dcc_video_non_gfx12_fails() - Verify video format restriction on pre-GFX12.
+ * @test: KUnit test context.
+ *
+ * Verify if video format DCC validation fails on non-GFX12 devices.
+ */
+static void dm_test_validate_dcc_video_non_gfx12_fails(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct dc_tiling_info tiling_info = {0};
+ struct dc_plane_dcc_param dcc = {0};
+ struct dc_plane_address address = {0};
+ struct plane_size plane_size = {0};
+
+ dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address,
+ &plane_size, test);
+ adev->family = AMDGPU_FAMILY_NV;
+
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
+ ROTATION_ANGLE_0, &tiling_info, &dcc,
+ &address, &plane_size),
+ -EINVAL);
+}
+
+/**
+ * dm_test_validate_dcc_missing_cap_func_fails() - Verify missing capability callback fails.
+ * @test: KUnit test context.
+ *
+ * Verify if validation fails when DCC capability callback is not provided.
+ */
+static void dm_test_validate_dcc_missing_cap_func_fails(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct dc_tiling_info tiling_info = {0};
+ struct dc_plane_dcc_param dcc = {0};
+ struct dc_plane_address address = {0};
+ struct plane_size plane_size = {0};
+
+ dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address,
+ &plane_size, test);
+ dc->cap_funcs.get_dcc_compression_cap = NULL;
+
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
+ ROTATION_ANGLE_0, &tiling_info, &dcc,
+ &address, &plane_size),
+ -EINVAL);
+}
+
+/**
+ * dm_test_validate_dcc_success_and_scan_mapping() - Verify success path and rotation-to-scan mapping.
+ * @test: KUnit test context.
+ *
+ * Verify if DCC validation succeeds and rotation-to-scan mapping is correct.
+ */
+static void dm_test_validate_dcc_success_and_scan_mapping(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct dc_tiling_info tiling_info = {0};
+ struct dc_plane_dcc_param dcc = {0};
+ struct dc_plane_address address = {0};
+ struct plane_size plane_size = {0};
+ struct dm_test_dcc_cap_ctx ctx = {
+ .callback_ret = true,
+ .capable = true,
+ .output_independent_64b_blks = true,
+ };
+
+ dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address,
+ &plane_size, test);
+ dc->cap_funcs.get_dcc_compression_cap = dm_test_get_dcc_compression_cap;
+ dm_test_dcc_ctx = &ctx;
+
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
+ ROTATION_ANGLE_90, &tiling_info, &dcc,
+ &address, &plane_size),
+ 0);
+ KUNIT_EXPECT_TRUE(test, ctx.called);
+ KUNIT_EXPECT_EQ(test, (int)ctx.captured_input.scan, (int)SCAN_DIRECTION_VERTICAL);
+ KUNIT_EXPECT_EQ(test, (int)ctx.captured_input.format,
+ (int)SURFACE_PIXEL_FORMAT_GRPH_ARGB8888);
+
+ dm_test_dcc_ctx = NULL;
+}
+
+/**
+ * dm_test_validate_dcc_independent_64b_mismatch_fails() - Verify 64B compatibility check.
+ * @test: KUnit test context.
+ *
+ * Verify if validation fails when independent_64b_blks values do not match.
+ */
+static void dm_test_validate_dcc_independent_64b_mismatch_fails(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc *dc;
+ struct dc_tiling_info tiling_info = {0};
+ struct dc_plane_dcc_param dcc = {0};
+ struct dc_plane_address address = {0};
+ struct plane_size plane_size = {0};
+ struct dm_test_dcc_cap_ctx ctx = {
+ .callback_ret = true,
+ .capable = true,
+ .output_independent_64b_blks = true,
+ };
+
+ dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address,
+ &plane_size, test);
+ dcc.independent_64b_blks = 0;
+ dc->cap_funcs.get_dcc_compression_cap = dm_test_get_dcc_compression_cap;
+ dm_test_dcc_ctx = &ctx;
+
+ KUNIT_EXPECT_EQ(test,
+ amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
+ ROTATION_ANGLE_0, &tiling_info, &dcc,
+ &address, &plane_size),
+ -EINVAL);
+
+ dm_test_dcc_ctx = NULL;
+}
+
+/**
+ * dm_test_add_modifier_appends_value() - Verify one modifier append.
+ * @test: KUnit test context.
+ *
+ * Verify if a modifier is appended and size is updated.
+ */
+static void dm_test_add_modifier_appends_value(struct kunit *test)
+{
+ uint64_t size = 0;
+ uint64_t cap = 2;
+ uint64_t *mods = kmalloc_array(cap, sizeof(*mods), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, mods);
+
+ amdgpu_dm_plane_add_modifier(&mods, &size, &cap, 0x1234ULL);
+
+ KUNIT_ASSERT_NOT_NULL(test, mods);
+ KUNIT_EXPECT_EQ(test, size, 1ULL);
+ KUNIT_EXPECT_EQ(test, cap, 2ULL);
+ KUNIT_EXPECT_EQ(test, mods[0], 0x1234ULL);
+
+ kfree(mods);
+}
+
+/**
+ * dm_test_add_modifier_grows_capacity() - Verify add triggers growth and preserves old data.
+ * @test: KUnit test context.
+ *
+ * Verify if modifier array growth keeps old data and appends new data.
+ */
+static void dm_test_add_modifier_grows_capacity(struct kunit *test)
+{
+ uint64_t size = 1;
+ uint64_t cap = 1;
+ uint64_t *mods = kmalloc_array(cap, sizeof(*mods), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, mods);
+ mods[0] = 0xAAULL;
+
+ amdgpu_dm_plane_add_modifier(&mods, &size, &cap, 0xBBULL);
+
+ KUNIT_ASSERT_NOT_NULL(test, mods);
+ KUNIT_EXPECT_EQ(test, cap, 2ULL);
+ KUNIT_EXPECT_EQ(test, size, 2ULL);
+ KUNIT_EXPECT_EQ(test, mods[0], 0xAAULL);
+ KUNIT_EXPECT_EQ(test, mods[1], 0xBBULL);
+
+ kfree(mods);
+}
+
+/**
+ * dm_test_add_modifier_noop_when_mods_null() - Verify helper is a no-op on NULL mods list.
+ * @test: KUnit test context.
+ *
+ * Verify if add_modifier does nothing when the modifier list is NULL.
+ */
+static void dm_test_add_modifier_noop_when_mods_null(struct kunit *test)
+{
+ uint64_t size = 3;
+ uint64_t cap = 7;
+ uint64_t *mods = NULL;
+
+ amdgpu_dm_plane_add_modifier(&mods, &size, &cap, 0x55ULL);
+
+ KUNIT_EXPECT_PTR_EQ(test, mods, NULL);
+ KUNIT_EXPECT_EQ(test, size, 3ULL);
+ KUNIT_EXPECT_EQ(test, cap, 7ULL);
+}
+
+/**
+ * dm_test_fill_gfx8_tiling_info_2d_tiled() - Verify GFX8 2D tiled flag parsing.
+ * @test: KUnit test context.
+ *
+ * Verify if 2D tiled GFX8 flags populate expected tiling fields.
+ */
+static void dm_test_fill_gfx8_tiling_info_2d_tiled(struct kunit *test)
+{
+ struct dc_tiling_info tiling_info = {0};
+ uint64_t tiling_flags = 0;
+
+ tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_2D_TILED_THIN1);
+ tiling_flags |= AMDGPU_TILING_SET(BANK_WIDTH, 2);
+ tiling_flags |= AMDGPU_TILING_SET(BANK_HEIGHT, 1);
+ tiling_flags |= AMDGPU_TILING_SET(MACRO_TILE_ASPECT, 3);
+ tiling_flags |= AMDGPU_TILING_SET(TILE_SPLIT, 4);
+ tiling_flags |= AMDGPU_TILING_SET(NUM_BANKS, 2);
+ tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 7);
+
+ amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags);
+
+ KUNIT_EXPECT_EQ(test, (int)tiling_info.gfxversion, (int)DcGfxVersion8);
+ KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.array_mode, (int)DC_ARRAY_2D_TILED_THIN1);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.bank_width, 2U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.bank_height, 1U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_aspect, 3U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_split, 4U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.num_banks, 2U);
+ KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.tile_mode,
+ (int)DC_ADDR_SURF_MICRO_TILING_DISPLAY);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 7U);
+}
+
+/**
+ * dm_test_fill_gfx8_tiling_info_1d_tiled() - Verify GFX8 1D tiled flag parsing.
+ * @test: KUnit test context.
+ *
+ * Verify if 1D tiled GFX8 flags populate array mode and pipe config.
+ */
+static void dm_test_fill_gfx8_tiling_info_1d_tiled(struct kunit *test)
+{
+ struct dc_tiling_info tiling_info = {0};
+ uint64_t tiling_flags = 0;
+
+ tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_1D_TILED_THIN1);
+ tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 5);
+
+ amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags);
+
+ KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.array_mode, (int)DC_ARRAY_1D_TILED_THIN1);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 5U);
+}
+
+/**
+ * dm_test_fill_gfx8_tiling_info_other_mode() - Verify non-1D/non-2D mode handling.
+ * @test: KUnit test context.
+ *
+ * Verify if unsupported array mode keeps preset fields and updates pipe config.
+ */
+static void dm_test_fill_gfx8_tiling_info_other_mode(struct kunit *test)
+{
+ struct dc_tiling_info tiling_info = {0};
+ uint64_t tiling_flags = 0;
+
+ tiling_info.gfxversion = 0x7f;
+ tiling_info.gfx8.array_mode = 0x7f;
+ tiling_info.gfx8.tile_mode = 0x7f;
+ tiling_info.gfx8.num_banks = 0x7f;
+
+ tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 6);
+
+ amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags);
+
+ KUNIT_EXPECT_EQ(test, tiling_info.gfxversion, 0x7f);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.array_mode, 0x7f);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_mode, 0x7f);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.num_banks, 0x7f);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 6U);
+}
+
+/**
+ * dm_test_fill_gfx9_tiling_info_from_device_pre_10_3() - Verify GFX9 field copy before 10.3.
+ * @test: KUnit test context.
+ *
+ * Verify if pre-10.3 device fields are copied and existing num_pkrs is kept.
+ */
+static void dm_test_fill_gfx9_tiling_info_from_device_pre_10_3(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_tiling_info tiling_info = {0};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ adev->gfx.config.gb_addr_config_fields.num_pipes = 4;
+ adev->gfx.config.gb_addr_config_fields.num_banks = 8;
+ adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256;
+ adev->gfx.config.gb_addr_config_fields.num_se = 2;
+ adev->gfx.config.gb_addr_config_fields.max_compress_frags = 1;
+ adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 2;
+ adev->gfx.config.gb_addr_config_fields.num_pkrs = 3;
+ adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 2, 9);
+
+ tiling_info.gfx9.num_pkrs = 0x5a;
+
+ amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(adev, &tiling_info);
+
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 4U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 8U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.pipe_interleave, 256U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 2U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.max_compressed_frags, 1U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_rb_per_se, 2U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 0x5aU);
+}
+
+/**
+ * dm_test_fill_gfx9_tiling_info_from_device_10_3_plus() - Verify num_pkrs update on 10.3+.
+ * @test: KUnit test context.
+ *
+ * Verify if 10.3+ device fields are copied and num_pkrs is updated.
+ */
+static void dm_test_fill_gfx9_tiling_info_from_device_10_3_plus(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_tiling_info tiling_info = {0};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ adev->gfx.config.gb_addr_config_fields.num_pipes = 2;
+ adev->gfx.config.gb_addr_config_fields.num_banks = 4;
+ adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 128;
+ adev->gfx.config.gb_addr_config_fields.num_se = 1;
+ adev->gfx.config.gb_addr_config_fields.max_compress_frags = 2;
+ adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 1;
+ adev->gfx.config.gb_addr_config_fields.num_pkrs = 6;
+ adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 3, 0);
+
+ amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(adev, &tiling_info);
+
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 2U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 4U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.pipe_interleave, 128U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 1U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.max_compressed_frags, 2U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_rb_per_se, 1U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 6U);
+}
+
+/**
+ * dm_test_fill_gfx9_tiling_info_from_modifier_linear() - Verify non-AMD modifier keeps device values.
+ * @test: KUnit test context.
+ *
+ * Verify if linear modifier path keeps values from device configuration.
+ */
+static void dm_test_fill_gfx9_tiling_info_from_modifier_linear(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_tiling_info tiling_info = {0};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ adev->family = AMDGPU_FAMILY_NV;
+ adev->gfx.config.gb_addr_config_fields.num_pipes = 4;
+ adev->gfx.config.gb_addr_config_fields.num_banks = 8;
+ adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256;
+ adev->gfx.config.gb_addr_config_fields.num_se = 2;
+ adev->gfx.config.gb_addr_config_fields.max_compress_frags = 1;
+ adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 2;
+ adev->gfx.config.gb_addr_config_fields.num_pkrs = 3;
+ adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 3, 0);
+
+ amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(adev, &tiling_info,
+ DRM_FORMAT_MOD_LINEAR);
+
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 4U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 8U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.pipe_interleave, 256U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 2U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.max_compressed_frags, 1U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_rb_per_se, 2U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 3U);
+}
+
+/**
+ * dm_test_fill_gfx9_tiling_info_from_modifier_pre_nv() - Verify AMD modifier updates banks on pre-NV.
+ * @test: KUnit test context.
+ *
+ * Verify if AMD modifier updates pre-NV pipe, engine, and bank fields.
+ */
+static void dm_test_fill_gfx9_tiling_info_from_modifier_pre_nv(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_tiling_info tiling_info = {0};
+ uint64_t modifier;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ adev->family = AMDGPU_FAMILY_RV;
+ adev->gfx.config.gb_addr_config_fields.num_pipes = 4;
+ adev->gfx.config.gb_addr_config_fields.num_banks = 16;
+ adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256;
+ adev->gfx.config.gb_addr_config_fields.num_se = 2;
+ adev->gfx.config.gb_addr_config_fields.max_compress_frags = 1;
+ adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 2;
+ adev->gfx.config.gb_addr_config_fields.num_pkrs = 7;
+ adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 2, 9);
+
+ tiling_info.gfx9.num_pkrs = 0x5a;
+
+ modifier = AMD_FMT_MOD |
+ AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) |
+ AMD_FMT_MOD_SET(PIPE_XOR_BITS, 7) |
+ AMD_FMT_MOD_SET(BANK_XOR_BITS, 3) |
+ AMD_FMT_MOD_SET(PACKERS, 2);
+
+ amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(adev, &tiling_info, modifier);
+
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 32U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 4U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 8U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 0x5aU);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U);
+}
+
+/**
+ * dm_test_fill_gfx9_tiling_info_from_modifier_nv() - Verify AMD modifier updates packers on NV+.
+ * @test: KUnit test context.
+ *
+ * Verify if AMD modifier updates NV+ pipe, engine, and packer fields.
+ */
+static void dm_test_fill_gfx9_tiling_info_from_modifier_nv(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct dc_tiling_info tiling_info = {0};
+ uint64_t modifier;
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ adev->family = AMDGPU_FAMILY_NV;
+ adev->gfx.config.gb_addr_config_fields.num_pipes = 2;
+ adev->gfx.config.gb_addr_config_fields.num_banks = 9;
+ adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 128;
+ adev->gfx.config.gb_addr_config_fields.num_se = 1;
+ adev->gfx.config.gb_addr_config_fields.max_compress_frags = 2;
+ adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 1;
+ adev->gfx.config.gb_addr_config_fields.num_pkrs = 2;
+ adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 3, 0);
+
+ modifier = AMD_FMT_MOD |
+ AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) |
+ AMD_FMT_MOD_SET(PIPE_XOR_BITS, 6) |
+ AMD_FMT_MOD_SET(BANK_XOR_BITS, 2) |
+ AMD_FMT_MOD_SET(PACKERS, 3);
+
+ amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(adev, &tiling_info, modifier);
+
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 32U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 2U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 9U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 8U);
+ KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U);
+}
+
+static struct kunit_case amdgpu_dm_plane_test_cases[] = {
+ /* amdgpu_dm_plane_is_video_format() */
+ KUNIT_CASE(dm_test_plane_is_video_format_known_video),
+ /* amdgpu_dm_plane_fill_blending_from_plane_state() */
+ KUNIT_CASE(dm_test_fill_blending_defaults),
+ KUNIT_CASE(dm_test_fill_blending_premulti_alpha_format),
+ KUNIT_CASE(dm_test_fill_blending_coverage_alpha_format),
+ KUNIT_CASE(dm_test_fill_blending_global_alpha),
+ /* amdgpu_dm_plane_modifier_* helpers() */
+ KUNIT_CASE(dm_test_modifier_has_dcc),
+ KUNIT_CASE(dm_test_modifier_gfx9_swizzle_mode),
+ /* amdgpu_dm_plane_get_plane_formats() */
+ KUNIT_CASE(dm_test_get_plane_formats),
+ /* amdgpu_dm_plane_get_plane_modifiers() */
+ KUNIT_CASE(dm_test_get_plane_modifiers),
+ /* amdgpu_dm_plane_fill_dc_scaling_info() */
+ KUNIT_CASE(dm_test_fill_dc_scaling_info),
+ /* amdgpu_dm_plane_get_min_max_dc_plane_scaling() */
+ KUNIT_CASE(dm_test_get_min_max_dc_plane_scaling),
+ /* amdgpu_dm_plane_fill_plane_buffer_attributes() */
+ KUNIT_CASE(dm_test_fill_plane_buffer_attributes_gfx8),
+ /* amdgpu_dm_plane_get_cursor_position() */
+ KUNIT_CASE(dm_test_get_cursor_position),
+ /* amdgpu_dm_plane_format_mod_supported() */
+ KUNIT_CASE(dm_test_format_mod_supported),
+ /* amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers() */
+ KUNIT_CASE(dm_test_fill_gfx12_plane_attributes_from_modifiers),
+ /* amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers() */
+ KUNIT_CASE(dm_test_fill_gfx9_plane_attributes_from_modifiers),
+ /* amdgpu_dm_plane_helper_check_state() */
+ KUNIT_CASE(dm_test_helper_check_state_viewport_reject),
+ /* amdgpu_dm_plane_add_modifier() */
+ KUNIT_CASE(dm_test_add_modifier_appends_value),
+ KUNIT_CASE(dm_test_add_modifier_grows_capacity),
+ KUNIT_CASE(dm_test_add_modifier_noop_when_mods_null),
+ /* amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags() */
+ KUNIT_CASE(dm_test_fill_gfx8_tiling_info_2d_tiled),
+ KUNIT_CASE(dm_test_fill_gfx8_tiling_info_1d_tiled),
+ KUNIT_CASE(dm_test_fill_gfx8_tiling_info_other_mode),
+ /* amdgpu_dm_plane_fill_gfx9_tiling_info_from_device() */
+ KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_device_pre_10_3),
+ KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_device_10_3_plus),
+ /* amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier() */
+ KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_modifier_linear),
+ KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_modifier_pre_nv),
+ KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_modifier_nv),
+ /* amdgpu_dm_plane_validate_dcc() */
+ KUNIT_CASE(dm_test_validate_dcc_disabled_returns_success),
+ KUNIT_CASE(dm_test_validate_dcc_video_non_gfx12_fails),
+ KUNIT_CASE(dm_test_validate_dcc_missing_cap_func_fails),
+ KUNIT_CASE(dm_test_validate_dcc_success_and_scan_mapping),
+ KUNIT_CASE(dm_test_validate_dcc_independent_64b_mismatch_fails),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_plane_test_suite = {
+ .name = "amdgpu_dm_plane",
+ .test_cases = amdgpu_dm_plane_test_cases,
+};
+
+kunit_test_suite(amdgpu_dm_plane_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_plane");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c
new file mode 100644
index 000000000000..8d1d26bfcc16
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c
@@ -0,0 +1,2454 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_pp_smu.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+
+#include "dc.h"
+#include "dm_services.h"
+#include "dm_pp_smu.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_pp_smu.h"
+
+/* ---- Stub DPM layer ---- */
+
+/**
+ * struct stub_dpm_context - Tracks stub DPM callback invocations
+ * @ret_val: Return value for the next DPM callback
+ * @get_current_clocks_info: Clock info returned by stub get_current_clocks
+ * @get_clock_by_type_clocks: Clocks returned by stub get_clock_by_type
+ * @get_validation_clks: Validation clocks returned by stub
+ * @get_clock_by_type_with_latency_clks: Returned by stub with_latency
+ * @get_clock_by_type_with_voltage_clks: Returned by stub with_voltage
+ * @set_watermarks_ret: Return value for set_watermarks
+ * @display_clock_voltage_ret: Return value for display_clock_voltage_request
+ * @display_disable_memory_clock_switch_ret: Return for disable_memory_clock
+ * @get_max_sustainable_ret: Return for get_max_sustainable_clocks_by_dc
+ * @get_uclk_dpm_ret: Return for get_uclk_dpm_states
+ * @get_dpm_clock_table_ret: Return for get_dpm_clock_table
+ * @set_active_display_count_ret: Return for set_active_display_count
+ * @set_min_deep_sleep_dcefclk_ret: Return for set_min_deep_sleep_dcefclk
+ * @get_validation_clks_ret: Return for get_display_mode_validation_clocks
+ */
+struct stub_dpm_context {
+ int ret_val;
+ struct amd_pp_clock_info get_current_clocks_info;
+ struct amd_pp_clocks get_clock_by_type_clocks;
+ struct amd_pp_simple_clock_info get_validation_clks;
+ int get_validation_clks_ret;
+ struct pp_clock_levels_with_latency get_clock_by_type_with_latency_clks;
+ struct pp_clock_levels_with_voltage get_clock_by_type_with_voltage_clks;
+ int set_watermarks_ret;
+ int display_clock_voltage_ret;
+ int display_disable_memory_clock_switch_ret;
+ int get_max_sustainable_ret;
+ int get_uclk_dpm_ret;
+ int get_dpm_clock_table_ret;
+ int set_active_display_count_ret;
+ int set_min_deep_sleep_dcefclk_ret;
+};
+
+static struct stub_dpm_context *stub_dpm_ctx;
+
+static int stub_get_current_clocks(void *handle, struct amd_pp_clock_info *clocks)
+{
+ if (stub_dpm_ctx->ret_val)
+ return stub_dpm_ctx->ret_val;
+ *clocks = stub_dpm_ctx->get_current_clocks_info;
+ return 0;
+}
+
+static int stub_get_clock_by_type(void *handle, enum amd_pp_clock_type type,
+ struct amd_pp_clocks *clocks)
+{
+ if (stub_dpm_ctx->ret_val)
+ return stub_dpm_ctx->ret_val;
+ *clocks = stub_dpm_ctx->get_clock_by_type_clocks;
+ return 0;
+}
+
+static int stub_get_display_mode_validation_clocks(void *handle,
+ struct amd_pp_simple_clock_info *clocks)
+{
+ if (stub_dpm_ctx->get_validation_clks_ret)
+ return stub_dpm_ctx->get_validation_clks_ret;
+ *clocks = stub_dpm_ctx->get_validation_clks;
+ return 0;
+}
+
+static int stub_get_clock_by_type_with_latency(void *handle,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_latency *clocks)
+{
+ if (stub_dpm_ctx->ret_val)
+ return stub_dpm_ctx->ret_val;
+ *clocks = stub_dpm_ctx->get_clock_by_type_with_latency_clks;
+ return 0;
+}
+
+static int stub_get_clock_by_type_with_voltage(void *handle,
+ enum amd_pp_clock_type type,
+ struct pp_clock_levels_with_voltage *clocks)
+{
+ if (stub_dpm_ctx->ret_val)
+ return stub_dpm_ctx->ret_val;
+ *clocks = stub_dpm_ctx->get_clock_by_type_with_voltage_clks;
+ return 0;
+}
+
+static void stub_display_configuration_change(void *handle)
+{
+ /* No-op: satisfies display_configuration_changed callback */
+}
+
+static void stub_pm_compute_clocks(void *handle)
+{
+ /* No-op: satisfies pm_compute_clocks callback */
+}
+
+static int stub_set_watermarks_for_clocks_ranges(void *handle, void *clock_ranges)
+{
+ return stub_dpm_ctx->set_watermarks_ret;
+}
+
+static int stub_display_clock_voltage_request(void *handle,
+ struct pp_display_clock_request *clock)
+{
+ return stub_dpm_ctx->display_clock_voltage_ret;
+}
+
+static int stub_set_active_display_count(void *handle, uint32_t count)
+{
+ return stub_dpm_ctx->set_active_display_count_ret;
+}
+
+static int stub_set_min_deep_sleep_dcefclk(void *handle, uint32_t clock)
+{
+ return stub_dpm_ctx->set_min_deep_sleep_dcefclk_ret;
+}
+
+static int stub_set_hard_min_dcefclk_by_freq(void *handle, uint32_t clock)
+{
+ return 0;
+}
+
+static int stub_set_hard_min_fclk_by_freq(void *handle, uint32_t clock)
+{
+ return 0;
+}
+
+static int stub_notify_smu_enable_pwe(void *handle)
+{
+ return 0;
+}
+
+static int stub_display_disable_memory_clock_switch(void *handle,
+ bool disable_memory_clock_switch)
+{
+ return stub_dpm_ctx->display_disable_memory_clock_switch_ret;
+}
+
+static int stub_get_max_sustainable_clocks_by_dc(void *handle,
+ struct pp_smu_nv_clock_table *max_clocks)
+{
+ return stub_dpm_ctx->get_max_sustainable_ret;
+}
+
+static int stub_get_uclk_dpm_states(void *handle,
+ unsigned int *clock_values_in_khz,
+ unsigned int *num_states)
+{
+ return stub_dpm_ctx->get_uclk_dpm_ret;
+}
+
+static int stub_get_dpm_clock_table(void *handle, struct dpm_clocks *clock_table)
+{
+ return stub_dpm_ctx->get_dpm_clock_table_ret;
+}
+
+static const struct amd_pm_funcs stub_pp_funcs = {
+ .get_current_clocks = stub_get_current_clocks,
+ .get_clock_by_type = stub_get_clock_by_type,
+ .get_display_mode_validation_clocks = stub_get_display_mode_validation_clocks,
+ .get_clock_by_type_with_latency = stub_get_clock_by_type_with_latency,
+ .get_clock_by_type_with_voltage = stub_get_clock_by_type_with_voltage,
+ .display_configuration_changed = stub_display_configuration_change,
+ .pm_compute_clocks = stub_pm_compute_clocks,
+ .set_watermarks_for_clocks_ranges = stub_set_watermarks_for_clocks_ranges,
+ .display_clock_voltage_request = stub_display_clock_voltage_request,
+ .set_active_display_count = stub_set_active_display_count,
+ .set_min_deep_sleep_dcefclk = stub_set_min_deep_sleep_dcefclk,
+ .set_hard_min_dcefclk_by_freq = stub_set_hard_min_dcefclk_by_freq,
+ .set_hard_min_fclk_by_freq = stub_set_hard_min_fclk_by_freq,
+ .notify_smu_enable_pwe = stub_notify_smu_enable_pwe,
+ .display_disable_memory_clock_switch = stub_display_disable_memory_clock_switch,
+ .get_max_sustainable_clocks_by_dc = stub_get_max_sustainable_clocks_by_dc,
+ .get_uclk_dpm_states = stub_get_uclk_dpm_states,
+ .get_dpm_clock_table = stub_get_dpm_clock_table,
+};
+
+/**
+ * setup_stub_dpm - Initialize a stub DPM environment for testing
+ * @test: KUnit test context
+ * @adev: Pointer to amdgpu_device to configure
+ *
+ * Sets up adev->powerplay.pp_funcs and initializes adev->pm.mutex so that
+ * amdgpu_dpm_* functions can be safely called with stub callbacks.
+ */
+static void setup_stub_dpm(struct kunit *test, struct amdgpu_device *adev)
+{
+ stub_dpm_ctx = kunit_kzalloc(test, sizeof(*stub_dpm_ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, stub_dpm_ctx);
+
+ adev->powerplay.pp_funcs = &stub_pp_funcs;
+ adev->powerplay.pp_handle = adev;
+ mutex_init(&adev->pm.mutex);
+}
+
+/* ---- Tests for get_default_clock_levels ---- */
+
+/**
+ * dm_test_default_clock_levels_display - Test display clock default levels
+ * @test: KUnit test context
+ *
+ * Verify that get_default_clock_levels populates 6 display clock levels
+ * with the expected frequencies in kHz.
+ */
+static void dm_test_default_clock_levels_display(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+ uint32_t expected[] = { 300000, 400000, 496560, 626090, 685720, 757900 };
+ int i;
+
+ get_default_clock_levels(DM_PP_CLOCK_TYPE_DISPLAY_CLK, &clks);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 6U);
+ for (i = 0; i < 6; i++)
+ KUNIT_EXPECT_EQ(test, clks.clocks_in_khz[i], expected[i]);
+}
+
+/**
+ * dm_test_default_clock_levels_engine - Test engine clock default levels
+ * @test: KUnit test context
+ *
+ * Verify that get_default_clock_levels populates 6 engine clock levels
+ * with the expected frequencies in kHz.
+ */
+static void dm_test_default_clock_levels_engine(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+ uint32_t expected[] = { 300000, 360000, 423530, 514290, 626090, 720000 };
+ int i;
+
+ get_default_clock_levels(DM_PP_CLOCK_TYPE_ENGINE_CLK, &clks);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 6U);
+ for (i = 0; i < 6; i++)
+ KUNIT_EXPECT_EQ(test, clks.clocks_in_khz[i], expected[i]);
+}
+
+/**
+ * dm_test_default_clock_levels_memory - Test memory clock default levels
+ * @test: KUnit test context
+ *
+ * Verify that get_default_clock_levels populates 2 memory clock levels
+ * with the expected frequencies in kHz.
+ */
+static void dm_test_default_clock_levels_memory(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+
+ get_default_clock_levels(DM_PP_CLOCK_TYPE_MEMORY_CLK, &clks);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 2U);
+ KUNIT_EXPECT_EQ(test, clks.clocks_in_khz[0], 333000U);
+ KUNIT_EXPECT_EQ(test, clks.clocks_in_khz[1], 800000U);
+}
+
+/**
+ * dm_test_default_clock_levels_unknown - Test unknown clock type default
+ * @test: KUnit test context
+ *
+ * Verify that get_default_clock_levels sets num_levels to 0 for an
+ * unrecognized clock type.
+ */
+static void dm_test_default_clock_levels_unknown(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+
+ get_default_clock_levels(DM_PP_CLOCK_TYPE_FCLK, &clks);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 0U);
+}
+
+/* ---- Tests for dc_to_pp_clock_type ---- */
+
+/**
+ * dm_test_dc_to_pp_clock_type_display - Test display clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_DISPLAY_CLK maps to amd_pp_disp_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_display(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DISPLAY_CLK),
+ (int)amd_pp_disp_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_engine - Test engine clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_ENGINE_CLK maps to amd_pp_sys_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_engine(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_ENGINE_CLK),
+ (int)amd_pp_sys_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_memory - Test memory clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_MEMORY_CLK maps to amd_pp_mem_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_memory(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_MEMORY_CLK),
+ (int)amd_pp_mem_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_dcefclk - Test DCEF clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_DCEFCLK maps to amd_pp_dcef_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_dcefclk(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DCEFCLK),
+ (int)amd_pp_dcef_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_dcfclk - Test DCF clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_DCFCLK maps to amd_pp_dcf_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_dcfclk(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DCFCLK),
+ (int)amd_pp_dcf_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_pixelclk - Test pixel clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_PIXELCLK maps to amd_pp_pixel_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_pixelclk(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_PIXELCLK),
+ (int)amd_pp_pixel_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_fclk - Test FCLK type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_FCLK maps to amd_pp_f_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_fclk(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_FCLK),
+ (int)amd_pp_f_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_phyclk - Test display PHY clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_DISPLAYPHYCLK maps to amd_pp_phy_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_phyclk(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DISPLAYPHYCLK),
+ (int)amd_pp_phy_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_dppclk - Test DPP clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify DM_PP_CLOCK_TYPE_DPPCLK maps to amd_pp_dpp_clock.
+ */
+static void dm_test_dc_to_pp_clock_type_dppclk(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DPPCLK),
+ (int)amd_pp_dpp_clock);
+}
+
+/**
+ * dm_test_dc_to_pp_clock_type_invalid - Test invalid clock type mapping
+ * @test: KUnit test context
+ *
+ * Verify that an invalid clock type value maps to 0.
+ */
+static void dm_test_dc_to_pp_clock_type_invalid(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(0), 0);
+}
+
+/* ---- Tests for pp_to_dc_clock_levels ---- */
+
+/**
+ * dm_test_pp_to_dc_clock_levels_within_limit - Test normal copy within limit
+ * @test: KUnit test context
+ *
+ * Verify that pp_to_dc_clock_levels correctly copies clock values when the
+ * count is within DM_PP_MAX_CLOCK_LEVELS.
+ */
+static void dm_test_pp_to_dc_clock_levels_within_limit(struct kunit *test)
+{
+ struct amd_pp_clocks pp_clks = {};
+ struct dm_pp_clock_levels dc_clks = {};
+
+ pp_clks.count = 3;
+ pp_clks.clock[0] = 300000;
+ pp_clks.clock[1] = 500000;
+ pp_clks.clock[2] = 700000;
+
+ pp_to_dc_clock_levels(&pp_clks, &dc_clks, DM_PP_CLOCK_TYPE_ENGINE_CLK);
+
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 3U);
+ KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[0], 300000U);
+ KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[1], 500000U);
+ KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[2], 700000U);
+}
+
+/**
+ * dm_test_pp_to_dc_clock_levels_caps_at_max - Test count capping at max
+ * @test: KUnit test context
+ *
+ * Verify that pp_to_dc_clock_levels caps num_levels at DM_PP_MAX_CLOCK_LEVELS
+ * when the input count exceeds the maximum.
+ */
+static void dm_test_pp_to_dc_clock_levels_caps_at_max(struct kunit *test)
+{
+ struct amd_pp_clocks pp_clks = {};
+ struct dm_pp_clock_levels dc_clks = {};
+ uint32_t i;
+
+ pp_clks.count = DM_PP_MAX_CLOCK_LEVELS + 1;
+ for (i = 0; i < DM_PP_MAX_CLOCK_LEVELS; i++)
+ pp_clks.clock[i] = (i + 1) * 100000;
+
+ pp_to_dc_clock_levels(&pp_clks, &dc_clks, DM_PP_CLOCK_TYPE_ENGINE_CLK);
+
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, (uint32_t)DM_PP_MAX_CLOCK_LEVELS);
+}
+
+/* ---- Tests for pp_to_dc_clock_levels_with_latency ---- */
+
+/**
+ * dm_test_pp_to_dc_clock_levels_latency_within_limit - Test normal copy
+ * @test: KUnit test context
+ *
+ * Verify that pp_to_dc_clock_levels_with_latency correctly copies clock
+ * and latency values when count is within limits.
+ */
+static void dm_test_pp_to_dc_clock_levels_latency_within_limit(struct kunit *test)
+{
+ struct pp_clock_levels_with_latency pp_clks = {};
+ struct dm_pp_clock_levels_with_latency dc_clks = {};
+
+ pp_clks.num_levels = 2;
+ pp_clks.data[0].clocks_in_khz = 400000;
+ pp_clks.data[0].latency_in_us = 10;
+ pp_clks.data[1].clocks_in_khz = 800000;
+ pp_clks.data[1].latency_in_us = 20;
+
+ pp_to_dc_clock_levels_with_latency(&pp_clks, &dc_clks,
+ DM_PP_CLOCK_TYPE_ENGINE_CLK);
+
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 2U);
+ KUNIT_EXPECT_EQ(test, dc_clks.data[0].clocks_in_khz, 400000U);
+ KUNIT_EXPECT_EQ(test, dc_clks.data[0].latency_in_us, 10U);
+ KUNIT_EXPECT_EQ(test, dc_clks.data[1].clocks_in_khz, 800000U);
+ KUNIT_EXPECT_EQ(test, dc_clks.data[1].latency_in_us, 20U);
+}
+
+/**
+ * dm_test_pp_to_dc_clock_levels_latency_caps_at_max - Test count capping
+ * @test: KUnit test context
+ *
+ * Verify that pp_to_dc_clock_levels_with_latency caps num_levels at
+ * DM_PP_MAX_CLOCK_LEVELS when input exceeds the maximum.
+ */
+static void dm_test_pp_to_dc_clock_levels_latency_caps_at_max(struct kunit *test)
+{
+ struct pp_clock_levels_with_latency pp_clks = {};
+ struct dm_pp_clock_levels_with_latency dc_clks = {};
+
+ pp_clks.num_levels = DM_PP_MAX_CLOCK_LEVELS + 1;
+
+ pp_to_dc_clock_levels_with_latency(&pp_clks, &dc_clks,
+ DM_PP_CLOCK_TYPE_ENGINE_CLK);
+
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, (uint32_t)DM_PP_MAX_CLOCK_LEVELS);
+}
+
+/* ---- Tests for pp_to_dc_clock_levels_with_voltage ---- */
+
+/**
+ * dm_test_pp_to_dc_clock_levels_voltage_within_limit - Test normal copy
+ * @test: KUnit test context
+ *
+ * Verify that pp_to_dc_clock_levels_with_voltage correctly copies clock
+ * and voltage values when count is within limits.
+ */
+static void dm_test_pp_to_dc_clock_levels_voltage_within_limit(struct kunit *test)
+{
+ struct pp_clock_levels_with_voltage pp_clks = {};
+ struct dm_pp_clock_levels_with_voltage dc_clks = {};
+
+ pp_clks.num_levels = 2;
+ pp_clks.data[0].clocks_in_khz = 300000;
+ pp_clks.data[0].voltage_in_mv = 800;
+ pp_clks.data[1].clocks_in_khz = 600000;
+ pp_clks.data[1].voltage_in_mv = 950;
+
+ pp_to_dc_clock_levels_with_voltage(&pp_clks, &dc_clks,
+ DM_PP_CLOCK_TYPE_MEMORY_CLK);
+
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 2U);
+ KUNIT_EXPECT_EQ(test, dc_clks.data[0].clocks_in_khz, 300000U);
+ KUNIT_EXPECT_EQ(test, dc_clks.data[0].voltage_in_mv, 800U);
+ KUNIT_EXPECT_EQ(test, dc_clks.data[1].clocks_in_khz, 600000U);
+ KUNIT_EXPECT_EQ(test, dc_clks.data[1].voltage_in_mv, 950U);
+}
+
+/**
+ * dm_test_pp_to_dc_clock_levels_voltage_caps_at_max - Test count capping
+ * @test: KUnit test context
+ *
+ * Verify that pp_to_dc_clock_levels_with_voltage caps num_levels at
+ * DM_PP_MAX_CLOCK_LEVELS when input exceeds the maximum.
+ */
+static void dm_test_pp_to_dc_clock_levels_voltage_caps_at_max(struct kunit *test)
+{
+ struct pp_clock_levels_with_voltage pp_clks = {};
+ struct dm_pp_clock_levels_with_voltage dc_clks = {};
+
+ pp_clks.num_levels = DM_PP_MAX_CLOCK_LEVELS + 1;
+
+ pp_to_dc_clock_levels_with_voltage(&pp_clks, &dc_clks,
+ DM_PP_CLOCK_TYPE_MEMORY_CLK);
+
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, (uint32_t)DM_PP_MAX_CLOCK_LEVELS);
+}
+
+/* ---- Tests for dm_pp_get_funcs ---- */
+
+/**
+ * dm_test_get_funcs_rv - Test Raven PP SMU function table setup
+ * @test: KUnit test context
+ *
+ * Verify that DCN 1.0 initializes the Raven SMU function table and stores
+ * the DC context in the PP SMU handle.
+ */
+static void dm_test_get_funcs_rv(struct kunit *test)
+{
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ KUNIT_ASSERT_NOT_NULL(test, funcs);
+
+ ctx->dce_version = DCN_VERSION_1_0;
+
+ dm_pp_get_funcs(ctx, funcs);
+
+ KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_VER_RV);
+ KUNIT_EXPECT_PTR_EQ(test, funcs->rv_funcs.pp_smu.dm, ctx);
+ KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_wm_ranges != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_pme_wa_enable != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_display_count != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_min_deep_sleep_dcfclk != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_hard_min_dcfclk_by_freq != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_hard_min_fclk_by_freq != NULL);
+ KUNIT_EXPECT_FALSE(test, funcs->rv_funcs.set_hard_min_socclk_by_freq != NULL);
+}
+
+/**
+ * dm_test_get_funcs_rv_101 - Test DCN 1.01 Raven PP SMU setup
+ * @test: KUnit test context
+ *
+ * Verify that DCN 1.01 uses the same Raven SMU function table as DCN 1.0.
+ */
+static void dm_test_get_funcs_rv_101(struct kunit *test)
+{
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ KUNIT_ASSERT_NOT_NULL(test, funcs);
+
+ ctx->dce_version = DCN_VERSION_1_01;
+
+ dm_pp_get_funcs(ctx, funcs);
+
+ KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_VER_RV);
+ KUNIT_EXPECT_PTR_EQ(test, funcs->rv_funcs.pp_smu.dm, ctx);
+ KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_display_count != NULL);
+}
+
+/**
+ * dm_test_get_funcs_nv - Test Navi PP SMU function table setup
+ * @test: KUnit test context
+ *
+ * Verify that DCN 2.0 initializes the Navi SMU function table and leaves the
+ * unsupported PME workaround callback unset.
+ */
+static void dm_test_get_funcs_nv(struct kunit *test)
+{
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ KUNIT_ASSERT_NOT_NULL(test, funcs);
+
+ ctx->dce_version = DCN_VERSION_2_0;
+
+ dm_pp_get_funcs(ctx, funcs);
+
+ KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_VER_NV);
+ KUNIT_EXPECT_PTR_EQ(test, funcs->nv_funcs.pp_smu.dm, ctx);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_display_count != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_hard_min_dcfclk_by_freq != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_min_deep_sleep_dcfclk != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_voltage_by_freq != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_wm_ranges != NULL);
+ KUNIT_EXPECT_FALSE(test, funcs->nv_funcs.set_pme_wa_enable != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_hard_min_uclk_by_freq != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.get_maximum_sustainable_clocks != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.get_uclk_dpm_states != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_pstate_handshake_support != NULL);
+}
+
+/**
+ * dm_test_get_funcs_rn - Test Renoir PP SMU function table setup
+ * @test: KUnit test context
+ *
+ * Verify that DCN 2.1 initializes the Renoir SMU function table.
+ */
+static void dm_test_get_funcs_rn(struct kunit *test)
+{
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ KUNIT_ASSERT_NOT_NULL(test, funcs);
+
+ ctx->dce_version = DCN_VERSION_2_1;
+
+ dm_pp_get_funcs(ctx, funcs);
+
+ KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_VER_RN);
+ KUNIT_EXPECT_PTR_EQ(test, funcs->rn_funcs.pp_smu.dm, ctx);
+ KUNIT_EXPECT_TRUE(test, funcs->rn_funcs.set_wm_ranges != NULL);
+ KUNIT_EXPECT_TRUE(test, funcs->rn_funcs.get_dpm_clock_table != NULL);
+}
+
+/**
+ * dm_test_get_funcs_unsupported - Test unsupported DCE version handling
+ * @test: KUnit test context
+ *
+ * Verify that unsupported DCE versions do not initialize a PP SMU version or
+ * function table callbacks.
+ */
+static void dm_test_get_funcs_unsupported(struct kunit *test)
+{
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ KUNIT_ASSERT_NOT_NULL(test, funcs);
+
+ ctx->dce_version = DCE_VERSION_MAX;
+
+ dm_pp_get_funcs(ctx, funcs);
+
+ KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_UNSUPPORTED);
+ KUNIT_EXPECT_FALSE(test, funcs->rv_funcs.set_wm_ranges != NULL);
+}
+
+/* ---- Tests for amdgpu_device-backed entry points ---- */
+
+/**
+ * dm_test_apply_display_requirements_dpm_disabled - Test DPM-disabled path
+ * @test: KUnit test context
+ *
+ * Verify that dm_pp_apply_display_requirements returns true without touching
+ * the display configuration when DPM is disabled.
+ */
+static void dm_test_apply_display_requirements_dpm_disabled(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_display_configuration cfg = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ adev->pm.dpm_enabled = false;
+ ctx->driver_context = adev;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_apply_display_requirements(ctx, &cfg));
+}
+
+/**
+ * dm_test_apply_clock_for_voltage_invalid_type - Test invalid clock type path
+ * @test: KUnit test context
+ *
+ * Verify that dm_pp_apply_clock_for_voltage_request returns false for a clock
+ * type that does not map to a valid PP clock type, taking the early-return
+ * path before any SMU request is issued.
+ */
+static void dm_test_apply_clock_for_voltage_invalid_type(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_for_voltage_req req = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ ctx->driver_context = adev;
+ req.clk_type = (enum dm_pp_clock_type)0xffff;
+ req.clocks_in_khz = 500000;
+
+ KUNIT_EXPECT_FALSE(test, dm_pp_apply_clock_for_voltage_request(ctx, &req));
+}
+
+/* ---- Tests for build_pm_display_cfg ---- */
+
+/**
+ * dm_test_build_pm_display_cfg_scalar_fields - Test scalar field translation
+ * @test: KUnit test context
+ *
+ * Verify that build_pm_display_cfg copies the pass-through fields and applies
+ * the /10 (10 kHz) scaling, and sets the fixed constants.
+ */
+static void dm_test_build_pm_display_cfg_scalar_fields(struct kunit *test)
+{
+ struct amd_pp_display_configuration *pm =
+ kunit_kzalloc(test, sizeof(*pm), GFP_KERNEL);
+ struct dm_pp_display_configuration *pp =
+ kunit_kzalloc(test, sizeof(*pp), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, pm);
+ KUNIT_ASSERT_NOT_NULL(test, pp);
+
+ pp->cpu_cc6_disable = true;
+ pp->cpu_pstate_disable = true;
+ pp->cpu_pstate_separation_time = 7;
+ pp->nb_pstate_switch_disable = true;
+ pp->display_count = 2;
+ pp->min_engine_clock_khz = 300000;
+ pp->min_engine_clock_deep_sleep_khz = 50000;
+ pp->min_memory_clock_khz = 800000;
+ pp->min_dcfclock_khz = 600000;
+ pp->all_displays_in_sync = true;
+ pp->avail_mclk_switch_time_us = 11;
+ pp->disp_clk_khz = 400000;
+ pp->avail_mclk_switch_time_in_disp_active_us = 13;
+ pp->crtc_index = 3;
+ pp->line_time_in_us = 17;
+ pp->disp_configs[0].v_refresh = 60;
+
+ build_pm_display_cfg(pm, pp);
+
+ KUNIT_EXPECT_TRUE(test, pm->cpu_cc6_disable);
+ KUNIT_EXPECT_TRUE(test, pm->cpu_pstate_disable);
+ KUNIT_EXPECT_EQ(test, pm->cpu_pstate_separation_time, 7);
+ KUNIT_EXPECT_TRUE(test, pm->nb_pstate_switch_disable);
+ KUNIT_EXPECT_EQ(test, pm->num_display, 2);
+ KUNIT_EXPECT_EQ(test, pm->num_path_including_non_display, 2);
+ KUNIT_EXPECT_EQ(test, pm->min_core_set_clock, 30000);
+ KUNIT_EXPECT_EQ(test, pm->min_core_set_clock_in_sr, 5000);
+ KUNIT_EXPECT_EQ(test, pm->min_mem_set_clock, 80000);
+ KUNIT_EXPECT_EQ(test, pm->min_dcef_deep_sleep_set_clk, 5000);
+ KUNIT_EXPECT_EQ(test, pm->min_dcef_set_clk, 60000);
+ KUNIT_EXPECT_TRUE(test, pm->multi_monitor_in_sync);
+ KUNIT_EXPECT_EQ(test, pm->min_vblank_time, 11);
+ KUNIT_EXPECT_EQ(test, pm->display_clk, 40000);
+ KUNIT_EXPECT_EQ(test, pm->dce_tolerable_mclk_in_active_latency, 13);
+ KUNIT_EXPECT_EQ(test, pm->crtc_index, 3);
+ KUNIT_EXPECT_EQ(test, pm->line_time_in_us, 17);
+ KUNIT_EXPECT_EQ(test, pm->vrefresh, 60);
+ KUNIT_EXPECT_EQ(test, pm->crossfire_display_index, -1);
+ KUNIT_EXPECT_EQ(test, pm->min_bus_bandwidth, 0);
+}
+
+/**
+ * dm_test_build_pm_display_cfg_per_display - Test per-display translation
+ * @test: KUnit test context
+ *
+ * Verify that build_pm_display_cfg maps each display config, applying the
+ * controller_id = pipe_idx + 1 offset and copying the pixel clock.
+ */
+static void dm_test_build_pm_display_cfg_per_display(struct kunit *test)
+{
+ struct amd_pp_display_configuration *pm =
+ kunit_kzalloc(test, sizeof(*pm), GFP_KERNEL);
+ struct dm_pp_display_configuration *pp =
+ kunit_kzalloc(test, sizeof(*pp), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, pm);
+ KUNIT_ASSERT_NOT_NULL(test, pp);
+
+ pp->display_count = 2;
+ pp->disp_configs[0].pipe_idx = 0;
+ pp->disp_configs[0].pixel_clock = 148500;
+ pp->disp_configs[1].pipe_idx = 4;
+ pp->disp_configs[1].pixel_clock = 297000;
+
+ build_pm_display_cfg(pm, pp);
+
+ KUNIT_EXPECT_EQ(test, pm->displays[0].controller_id, 1);
+ KUNIT_EXPECT_EQ(test, pm->displays[0].pixel_clock, 148500);
+ KUNIT_EXPECT_EQ(test, pm->displays[1].controller_id, 5);
+ KUNIT_EXPECT_EQ(test, pm->displays[1].pixel_clock, 297000);
+}
+
+/* ---- Tests for build_wm_clock_ranges_soc15 ---- */
+
+/**
+ * dm_test_build_wm_clock_ranges_dmif - Test reader (DMIF) watermark sets
+ * @test: KUnit test context
+ *
+ * Verify that build_wm_clock_ranges_soc15 copies the reader set count,
+ * maps wm_inst to wm_set_id (clamping instances > 3 to WM_SET_A), and
+ * converts every clock from MHz to kHz (x1000) into the DMIF clock ranges.
+ */
+static void dm_test_build_wm_clock_ranges_dmif(struct kunit *test)
+{
+ struct pp_smu_wm_range_sets *ranges =
+ kunit_kzalloc(test, sizeof(*ranges), GFP_KERNEL);
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm =
+ kunit_kzalloc(test, sizeof(*wm), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, ranges);
+ KUNIT_ASSERT_NOT_NULL(test, wm);
+
+ ranges->num_reader_wm_sets = 2;
+ /* set 0: wm_inst within range -> preserved */
+ ranges->reader_wm_sets[0].wm_inst = 2;
+ ranges->reader_wm_sets[0].max_drain_clk_mhz = 600;
+ ranges->reader_wm_sets[0].min_drain_clk_mhz = 300;
+ ranges->reader_wm_sets[0].max_fill_clk_mhz = 800;
+ ranges->reader_wm_sets[0].min_fill_clk_mhz = 400;
+ /* set 1: wm_inst > 3 -> clamped to WM_SET_A */
+ ranges->reader_wm_sets[1].wm_inst = 5;
+ ranges->reader_wm_sets[1].max_drain_clk_mhz = 700;
+ ranges->reader_wm_sets[1].min_drain_clk_mhz = 350;
+ ranges->reader_wm_sets[1].max_fill_clk_mhz = 900;
+ ranges->reader_wm_sets[1].min_fill_clk_mhz = 450;
+
+ build_wm_clock_ranges_soc15(ranges, wm);
+
+ KUNIT_EXPECT_EQ(test, wm->num_wm_dmif_sets, 2U);
+ KUNIT_EXPECT_EQ(test, wm->num_wm_mcif_sets, 0U);
+
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_set_id, WM_SET_C);
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_max_dcfclk_clk_in_khz, 600000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_min_dcfclk_clk_in_khz, 300000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_max_mem_clk_in_khz, 800000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_min_mem_clk_in_khz, 400000U);
+
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_set_id, WM_SET_A);
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_max_dcfclk_clk_in_khz, 700000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_min_dcfclk_clk_in_khz, 350000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_max_mem_clk_in_khz, 900000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_min_mem_clk_in_khz, 450000U);
+}
+
+/**
+ * dm_test_build_wm_clock_ranges_mcif - Test writer (MCIF) watermark sets
+ * @test: KUnit test context
+ *
+ * Verify that build_wm_clock_ranges_soc15 copies the writer set count and
+ * maps the writer clocks into the MCIF ranges: fill clocks become socclk
+ * and drain clocks become mem clk, each converted from MHz to kHz.
+ */
+static void dm_test_build_wm_clock_ranges_mcif(struct kunit *test)
+{
+ struct pp_smu_wm_range_sets *ranges =
+ kunit_kzalloc(test, sizeof(*ranges), GFP_KERNEL);
+ struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm =
+ kunit_kzalloc(test, sizeof(*wm), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, ranges);
+ KUNIT_ASSERT_NOT_NULL(test, wm);
+
+ ranges->num_writer_wm_sets = 2;
+ ranges->writer_wm_sets[0].wm_inst = 1;
+ ranges->writer_wm_sets[0].max_fill_clk_mhz = 1200;
+ ranges->writer_wm_sets[0].min_fill_clk_mhz = 600;
+ ranges->writer_wm_sets[0].max_drain_clk_mhz = 1000;
+ ranges->writer_wm_sets[0].min_drain_clk_mhz = 500;
+ /* set 1: wm_inst > 3 -> clamped to WM_SET_A */
+ ranges->writer_wm_sets[1].wm_inst = 5;
+ ranges->writer_wm_sets[1].max_fill_clk_mhz = 1400;
+ ranges->writer_wm_sets[1].min_fill_clk_mhz = 700;
+ ranges->writer_wm_sets[1].max_drain_clk_mhz = 1100;
+ ranges->writer_wm_sets[1].min_drain_clk_mhz = 550;
+
+ build_wm_clock_ranges_soc15(ranges, wm);
+
+ KUNIT_EXPECT_EQ(test, wm->num_wm_dmif_sets, 0U);
+ KUNIT_EXPECT_EQ(test, wm->num_wm_mcif_sets, 2U);
+
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_set_id, WM_SET_B);
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_max_socclk_clk_in_khz, 1200000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_min_socclk_clk_in_khz, 600000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_max_mem_clk_in_khz, 1000000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_min_mem_clk_in_khz, 500000U);
+
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_set_id, WM_SET_A);
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_max_socclk_clk_in_khz, 1400000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_min_socclk_clk_in_khz, 700000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_max_mem_clk_in_khz, 1100000U);
+ KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_min_mem_clk_in_khz, 550000U);
+}
+
+/* ---- Tests for cap_clock_levels_to_validation ---- */
+
+/**
+ * dm_test_cap_clock_levels_engine_caps - Test engine clock level capping
+ * @test: KUnit test context
+ *
+ * Verify that for engine clocks, num_levels is reduced to the index of the
+ * first level whose frequency exceeds the engine validation clock.
+ */
+static void dm_test_cap_clock_levels_engine_caps(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+ struct amd_pp_simple_clock_info validation = {
+ .engine_max_clock = 450000,
+ .memory_max_clock = 800000,
+ };
+
+ clks.num_levels = 3;
+ clks.clocks_in_khz[0] = 300000;
+ clks.clocks_in_khz[1] = 400000;
+ clks.clocks_in_khz[2] = 500000;
+
+ cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_ENGINE_CLK, &validation);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 2U);
+}
+
+/**
+ * dm_test_cap_clock_levels_engine_first_exceeds - Test floor of one level
+ * @test: KUnit test context
+ *
+ * Verify that when the very first engine clock level already exceeds the
+ * validation clock, num_levels is clamped to 1 rather than 0.
+ */
+static void dm_test_cap_clock_levels_engine_first_exceeds(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+ struct amd_pp_simple_clock_info validation = {
+ .engine_max_clock = 100000,
+ .memory_max_clock = 800000,
+ };
+
+ clks.num_levels = 3;
+ clks.clocks_in_khz[0] = 300000;
+ clks.clocks_in_khz[1] = 400000;
+ clks.clocks_in_khz[2] = 500000;
+
+ cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_ENGINE_CLK, &validation);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 1U);
+}
+
+/**
+ * dm_test_cap_clock_levels_memory_caps - Test memory clock level capping
+ * @test: KUnit test context
+ *
+ * Verify that for memory clocks, num_levels is reduced based on the memory
+ * validation clock (and is unaffected by the engine validation clock).
+ */
+static void dm_test_cap_clock_levels_memory_caps(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+ struct amd_pp_simple_clock_info validation = {
+ .engine_max_clock = 100000,
+ .memory_max_clock = 700000,
+ };
+
+ clks.num_levels = 2;
+ clks.clocks_in_khz[0] = 333000;
+ clks.clocks_in_khz[1] = 800000;
+
+ cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_MEMORY_CLK, &validation);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 1U);
+}
+
+/**
+ * dm_test_cap_clock_levels_within_limit - Test no capping when within limit
+ * @test: KUnit test context
+ *
+ * Verify that num_levels is left unchanged when no level exceeds the
+ * validation clock.
+ */
+static void dm_test_cap_clock_levels_within_limit(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+ struct amd_pp_simple_clock_info validation = {
+ .engine_max_clock = 999000,
+ .memory_max_clock = 999000,
+ };
+
+ clks.num_levels = 3;
+ clks.clocks_in_khz[0] = 300000;
+ clks.clocks_in_khz[1] = 400000;
+ clks.clocks_in_khz[2] = 500000;
+
+ cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_ENGINE_CLK, &validation);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 3U);
+}
+
+/**
+ * dm_test_cap_clock_levels_other_type - Test non-engine/memory types ignored
+ * @test: KUnit test context
+ *
+ * Verify that for clock types other than engine or memory, num_levels is
+ * left unchanged regardless of the validation clocks.
+ */
+static void dm_test_cap_clock_levels_other_type(struct kunit *test)
+{
+ struct dm_pp_clock_levels clks = { 0 };
+ struct amd_pp_simple_clock_info validation = {
+ .engine_max_clock = 1,
+ .memory_max_clock = 1,
+ };
+
+ clks.num_levels = 3;
+ clks.clocks_in_khz[0] = 300000;
+ clks.clocks_in_khz[1] = 400000;
+ clks.clocks_in_khz[2] = 500000;
+
+ cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_DISPLAY_CLK, &validation);
+
+ KUNIT_EXPECT_EQ(test, clks.num_levels, 3U);
+}
+
+/* ---- Tests for pp_smu_nv_clock_id_to_pp ---- */
+
+/**
+ * dm_test_nv_clock_id_dispclk - Test DISPCLK id mapping
+ * @test: KUnit test context
+ *
+ * Verify that PP_SMU_NV_DISPCLK maps to amd_pp_disp_clock and returns true.
+ */
+static void dm_test_nv_clock_id_dispclk(struct kunit *test)
+{
+ enum amd_pp_clock_type clock_type = amd_pp_mem_clock;
+
+ KUNIT_EXPECT_TRUE(test, pp_smu_nv_clock_id_to_pp(PP_SMU_NV_DISPCLK, &clock_type));
+ KUNIT_EXPECT_EQ(test, clock_type, amd_pp_disp_clock);
+}
+
+/**
+ * dm_test_nv_clock_id_phyclk - Test PHYCLK id mapping
+ * @test: KUnit test context
+ *
+ * Verify that PP_SMU_NV_PHYCLK maps to amd_pp_phy_clock and returns true.
+ */
+static void dm_test_nv_clock_id_phyclk(struct kunit *test)
+{
+ enum amd_pp_clock_type clock_type = amd_pp_mem_clock;
+
+ KUNIT_EXPECT_TRUE(test, pp_smu_nv_clock_id_to_pp(PP_SMU_NV_PHYCLK, &clock_type));
+ KUNIT_EXPECT_EQ(test, clock_type, amd_pp_phy_clock);
+}
+
+/**
+ * dm_test_nv_clock_id_pixelclk - Test PIXELCLK id mapping
+ * @test: KUnit test context
+ *
+ * Verify that PP_SMU_NV_PIXELCLK maps to amd_pp_pixel_clock and returns true.
+ */
+static void dm_test_nv_clock_id_pixelclk(struct kunit *test)
+{
+ enum amd_pp_clock_type clock_type = amd_pp_mem_clock;
+
+ KUNIT_EXPECT_TRUE(test, pp_smu_nv_clock_id_to_pp(PP_SMU_NV_PIXELCLK, &clock_type));
+ KUNIT_EXPECT_EQ(test, clock_type, amd_pp_pixel_clock);
+}
+
+/**
+ * dm_test_nv_clock_id_invalid - Test unknown id is rejected
+ * @test: KUnit test context
+ *
+ * Verify that an unknown clock id returns false and leaves the output
+ * clock_type untouched, guarding against the previously uninitialized path.
+ */
+static void dm_test_nv_clock_id_invalid(struct kunit *test)
+{
+ enum amd_pp_clock_type clock_type = amd_pp_dcef_clock;
+
+ KUNIT_EXPECT_FALSE(test, pp_smu_nv_clock_id_to_pp((enum pp_smu_nv_clock_id)0xff,
+ &clock_type));
+ KUNIT_EXPECT_EQ(test, clock_type, amd_pp_dcef_clock);
+}
+
+/* ---- Tests using stub DPM layer ---- */
+
+/**
+ * dm_test_apply_display_requirements_dpm_enabled - Test DPM-enabled path
+ * @test: KUnit test context
+ *
+ * Verify that dm_pp_apply_display_requirements calls build_pm_display_cfg
+ * and the DPM callbacks when DPM is enabled, and returns true.
+ */
+static void dm_test_apply_display_requirements_dpm_enabled(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_display_configuration cfg = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ adev->pm.dpm_enabled = true;
+
+ cfg.display_count = 1;
+ cfg.min_engine_clock_khz = 300000;
+ cfg.disp_configs[0].v_refresh = 60;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_apply_display_requirements(ctx, &cfg));
+ KUNIT_EXPECT_EQ(test, adev->pm.pm_display_cfg.min_core_set_clock, 30000);
+ KUNIT_EXPECT_EQ(test, adev->pm.pm_display_cfg.vrefresh, 60);
+}
+
+/**
+ * dm_test_get_clock_levels_by_type_dpm_error - Test DPM error fallback
+ * @test: KUnit test context
+ *
+ * Verify that dm_pp_get_clock_levels_by_type falls back to default clock
+ * levels when amdgpu_dpm_get_clock_by_type returns an error.
+ */
+static void dm_test_get_clock_levels_by_type_dpm_error(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_levels dc_clks = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ stub_dpm_ctx->ret_val = -EINVAL;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type(ctx,
+ DM_PP_CLOCK_TYPE_DISPLAY_CLK, &dc_clks));
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 6U);
+ KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[0], 300000U);
+}
+
+/**
+ * dm_test_get_clock_levels_by_type_success - Test successful clock query
+ * @test: KUnit test context
+ *
+ * Verify that dm_pp_get_clock_levels_by_type returns the queried clocks
+ * capped by validation clocks.
+ */
+static void dm_test_get_clock_levels_by_type_success(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_levels dc_clks = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+
+ stub_dpm_ctx->get_clock_by_type_clocks.count = 3;
+ stub_dpm_ctx->get_clock_by_type_clocks.clock[0] = 300000;
+ stub_dpm_ctx->get_clock_by_type_clocks.clock[1] = 500000;
+ stub_dpm_ctx->get_clock_by_type_clocks.clock[2] = 700000;
+
+ /* validation at 60000 * 10 = 600000 kHz → caps to 2 levels */
+ stub_dpm_ctx->get_validation_clks.engine_max_clock = 60000;
+ stub_dpm_ctx->get_validation_clks.memory_max_clock = 80000;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type(ctx,
+ DM_PP_CLOCK_TYPE_ENGINE_CLK, &dc_clks));
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 2U);
+ KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[0], 300000U);
+ KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[1], 500000U);
+}
+
+/**
+ * dm_test_get_clock_levels_by_type_validation_fallback - Test validation error
+ * @test: KUnit test context
+ *
+ * Verify that dm_pp_get_clock_levels_by_type uses default validation clocks
+ * (engine=720000, memory=800000 kHz) when get_display_mode_validation_clocks
+ * returns an error, capping levels accordingly.
+ */
+static void dm_test_get_clock_levels_by_type_validation_fallback(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_levels dc_clks = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+
+ /* get_clock_by_type succeeds with 3 engine clock levels */
+ stub_dpm_ctx->get_clock_by_type_clocks.count = 3;
+ stub_dpm_ctx->get_clock_by_type_clocks.clock[0] = 300000;
+ stub_dpm_ctx->get_clock_by_type_clocks.clock[1] = 500000;
+ stub_dpm_ctx->get_clock_by_type_clocks.clock[2] = 800000;
+
+ /* Force validation clocks to fail → triggers default path */
+ stub_dpm_ctx->get_validation_clks_ret = -EINVAL;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type(ctx,
+ DM_PP_CLOCK_TYPE_ENGINE_CLK, &dc_clks));
+ /*
+ * Default validation: engine_max_clock = 72000 * 10 = 720000 kHz.
+ * Clocks 300000 and 500000 are within limit, 800000 exceeds it,
+ * so num_levels is capped to 2.
+ */
+ KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 2U);
+ KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[0], 300000U);
+ KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[1], 500000U);
+}
+
+/**
+ * dm_test_get_clock_levels_with_latency_success - Test latency clock query
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_get_clock_levels_by_type_with_latency returns true and
+ * copies the clock/latency data from the DPM backend.
+ */
+static void dm_test_get_clock_levels_with_latency_success(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_levels_with_latency info = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+
+ stub_dpm_ctx->get_clock_by_type_with_latency_clks.num_levels = 1;
+ stub_dpm_ctx->get_clock_by_type_with_latency_clks.data[0].clocks_in_khz = 600000;
+ stub_dpm_ctx->get_clock_by_type_with_latency_clks.data[0].latency_in_us = 15;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type_with_latency(ctx,
+ DM_PP_CLOCK_TYPE_ENGINE_CLK, &info));
+ KUNIT_EXPECT_EQ(test, info.num_levels, 1U);
+ KUNIT_EXPECT_EQ(test, info.data[0].clocks_in_khz, 600000U);
+ KUNIT_EXPECT_EQ(test, info.data[0].latency_in_us, 15U);
+}
+
+/**
+ * dm_test_get_clock_levels_with_latency_failure - Test latency query error
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_get_clock_levels_by_type_with_latency returns false on DPM error.
+ */
+static void dm_test_get_clock_levels_with_latency_failure(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_levels_with_latency info = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ stub_dpm_ctx->ret_val = -EINVAL;
+
+ KUNIT_EXPECT_FALSE(test, dm_pp_get_clock_levels_by_type_with_latency(ctx,
+ DM_PP_CLOCK_TYPE_ENGINE_CLK, &info));
+}
+
+/**
+ * dm_test_get_clock_levels_with_voltage_success - Test voltage clock query
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_get_clock_levels_by_type_with_voltage returns true and
+ * copies the clock/voltage data from the DPM backend.
+ */
+static void dm_test_get_clock_levels_with_voltage_success(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_levels_with_voltage info = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+
+ stub_dpm_ctx->get_clock_by_type_with_voltage_clks.num_levels = 1;
+ stub_dpm_ctx->get_clock_by_type_with_voltage_clks.data[0].clocks_in_khz = 400000;
+ stub_dpm_ctx->get_clock_by_type_with_voltage_clks.data[0].voltage_in_mv = 900;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type_with_voltage(ctx,
+ DM_PP_CLOCK_TYPE_MEMORY_CLK, &info));
+ KUNIT_EXPECT_EQ(test, info.num_levels, 1U);
+ KUNIT_EXPECT_EQ(test, info.data[0].clocks_in_khz, 400000U);
+ KUNIT_EXPECT_EQ(test, info.data[0].voltage_in_mv, 900U);
+}
+
+/**
+ * dm_test_get_clock_levels_with_voltage_failure - Test voltage query error
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_get_clock_levels_by_type_with_voltage returns false on DPM error.
+ */
+static void dm_test_get_clock_levels_with_voltage_failure(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_levels_with_voltage info = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ stub_dpm_ctx->ret_val = -EINVAL;
+
+ KUNIT_EXPECT_FALSE(test, dm_pp_get_clock_levels_by_type_with_voltage(ctx,
+ DM_PP_CLOCK_TYPE_MEMORY_CLK, &info));
+}
+
+/**
+ * dm_test_notify_wm_clock_changes_polaris - Test Polaris watermark path
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_notify_wm_clock_changes returns true for Polaris ASICs
+ * when the DPM set_watermarks call succeeds.
+ */
+static void dm_test_notify_wm_clock_changes_polaris(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_wm_sets_with_clock_ranges wm = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ adev->asic_type = CHIP_POLARIS10;
+ stub_dpm_ctx->set_watermarks_ret = 0;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_notify_wm_clock_changes(ctx, &wm));
+}
+
+/**
+ * dm_test_notify_wm_clock_changes_non_polaris - Test non-Polaris path
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_notify_wm_clock_changes returns false for non-Polaris ASICs.
+ */
+static void dm_test_notify_wm_clock_changes_non_polaris(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_wm_sets_with_clock_ranges wm = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ adev->asic_type = CHIP_NAVI10;
+
+ KUNIT_EXPECT_FALSE(test, dm_pp_notify_wm_clock_changes(ctx, &wm));
+}
+
+/**
+ * dm_test_apply_clock_for_voltage_success - Test successful voltage request
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_apply_clock_for_voltage_request returns true when the DPM
+ * callback succeeds for a valid clock type.
+ */
+static void dm_test_apply_clock_for_voltage_success(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_for_voltage_req req = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ stub_dpm_ctx->display_clock_voltage_ret = 0;
+
+ req.clk_type = DM_PP_CLOCK_TYPE_ENGINE_CLK;
+ req.clocks_in_khz = 500000;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_apply_clock_for_voltage_request(ctx, &req));
+}
+
+/**
+ * dm_test_apply_clock_for_voltage_eopnotsupp - Test EOPNOTSUPP treated as success
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_apply_clock_for_voltage_request returns true when the DPM
+ * callback returns -EOPNOTSUPP (not supported is non-fatal).
+ */
+static void dm_test_apply_clock_for_voltage_eopnotsupp(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_for_voltage_req req = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ stub_dpm_ctx->display_clock_voltage_ret = -EOPNOTSUPP;
+
+ req.clk_type = DM_PP_CLOCK_TYPE_ENGINE_CLK;
+ req.clocks_in_khz = 500000;
+
+ KUNIT_EXPECT_TRUE(test, dm_pp_apply_clock_for_voltage_request(ctx, &req));
+}
+
+/**
+ * dm_test_apply_clock_for_voltage_fail - Test DPM error returns false
+ * @test: KUnit test context
+ *
+ * Verify dm_pp_apply_clock_for_voltage_request returns false when the DPM
+ * callback fails with an error other than -EOPNOTSUPP.
+ */
+static void dm_test_apply_clock_for_voltage_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct dm_pp_clock_for_voltage_req req = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ stub_dpm_ctx->display_clock_voltage_ret = -EIO;
+
+ req.clk_type = DM_PP_CLOCK_TYPE_ENGINE_CLK;
+ req.clocks_in_khz = 500000;
+
+ KUNIT_EXPECT_FALSE(test, dm_pp_apply_clock_for_voltage_request(ctx, &req));
+}
+
+/* ---- Tests for pp_nv_set_display_count ---- */
+
+/**
+ * dm_test_nv_set_display_count_ok - Test successful display count set
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_display_count returns PP_SMU_RESULT_OK on success.
+ */
+static void dm_test_nv_set_display_count_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->set_active_display_count_ret = 0;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_display_count(&pp_smu, 2),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_nv_set_display_count_unsupported - Test EOPNOTSUPP mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_display_count returns PP_SMU_RESULT_UNSUPPORTED when
+ * the DPM callback returns -EOPNOTSUPP.
+ */
+static void dm_test_nv_set_display_count_unsupported(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->set_active_display_count_ret = -EOPNOTSUPP;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_display_count(&pp_smu, 2),
+ (int)PP_SMU_RESULT_UNSUPPORTED);
+}
+
+/**
+ * dm_test_nv_set_display_count_fail - Test generic error mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_display_count returns PP_SMU_RESULT_FAIL on a generic
+ * DPM error.
+ */
+static void dm_test_nv_set_display_count_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->set_active_display_count_ret = -EIO;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_display_count(&pp_smu, 2),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+/* ---- Tests for pp_nv_set_voltage_by_freq ---- */
+
+/**
+ * dm_test_nv_set_voltage_by_freq_ok - Test successful voltage-by-freq
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_voltage_by_freq returns PP_SMU_RESULT_OK on success.
+ */
+static void dm_test_nv_set_voltage_by_freq_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_clock_voltage_ret = 0;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_voltage_by_freq(&pp_smu, PP_SMU_NV_DISPCLK, 600),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_nv_set_voltage_by_freq_invalid_id - Test invalid clock id
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_voltage_by_freq returns PP_SMU_RESULT_FAIL for an
+ * unrecognized clock id without calling DPM.
+ */
+static void dm_test_nv_set_voltage_by_freq_invalid_id(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)pp_nv_set_voltage_by_freq(&pp_smu, (enum pp_smu_nv_clock_id)0xff, 600),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+/* ---- Tests for pp_nv_set_pstate_handshake_support ---- */
+
+/**
+ * dm_test_nv_pstate_handshake_ok - Test successful pstate handshake
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_pstate_handshake_support returns PP_SMU_RESULT_OK
+ * when the DPM callback succeeds.
+ */
+static void dm_test_nv_pstate_handshake_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_disable_memory_clock_switch_ret = 0;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_pstate_handshake_support(&pp_smu, true),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_nv_pstate_handshake_fail - Test failed pstate handshake
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_pstate_handshake_support returns PP_SMU_RESULT_FAIL
+ * when the DPM callback returns non-zero.
+ */
+static void dm_test_nv_pstate_handshake_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_disable_memory_clock_switch_ret = -EIO;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_pstate_handshake_support(&pp_smu, true),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+/* ---- Tests for pp_rn_get_dpm_clock_table ---- */
+
+/**
+ * dm_test_rn_get_dpm_clock_table_ok - Test successful DPM clock table
+ * @test: KUnit test context
+ *
+ * Verify pp_rn_get_dpm_clock_table returns PP_SMU_RESULT_OK on success.
+ */
+static void dm_test_rn_get_dpm_clock_table_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ struct dpm_clocks clock_table = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_dpm_clock_table_ret = 0;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_rn_get_dpm_clock_table(&pp_smu, &clock_table),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_rn_get_dpm_clock_table_unsupported - Test EOPNOTSUPP mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_rn_get_dpm_clock_table returns PP_SMU_RESULT_UNSUPPORTED.
+ */
+static void dm_test_rn_get_dpm_clock_table_unsupported(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ struct dpm_clocks clock_table = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_dpm_clock_table_ret = -EOPNOTSUPP;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_rn_get_dpm_clock_table(&pp_smu, &clock_table),
+ (int)PP_SMU_RESULT_UNSUPPORTED);
+}
+
+/**
+ * dm_test_rn_get_dpm_clock_table_fail - Test generic error mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_rn_get_dpm_clock_table returns PP_SMU_RESULT_FAIL.
+ */
+static void dm_test_rn_get_dpm_clock_table_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ struct dpm_clocks clock_table = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_dpm_clock_table_ret = -EIO;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_rn_get_dpm_clock_table(&pp_smu, &clock_table),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+/* ---- Tests for pp_rv_set_wm_ranges ---- */
+
+/**
+ * dm_test_rv_set_wm_ranges - Test Raven watermark range forwarding
+ * @test: KUnit test context
+ *
+ * Verify pp_rv_set_wm_ranges converts watermark ranges via
+ * build_wm_clock_ranges_soc15 and forwards them to DPM without crashing.
+ */
+static void dm_test_rv_set_wm_ranges(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ struct pp_smu_wm_range_sets ranges = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+
+ ranges.num_reader_wm_sets = 1;
+ ranges.reader_wm_sets[0].wm_inst = 0;
+ ranges.reader_wm_sets[0].max_drain_clk_mhz = 600;
+ ranges.reader_wm_sets[0].min_drain_clk_mhz = 300;
+
+ pp_rv_set_wm_ranges(&pp_smu, &ranges);
+
+ /* Reaching here without crash confirms coverage */
+ KUNIT_SUCCEED(test);
+}
+
+/* ---- Tests for pp_rv_set_pme_wa_enable ---- */
+
+/**
+ * dm_test_rv_set_pme_wa_enable - Test Raven PME workaround enable
+ * @test: KUnit test context
+ *
+ * Verify pp_rv_set_pme_wa_enable forwards the call to DPM without crashing.
+ */
+static void dm_test_rv_set_pme_wa_enable(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+
+ pp_rv_set_pme_wa_enable(&pp_smu);
+
+ KUNIT_SUCCEED(test);
+}
+
+/* ---- Tests for pp_rv_set_active_display_count ---- */
+
+/**
+ * dm_test_rv_set_active_display_count - Test Raven display count forwarding
+ * @test: KUnit test context
+ *
+ * Verify pp_rv_set_active_display_count forwards the count to DPM without
+ * crashing.
+ */
+static void dm_test_rv_set_active_display_count(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+
+ pp_rv_set_active_display_count(&pp_smu, 2);
+
+ KUNIT_SUCCEED(test);
+}
+
+/* ---- Tests for pp_rv_set_min_deep_sleep_dcfclk ---- */
+
+/**
+ * dm_test_rv_set_min_deep_sleep_dcfclk - Test Raven deep sleep clock
+ * @test: KUnit test context
+ *
+ * Verify pp_rv_set_min_deep_sleep_dcfclk forwards the clock value to DPM
+ * without crashing.
+ */
+static void dm_test_rv_set_min_deep_sleep_dcfclk(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+
+ pp_rv_set_min_deep_sleep_dcfclk(&pp_smu, 300);
+
+ KUNIT_SUCCEED(test);
+}
+
+/* ---- Tests for pp_rv_set_hard_min_dcefclk_by_freq ---- */
+
+/**
+ * dm_test_rv_set_hard_min_dcefclk_by_freq - Test Raven hard min DCEFCLK
+ * @test: KUnit test context
+ *
+ * Verify pp_rv_set_hard_min_dcefclk_by_freq forwards the frequency to DPM
+ * without crashing.
+ */
+static void dm_test_rv_set_hard_min_dcefclk_by_freq(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+
+ pp_rv_set_hard_min_dcefclk_by_freq(&pp_smu, 600);
+
+ KUNIT_SUCCEED(test);
+}
+
+/* ---- Tests for pp_rv_set_hard_min_fclk_by_freq ---- */
+
+/**
+ * dm_test_rv_set_hard_min_fclk_by_freq - Test Raven hard min FCLK
+ * @test: KUnit test context
+ *
+ * Verify pp_rv_set_hard_min_fclk_by_freq forwards the frequency to DPM
+ * without crashing.
+ */
+static void dm_test_rv_set_hard_min_fclk_by_freq(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+
+ pp_rv_set_hard_min_fclk_by_freq(&pp_smu, 800);
+
+ KUNIT_SUCCEED(test);
+}
+
+/* ---- Tests for pp_nv_set_wm_ranges ---- */
+
+/**
+ * dm_test_nv_set_wm_ranges - Test Navi watermark range forwarding
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_wm_ranges forwards ranges to DPM and unconditionally
+ * returns PP_SMU_RESULT_OK.
+ */
+static void dm_test_nv_set_wm_ranges(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ struct pp_smu_wm_range_sets ranges = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+
+ ranges.num_reader_wm_sets = 1;
+ ranges.reader_wm_sets[0].wm_inst = 0;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_wm_ranges(&pp_smu, &ranges),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/* ---- Tests for pp_nv_set_min_deep_sleep_dcfclk ---- */
+
+/**
+ * dm_test_nv_set_min_deep_sleep_dcfclk_ok - Test successful deep sleep set
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_min_deep_sleep_dcfclk returns PP_SMU_RESULT_OK on success.
+ */
+static void dm_test_nv_set_min_deep_sleep_dcfclk_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->set_min_deep_sleep_dcefclk_ret = 0;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_min_deep_sleep_dcfclk(&pp_smu, 300),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_nv_set_min_deep_sleep_dcfclk_unsupported - Test EOPNOTSUPP mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_min_deep_sleep_dcfclk returns PP_SMU_RESULT_UNSUPPORTED.
+ */
+static void dm_test_nv_set_min_deep_sleep_dcfclk_unsupported(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->set_min_deep_sleep_dcefclk_ret = -EOPNOTSUPP;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_min_deep_sleep_dcfclk(&pp_smu, 300),
+ (int)PP_SMU_RESULT_UNSUPPORTED);
+}
+
+/**
+ * dm_test_nv_set_min_deep_sleep_dcfclk_fail - Test generic error mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_min_deep_sleep_dcfclk returns PP_SMU_RESULT_FAIL.
+ */
+static void dm_test_nv_set_min_deep_sleep_dcfclk_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->set_min_deep_sleep_dcefclk_ret = -EIO;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_min_deep_sleep_dcfclk(&pp_smu, 300),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+/* ---- Tests for pp_nv_set_hard_min_dcefclk_by_freq ---- */
+
+/**
+ * dm_test_nv_set_hard_min_dcefclk_ok - Test successful hard min DCEFCLK
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_hard_min_dcefclk_by_freq returns PP_SMU_RESULT_OK.
+ */
+static void dm_test_nv_set_hard_min_dcefclk_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_clock_voltage_ret = 0;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_dcefclk_by_freq(&pp_smu, 600),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_nv_set_hard_min_dcefclk_unsupported - Test EOPNOTSUPP mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_hard_min_dcefclk_by_freq returns PP_SMU_RESULT_UNSUPPORTED.
+ */
+static void dm_test_nv_set_hard_min_dcefclk_unsupported(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_clock_voltage_ret = -EOPNOTSUPP;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_dcefclk_by_freq(&pp_smu, 600),
+ (int)PP_SMU_RESULT_UNSUPPORTED);
+}
+
+/**
+ * dm_test_nv_set_hard_min_dcefclk_fail - Test generic error mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_hard_min_dcefclk_by_freq returns PP_SMU_RESULT_FAIL.
+ */
+static void dm_test_nv_set_hard_min_dcefclk_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_clock_voltage_ret = -EIO;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_dcefclk_by_freq(&pp_smu, 600),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+/* ---- Tests for pp_nv_set_hard_min_uclk_by_freq ---- */
+
+/**
+ * dm_test_nv_set_hard_min_uclk_ok - Test successful hard min UCLK
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_hard_min_uclk_by_freq returns PP_SMU_RESULT_OK.
+ */
+static void dm_test_nv_set_hard_min_uclk_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_clock_voltage_ret = 0;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_uclk_by_freq(&pp_smu, 800),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_nv_set_hard_min_uclk_unsupported - Test EOPNOTSUPP mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_hard_min_uclk_by_freq returns PP_SMU_RESULT_UNSUPPORTED.
+ */
+static void dm_test_nv_set_hard_min_uclk_unsupported(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_clock_voltage_ret = -EOPNOTSUPP;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_uclk_by_freq(&pp_smu, 800),
+ (int)PP_SMU_RESULT_UNSUPPORTED);
+}
+
+/**
+ * dm_test_nv_set_hard_min_uclk_fail - Test generic error mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_set_hard_min_uclk_by_freq returns PP_SMU_RESULT_FAIL.
+ */
+static void dm_test_nv_set_hard_min_uclk_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->display_clock_voltage_ret = -EIO;
+
+ KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_uclk_by_freq(&pp_smu, 800),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+/* ---- Tests for pp_nv_get_maximum_sustainable_clocks ---- */
+
+/**
+ * dm_test_nv_get_max_sustainable_clocks_ok - Test successful query
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_get_maximum_sustainable_clocks returns PP_SMU_RESULT_OK.
+ */
+static void dm_test_nv_get_max_sustainable_clocks_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ struct pp_smu_nv_clock_table max_clocks = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_max_sustainable_ret = 0;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)pp_nv_get_maximum_sustainable_clocks(&pp_smu, &max_clocks),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_nv_get_max_sustainable_clocks_unsupported - Test EOPNOTSUPP
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_get_maximum_sustainable_clocks returns PP_SMU_RESULT_UNSUPPORTED.
+ */
+static void dm_test_nv_get_max_sustainable_clocks_unsupported(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ struct pp_smu_nv_clock_table max_clocks = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_max_sustainable_ret = -EOPNOTSUPP;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)pp_nv_get_maximum_sustainable_clocks(&pp_smu, &max_clocks),
+ (int)PP_SMU_RESULT_UNSUPPORTED);
+}
+
+/**
+ * dm_test_nv_get_max_sustainable_clocks_fail - Test generic error
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_get_maximum_sustainable_clocks returns PP_SMU_RESULT_FAIL.
+ */
+static void dm_test_nv_get_max_sustainable_clocks_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ struct pp_smu_nv_clock_table max_clocks = {};
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_max_sustainable_ret = -EIO;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)pp_nv_get_maximum_sustainable_clocks(&pp_smu, &max_clocks),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+/* ---- Tests for pp_nv_get_uclk_dpm_states ---- */
+
+/**
+ * dm_test_nv_get_uclk_dpm_states_ok - Test successful DPM states query
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_get_uclk_dpm_states returns PP_SMU_RESULT_OK.
+ */
+static void dm_test_nv_get_uclk_dpm_states_ok(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ unsigned int clock_values[4] = {};
+ unsigned int num_states = 0;
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_uclk_dpm_ret = 0;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)pp_nv_get_uclk_dpm_states(&pp_smu, clock_values, &num_states),
+ (int)PP_SMU_RESULT_OK);
+}
+
+/**
+ * dm_test_nv_get_uclk_dpm_states_unsupported - Test EOPNOTSUPP mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_get_uclk_dpm_states returns PP_SMU_RESULT_UNSUPPORTED.
+ */
+static void dm_test_nv_get_uclk_dpm_states_unsupported(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ unsigned int clock_values[4] = {};
+ unsigned int num_states = 0;
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_uclk_dpm_ret = -EOPNOTSUPP;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)pp_nv_get_uclk_dpm_states(&pp_smu, clock_values, &num_states),
+ (int)PP_SMU_RESULT_UNSUPPORTED);
+}
+
+/**
+ * dm_test_nv_get_uclk_dpm_states_fail - Test generic error mapping
+ * @test: KUnit test context
+ *
+ * Verify pp_nv_get_uclk_dpm_states returns PP_SMU_RESULT_FAIL.
+ */
+static void dm_test_nv_get_uclk_dpm_states_fail(struct kunit *test)
+{
+ struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ struct pp_smu pp_smu = {};
+ unsigned int clock_values[4] = {};
+ unsigned int num_states = 0;
+
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ setup_stub_dpm(test, adev);
+ ctx->driver_context = adev;
+ pp_smu.dm = ctx;
+ stub_dpm_ctx->get_uclk_dpm_ret = -EIO;
+
+ KUNIT_EXPECT_EQ(test,
+ (int)pp_nv_get_uclk_dpm_states(&pp_smu, clock_values, &num_states),
+ (int)PP_SMU_RESULT_FAIL);
+}
+
+static struct kunit_case dm_pp_smu_test_cases[] = {
+ /* get_default_clock_levels */
+ KUNIT_CASE(dm_test_default_clock_levels_display),
+ KUNIT_CASE(dm_test_default_clock_levels_engine),
+ KUNIT_CASE(dm_test_default_clock_levels_memory),
+ KUNIT_CASE(dm_test_default_clock_levels_unknown),
+ /* dc_to_pp_clock_type */
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_display),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_engine),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_memory),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_dcefclk),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_dcfclk),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_pixelclk),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_fclk),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_phyclk),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_dppclk),
+ KUNIT_CASE(dm_test_dc_to_pp_clock_type_invalid),
+ /* pp_to_dc_clock_levels */
+ KUNIT_CASE(dm_test_pp_to_dc_clock_levels_within_limit),
+ KUNIT_CASE(dm_test_pp_to_dc_clock_levels_caps_at_max),
+ /* pp_to_dc_clock_levels_with_latency */
+ KUNIT_CASE(dm_test_pp_to_dc_clock_levels_latency_within_limit),
+ KUNIT_CASE(dm_test_pp_to_dc_clock_levels_latency_caps_at_max),
+ /* pp_to_dc_clock_levels_with_voltage */
+ KUNIT_CASE(dm_test_pp_to_dc_clock_levels_voltage_within_limit),
+ KUNIT_CASE(dm_test_pp_to_dc_clock_levels_voltage_caps_at_max),
+ /* dm_pp_get_funcs */
+ KUNIT_CASE(dm_test_get_funcs_rv),
+ KUNIT_CASE(dm_test_get_funcs_rv_101),
+ KUNIT_CASE(dm_test_get_funcs_nv),
+ KUNIT_CASE(dm_test_get_funcs_rn),
+ KUNIT_CASE(dm_test_get_funcs_unsupported),
+ /* amdgpu_device-backed entry points */
+ KUNIT_CASE(dm_test_apply_display_requirements_dpm_disabled),
+ KUNIT_CASE(dm_test_apply_clock_for_voltage_invalid_type),
+ /* build_pm_display_cfg */
+ KUNIT_CASE(dm_test_build_pm_display_cfg_scalar_fields),
+ KUNIT_CASE(dm_test_build_pm_display_cfg_per_display),
+ /* build_wm_clock_ranges_soc15 */
+ KUNIT_CASE(dm_test_build_wm_clock_ranges_dmif),
+ KUNIT_CASE(dm_test_build_wm_clock_ranges_mcif),
+ /* cap_clock_levels_to_validation */
+ KUNIT_CASE(dm_test_cap_clock_levels_engine_caps),
+ KUNIT_CASE(dm_test_cap_clock_levels_engine_first_exceeds),
+ KUNIT_CASE(dm_test_cap_clock_levels_memory_caps),
+ KUNIT_CASE(dm_test_cap_clock_levels_within_limit),
+ KUNIT_CASE(dm_test_cap_clock_levels_other_type),
+ /* pp_smu_nv_clock_id_to_pp */
+ KUNIT_CASE(dm_test_nv_clock_id_dispclk),
+ KUNIT_CASE(dm_test_nv_clock_id_phyclk),
+ KUNIT_CASE(dm_test_nv_clock_id_pixelclk),
+ KUNIT_CASE(dm_test_nv_clock_id_invalid),
+ /* dm_pp_apply_display_requirements (DPM enabled) */
+ KUNIT_CASE(dm_test_apply_display_requirements_dpm_enabled),
+ /* dm_pp_get_clock_levels_by_type */
+ KUNIT_CASE(dm_test_get_clock_levels_by_type_dpm_error),
+ KUNIT_CASE(dm_test_get_clock_levels_by_type_success),
+ KUNIT_CASE(dm_test_get_clock_levels_by_type_validation_fallback),
+ /* dm_pp_get_clock_levels_by_type_with_latency */
+ KUNIT_CASE(dm_test_get_clock_levels_with_latency_success),
+ KUNIT_CASE(dm_test_get_clock_levels_with_latency_failure),
+ /* dm_pp_get_clock_levels_by_type_with_voltage */
+ KUNIT_CASE(dm_test_get_clock_levels_with_voltage_success),
+ KUNIT_CASE(dm_test_get_clock_levels_with_voltage_failure),
+ /* dm_pp_notify_wm_clock_changes */
+ KUNIT_CASE(dm_test_notify_wm_clock_changes_polaris),
+ KUNIT_CASE(dm_test_notify_wm_clock_changes_non_polaris),
+ /* dm_pp_apply_clock_for_voltage_request (with DPM) */
+ KUNIT_CASE(dm_test_apply_clock_for_voltage_success),
+ KUNIT_CASE(dm_test_apply_clock_for_voltage_eopnotsupp),
+ KUNIT_CASE(dm_test_apply_clock_for_voltage_fail),
+ /* pp_nv_set_display_count */
+ KUNIT_CASE(dm_test_nv_set_display_count_ok),
+ KUNIT_CASE(dm_test_nv_set_display_count_unsupported),
+ KUNIT_CASE(dm_test_nv_set_display_count_fail),
+ /* pp_nv_set_voltage_by_freq */
+ KUNIT_CASE(dm_test_nv_set_voltage_by_freq_ok),
+ KUNIT_CASE(dm_test_nv_set_voltage_by_freq_invalid_id),
+ /* pp_nv_set_pstate_handshake_support */
+ KUNIT_CASE(dm_test_nv_pstate_handshake_ok),
+ KUNIT_CASE(dm_test_nv_pstate_handshake_fail),
+ /* pp_rn_get_dpm_clock_table */
+ KUNIT_CASE(dm_test_rn_get_dpm_clock_table_ok),
+ KUNIT_CASE(dm_test_rn_get_dpm_clock_table_unsupported),
+ KUNIT_CASE(dm_test_rn_get_dpm_clock_table_fail),
+ /* pp_rv_set_wm_ranges */
+ KUNIT_CASE(dm_test_rv_set_wm_ranges),
+ /* pp_rv_set_pme_wa_enable */
+ KUNIT_CASE(dm_test_rv_set_pme_wa_enable),
+ /* pp_rv_set_active_display_count */
+ KUNIT_CASE(dm_test_rv_set_active_display_count),
+ /* pp_rv_set_min_deep_sleep_dcfclk */
+ KUNIT_CASE(dm_test_rv_set_min_deep_sleep_dcfclk),
+ /* pp_rv_set_hard_min_dcefclk_by_freq */
+ KUNIT_CASE(dm_test_rv_set_hard_min_dcefclk_by_freq),
+ /* pp_rv_set_hard_min_fclk_by_freq */
+ KUNIT_CASE(dm_test_rv_set_hard_min_fclk_by_freq),
+ /* pp_nv_set_wm_ranges */
+ KUNIT_CASE(dm_test_nv_set_wm_ranges),
+ /* pp_nv_set_min_deep_sleep_dcfclk */
+ KUNIT_CASE(dm_test_nv_set_min_deep_sleep_dcfclk_ok),
+ KUNIT_CASE(dm_test_nv_set_min_deep_sleep_dcfclk_unsupported),
+ KUNIT_CASE(dm_test_nv_set_min_deep_sleep_dcfclk_fail),
+ /* pp_nv_set_hard_min_dcefclk_by_freq */
+ KUNIT_CASE(dm_test_nv_set_hard_min_dcefclk_ok),
+ KUNIT_CASE(dm_test_nv_set_hard_min_dcefclk_unsupported),
+ KUNIT_CASE(dm_test_nv_set_hard_min_dcefclk_fail),
+ /* pp_nv_set_hard_min_uclk_by_freq */
+ KUNIT_CASE(dm_test_nv_set_hard_min_uclk_ok),
+ KUNIT_CASE(dm_test_nv_set_hard_min_uclk_unsupported),
+ KUNIT_CASE(dm_test_nv_set_hard_min_uclk_fail),
+ /* pp_nv_get_maximum_sustainable_clocks */
+ KUNIT_CASE(dm_test_nv_get_max_sustainable_clocks_ok),
+ KUNIT_CASE(dm_test_nv_get_max_sustainable_clocks_unsupported),
+ KUNIT_CASE(dm_test_nv_get_max_sustainable_clocks_fail),
+ /* pp_nv_get_uclk_dpm_states */
+ KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_ok),
+ KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_unsupported),
+ KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_fail),
+ {}
+};
+
+static struct kunit_suite dm_pp_smu_test_suite = {
+ .name = "amdgpu_dm_pp_smu",
+ .test_cases = dm_pp_smu_test_cases,
+};
+
+kunit_test_suite(dm_pp_smu_test_suite);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_pp_smu");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c
index 09084f70a405..09bd98e93047 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c
@@ -7,29 +7,331 @@
#include <kunit/test.h>
+#include "dc.h"
+#include "core_types.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
#include "amdgpu_dm_psr.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+#include "power_helpers.h"
-/*
- * Helper: allocate and zero-initialise a dc_link sufficient for
- * amdgpu_dm_psr_fill_caps() testing. The function only accesses
- * embedded members (dpcd_caps, psr_settings) so no pointer fields
- * need to be wired up.
- */
-static struct dc_link *alloc_test_link(struct kunit *test)
+static struct dc_stream_state *alloc_test_psr_stream(struct kunit *test)
{
struct dc_link *link;
- link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, link);
+ link = dm_kunit_alloc_link(test);
+ link->psr_settings.psr_feature_enabled = true;
+
+ return dm_kunit_alloc_stream(test, link);
+}
+
+static struct core_power *create_test_power_module(struct kunit *test,
+ struct dc_stream_state *stream, struct psr_caps *caps)
+{
+ struct core_power *core_power;
+
+ core_power = kunit_kzalloc(test, sizeof(*core_power), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, core_power);
+
+ core_power->map = kunit_kzalloc(test, sizeof(*core_power->map), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, core_power->map);
+
+ core_power->map[0].stream = stream;
+ core_power->map[0].caps = caps;
+ core_power->map[0].psr_events = psr_event_vsync;
+ core_power->num_entities = 1;
+
+ return core_power;
+}
+
+static struct dc_link *alloc_test_psrsu_link(struct kunit *test)
+{
+ struct dc_link *link = dm_kunit_alloc_link(test);
+ struct dc_context *ctx;
+ struct dc *dc;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+
+ link->ctx = ctx;
+ ctx->dc = dc;
+ dc->ctx = ctx;
+ dc->caps.dmcub_support = true;
+ ctx->dce_version = DCN_VERSION_3_1;
+ link->dpcd_caps.edp_rev = DP_EDP_14;
+ link->dpcd_caps.psr_info.psr_version = DP_PSR2_WITH_Y_COORD_ET_SUPPORTED;
+ link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1;
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.Y_COORDINATE_REQUIRED = 1;
+
+ return link;
+}
+
+static struct dc_link *alloc_test_psr_caps_link(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+
+ link->ctx->dc->caps.dmub_caps.psr = true;
+ link->connector_signal = SIGNAL_TYPE_EDP;
+ link->type = dc_connection_single;
return link;
}
+static struct amdgpu_dm_connector *alloc_test_aconnector(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector;
+
+ aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, aconnector);
+
+ return aconnector;
+}
+
+/* Tests for link_supports_psrsu() */
+
+/**
+ * dm_test_link_supports_psrsu_no_dmcub() - DMCUB support is required.
+ * @test: KUnit test context.
+ */
+static void dm_test_link_supports_psrsu_no_dmcub(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+
+ link->ctx->dc->caps.dmcub_support = false;
+
+ KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link));
+}
+
+/**
+ * dm_test_link_supports_psrsu_old_dcn() - DCN version 3.1 or newer is required.
+ * @test: KUnit test context.
+ */
+static void dm_test_link_supports_psrsu_old_dcn(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+
+ link->ctx->dce_version = DCN_VERSION_3_0;
+
+ KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link));
+}
+
+/**
+ * dm_test_link_supports_psrsu_panel_unsupported() - Panel PSR-SU caps are required.
+ * @test: KUnit test context.
+ */
+static void dm_test_link_supports_psrsu_panel_unsupported(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+
+ link->dpcd_caps.psr_info.psr_version = 0;
+
+ KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link));
+}
+
+/**
+ * dm_test_link_supports_psrsu_missing_alpm() - AUX wake ALPM is required.
+ * @test: KUnit test context.
+ */
+static void dm_test_link_supports_psrsu_missing_alpm(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+
+ link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 0;
+
+ KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link));
+}
+
+/**
+ * dm_test_link_supports_psrsu_missing_y_coordinate() - Y coordinate support is required.
+ * @test: KUnit test context.
+ */
+static void dm_test_link_supports_psrsu_missing_y_coordinate(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.Y_COORDINATE_REQUIRED = 0;
+
+ KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link));
+}
+
+/**
+ * dm_test_link_supports_psrsu_missing_granularity() - Required granularity must
+ * be reported by the panel.
+ * @test: KUnit test context.
+ */
+static void dm_test_link_supports_psrsu_missing_granularity(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+
+ link->dpcd_caps.psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED = 1;
+ link->dpcd_caps.psr_info.psr2_su_y_granularity_cap = 0;
+
+ KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link));
+}
+
+/**
+ * dm_test_link_supports_psrsu_debug_mask_disabled() - Debug mask disables PSR-SU.
+ * @test: KUnit test context.
+ */
+static void dm_test_link_supports_psrsu_debug_mask_disabled(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+ unsigned int old_debug_mask;
+
+ old_debug_mask = amdgpu_dm_psr_get_dc_debug_mask();
+ amdgpu_dm_psr_set_dc_debug_mask(old_debug_mask | DC_DISABLE_PSR_SU);
+
+ KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link));
+ amdgpu_dm_psr_set_dc_debug_mask(old_debug_mask);
+}
+
+/**
+ * dm_test_link_supports_psrsu_temporarily_disabled() - Supported panels still
+ * return false while PSR-SU is temporarily disabled.
+ * @test: KUnit test context.
+ */
+static void dm_test_link_supports_psrsu_temporarily_disabled(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psrsu_link(test);
+ unsigned int old_debug_mask;
+
+ old_debug_mask = amdgpu_dm_psr_get_dc_debug_mask();
+ amdgpu_dm_psr_set_dc_debug_mask(old_debug_mask & ~DC_DISABLE_PSR_SU);
+
+ KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link));
+ amdgpu_dm_psr_set_dc_debug_mask(old_debug_mask);
+}
+
+/* End of tests for link_supports_psrsu() */
+
+/* Tests for amdgpu_dm_set_psr_caps() */
+
+/**
+ * dm_test_set_psr_caps_null_link() - NULL link is rejected.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_null_link(struct kunit *test)
+{
+ struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(NULL, aconnector));
+}
+
+/**
+ * dm_test_set_psr_caps_null_connector() - NULL connector is rejected.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_null_connector(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psr_caps_link(test);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, NULL));
+}
+
+/**
+ * dm_test_set_psr_caps_no_dmub_psr() - DMUB PSR capability is required.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_no_dmub_psr(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psr_caps_link(test);
+ struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test);
+
+ link->psr_settings.psr_version = DC_PSR_VERSION_1;
+ link->ctx->dc->caps.dmub_caps.psr = false;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector));
+ KUNIT_EXPECT_EQ(test, link->psr_settings.psr_version,
+ DC_PSR_VERSION_UNSUPPORTED);
+}
+
+/**
+ * dm_test_set_psr_caps_non_edp() - Only eDP links can enable PSR.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_non_edp(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psr_caps_link(test);
+ struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test);
+
+ link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector));
+}
+
+/**
+ * dm_test_set_psr_caps_disconnected() - Disconnected links cannot enable PSR.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_disconnected(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psr_caps_link(test);
+ struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test);
+
+ link->type = dc_connection_none;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector));
+}
+
+/**
+ * dm_test_set_psr_caps_no_dpcd_psr() - DPCD PSR version is required.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_no_dpcd_psr(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psr_caps_link(test);
+ struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test);
+
+ link->dpcd_caps.psr_info.psr_version = 0;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector));
+}
+
+/**
+ * dm_test_set_psr_caps_edp1_disabled() - eDP panel instance 1 is blocked.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_edp1_disabled(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psr_caps_link(test);
+ struct dc_link *edp0 = dm_kunit_alloc_link(test);
+ struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test);
+ struct dc *dc = link->ctx->dc;
+
+ edp0->connector_signal = SIGNAL_TYPE_EDP;
+ dc->links[0] = edp0;
+ dc->links[1] = link;
+ dc->link_count = 2;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector));
+}
+
+/**
+ * dm_test_set_psr_caps_success_psr1() - Valid eDP link enables PSR1 caps.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_success_psr1(struct kunit *test)
+{
+ struct dc_link *link = alloc_test_psr_caps_link(test);
+ struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test);
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_set_psr_caps(link, aconnector));
+ KUNIT_EXPECT_EQ(test, link->psr_settings.psr_version, DC_PSR_VERSION_1);
+ KUNIT_EXPECT_EQ(test, (int)aconnector->psr_caps.psr_version, 1);
+ KUNIT_EXPECT_EQ(test, (int)aconnector->psr_caps.support_ver,
+ DP_PSR2_WITH_Y_COORD_ET_SUPPORTED);
+}
+
+/* End of tests for amdgpu_dm_set_psr_caps() */
+
/* Tests for amdgpu_dm_psr_fill_caps() — PSR version mapping */
static void dm_test_psr_fill_caps_version_1(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -42,7 +344,7 @@ static void dm_test_psr_fill_caps_version_1(struct kunit *test)
static void dm_test_psr_fill_caps_version_su1(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -55,7 +357,7 @@ static void dm_test_psr_fill_caps_version_su1(struct kunit *test)
static void dm_test_psr_fill_caps_version_unsupported(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -74,7 +376,7 @@ static void dm_test_psr_fill_caps_version_unsupported(struct kunit *test)
static void dm_test_psr_fill_caps_setup_time_zero(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -88,7 +390,7 @@ static void dm_test_psr_fill_caps_setup_time_zero(struct kunit *test)
static void dm_test_psr_fill_caps_setup_time_mid(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -102,7 +404,7 @@ static void dm_test_psr_fill_caps_setup_time_mid(struct kunit *test)
static void dm_test_psr_fill_caps_setup_time_max(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -118,7 +420,7 @@ static void dm_test_psr_fill_caps_setup_time_max(struct kunit *test)
static void dm_test_psr_fill_caps_link_training_required(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -131,7 +433,7 @@ static void dm_test_psr_fill_caps_link_training_required(struct kunit *test)
static void dm_test_psr_fill_caps_link_training_not_required(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -146,7 +448,7 @@ static void dm_test_psr_fill_caps_link_training_not_required(struct kunit *test)
static void dm_test_psr_fill_caps_dpcd_fields(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -172,7 +474,7 @@ static void dm_test_psr_fill_caps_dpcd_fields(struct kunit *test)
static void dm_test_psr_fill_caps_dpcd_fields_unset(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0xFF, sizeof(caps));
@@ -193,7 +495,7 @@ static void dm_test_psr_fill_caps_dpcd_fields_unset(struct kunit *test)
static void dm_test_psr_fill_caps_rate_control_always_zero(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
/* Pre-fill caps with non-zero to verify overwrite */
@@ -206,7 +508,7 @@ static void dm_test_psr_fill_caps_rate_control_always_zero(struct kunit *test)
static void dm_test_psr_fill_caps_power_opts_z10_always_set(struct kunit *test)
{
- struct dc_link *link = alloc_test_link(test);
+ struct dc_link *link = dm_kunit_alloc_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
@@ -221,6 +523,24 @@ static void dm_test_psr_fill_caps_power_opts_z10_always_set(struct kunit *test)
(caps.psr_power_opt_flag &
psr_power_opt_z10_static_screen) != 0);
}
+
+static void dm_test_psr_fill_caps_power_opts_smu_opt_set(struct kunit *test)
+{
+ struct dc_link *link = dm_kunit_alloc_link(test);
+ struct psr_caps caps;
+ unsigned int old_feature_mask;
+
+ memset(&caps, 0, sizeof(caps));
+ old_feature_mask = amdgpu_dm_psr_get_dc_feature_mask();
+ amdgpu_dm_psr_set_dc_feature_mask(old_feature_mask | DC_PSR_ALLOW_SMU_OPT);
+
+ amdgpu_dm_psr_fill_caps(link, &caps);
+ amdgpu_dm_psr_set_dc_feature_mask(old_feature_mask);
+
+ KUNIT_EXPECT_TRUE(test,
+ (caps.psr_power_opt_flag &
+ psr_power_opt_smu_opt_static_screen) != 0);
+}
/* End of tests for amdgpu_dm_psr_fill_caps() */
/* Tests for amdgpu_dm_psr_set_event() — early-exit validation guards */
@@ -258,9 +578,155 @@ static void dm_test_psr_set_event_psr_not_enabled(struct kunit *test)
KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_set_event(NULL, stream, true, psr_event_vsync, false));
}
+
+/**
+ * dm_test_psr_set_event_get_event_fails() - Failed power event read returns false.
+ * @test: KUnit test context.
+ */
+static void dm_test_psr_set_event_get_event_fails(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_stream_state *stream = alloc_test_psr_stream(test);
+
+ dm->power_module = NULL;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_set_event(dm, stream, true, psr_event_vsync, false));
+}
+
+/**
+ * dm_test_psr_set_event_already_set() - Already set event returns true.
+ * @test: KUnit test context.
+ */
+static void dm_test_psr_set_event_already_set(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_stream_state *stream = alloc_test_psr_stream(test);
+ struct psr_caps caps = {0};
+ struct core_power *core_power;
+
+ caps.psr_version = 1;
+ core_power = create_test_power_module(test, stream, &caps);
+ dm->power_module = &core_power->mod_public;
+
+ KUNIT_EXPECT_TRUE(test,
+ amdgpu_dm_psr_set_event(dm, stream, true, psr_event_vsync, false));
+ KUNIT_EXPECT_EQ(test, core_power->map[0].psr_events,
+ (unsigned int)psr_event_vsync);
+}
+
+/**
+ * dm_test_psr_set_event_updates_event() - Changed event delegates to mod_power.
+ * @test: KUnit test context.
+ */
+static void dm_test_psr_set_event_updates_event(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_stream_state *stream = alloc_test_psr_stream(test);
+ struct psr_caps caps = {0};
+ struct core_power *core_power;
+
+ caps.psr_version = 1;
+ core_power = create_test_power_module(test, stream, &caps);
+ dm->power_module = &core_power->mod_public;
+
+ KUNIT_EXPECT_TRUE(test,
+ amdgpu_dm_psr_set_event(dm, stream, true, psr_event_full_screen, false));
+ KUNIT_EXPECT_EQ(test, core_power->map[0].psr_events,
+ (unsigned int)(psr_event_vsync | psr_event_full_screen));
+}
/* End of tests for amdgpu_dm_psr_set_event() */
+/* Tests for amdgpu_dm_psr_is_active_allowed() */
+
+/**
+ * dm_test_psr_is_active_allowed_no_streams() - Empty DC state disallows PSR.
+ * @test: KUnit test context.
+ */
+static void dm_test_psr_is_active_allowed_no_streams(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_is_active_allowed(dm));
+}
+
+/**
+ * dm_test_psr_is_active_allowed_null_link() - Streams without links are skipped.
+ * @test: KUnit test context.
+ */
+static void dm_test_psr_is_active_allowed_null_link(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_state *state = dm->dc->current_state;
+
+ dm_kunit_add_stream_to_state(test, state, 0, NULL);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_is_active_allowed(dm));
+}
+
+/**
+ * dm_test_psr_is_active_allowed_requires_enabled_and_allowed() - Both link flags
+ * must be set before PSR active is allowed.
+ * @test: KUnit test context.
+ */
+static void dm_test_psr_is_active_allowed_requires_enabled_and_allowed(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_state *state = dm->dc->current_state;
+ struct dc_link *link = dm_kunit_alloc_link(test);
+
+ dm_kunit_add_stream_to_state(test, state, 0, link);
+ link->psr_settings.psr_allow_active = true;
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_is_active_allowed(dm));
+
+ link->psr_settings.psr_allow_active = false;
+ link->psr_settings.psr_feature_enabled = true;
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_is_active_allowed(dm));
+}
+
+/**
+ * dm_test_psr_is_active_allowed_any_stream() - Any enabled and allowed stream
+ * permits active PSR.
+ * @test: KUnit test context.
+ */
+static void dm_test_psr_is_active_allowed_any_stream(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
+ struct dc_state *state = dm->dc->current_state;
+ struct dc_link *disabled_link = dm_kunit_alloc_link(test);
+ struct dc_link *allowed_link = dm_kunit_alloc_link(test);
+
+ disabled_link->psr_settings.psr_allow_active = true;
+ allowed_link->psr_settings.psr_feature_enabled = true;
+ allowed_link->psr_settings.psr_allow_active = true;
+
+ dm_kunit_add_stream_to_state(test, state, 0, disabled_link);
+ dm_kunit_add_stream_to_state(test, state, 1, allowed_link);
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_psr_is_active_allowed(dm));
+}
+
+/* End of tests for amdgpu_dm_psr_is_active_allowed() */
+
static struct kunit_case dm_psr_test_cases[] = {
+ /* link_supports_psrsu */
+ KUNIT_CASE(dm_test_link_supports_psrsu_no_dmcub),
+ KUNIT_CASE(dm_test_link_supports_psrsu_old_dcn),
+ KUNIT_CASE(dm_test_link_supports_psrsu_panel_unsupported),
+ KUNIT_CASE(dm_test_link_supports_psrsu_missing_alpm),
+ KUNIT_CASE(dm_test_link_supports_psrsu_missing_y_coordinate),
+ KUNIT_CASE(dm_test_link_supports_psrsu_missing_granularity),
+ KUNIT_CASE(dm_test_link_supports_psrsu_debug_mask_disabled),
+ KUNIT_CASE(dm_test_link_supports_psrsu_temporarily_disabled),
+ /* amdgpu_dm_set_psr_caps */
+ KUNIT_CASE(dm_test_set_psr_caps_null_link),
+ KUNIT_CASE(dm_test_set_psr_caps_null_connector),
+ KUNIT_CASE(dm_test_set_psr_caps_no_dmub_psr),
+ KUNIT_CASE(dm_test_set_psr_caps_non_edp),
+ KUNIT_CASE(dm_test_set_psr_caps_disconnected),
+ KUNIT_CASE(dm_test_set_psr_caps_no_dpcd_psr),
+ KUNIT_CASE(dm_test_set_psr_caps_edp1_disabled),
+ KUNIT_CASE(dm_test_set_psr_caps_success_psr1),
+ /* amdgpu_dm_psr_fill_caps */
KUNIT_CASE(dm_test_psr_fill_caps_version_1),
KUNIT_CASE(dm_test_psr_fill_caps_version_su1),
KUNIT_CASE(dm_test_psr_fill_caps_version_unsupported),
@@ -273,9 +739,19 @@ static struct kunit_case dm_psr_test_cases[] = {
KUNIT_CASE(dm_test_psr_fill_caps_dpcd_fields_unset),
KUNIT_CASE(dm_test_psr_fill_caps_rate_control_always_zero),
KUNIT_CASE(dm_test_psr_fill_caps_power_opts_z10_always_set),
+ KUNIT_CASE(dm_test_psr_fill_caps_power_opts_smu_opt_set),
+ /* amdgpu_dm_psr_set_event */
KUNIT_CASE(dm_test_psr_set_event_null_stream),
KUNIT_CASE(dm_test_psr_set_event_null_link),
KUNIT_CASE(dm_test_psr_set_event_psr_not_enabled),
+ KUNIT_CASE(dm_test_psr_set_event_get_event_fails),
+ KUNIT_CASE(dm_test_psr_set_event_already_set),
+ KUNIT_CASE(dm_test_psr_set_event_updates_event),
+ /* amdgpu_dm_psr_is_active_allowed */
+ KUNIT_CASE(dm_test_psr_is_active_allowed_no_streams),
+ KUNIT_CASE(dm_test_psr_is_active_allowed_null_link),
+ KUNIT_CASE(dm_test_psr_is_active_allowed_requires_enabled_and_allowed),
+ KUNIT_CASE(dm_test_psr_is_active_allowed_any_stream),
{}
};
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c
new file mode 100644
index 000000000000..a09f31ee0a2a
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_quirks.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include "dc.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+
+/* Tests for retrieve_dmi_info() */
+
+/*
+ * Verify that retrieve_dmi_info() always initialises aux_hpd_discon_quirk to
+ * false, even when the caller had previously set it to true.
+ */
+/**
+ * dm_test_quirks_aux_hpd_discon_reset - Test Quirks aux hpd discon reset
+ * @test: The KUnit test context
+ */
+static void dm_test_quirks_aux_hpd_discon_reset(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm);
+
+ dm->aux_hpd_discon_quirk = true;
+
+ retrieve_dmi_info(dm);
+
+ /*
+ * In a KUnit / UML environment no real DMI table is present, so
+ * dmi_check_system() returns 0 and retrieve_dmi_info() leaves the
+ * quirk at its initialised-to-false value.
+ */
+ KUNIT_EXPECT_FALSE(test, dm->aux_hpd_discon_quirk);
+}
+
+/*
+ * Verify that retrieve_dmi_info() always initialises edp0_on_dp1_quirk to
+ * false, even when the caller had previously set it to true.
+ */
+/**
+ * dm_test_quirks_edp0_on_dp1_reset - Test Quirks edp0 on dp1 reset
+ * @test: The KUnit test context
+ */
+static void dm_test_quirks_edp0_on_dp1_reset(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm);
+
+ dm->edp0_on_dp1_quirk = true;
+
+ retrieve_dmi_info(dm);
+
+ KUNIT_EXPECT_FALSE(test, dm->edp0_on_dp1_quirk);
+}
+
+/*
+ * Verify that when no DMI match is found both quirks remain false after a
+ * fresh (zero-initialised) dm is passed to retrieve_dmi_info().
+ */
+/**
+ * dm_test_quirks_no_dmi_match_both_false - Test Quirks no dmi match both false
+ * @test: The KUnit test context
+ */
+static void dm_test_quirks_no_dmi_match_both_false(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm);
+
+ retrieve_dmi_info(dm);
+
+ KUNIT_EXPECT_FALSE(test, dm->aux_hpd_discon_quirk);
+ KUNIT_EXPECT_FALSE(test, dm->edp0_on_dp1_quirk);
+}
+
+static struct kunit_case amdgpu_dm_quirks_tests[] = {
+ /* retrieve_dmi_info */
+ KUNIT_CASE(dm_test_quirks_aux_hpd_discon_reset),
+ KUNIT_CASE(dm_test_quirks_edp0_on_dp1_reset),
+ KUNIT_CASE(dm_test_quirks_no_dmi_match_both_false),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_quirks_test_suite = {
+ .name = "amdgpu_dm_quirks",
+ .test_cases = amdgpu_dm_quirks_tests,
+};
+
+kunit_test_suite(amdgpu_dm_quirks_test_suite);
+
+MODULE_AUTHOR("AMD");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_quirks");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c
index 28ff8bbcc0f7..6f633b1bbaca 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c
@@ -8,12 +8,13 @@
#include <kunit/test.h>
#include "dc.h"
+#include "dc_dmub_srv.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
-
-/* Extern declaration for the function under test */
-extern bool amdgpu_dm_link_supports_replay(struct dc_link *link,
- struct amdgpu_dm_connector *aconnector);
+#include "amdgpu_dm_replay.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+#include "modules/power/power_helpers.h"
+#include "dmub/dmub_srv.h"
/*
* Helper: allocate a dc_link, amdgpu_dm_connector, and dm_connector_state
@@ -23,6 +24,9 @@ struct replay_test_ctx {
struct dc_link *link;
struct amdgpu_dm_connector *aconnector;
struct dm_connector_state *dm_state;
+ struct dc *dc;
+ struct dc_context *dc_ctx;
+ struct dc_stream_state *stream;
};
static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test)
@@ -32,8 +36,9 @@ static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test)
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx);
- ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL);
- KUNIT_ASSERT_NOT_NULL(test, ctx->link);
+ ctx->link = dm_kunit_alloc_link_with_ctx(test);
+ ctx->dc_ctx = ctx->link->ctx;
+ ctx->dc = ctx->dc_ctx->dc;
ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
@@ -41,6 +46,8 @@ static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test)
ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state);
+ ctx->stream = dm_kunit_alloc_stream(test, ctx->link);
+
/* Wire connector state so to_dm_connector_state() works */
ctx->aconnector->base.state = &ctx->dm_state->base;
@@ -55,6 +62,7 @@ static void set_all_replay_caps(struct replay_test_ctx *ctx)
{
ctx->dm_state->freesync_capable = true;
ctx->aconnector->vsdb_info.replay_mode = true;
+ ctx->link->connector_signal = SIGNAL_TYPE_EDP;
ctx->link->dpcd_caps.edp_rev = EDP_REVISION_13;
ctx->link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1;
ctx->link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1;
@@ -181,7 +189,398 @@ static void dm_test_replay_both_deviations_zero(struct kunit *test)
/* End of tests for amdgpu_dm_link_supports_replay() */
+/* Tests for amdgpu_dm_set_replay_caps() */
+
+/**
+ * dm_test_replay_set_caps_already_supported - Verify cached Replay support
+ * @test: KUnit test context
+ *
+ * When replay_supported is already set, amdgpu_dm_set_replay_caps() should
+ * return true without revalidating the link capabilities.
+ */
+static void dm_test_replay_set_caps_already_supported(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ ctx->link->replay_settings.config.replay_supported = true;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector));
+}
+
+/**
+ * dm_test_replay_set_caps_non_embedded_signal - Verify non-eDP rejection
+ * @test: KUnit test context
+ *
+ * When the link signal is not embedded, amdgpu_dm_set_replay_caps() should
+ * reject Replay even if the sink capability fields are otherwise valid.
+ */
+static void dm_test_replay_set_caps_non_embedded_signal(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ set_all_replay_caps(ctx);
+ ctx->link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector));
+}
+
+/**
+ * dm_test_replay_set_caps_disallowed_by_panel - Verify panel policy rejection
+ * @test: KUnit test context
+ *
+ * When the panel configuration disallows Replay, amdgpu_dm_set_replay_caps()
+ * should return false before accepting the capability set.
+ */
+static void dm_test_replay_set_caps_disallowed_by_panel(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ set_all_replay_caps(ctx);
+ ctx->link->panel_config.psr.disallow_replay = true;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector));
+}
+
+/**
+ * dm_test_replay_set_caps_link_not_supported - Verify capability rejection
+ * @test: KUnit test context
+ *
+ * When amdgpu_dm_link_supports_replay() rejects the link, the higher-level
+ * Replay setup helper should also return false.
+ */
+static void dm_test_replay_set_caps_link_not_supported(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ set_all_replay_caps(ctx);
+ ctx->dm_state->freesync_capable = false;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector));
+}
+
+/**
+ * dm_test_replay_set_caps_missing_dmub_srv - Verify missing DMUB rejection
+ * @test: KUnit test context
+ *
+ * When the link and connector support Replay but no DMUB service is available,
+ * amdgpu_dm_set_replay_caps() should return false.
+ */
+static void dm_test_replay_set_caps_missing_dmub_srv(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ set_all_replay_caps(ctx);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector));
+}
+
+/**
+ * dm_test_replay_set_caps_success - Verify successful Replay configuration
+ * @test: KUnit test context
+ *
+ * When all prerequisites are met (embedded signal, panel allows replay, link
+ * supports replay, DMUB present with replay support), amdgpu_dm_set_replay_caps()
+ * should configure the link replay settings and return true.
+ */
+static void dm_test_replay_set_caps_success(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+ struct dc_dmub_srv *dmub_srv;
+ struct dmub_srv *dmub;
+
+ set_all_replay_caps(ctx);
+
+ dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dmub_srv);
+
+ dmub = kunit_kzalloc(test, sizeof(*dmub), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dmub);
+
+ dmub->feature_caps.replay_supported = 1;
+ dmub_srv->dmub = dmub;
+ ctx->dc_ctx->dmub_srv = dmub_srv;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector));
+ KUNIT_EXPECT_TRUE(test, ctx->link->replay_settings.config.replay_supported);
+}
+
+/* Tests for amdgpu_dm_link_setup_replay() */
+
+/**
+ * dm_test_replay_link_setup_null_stream - Verify NULL stream rejection
+ * @test: KUnit test context
+ *
+ * amdgpu_dm_link_setup_replay() should return false when no stream is provided.
+ */
+static void dm_test_replay_link_setup_null_stream(struct kunit *test)
+{
+ struct mod_vrr_params vrr_params = { 0 };
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(NULL, &vrr_params));
+}
+
+/**
+ * dm_test_replay_link_setup_null_link - Verify NULL stream link rejection
+ * @test: KUnit test context
+ *
+ * amdgpu_dm_link_setup_replay() should return false when the stream has no
+ * associated link.
+ */
+static void dm_test_replay_link_setup_null_link(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+ struct mod_vrr_params vrr_params = { 0 };
+
+ ctx->stream->link = NULL;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params));
+}
+
+/**
+ * dm_test_replay_link_setup_null_vrr_params - Verify NULL VRR params rejection
+ * @test: KUnit test context
+ *
+ * amdgpu_dm_link_setup_replay() should return false when VRR parameters are
+ * not supplied.
+ */
+static void dm_test_replay_link_setup_null_vrr_params(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, NULL));
+}
+
+/**
+ * dm_test_replay_link_setup_not_supported - Verify unsupported Replay rejection
+ * @test: KUnit test context
+ *
+ * amdgpu_dm_link_setup_replay() should return false when Replay is not marked
+ * supported on the link configuration.
+ */
+static void dm_test_replay_link_setup_not_supported(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+ struct mod_vrr_params vrr_params = { 0 };
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params));
+}
+
+/**
+ * dm_test_replay_link_setup_already_enabled - Verify enabled Replay success
+ * @test: KUnit test context
+ *
+ * When Replay is already enabled, amdgpu_dm_link_setup_replay() should return
+ * true without recalculating coasting vtotal state.
+ */
+static void dm_test_replay_link_setup_already_enabled(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+ struct mod_vrr_params vrr_params = { 0 };
+
+ ctx->link->replay_settings.config.replay_supported = true;
+ ctx->link->replay_settings.replay_feature_enabled = true;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params));
+}
+
+/**
+ * dm_test_replay_link_setup_success - Verify coasting vtotal configuration
+ * @test: KUnit test context
+ *
+ * When Replay is supported but not yet enabled, amdgpu_dm_link_setup_replay()
+ * should calculate the link-off frame count and set the coasting vtotal values,
+ * then return true.
+ */
+static void dm_test_replay_link_setup_success(struct kunit *test)
+{
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+ struct mod_vrr_params vrr_params = { 0 };
+
+ ctx->link->replay_settings.config.replay_supported = true;
+ ctx->link->replay_settings.config.replay_version = DC_FREESYNC_REPLAY;
+
+ /* Set timing so calculate_replay_link_off_frame_count computes */
+ ctx->stream->timing.v_total = 1125;
+ ctx->stream->timing.h_total = 2200;
+ ctx->stream->timing.pix_clk_100hz = 1485000;
+ ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 4;
+ ctx->link->dpcd_caps.pr_info.max_deviation_line = 10;
+
+ /* min_refresh_in_uhz = 0 makes calc return v_total directly */
+ vrr_params.min_refresh_in_uhz = 0;
+
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params));
+
+ /* Verify coasting vtotal was set */
+ KUNIT_EXPECT_EQ(test,
+ ctx->link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM],
+ (uint32_t)1125);
+ KUNIT_EXPECT_EQ(test,
+ ctx->link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC],
+ (uint32_t)1125);
+
+ /* Verify link_off_frame_count was calculated: 2200*10/(4*1125) = 4 */
+ KUNIT_EXPECT_EQ(test,
+ ctx->link->replay_settings.link_off_frame_count,
+ (uint32_t)4);
+}
+
+/* Tests for amdgpu_dm_replay_set_event() */
+
+/**
+ * dm_test_replay_set_event_null_stream - Verify NULL stream rejection
+ * @test: KUnit test context
+ *
+ * amdgpu_dm_replay_set_event() should return false when no stream is provided.
+ */
+static void dm_test_replay_set_event_null_stream(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, NULL, true,
+ replay_event_vsync, false));
+}
+
+/**
+ * dm_test_replay_set_event_null_link - Verify NULL stream link rejection
+ * @test: KUnit test context
+ *
+ * amdgpu_dm_replay_set_event() should return false when the stream has no
+ * associated link.
+ */
+static void dm_test_replay_set_event_null_link(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm);
+
+ ctx->stream->link = NULL;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true,
+ replay_event_vsync, false));
+}
+
+/**
+ * dm_test_replay_set_event_feature_disabled - Verify disabled Replay rejection
+ * @test: KUnit test context
+ *
+ * amdgpu_dm_replay_set_event() should return false when Replay is not enabled
+ * on the stream link.
+ */
+static void dm_test_replay_set_event_feature_disabled(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm);
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true,
+ replay_event_vsync, false));
+}
+
+/**
+ * dm_test_replay_set_event_missing_power_module - Verify missing power rejection
+ * @test: KUnit test context
+ *
+ * When Replay is enabled but no power module is available, the event helper
+ * should return false after failing to read the current Replay events.
+ */
+static void dm_test_replay_set_event_missing_power_module(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm);
+
+ ctx->link->replay_settings.replay_feature_enabled = true;
+
+ KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true,
+ replay_event_vsync, false));
+}
+
+/**
+ * dm_test_replay_set_event_already_set - Verify no-op when event already active
+ * @test: KUnit test context
+ *
+ * When the requested event is already in the desired state, the function should
+ * return true without calling mod_power_set_replay_event().
+ */
+static void dm_test_replay_set_event_already_set(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+ struct core_power *core_power;
+ struct power_entity *map;
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm);
+
+ core_power = kunit_kzalloc(test, sizeof(*core_power), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, core_power);
+
+ map = kunit_kzalloc(test, sizeof(*map), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, map);
+
+ /* Wire the power module so mod_power_get_replay_event() succeeds */
+ map->stream = ctx->stream;
+ map->replay_events = replay_event_vsync;
+ core_power->map = map;
+ core_power->num_entities = 1;
+ dm->power_module = &core_power->mod_public;
+
+ ctx->link->replay_settings.replay_feature_enabled = true;
+
+ /* Event already set — should return true without calling set */
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true,
+ replay_event_vsync, false));
+}
+
+/**
+ * dm_test_replay_set_event_already_clear - Verify no-op when event already cleared
+ * @test: KUnit test context
+ *
+ * When clearing an event that is not currently active, the function should
+ * return true without calling mod_power_set_replay_event().
+ */
+static void dm_test_replay_set_event_already_clear(struct kunit *test)
+{
+ struct amdgpu_display_manager *dm;
+ struct replay_test_ctx *ctx = alloc_replay_ctx(test);
+ struct core_power *core_power;
+ struct power_entity *map;
+
+ dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dm);
+
+ core_power = kunit_kzalloc(test, sizeof(*core_power), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, core_power);
+
+ map = kunit_kzalloc(test, sizeof(*map), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, map);
+
+ /* Wire the power module — replay_events has NO vsync bit */
+ map->stream = ctx->stream;
+ map->replay_events = 0;
+ core_power->map = map;
+ core_power->num_entities = 1;
+ dm->power_module = &core_power->mod_public;
+
+ ctx->link->replay_settings.replay_feature_enabled = true;
+
+ /* Clearing an event that's already clear — should return true */
+ KUNIT_EXPECT_TRUE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, false,
+ replay_event_vsync, false));
+}
+
static struct kunit_case dm_replay_test_cases[] = {
+ /* amdgpu_dm_link_supports_replay */
KUNIT_CASE(dm_test_replay_supports_all_caps),
KUNIT_CASE(dm_test_replay_no_freesync),
KUNIT_CASE(dm_test_replay_no_vsdb_replay_mode),
@@ -191,6 +590,27 @@ static struct kunit_case dm_replay_test_cases[] = {
KUNIT_CASE(dm_test_replay_zero_pixel_deviation),
KUNIT_CASE(dm_test_replay_zero_max_deviation_line),
KUNIT_CASE(dm_test_replay_both_deviations_zero),
+ /* amdgpu_dm_set_replay_caps */
+ KUNIT_CASE(dm_test_replay_set_caps_already_supported),
+ KUNIT_CASE(dm_test_replay_set_caps_non_embedded_signal),
+ KUNIT_CASE(dm_test_replay_set_caps_disallowed_by_panel),
+ KUNIT_CASE(dm_test_replay_set_caps_link_not_supported),
+ KUNIT_CASE(dm_test_replay_set_caps_missing_dmub_srv),
+ KUNIT_CASE(dm_test_replay_set_caps_success),
+ /* amdgpu_dm_link_setup_replay */
+ KUNIT_CASE(dm_test_replay_link_setup_null_stream),
+ KUNIT_CASE(dm_test_replay_link_setup_null_link),
+ KUNIT_CASE(dm_test_replay_link_setup_null_vrr_params),
+ KUNIT_CASE(dm_test_replay_link_setup_not_supported),
+ KUNIT_CASE(dm_test_replay_link_setup_already_enabled),
+ KUNIT_CASE(dm_test_replay_link_setup_success),
+ /* amdgpu_dm_replay_set_event */
+ KUNIT_CASE(dm_test_replay_set_event_null_stream),
+ KUNIT_CASE(dm_test_replay_set_event_null_link),
+ KUNIT_CASE(dm_test_replay_set_event_feature_disabled),
+ KUNIT_CASE(dm_test_replay_set_event_missing_power_module),
+ KUNIT_CASE(dm_test_replay_set_event_already_set),
+ KUNIT_CASE(dm_test_replay_set_event_already_clear),
{}
};
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_services_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_services_test.c
new file mode 100644
index 000000000000..e48bac7fb024
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_services_test.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_services.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+#include "dm_services.h"
+#include "dm_services_types.h"
+
+/* Tests for dm_get_elapse_time_in_ns() */
+
+/**
+ * dm_test_get_elapse_time_zero_delta - Test Get elapse time zero delta
+ * @test: The KUnit test context
+ */
+static void dm_test_get_elapse_time_zero_delta(struct kunit *test)
+{
+ unsigned long long ts = 1000000ULL;
+
+ KUNIT_EXPECT_EQ(test, dm_get_elapse_time_in_ns(NULL, ts, ts), 0ULL);
+}
+
+/**
+ * dm_test_get_elapse_time_positive_delta - Test Get elapse time positive delta
+ * @test: The KUnit test context
+ */
+static void dm_test_get_elapse_time_positive_delta(struct kunit *test)
+{
+ unsigned long long current_ts = 5000000ULL;
+ unsigned long long last_ts = 1000000ULL;
+
+ KUNIT_EXPECT_EQ(test, dm_get_elapse_time_in_ns(NULL, current_ts, last_ts),
+ 4000000ULL);
+}
+
+/**
+ * dm_test_get_elapse_time_large_delta - Test Get elapse time large delta
+ * @test: The KUnit test context
+ */
+static void dm_test_get_elapse_time_large_delta(struct kunit *test)
+{
+ unsigned long long current_ts = ULLONG_MAX;
+ unsigned long long last_ts = 0ULL;
+
+ KUNIT_EXPECT_EQ(test, dm_get_elapse_time_in_ns(NULL, current_ts, last_ts),
+ ULLONG_MAX);
+}
+
+/**
+ * dm_test_get_elapse_time_wraparound - Test Get elapse time wraparound
+ * @test: The KUnit test context
+ */
+static void dm_test_get_elapse_time_wraparound(struct kunit *test)
+{
+ /* Unsigned wraparound: result = ULLONG_MAX - last + current + 1 */
+ unsigned long long current_ts = 5ULL;
+ unsigned long long last_ts = ULLONG_MAX - 4ULL;
+
+ KUNIT_EXPECT_EQ(test, dm_get_elapse_time_in_ns(NULL, current_ts, last_ts),
+ 10ULL);
+}
+
+/* Tests for dm_perf_trace_timestamp() */
+
+/**
+ * dm_test_perf_trace_timestamp_basic - Test Perf trace timestamp basic
+ * @test: The KUnit test context
+ *
+ * The tracepoint is a no-op without an attached probe, so this verifies the
+ * function dereferences ctx->perf_trace safely and does not crash.
+ */
+static void dm_test_perf_trace_timestamp_basic(struct kunit *test)
+{
+ struct dc_context *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx);
+ ctx->perf_trace = kunit_kzalloc(test, sizeof(*ctx->perf_trace), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, ctx->perf_trace);
+
+ ctx->perf_trace->read_count = 10;
+ ctx->perf_trace->write_count = 20;
+
+ dm_perf_trace_timestamp(__func__, __LINE__, ctx);
+}
+
+/* Tests for dm_trace_smu_enter() */
+
+/**
+ * dm_test_trace_smu_enter_null_ctx - Test Trace smu enter null ctx
+ * @test: The KUnit test context
+ */
+static void dm_test_trace_smu_enter_null_ctx(struct kunit *test)
+{
+ /* Empty stub — must not crash with NULL ctx */
+ dm_trace_smu_enter(0, 0, 0, NULL);
+}
+
+/**
+ * dm_test_trace_smu_enter_with_params - Test Trace smu enter with params
+ * @test: The KUnit test context
+ */
+static void dm_test_trace_smu_enter_with_params(struct kunit *test)
+{
+ /* Exercise non-zero msg_id, param_in, and delay */
+ dm_trace_smu_enter(0xFF, 0x12345678, 1000, NULL);
+}
+
+/* Tests for dm_trace_smu_exit() */
+
+/**
+ * dm_test_trace_smu_exit_success_null_ctx - Test Trace smu exit success null ctx
+ * @test: The KUnit test context
+ */
+static void dm_test_trace_smu_exit_success_null_ctx(struct kunit *test)
+{
+ /* Empty stub — must not crash on success path with NULL ctx */
+ dm_trace_smu_exit(true, 0x0, NULL);
+}
+
+/**
+ * dm_test_trace_smu_exit_failure_null_ctx - Test Trace smu exit failure null ctx
+ * @test: The KUnit test context
+ */
+static void dm_test_trace_smu_exit_failure_null_ctx(struct kunit *test)
+{
+ /* Empty stub — must not crash on failure path with NULL ctx */
+ dm_trace_smu_exit(false, 0x0, NULL);
+}
+
+/**
+ * dm_test_trace_smu_exit_with_response - Test Trace smu exit with response
+ * @test: The KUnit test context
+ */
+static void dm_test_trace_smu_exit_with_response(struct kunit *test)
+{
+ /* Exercise non-zero response value */
+ dm_trace_smu_exit(true, 0xDEADBEEF, NULL);
+}
+
+/* Tests for dm_query_extended_brightness_caps() */
+
+/**
+ * dm_test_query_brightness_caps_null_ctx - Test Query brightness caps null ctx
+ * @test: The KUnit test context
+ */
+static void dm_test_query_brightness_caps_null_ctx(struct kunit *test)
+{
+ struct dm_acpi_atif_backlight_caps caps = {};
+
+ KUNIT_EXPECT_FALSE(test,
+ dm_query_extended_brightness_caps(NULL, AcpiDisplayType_LCD1, &caps));
+}
+
+/**
+ * dm_test_query_brightness_caps_null_caps - Test Query brightness caps null caps
+ * @test: The KUnit test context
+ */
+static void dm_test_query_brightness_caps_null_caps(struct kunit *test)
+{
+ struct dc_context ctx = {};
+
+ ctx.driver_context = (void *)0x1; /* non-NULL sentinel */
+
+ KUNIT_EXPECT_FALSE(test,
+ dm_query_extended_brightness_caps(&ctx, AcpiDisplayType_LCD1, NULL));
+}
+
+/**
+ * dm_test_query_brightness_caps_null_driver_ctx - Test Query brightness caps null driver ctx
+ * @test: The KUnit test context
+ */
+static void dm_test_query_brightness_caps_null_driver_ctx(struct kunit *test)
+{
+ struct dc_context ctx = {};
+ struct dm_acpi_atif_backlight_caps caps = {};
+
+ ctx.driver_context = NULL;
+
+ KUNIT_EXPECT_FALSE(test,
+ dm_query_extended_brightness_caps(&ctx, AcpiDisplayType_LCD1, &caps));
+}
+
+/**
+ * dm_test_query_brightness_caps_lcd2_null_ctx - Test Query brightness caps lcd2 null ctx
+ * @test: The KUnit test context
+ */
+static void dm_test_query_brightness_caps_lcd2_null_ctx(struct kunit *test)
+{
+ struct dm_acpi_atif_backlight_caps caps = {};
+
+ KUNIT_EXPECT_FALSE(test,
+ dm_query_extended_brightness_caps(NULL, AcpiDisplayType_LCD2, &caps));
+}
+
+/**
+ * dm_test_query_brightness_caps_lcd1_success - Test Query brightness caps lcd1 success
+ * @test: The KUnit test context
+ */
+static void dm_test_query_brightness_caps_lcd1_success(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct amdgpu_dm_backlight_caps *source_caps;
+ struct dc_context ctx = {};
+ struct dm_acpi_atif_backlight_caps caps = {};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ source_caps = &adev->dm.backlight_caps[0];
+ source_caps->caps_valid = true;
+ source_caps->min_input_signal = 12;
+ source_caps->max_input_signal = 240;
+ source_caps->ac_level = 80;
+ source_caps->dc_level = 40;
+ source_caps->data_points = 2;
+ source_caps->luminance_data[0].luminance = 10;
+ source_caps->luminance_data[0].input_signal = 22;
+ source_caps->luminance_data[1].luminance = 90;
+ source_caps->luminance_data[1].input_signal = 200;
+ ctx.driver_context = adev;
+
+ KUNIT_EXPECT_TRUE(test,
+ dm_query_extended_brightness_caps(&ctx, AcpiDisplayType_LCD1, &caps));
+ KUNIT_EXPECT_EQ(test, caps.num_data_points, 2);
+ KUNIT_EXPECT_EQ(test, caps.max_input_signal, 240);
+ KUNIT_EXPECT_EQ(test, caps.min_input_signal, 12);
+ KUNIT_EXPECT_EQ(test, caps.ac_level_percentage, 80);
+ KUNIT_EXPECT_EQ(test, caps.dc_level_percentage, 40);
+ KUNIT_EXPECT_EQ(test, caps.data_points[0].luminance, 10);
+ KUNIT_EXPECT_EQ(test, caps.data_points[0].signal_level, 22);
+ KUNIT_EXPECT_EQ(test, caps.data_points[1].luminance, 90);
+ KUNIT_EXPECT_EQ(test, caps.data_points[1].signal_level, 200);
+}
+
+/**
+ * dm_test_query_brightness_caps_non_lcd1_uses_second_slot - Test Query brightness caps non lcd1 uses second slot
+ * @test: The KUnit test context
+ */
+static void dm_test_query_brightness_caps_non_lcd1_uses_second_slot(struct kunit *test)
+{
+ struct amdgpu_device *adev;
+ struct amdgpu_dm_backlight_caps *source_caps;
+ struct dc_context ctx = {};
+ struct dm_acpi_atif_backlight_caps caps = {};
+
+ adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, adev);
+
+ adev->dm.backlight_caps[0].caps_valid = true;
+ adev->dm.backlight_caps[0].min_input_signal = 1;
+ adev->dm.backlight_caps[0].max_input_signal = 2;
+ source_caps = &adev->dm.backlight_caps[1];
+ source_caps->caps_valid = true;
+ source_caps->min_input_signal = 33;
+ source_caps->max_input_signal = 199;
+ source_caps->ac_level = 70;
+ source_caps->dc_level = 30;
+ source_caps->data_points = 0;
+ ctx.driver_context = adev;
+
+ KUNIT_EXPECT_TRUE(test,
+ dm_query_extended_brightness_caps(&ctx, AcpiDisplayType_DFP1, &caps));
+ KUNIT_EXPECT_EQ(test, caps.num_data_points, 0);
+ KUNIT_EXPECT_EQ(test, caps.max_input_signal, 199);
+ KUNIT_EXPECT_EQ(test, caps.min_input_signal, 33);
+ KUNIT_EXPECT_EQ(test, caps.ac_level_percentage, 70);
+ KUNIT_EXPECT_EQ(test, caps.dc_level_percentage, 30);
+ KUNIT_EXPECT_EQ(test, caps.data_points[0].luminance, 0);
+ KUNIT_EXPECT_EQ(test, caps.data_points[0].signal_level, 0);
+}
+
+static struct kunit_case amdgpu_dm_services_test_cases[] = {
+ /* dm_get_elapse_time_in_ns */
+ KUNIT_CASE(dm_test_get_elapse_time_zero_delta),
+ KUNIT_CASE(dm_test_get_elapse_time_positive_delta),
+ KUNIT_CASE(dm_test_get_elapse_time_large_delta),
+ KUNIT_CASE(dm_test_get_elapse_time_wraparound),
+ /* dm_perf_trace_timestamp */
+ KUNIT_CASE(dm_test_perf_trace_timestamp_basic),
+ /* dm_trace_smu_enter */
+ KUNIT_CASE(dm_test_trace_smu_enter_null_ctx),
+ KUNIT_CASE(dm_test_trace_smu_enter_with_params),
+ /* dm_trace_smu_exit */
+ KUNIT_CASE(dm_test_trace_smu_exit_success_null_ctx),
+ KUNIT_CASE(dm_test_trace_smu_exit_failure_null_ctx),
+ KUNIT_CASE(dm_test_trace_smu_exit_with_response),
+ /* dm_query_extended_brightness_caps */
+ KUNIT_CASE(dm_test_query_brightness_caps_null_ctx),
+ KUNIT_CASE(dm_test_query_brightness_caps_null_caps),
+ KUNIT_CASE(dm_test_query_brightness_caps_null_driver_ctx),
+ KUNIT_CASE(dm_test_query_brightness_caps_lcd2_null_ctx),
+ KUNIT_CASE(dm_test_query_brightness_caps_lcd1_success),
+ KUNIT_CASE(dm_test_query_brightness_caps_non_lcd1_uses_second_slot),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_services_test_suite = {
+ .name = "amdgpu_dm_services",
+ .test_cases = amdgpu_dm_services_test_cases,
+};
+
+kunit_test_suite(amdgpu_dm_services_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_services");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
new file mode 100644
index 000000000000..0b29bf0a7d04
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c
@@ -0,0 +1,949 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include "dc.h"
+#include "amdgpu_mode.h"
+#include "amdgpu_dm.h"
+
+/* Tests for dm_plane_layer_index_cmp() */
+
+/**
+ * dm_test_plane_layer_index_cmp_equal - Test Plane layer index cmp equal
+ * @test: The KUnit test context
+ */
+static void dm_test_plane_layer_index_cmp_equal(struct kunit *test)
+{
+ struct dc_plane_state *plane_a;
+ struct dc_plane_state *plane_b;
+ struct dc_surface_update sa, sb;
+
+ plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a);
+ plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b);
+
+ plane_a->layer_index = 5;
+ plane_b->layer_index = 5;
+ sa.surface = plane_a;
+ sb.surface = plane_b;
+
+ KUNIT_EXPECT_EQ(test, dm_plane_layer_index_cmp(&sa, &sb), 0);
+}
+
+/**
+ * dm_test_plane_layer_index_cmp_descending - Test Plane layer index cmp descending
+ * @test: The KUnit test context
+ */
+static void dm_test_plane_layer_index_cmp_descending(struct kunit *test)
+{
+ struct dc_plane_state *plane_a;
+ struct dc_plane_state *plane_b;
+ struct dc_surface_update sa, sb;
+
+ plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a);
+ plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b);
+
+ plane_a->layer_index = 3;
+ plane_b->layer_index = 7;
+ sa.surface = plane_a;
+ sb.surface = plane_b;
+
+ /* b has higher index, so cmp(a,b) = b - a > 0 (b sorts first) */
+ KUNIT_EXPECT_GT(test, dm_plane_layer_index_cmp(&sa, &sb), 0);
+}
+
+/**
+ * dm_test_plane_layer_index_cmp_ascending - Test Plane layer index cmp ascending
+ * @test: The KUnit test context
+ */
+static void dm_test_plane_layer_index_cmp_ascending(struct kunit *test)
+{
+ struct dc_plane_state *plane_a;
+ struct dc_plane_state *plane_b;
+ struct dc_surface_update sa, sb;
+
+ plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a);
+ plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b);
+
+ plane_a->layer_index = 9;
+ plane_b->layer_index = 2;
+ sa.surface = plane_a;
+ sb.surface = plane_b;
+
+ /* a has higher index, so cmp(a,b) = b - a < 0 (a sorts first) */
+ KUNIT_EXPECT_LT(test, dm_plane_layer_index_cmp(&sa, &sb), 0);
+}
+
+/* Tests for fill_plane_color_attributes() */
+
+/**
+ * dm_test_fill_color_attr_rgb_format - Test Fill color attr rgb format
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_color_attr_rgb_format(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
+ int ret;
+
+ /* RGB format: should return 0 and set SRGB regardless of encoding */
+ plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
+ plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
+
+ ret = fill_plane_color_attributes(&plane_state,
+ SURFACE_PIXEL_FORMAT_GRPH_ARGB8888,
+ &color_space);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_SRGB);
+}
+
+/**
+ * dm_test_fill_color_attr_bt601_full - Test Fill color attr bt601 full
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_color_attr_bt601_full(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
+ int ret;
+
+ plane_state.color_encoding = DRM_COLOR_YCBCR_BT601;
+ plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
+
+ ret = fill_plane_color_attributes(&plane_state,
+ SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
+ &color_space);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_YCBCR601);
+}
+
+/**
+ * dm_test_fill_color_attr_bt601_limited - Test Fill color attr bt601 limited
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_color_attr_bt601_limited(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
+ int ret;
+
+ plane_state.color_encoding = DRM_COLOR_YCBCR_BT601;
+ plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
+
+ ret = fill_plane_color_attributes(&plane_state,
+ SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
+ &color_space);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, (int)color_space,
+ (int)COLOR_SPACE_YCBCR601_LIMITED);
+}
+
+/**
+ * dm_test_fill_color_attr_bt709_full - Test Fill color attr bt709 full
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_color_attr_bt709_full(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
+ int ret;
+
+ plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
+ plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
+
+ ret = fill_plane_color_attributes(&plane_state,
+ SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
+ &color_space);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_YCBCR709);
+}
+
+/**
+ * dm_test_fill_color_attr_bt709_limited - Test Fill color attr bt709 limited
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_color_attr_bt709_limited(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
+ int ret;
+
+ plane_state.color_encoding = DRM_COLOR_YCBCR_BT709;
+ plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
+
+ ret = fill_plane_color_attributes(&plane_state,
+ SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
+ &color_space);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, (int)color_space,
+ (int)COLOR_SPACE_YCBCR709_LIMITED);
+}
+
+/**
+ * dm_test_fill_color_attr_bt2020_full - Test Fill color attr bt2020 full
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_color_attr_bt2020_full(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
+ int ret;
+
+ plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020;
+ plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
+
+ ret = fill_plane_color_attributes(&plane_state,
+ SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
+ &color_space);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, (int)color_space,
+ (int)COLOR_SPACE_2020_YCBCR_FULL);
+}
+
+/**
+ * dm_test_fill_color_attr_bt2020_limited - Test Fill color attr bt2020 limited
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_color_attr_bt2020_limited(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
+ int ret;
+
+ plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020;
+ plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
+
+ ret = fill_plane_color_attributes(&plane_state,
+ SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
+ &color_space);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, (int)color_space,
+ (int)COLOR_SPACE_2020_YCBCR_LIMITED);
+}
+
+/**
+ * dm_test_fill_color_attr_invalid_encoding - Test Fill color attr invalid encoding
+ * @test: The KUnit test context
+ */
+static void dm_test_fill_color_attr_invalid_encoding(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ enum dc_color_space color_space = COLOR_SPACE_UNKNOWN;
+ int ret;
+
+ plane_state.color_encoding = 99;
+ plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE;
+
+ ret = fill_plane_color_attributes(&plane_state,
+ SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr,
+ &color_space);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/* Tests for modereset_required() */
+
+/**
+ * dm_test_modereset_required_when_inactive_and_modeset - Test Modereset required when inactive and modeset
+ * @test: The KUnit test context
+ */
+static void dm_test_modereset_required_when_inactive_and_modeset(struct kunit *test)
+{
+ struct drm_crtc_state crtc_state = { 0 };
+
+ crtc_state.active = false;
+ crtc_state.mode_changed = true;
+
+ KUNIT_EXPECT_TRUE(test, modereset_required(&crtc_state));
+}
+
+/**
+ * dm_test_modereset_not_required_when_active_and_modeset - Test Modereset not required when active and modeset
+ * @test: The KUnit test context
+ */
+static void dm_test_modereset_not_required_when_active_and_modeset(struct kunit *test)
+{
+ struct drm_crtc_state crtc_state = { 0 };
+
+ crtc_state.active = true;
+ crtc_state.mode_changed = true;
+
+ KUNIT_EXPECT_FALSE(test, modereset_required(&crtc_state));
+}
+
+/**
+ * dm_test_modereset_not_required_when_inactive_without_modeset - Test Modereset not required when inactive without modeset
+ * @test: The KUnit test context
+ */
+static void dm_test_modereset_not_required_when_inactive_without_modeset(struct kunit *test)
+{
+ struct drm_crtc_state crtc_state = { 0 };
+
+ crtc_state.active = false;
+ crtc_state.mode_changed = false;
+
+ KUNIT_EXPECT_FALSE(test, modereset_required(&crtc_state));
+}
+
+/* Tests for dm_get_oriented_plane_size() */
+
+/**
+ * dm_test_oriented_plane_size_rotate_0 - Test Oriented plane size rotate 0
+ * @test: The KUnit test context
+ */
+static void dm_test_oriented_plane_size_rotate_0(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ int src_w = 0;
+ int src_h = 0;
+
+ plane_state.rotation = DRM_MODE_ROTATE_0;
+ plane_state.src_w = 1920 << 16;
+ plane_state.src_h = 1080 << 16;
+
+ dm_get_oriented_plane_size(&plane_state, &src_w, &src_h);
+
+ KUNIT_EXPECT_EQ(test, src_w, 1920);
+ KUNIT_EXPECT_EQ(test, src_h, 1080);
+}
+
+/**
+ * dm_test_oriented_plane_size_rotate_90 - Test Oriented plane size rotate 90
+ * @test: The KUnit test context
+ */
+static void dm_test_oriented_plane_size_rotate_90(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ int src_w = 0;
+ int src_h = 0;
+
+ plane_state.rotation = DRM_MODE_ROTATE_90;
+ plane_state.src_w = 1920 << 16;
+ plane_state.src_h = 1080 << 16;
+
+ dm_get_oriented_plane_size(&plane_state, &src_w, &src_h);
+
+ KUNIT_EXPECT_EQ(test, src_w, 1080);
+ KUNIT_EXPECT_EQ(test, src_h, 1920);
+}
+
+/**
+ * dm_test_oriented_plane_size_rotate_180 - Test Oriented plane size rotate 180
+ * @test: The KUnit test context
+ */
+static void dm_test_oriented_plane_size_rotate_180(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ int src_w = 0;
+ int src_h = 0;
+
+ plane_state.rotation = DRM_MODE_ROTATE_180;
+ plane_state.src_w = 1920 << 16;
+ plane_state.src_h = 1080 << 16;
+
+ dm_get_oriented_plane_size(&plane_state, &src_w, &src_h);
+
+ KUNIT_EXPECT_EQ(test, src_w, 1920);
+ KUNIT_EXPECT_EQ(test, src_h, 1080);
+}
+
+/**
+ * dm_test_oriented_plane_size_rotate_270 - Test Oriented plane size rotate 270
+ * @test: The KUnit test context
+ */
+static void dm_test_oriented_plane_size_rotate_270(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ int src_w = 0;
+ int src_h = 0;
+
+ plane_state.rotation = DRM_MODE_ROTATE_270;
+ plane_state.src_w = 1920 << 16;
+ plane_state.src_h = 1080 << 16;
+
+ dm_get_oriented_plane_size(&plane_state, &src_w, &src_h);
+
+ KUNIT_EXPECT_EQ(test, src_w, 1080);
+ KUNIT_EXPECT_EQ(test, src_h, 1920);
+}
+
+/* Tests for dm_get_plane_scale() */
+
+/**
+ * dm_test_get_plane_scale_identity - Test Get plane scale identity
+ * @test: The KUnit test context
+ */
+static void dm_test_get_plane_scale_identity(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ int scale_w = 0;
+ int scale_h = 0;
+
+ plane_state.rotation = DRM_MODE_ROTATE_0;
+ plane_state.src_w = 1920 << 16;
+ plane_state.src_h = 1080 << 16;
+ plane_state.crtc_w = 1920;
+ plane_state.crtc_h = 1080;
+
+ dm_get_plane_scale(&plane_state, &scale_w, &scale_h);
+
+ KUNIT_EXPECT_EQ(test, scale_w, 1000);
+ KUNIT_EXPECT_EQ(test, scale_h, 1000);
+}
+
+/**
+ * dm_test_get_plane_scale_rotate_90_identity - Test Get plane scale rotate 90 identity
+ * @test: The KUnit test context
+ */
+static void dm_test_get_plane_scale_rotate_90_identity(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ int scale_w = 0;
+ int scale_h = 0;
+
+ plane_state.rotation = DRM_MODE_ROTATE_90;
+ plane_state.src_w = 1920 << 16;
+ plane_state.src_h = 1080 << 16;
+ plane_state.crtc_w = 1080;
+ plane_state.crtc_h = 1920;
+
+ dm_get_plane_scale(&plane_state, &scale_w, &scale_h);
+
+ KUNIT_EXPECT_EQ(test, scale_w, 1000);
+ KUNIT_EXPECT_EQ(test, scale_h, 1000);
+}
+
+/**
+ * dm_test_get_plane_scale_zero_src_width - Test Get plane scale zero src width
+ * @test: The KUnit test context
+ */
+static void dm_test_get_plane_scale_zero_src_width(struct kunit *test)
+{
+ struct drm_plane_state plane_state = { 0 };
+ int scale_w = 0;
+ int scale_h = 0;
+
+ plane_state.rotation = DRM_MODE_ROTATE_0;
+ plane_state.src_w = 0;
+ plane_state.src_h = 1080 << 16;
+ plane_state.crtc_w = 100;
+ plane_state.crtc_h = 200;
+
+ dm_get_plane_scale(&plane_state, &scale_w, &scale_h);
+
+ KUNIT_EXPECT_EQ(test, scale_w, 0);
+ KUNIT_EXPECT_EQ(test, scale_h, 185);
+}
+
+/* Tests for is_scaling_state_different() */
+
+/**
+ * dm_test_scaling_state_same - Test identical scaling states compare equal
+ * @test: The KUnit test context
+ */
+static void dm_test_scaling_state_same(struct kunit *test)
+{
+ struct dm_connector_state *a;
+ struct dm_connector_state *b;
+
+ a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL);
+ b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, a);
+ KUNIT_ASSERT_NOT_NULL(test, b);
+
+ a->scaling = RMX_FULL;
+ a->underscan_enable = false;
+ *b = *a;
+
+ KUNIT_EXPECT_FALSE(test, is_scaling_state_different(a, b));
+}
+
+/**
+ * dm_test_scaling_state_scaling_changed - Test differing scaling mode is detected
+ * @test: The KUnit test context
+ */
+static void dm_test_scaling_state_scaling_changed(struct kunit *test)
+{
+ struct dm_connector_state *a;
+ struct dm_connector_state *b;
+
+ a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL);
+ b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, a);
+ KUNIT_ASSERT_NOT_NULL(test, b);
+
+ a->scaling = RMX_FULL;
+ b->scaling = RMX_CENTER;
+
+ KUNIT_EXPECT_TRUE(test, is_scaling_state_different(a, b));
+}
+
+/**
+ * dm_test_scaling_state_underscan_enabled - Test enabling underscan with borders differs
+ * @test: The KUnit test context
+ */
+static void dm_test_scaling_state_underscan_enabled(struct kunit *test)
+{
+ struct dm_connector_state *old_state;
+ struct dm_connector_state *new_state;
+
+ old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
+ new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, old_state);
+ KUNIT_ASSERT_NOT_NULL(test, new_state);
+
+ /* new enables underscan with non-zero borders, old has it disabled */
+ new_state->underscan_enable = true;
+ new_state->underscan_hborder = 16;
+ new_state->underscan_vborder = 16;
+ old_state->underscan_enable = false;
+
+ KUNIT_EXPECT_TRUE(test, is_scaling_state_different(new_state, old_state));
+}
+
+/**
+ * dm_test_scaling_state_underscan_border_changed - Test changed underscan borders differ
+ * @test: The KUnit test context
+ */
+static void dm_test_scaling_state_underscan_border_changed(struct kunit *test)
+{
+ struct dm_connector_state *a;
+ struct dm_connector_state *b;
+
+ a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL);
+ b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, a);
+ KUNIT_ASSERT_NOT_NULL(test, b);
+
+ a->underscan_enable = true;
+ a->underscan_hborder = 16;
+ a->underscan_vborder = 16;
+ *b = *a;
+ b->underscan_hborder = 32;
+
+ KUNIT_EXPECT_TRUE(test, is_scaling_state_different(a, b));
+}
+
+/* Tests for is_timing_unchanged_for_freesync() */
+
+/**
+ * dm_test_timing_unchanged_null_args - Test NULL crtc states return false
+ * @test: The KUnit test context
+ */
+static void dm_test_timing_unchanged_null_args(struct kunit *test)
+{
+ struct drm_crtc_state crtc_state = { 0 };
+
+ KUNIT_EXPECT_FALSE(test,
+ is_timing_unchanged_for_freesync(NULL, &crtc_state));
+ KUNIT_EXPECT_FALSE(test,
+ is_timing_unchanged_for_freesync(&crtc_state, NULL));
+}
+
+/**
+ * dm_test_timing_unchanged_identical_modes - Test identical modes are not "unchanged"
+ * @test: The KUnit test context
+ *
+ * The helper only returns true when vtotal/vsync shift (vrr) while the rest
+ * of the timing stays fixed, so identical modes must return false.
+ */
+static void dm_test_timing_unchanged_identical_modes(struct kunit *test)
+{
+ struct drm_crtc_state old_state = { 0 };
+ struct drm_crtc_state new_state = { 0 };
+
+ old_state.mode.clock = 148500;
+ old_state.mode.hdisplay = 1920;
+ old_state.mode.vdisplay = 1080;
+ old_state.mode.htotal = 2200;
+ old_state.mode.vtotal = 1125;
+ new_state.mode = old_state.mode;
+
+ KUNIT_EXPECT_FALSE(test,
+ is_timing_unchanged_for_freesync(&old_state, &new_state));
+}
+
+/**
+ * dm_test_timing_unchanged_vrr_shift - Test vrr-style vtotal/vsync shift is detected
+ * @test: The KUnit test context
+ */
+static void dm_test_timing_unchanged_vrr_shift(struct kunit *test)
+{
+ struct drm_crtc_state old_state = { 0 };
+ struct drm_crtc_state new_state = { 0 };
+
+ old_state.mode.clock = 148500;
+ old_state.mode.hdisplay = 1920;
+ old_state.mode.vdisplay = 1080;
+ old_state.mode.htotal = 2200;
+ old_state.mode.vtotal = 1125;
+ old_state.mode.hsync_start = 2008;
+ old_state.mode.vsync_start = 1084;
+ old_state.mode.hsync_end = 2052;
+ old_state.mode.vsync_end = 1089;
+
+ /* Same horizontal timing, vertical totals/sync shifted by 125 lines */
+ new_state.mode = old_state.mode;
+ new_state.mode.vtotal = 1250;
+ new_state.mode.vsync_start = 1209;
+ new_state.mode.vsync_end = 1214;
+
+ KUNIT_EXPECT_TRUE(test,
+ is_timing_unchanged_for_freesync(&old_state, &new_state));
+}
+
+/**
+ * dm_test_timing_unchanged_clock_changed - Test pixel clock change returns false
+ * @test: The KUnit test context
+ */
+static void dm_test_timing_unchanged_clock_changed(struct kunit *test)
+{
+ struct drm_crtc_state old_state = { 0 };
+ struct drm_crtc_state new_state = { 0 };
+
+ old_state.mode.clock = 148500;
+ old_state.mode.htotal = 2200;
+ old_state.mode.vtotal = 1125;
+ old_state.mode.vsync_start = 1084;
+ old_state.mode.vsync_end = 1089;
+
+ new_state.mode = old_state.mode;
+ new_state.mode.clock = 297000;
+ new_state.mode.vtotal = 1250;
+ new_state.mode.vsync_start = 1209;
+ new_state.mode.vsync_end = 1214;
+
+ KUNIT_EXPECT_FALSE(test,
+ is_timing_unchanged_for_freesync(&old_state, &new_state));
+}
+
+/* Tests for set_freesync_fixed_config() */
+
+/**
+ * dm_test_set_freesync_fixed_config_60hz - Test fixed refresh computed for 1080p60
+ * @test: The KUnit test context
+ */
+static void dm_test_set_freesync_fixed_config_60hz(struct kunit *test)
+{
+ struct dm_crtc_state dm_crtc_state = { 0 };
+
+ dm_crtc_state.base.mode.clock = 148500;
+ dm_crtc_state.base.mode.htotal = 2200;
+ dm_crtc_state.base.mode.vtotal = 1125;
+
+ set_freesync_fixed_config(&dm_crtc_state);
+
+ KUNIT_EXPECT_EQ(test, (int)dm_crtc_state.freesync_config.state,
+ (int)VRR_STATE_ACTIVE_FIXED);
+ /* 148500 kHz / (2200 * 1125) = 60 Hz = 60000000 uHz */
+ KUNIT_EXPECT_EQ(test, dm_crtc_state.freesync_config.fixed_refresh_in_uhz,
+ 60000000U);
+}
+
+/* Tests for is_dc_timing_adjust_needed() */
+
+/**
+ * dm_test_dc_timing_adjust_pending - Test a pending hw timing adjust forces true
+ * @test: The KUnit test context
+ */
+static void dm_test_dc_timing_adjust_pending(struct kunit *test)
+{
+ struct dm_crtc_state *old_state, *new_state;
+ struct dc_stream_state *stream;
+
+ old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
+ new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ new_state->stream = stream;
+ stream->adjust.timing_adjust_pending = 1;
+
+ KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
+}
+
+/**
+ * dm_test_dc_timing_adjust_active_fixed - Test VRR active-fixed forces true
+ * @test: The KUnit test context
+ */
+static void dm_test_dc_timing_adjust_active_fixed(struct kunit *test)
+{
+ struct dm_crtc_state *old_state, *new_state;
+ struct dc_stream_state *stream;
+
+ old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
+ new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ new_state->stream = stream;
+ new_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
+
+ KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
+}
+
+/**
+ * dm_test_dc_timing_adjust_vrr_toggle - Test a change in vrr active state forces true
+ * @test: The KUnit test context
+ */
+static void dm_test_dc_timing_adjust_vrr_toggle(struct kunit *test)
+{
+ struct dm_crtc_state *old_state, *new_state;
+ struct dc_stream_state *stream;
+
+ old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
+ new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ new_state->stream = stream;
+ old_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE;
+ new_state->freesync_config.state = VRR_STATE_INACTIVE;
+
+ KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state));
+}
+
+/**
+ * dm_test_dc_timing_adjust_not_needed - Test steady-state timing needs no adjust
+ * @test: The KUnit test context
+ */
+static void dm_test_dc_timing_adjust_not_needed(struct kunit *test)
+{
+ struct dm_crtc_state *old_state, *new_state;
+ struct dc_stream_state *stream;
+
+ old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state);
+ new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state);
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ new_state->stream = stream;
+ old_state->freesync_config.state = VRR_STATE_INACTIVE;
+ new_state->freesync_config.state = VRR_STATE_INACTIVE;
+
+ KUNIT_EXPECT_FALSE(test, is_dc_timing_adjust_needed(old_state, new_state));
+}
+
+/* Tests for set_multisync_trigger_params() */
+
+/**
+ * dm_test_multisync_trigger_disabled - Test disabled reset leaves params untouched
+ * @test: The KUnit test context
+ */
+static void dm_test_multisync_trigger_disabled(struct kunit *test)
+{
+ struct dc_stream_state *stream;
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+
+ stream->triggered_crtc_reset.enabled = false;
+ stream->triggered_crtc_reset.event = CRTC_EVENT_VSYNC_FALLING;
+ stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_LINE;
+
+ set_multisync_trigger_params(stream);
+
+ /* Nothing should change when the reset trigger is disabled */
+ KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event,
+ (int)CRTC_EVENT_VSYNC_FALLING);
+ KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay,
+ (int)TRIGGER_DELAY_NEXT_LINE);
+}
+
+/**
+ * dm_test_multisync_trigger_rising - Test positive vsync polarity selects rising edge
+ * @test: The KUnit test context
+ */
+static void dm_test_multisync_trigger_rising(struct kunit *test)
+{
+ struct dc_stream_state *stream;
+ struct dc_stream_state *master;
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+ master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master);
+
+ master->timing.flags.VSYNC_POSITIVE_POLARITY = 1;
+ stream->triggered_crtc_reset.enabled = true;
+ stream->triggered_crtc_reset.event_source = master;
+
+ set_multisync_trigger_params(stream);
+
+ KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event,
+ (int)CRTC_EVENT_VSYNC_RISING);
+ KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay,
+ (int)TRIGGER_DELAY_NEXT_PIXEL);
+}
+
+/**
+ * dm_test_multisync_trigger_falling - Test negative vsync polarity selects falling edge
+ * @test: The KUnit test context
+ */
+static void dm_test_multisync_trigger_falling(struct kunit *test)
+{
+ struct dc_stream_state *stream;
+ struct dc_stream_state *master;
+
+ stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream);
+ master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master);
+
+ master->timing.flags.VSYNC_POSITIVE_POLARITY = 0;
+ stream->triggered_crtc_reset.enabled = true;
+ stream->triggered_crtc_reset.event_source = master;
+
+ set_multisync_trigger_params(stream);
+
+ KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event,
+ (int)CRTC_EVENT_VSYNC_FALLING);
+ KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay,
+ (int)TRIGGER_DELAY_NEXT_PIXEL);
+}
+
+/* Tests for set_master_stream() */
+
+/**
+ * dm_test_master_stream_highest_refresh - Test highest refresh-rate stream becomes master
+ * @test: The KUnit test context
+ */
+static void dm_test_master_stream_highest_refresh(struct kunit *test)
+{
+ struct dc_stream_state *stream0, *stream1;
+ struct dc_stream_state *stream_set[2];
+
+ stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0);
+ stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1);
+ stream_set[0] = stream0;
+ stream_set[1] = stream1;
+
+ /* stream0: 60Hz, stream1: 120Hz -> stream1 is master */
+ stream0->triggered_crtc_reset.enabled = true;
+ stream0->timing.pix_clk_100hz = 1485000;
+ stream0->timing.h_total = 2200;
+ stream0->timing.v_total = 1125;
+
+ stream1->triggered_crtc_reset.enabled = true;
+ stream1->timing.pix_clk_100hz = 2970000;
+ stream1->timing.h_total = 2200;
+ stream1->timing.v_total = 1125;
+
+ set_master_stream(stream_set, 2);
+
+ KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source,
+ stream1);
+ KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source,
+ stream1);
+}
+
+/**
+ * dm_test_master_stream_defaults_to_first - Test default master when none triggered
+ * @test: The KUnit test context
+ *
+ * When no stream has the reset trigger enabled, master_stream stays 0 and all
+ * streams point at the first stream as their event source.
+ */
+static void dm_test_master_stream_defaults_to_first(struct kunit *test)
+{
+ struct dc_stream_state *stream0, *stream1;
+ struct dc_stream_state *stream_set[2];
+
+ stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0);
+ stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1);
+ stream_set[0] = stream0;
+ stream_set[1] = stream1;
+
+ set_master_stream(stream_set, 2);
+
+ KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source,
+ stream0);
+ KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source,
+ stream0);
+}
+
+static struct kunit_case amdgpu_dm_tests[] = {
+ /* dm_plane_layer_index_cmp */
+ KUNIT_CASE(dm_test_plane_layer_index_cmp_equal),
+ KUNIT_CASE(dm_test_plane_layer_index_cmp_descending),
+ KUNIT_CASE(dm_test_plane_layer_index_cmp_ascending),
+ /* fill_plane_color_attributes */
+ KUNIT_CASE(dm_test_fill_color_attr_rgb_format),
+ KUNIT_CASE(dm_test_fill_color_attr_bt601_full),
+ KUNIT_CASE(dm_test_fill_color_attr_bt601_limited),
+ KUNIT_CASE(dm_test_fill_color_attr_bt709_full),
+ KUNIT_CASE(dm_test_fill_color_attr_bt709_limited),
+ KUNIT_CASE(dm_test_fill_color_attr_bt2020_full),
+ KUNIT_CASE(dm_test_fill_color_attr_bt2020_limited),
+ KUNIT_CASE(dm_test_fill_color_attr_invalid_encoding),
+ /* modereset_required */
+ KUNIT_CASE(dm_test_modereset_required_when_inactive_and_modeset),
+ KUNIT_CASE(dm_test_modereset_not_required_when_active_and_modeset),
+ KUNIT_CASE(dm_test_modereset_not_required_when_inactive_without_modeset),
+ /* dm_get_oriented_plane_size */
+ KUNIT_CASE(dm_test_oriented_plane_size_rotate_0),
+ KUNIT_CASE(dm_test_oriented_plane_size_rotate_90),
+ KUNIT_CASE(dm_test_oriented_plane_size_rotate_180),
+ KUNIT_CASE(dm_test_oriented_plane_size_rotate_270),
+ /* dm_get_plane_scale */
+ KUNIT_CASE(dm_test_get_plane_scale_identity),
+ KUNIT_CASE(dm_test_get_plane_scale_rotate_90_identity),
+ KUNIT_CASE(dm_test_get_plane_scale_zero_src_width),
+ /* is_scaling_state_different */
+ KUNIT_CASE(dm_test_scaling_state_same),
+ KUNIT_CASE(dm_test_scaling_state_scaling_changed),
+ KUNIT_CASE(dm_test_scaling_state_underscan_enabled),
+ KUNIT_CASE(dm_test_scaling_state_underscan_border_changed),
+ /* is_timing_unchanged_for_freesync */
+ KUNIT_CASE(dm_test_timing_unchanged_null_args),
+ KUNIT_CASE(dm_test_timing_unchanged_identical_modes),
+ KUNIT_CASE(dm_test_timing_unchanged_vrr_shift),
+ KUNIT_CASE(dm_test_timing_unchanged_clock_changed),
+ /* set_freesync_fixed_config */
+ KUNIT_CASE(dm_test_set_freesync_fixed_config_60hz),
+ /* is_dc_timing_adjust_needed */
+ KUNIT_CASE(dm_test_dc_timing_adjust_pending),
+ KUNIT_CASE(dm_test_dc_timing_adjust_active_fixed),
+ KUNIT_CASE(dm_test_dc_timing_adjust_vrr_toggle),
+ KUNIT_CASE(dm_test_dc_timing_adjust_not_needed),
+ /* set_multisync_trigger_params */
+ KUNIT_CASE(dm_test_multisync_trigger_disabled),
+ KUNIT_CASE(dm_test_multisync_trigger_rising),
+ KUNIT_CASE(dm_test_multisync_trigger_falling),
+ /* set_master_stream */
+ KUNIT_CASE(dm_test_master_stream_highest_refresh),
+ KUNIT_CASE(dm_test_master_stream_defaults_to_first),
+ {}
+};
+
+static struct kunit_suite amdgpu_dm_test_suite = {
+ .name = "amdgpu_dm",
+ .test_cases = amdgpu_dm_tests,
+};
+
+kunit_test_suite(amdgpu_dm_test_suite);
+
+MODULE_AUTHOR("AMD");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c
new file mode 100644
index 000000000000..c71f61a2438d
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * KUnit tests for amdgpu_dm_wb.c
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_kunit_helpers.h>
+#include <drm/drm_mode.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_writeback.h>
+
+#include "dc.h"
+#include "amdgpu.h"
+#include "amdgpu_dm.h"
+#include "amdgpu_dm_wb.h"
+#include "amdgpu_dm_kunit_test_helpers.h"
+
+
+/* Helper functions */
+
+static struct drm_crtc_state *alloc_test_crtc_state(struct kunit *test,
+ int hdisplay, int vdisplay)
+{
+ struct drm_crtc_state *crtc_state;
+
+ crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, crtc_state);
+
+ crtc_state->mode.hdisplay = hdisplay;
+ crtc_state->mode.vdisplay = vdisplay;
+
+ return crtc_state;
+}
+
+static struct drm_connector_state *alloc_test_conn_state(struct kunit *test,
+ int fb_width,
+ int fb_height,
+ u32 format)
+{
+ struct drm_connector_state *conn_state;
+ struct drm_writeback_job *job;
+ struct drm_framebuffer *fb;
+ struct drm_format_info *fmt_info;
+
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ job = kunit_kzalloc(test, sizeof(*job), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, job);
+
+ fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, fb);
+
+ fmt_info = kunit_kzalloc(test, sizeof(*fmt_info), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, fmt_info);
+
+ fb->width = fb_width;
+ fb->height = fb_height;
+ fmt_info->format = format;
+ fb->format = fmt_info;
+
+ job->fb = fb;
+ conn_state->writeback_job = job;
+
+ return conn_state;
+}
+
+
+
+/* Tests for amdgpu_dm_wb_encoder_atomic_check */
+
+/**
+ * dm_test_wb_atomic_check_no_job - Verify early return when no writeback job
+ * @test: KUnit test context
+ *
+ * When conn_state->writeback_job is NULL, no writeback is requested and the
+ * function should return 0 without further validation.
+ */
+static void dm_test_wb_atomic_check_no_job(struct kunit *test)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ int ret;
+
+ crtc_state = alloc_test_crtc_state(test, 1920, 1080);
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ /* No writeback_job — should return 0 */
+ conn_state->writeback_job = NULL;
+ ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/**
+ * dm_test_wb_atomic_check_no_fb - Verify early return when job has no framebuffer
+ * @test: KUnit test context
+ *
+ * When a writeback job exists but job->fb is NULL, the function should return 0
+ * without validating dimensions or pixel format.
+ */
+static void dm_test_wb_atomic_check_no_fb(struct kunit *test)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ struct drm_writeback_job *job;
+ int ret;
+
+ crtc_state = alloc_test_crtc_state(test, 1920, 1080);
+ conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, conn_state);
+
+ job = kunit_kzalloc(test, sizeof(*job), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, job);
+
+ /* writeback_job exists but no fb — should return 0 */
+ job->fb = NULL;
+ conn_state->writeback_job = job;
+ ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/**
+ * dm_test_wb_atomic_check_valid - Verify success with matching size and supported format
+ * @test: KUnit test context
+ *
+ * When the framebuffer dimensions match the CRTC mode and the pixel format is
+ * in the supported formats list, the function should return 0.
+ */
+static void dm_test_wb_atomic_check_valid(struct kunit *test)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ int ret;
+
+ crtc_state = alloc_test_crtc_state(test, 1920, 1080);
+ conn_state = alloc_test_conn_state(test, 1920, 1080,
+ DRM_FORMAT_XRGB2101010);
+
+ ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+}
+
+/**
+ * dm_test_wb_atomic_check_size_mismatch - Verify rejection when both dimensions differ
+ * @test: KUnit test context
+ *
+ * When both framebuffer width and height differ from the CRTC mode, the
+ * function should return -EINVAL.
+ */
+static void dm_test_wb_atomic_check_size_mismatch(struct kunit *test)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ int ret;
+
+ /* FB is 3840x2160 but mode is 1920x1080 */
+ crtc_state = alloc_test_crtc_state(test, 1920, 1080);
+ conn_state = alloc_test_conn_state(test, 3840, 2160,
+ DRM_FORMAT_XRGB2101010);
+
+ ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/**
+ * dm_test_wb_atomic_check_width_mismatch - Verify rejection when width alone differs
+ * @test: KUnit test context
+ *
+ * When only the framebuffer width differs from the CRTC mode hdisplay, the
+ * function should return -EINVAL.
+ */
+static void dm_test_wb_atomic_check_width_mismatch(struct kunit *test)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ int ret;
+
+ /* Width doesn't match */
+ crtc_state = alloc_test_crtc_state(test, 1920, 1080);
+ conn_state = alloc_test_conn_state(test, 1280, 1080,
+ DRM_FORMAT_XRGB2101010);
+
+ ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/**
+ * dm_test_wb_atomic_check_height_mismatch - Verify rejection when height alone differs
+ * @test: KUnit test context
+ *
+ * When only the framebuffer height differs from the CRTC mode vdisplay, the
+ * function should return -EINVAL.
+ */
+static void dm_test_wb_atomic_check_height_mismatch(struct kunit *test)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ int ret;
+
+ /* Height doesn't match */
+ crtc_state = alloc_test_crtc_state(test, 1920, 1080);
+ conn_state = alloc_test_conn_state(test, 1920, 720,
+ DRM_FORMAT_XRGB2101010);
+
+ ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/**
+ * dm_test_wb_atomic_check_invalid_format - Verify rejection of unsupported pixel format
+ * @test: KUnit test context
+ *
+ * When the framebuffer dimensions match but the pixel format is not in
+ * amdgpu_dm_wb_formats[], the function should return -EINVAL.
+ */
+static void dm_test_wb_atomic_check_invalid_format(struct kunit *test)
+{
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ int ret;
+
+ /* Correct size but unsupported format */
+ crtc_state = alloc_test_crtc_state(test, 1920, 1080);
+ conn_state = alloc_test_conn_state(test, 1920, 1080,
+ DRM_FORMAT_XRGB8888);
+
+ ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state);
+ KUNIT_EXPECT_EQ(test, ret, -EINVAL);
+}
+
+/* Tests for amdgpu_dm_wb_connector_get_modes using DRM mock */
+
+static const struct drm_connector_funcs dm_wb_test_connector_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .reset = drm_atomic_helper_connector_reset,
+};
+
+/**
+ * dm_test_wb_get_modes_returns_modes - Verify at least one mode is returned
+ * @test: KUnit test context
+ *
+ * Uses a DRM mock connector to verify that amdgpu_dm_wb_connector_get_modes()
+ * populates the connector with at least one display mode.
+ */
+static void dm_test_wb_get_modes_returns_modes(struct kunit *test)
+{
+ struct device *dev;
+ struct drm_device *drm;
+ struct drm_connector *connector;
+ int count;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+ sizeof(*drm), 0,
+ DRIVER_MODESET | DRIVER_ATOMIC);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, connector);
+
+ drmm_connector_init(drm, connector, &dm_wb_test_connector_funcs,
+ DRM_MODE_CONNECTOR_VIRTUAL, NULL);
+
+ count = amdgpu_dm_wb_connector_get_modes(connector);
+
+ /* drm_add_modes_noedid should return at least one mode */
+ KUNIT_EXPECT_GT(test, count, 0);
+}
+
+/**
+ * dm_test_wb_get_modes_bounded_by_max - Verify all modes are within max resolution
+ * @test: KUnit test context
+ *
+ * Uses a DRM mock connector to verify that all modes returned by
+ * amdgpu_dm_wb_connector_get_modes() have hdisplay <= 3840 and
+ * vdisplay <= 2160, matching the DWB hardware maximum.
+ */
+static void dm_test_wb_get_modes_bounded_by_max(struct kunit *test)
+{
+ struct device *dev;
+ struct drm_device *drm;
+ struct drm_connector *connector;
+ struct drm_display_mode *mode;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ drm = __drm_kunit_helper_alloc_drm_device(test, dev,
+ sizeof(*drm), 0,
+ DRIVER_MODESET | DRIVER_ATOMIC);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
+
+ connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, connector);
+
+ drmm_connector_init(drm, connector, &dm_wb_test_connector_funcs,
+ DRM_MODE_CONNECTOR_VIRTUAL, NULL);
+
+ amdgpu_dm_wb_connector_get_modes(connector);
+
+ /* All modes must fit within 3840x2160 */
+ list_for_each_entry(mode, &connector->probed_modes, head) {
+ KUNIT_EXPECT_LE(test, mode->hdisplay, 3840);
+ KUNIT_EXPECT_LE(test, mode->vdisplay, 2160);
+ }
+}
+
+/* Tests for amdgpu_dm_wb_connector_init using DRM mock */
+
+/**
+ * dm_test_wb_connector_init_success - Verify writeback connector initialization
+ * @test: KUnit test context
+ *
+ * Uses a DRM mock device embedded in struct amdgpu_device to verify that
+ * amdgpu_dm_wb_connector_init() initializes the writeback connector, stores
+ * the DC link, installs connector state through reset, and wires the expected
+ * DRM callbacks.
+ */
+static void dm_test_wb_connector_init_success(struct kunit *test)
+{
+ struct amdgpu_dm_wb_connector *wbcon;
+ struct amdgpu_display_manager *dm;
+ struct amdgpu_device *adev;
+ struct dc_link *link;
+ struct dc *dc;
+ int ret;
+
+ adev = dm_kunit_alloc_adev(test);
+ adev->mode_info.num_crtc = 1;
+ dm = &adev->dm;
+ dm->adev = adev;
+
+ dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dc);
+
+ link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, link);
+
+ dc->links[0] = link;
+ dm->dc = dc;
+
+ wbcon = kunit_kzalloc(test, sizeof(*wbcon), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, wbcon);
+
+ ret = amdgpu_dm_wb_connector_init(dm, wbcon, 0);
+
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_PTR_EQ(test, wbcon->link, link);
+ KUNIT_EXPECT_TRUE(test, wbcon->base.base.funcs != NULL);
+ KUNIT_EXPECT_TRUE(test, wbcon->base.base.helper_private != NULL);
+ KUNIT_EXPECT_TRUE(test, wbcon->base.base.state != NULL);
+ KUNIT_EXPECT_TRUE(test, wbcon->base.encoder.funcs != NULL);
+ KUNIT_EXPECT_EQ(test, wbcon->base.encoder.possible_crtcs, 0x1);
+}
+
+static struct kunit_case dm_wb_test_cases[] = {
+ /* amdgpu_dm_wb_encoder_atomic_check */
+ KUNIT_CASE(dm_test_wb_atomic_check_no_job),
+ KUNIT_CASE(dm_test_wb_atomic_check_no_fb),
+ KUNIT_CASE(dm_test_wb_atomic_check_valid),
+ KUNIT_CASE(dm_test_wb_atomic_check_size_mismatch),
+ KUNIT_CASE(dm_test_wb_atomic_check_width_mismatch),
+ KUNIT_CASE(dm_test_wb_atomic_check_height_mismatch),
+ KUNIT_CASE(dm_test_wb_atomic_check_invalid_format),
+ /* amdgpu_dm_wb_connector_get_modes */
+ KUNIT_CASE(dm_test_wb_get_modes_returns_modes),
+ KUNIT_CASE(dm_test_wb_get_modes_bounded_by_max),
+ /* amdgpu_dm_wb_connector_init */
+ KUNIT_CASE(dm_test_wb_connector_init_success),
+ {}
+};
+
+static struct kunit_suite dm_wb_test_suite = {
+ .name = "amdgpu_dm_wb",
+ .test_cases = dm_wb_test_cases,
+};
+
+kunit_test_suite(dm_wb_test_suite);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_wb");
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c
index 4a60c5f54a04..6ee3da89d058 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c
@@ -10,6 +10,7 @@
#include "dcn31/dcn31_clk_mgr.h"
#include "dcn32/dcn32_clk_mgr.h"
#include "dcn401/dcn401_clk_mgr.h"
+#include "hw_sequencer.h"
#include "reg_helper.h"
#include "core_types.h"
#include "dm_helpers.h"
@@ -1085,7 +1086,8 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
struct clk_mgr *clk_mgr_base,
struct dc_state *context,
struct dc_clocks *new_clocks,
- bool safe_to_lower)
+ bool safe_to_lower,
+ unsigned int num_steps_start)
{
struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base);
struct dcn401_clk_mgr *clk_mgr401 = TO_DCN401_CLK_MGR(clk_mgr_internal);
@@ -1100,7 +1102,7 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
bool frl_present = false;
unsigned int i;
- unsigned int num_steps = 0;
+ unsigned int num_steps = num_steps_start;
/* CLK_MGR401_READ_CLOCKS_FROM_DENTIST */
if (clk_mgr_base->clks.dispclk_khz == 0 ||
@@ -1239,6 +1241,44 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
return num_steps;
}
+/*
+ * Build-for-BLS functions.
+ * These build both bandwidth and display clock sequences into the clk_mgr's
+ * internal block sequence array, then add a single CLK_MGR_UPDATE_CLOCKS step
+ * to the HWSS block sequence whose executor will call
+ * execute_clk_mgr_block_sequence to dispatch all accumulated steps.
+ */
+void dcn401_build_clock_update_for_bls(
+ struct clk_mgr *clk_mgr_base,
+ struct dc_state *context,
+ bool safe_to_lower,
+ struct block_sequence_state *seq_state)
+{
+ struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base);
+ struct dcn401_clk_mgr *clk_mgr401 = TO_DCN401_CLK_MGR(clk_mgr_internal);
+ unsigned int num_bw_steps;
+ unsigned int total_steps;
+
+ /* Build bandwidth clocks sequence starting at index 0 */
+ num_bw_steps = dcn401_build_update_bandwidth_clocks_sequence(clk_mgr_base,
+ context,
+ &context->bw_ctx.bw.dcn.clk,
+ safe_to_lower);
+
+ /* Build display clocks sequence appended after bandwidth steps */
+ total_steps = dcn401_build_update_display_clocks_sequence(clk_mgr_base,
+ context,
+ &context->bw_ctx.bw.dcn.clk,
+ safe_to_lower,
+ num_bw_steps);
+
+ /* Store total step count for the executor */
+ clk_mgr401->num_block_sequence_steps = total_steps;
+
+ /* Add single HWSS step that will execute all clk_mgr block sequence steps */
+ hwss_add_clk_mgr_update_clocks(seq_state, clk_mgr_base);
+}
+
static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base,
struct dc_state *context,
bool safe_to_lower)
@@ -1260,7 +1300,8 @@ static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base,
num_steps = dcn401_build_update_display_clocks_sequence(clk_mgr_base,
context,
&context->bw_ctx.bw.dcn.clk,
- safe_to_lower);
+ safe_to_lower,
+ 0);
/* execute sequence */
dcn401_execute_block_sequence(clk_mgr_base, num_steps);
@@ -1549,6 +1590,14 @@ unsigned int dcn401_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_typ
return 0;
}
+static void dcn401_execute_clk_mgr_block_sequence_bls(struct clk_mgr *clk_mgr_base)
+{
+ struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base);
+ struct dcn401_clk_mgr *clk_mgr401 = TO_DCN401_CLK_MGR(clk_mgr_internal);
+
+ dcn401_execute_block_sequence(clk_mgr_base, clk_mgr401->num_block_sequence_steps);
+}
+
static struct clk_mgr_funcs dcn401_funcs = {
.get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz,
.get_dtb_ref_clk_frequency = dcn401_get_dtb_ref_freq_khz,
@@ -1566,6 +1615,8 @@ static struct clk_mgr_funcs dcn401_funcs = {
.get_hard_min_fclk = dcn401_get_hard_min_fclk,
.is_dc_mode_present = dcn401_is_dc_mode_present,
.get_max_clock_khz = dcn401_get_max_clock_khz,
+ .build_clock_update_for_bls = dcn401_build_clock_update_for_bls,
+ .execute_clk_mgr_block_sequence = dcn401_execute_clk_mgr_block_sequence_bls,
};
struct clk_mgr_internal *dcn401_clk_mgr_construct(
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h
index 370d2ddd6064..d4cd69a5a8dd 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h
@@ -102,6 +102,7 @@ struct dcn401_clk_mgr {
struct clk_mgr_internal base;
struct dcn401_clk_mgr_block_sequence block_sequence[DCN401_CLK_MGR_MAX_SEQUENCE_SIZE];
+ unsigned int num_block_sequence_steps;
};
void dcn401_init_clocks(struct clk_mgr *clk_mgr_base);
@@ -114,4 +115,12 @@ void dcn401_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr);
unsigned int dcn401_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_type clk_type);
+struct block_sequence_state;
+
+void dcn401_build_clock_update_for_bls(
+ struct clk_mgr *clk_mgr_base,
+ struct dc_state *context,
+ bool safe_to_lower,
+ struct block_sequence_state *seq_state);
+
#endif /* __DCN401_CLK_MGR_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 175106cce5a4..28339e4b6d67 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -2310,7 +2310,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
for (i = 0; i < context->stream_count; i++) {
uint32_t prev_dsc_changed = context->streams[i]->update_flags.bits.dsc_changed;
- context->streams[i]->update_flags.raw = 0xFFFFFFFF;
+ stream_update_flags_set_full(&context->streams[i]->update_flags);
context->streams[i]->update_flags.bits.dsc_changed = prev_dsc_changed;
}
@@ -2416,7 +2416,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
/* Clear update flags that were set earlier to avoid redundant programming */
for (i = 0; i < context->stream_count; i++) {
- context->streams[i]->update_flags.raw = 0x0;
+ stream_update_flags_clear(&context->streams[i]->update_flags);
}
old_state = dc->current_state;
@@ -2764,7 +2764,7 @@ static bool is_surface_in_context(
static struct surface_update_descriptor get_plane_info_update_type(const struct dc_surface_update *u)
{
- union surface_update_flags *update_flags = &u->surface->update_flags;
+ struct pipe_update_bits *update_bits = &u->surface->update_bits;
struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
if (!u->plane_info)
@@ -2774,37 +2774,37 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct
elevate_update_type(&update_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
if (u->plane_info->color_space != u->surface->color_space) {
- update_flags->bits.color_space_change = 1;
+ update_bits->color_space_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->horizontal_mirror != u->surface->horizontal_mirror) {
- update_flags->bits.horizontal_mirror_change = 1;
+ update_bits->horizontal_mirror_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->rotation != u->surface->rotation) {
- update_flags->bits.rotation_change = 1;
+ update_bits->rotation_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->format != u->surface->format) {
- update_flags->bits.pixel_format_change = 1;
+ update_bits->pixel_format_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->stereo_format != u->surface->stereo_format) {
- update_flags->bits.stereo_format_change = 1;
+ update_bits->stereo_format_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->per_pixel_alpha != u->surface->per_pixel_alpha) {
- update_flags->bits.per_pixel_alpha_change = 1;
+ update_bits->per_pixel_alpha_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->global_alpha_value != u->surface->global_alpha_value) {
- update_flags->bits.global_alpha_change = 1;
+ update_bits->global_alpha_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
@@ -2816,7 +2816,7 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct
* stutter period calculation. Triggering a full update will
* recalculate stutter period.
*/
- update_flags->bits.dcc_change = 1;
+ update_bits->dcc_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
@@ -2825,25 +2825,25 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct
/* different bytes per element will require full bandwidth
* and DML calculation
*/
- update_flags->bits.bpp_change = 1;
+ update_bits->bpp_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->plane_size.surface_pitch != u->surface->plane_size.surface_pitch
|| u->plane_info->plane_size.chroma_pitch != u->surface->plane_size.chroma_pitch) {
- update_flags->bits.plane_size_change = 1;
+ update_bits->plane_size_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
const struct dc_tiling_info *tiling = &u->plane_info->tiling_info;
if (memcmp(tiling, &u->surface->tiling_info, sizeof(*tiling)) != 0) {
- update_flags->bits.swizzle_change = 1;
+ update_bits->swizzle_change = 1;
if (tiling->flags.avoid_full_update_on_tiling_change) {
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
} else {
- update_flags->bits.bandwidth_change = 1;
+ update_bits->bandwidth_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
}
@@ -2853,10 +2853,10 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct
}
static struct surface_update_descriptor get_scaling_info_update_type(
- const struct dc_check_config *check_config,
- const struct dc_surface_update *u)
+ const struct dc_check_config *check_config,
+ const struct dc_surface_update *u)
{
- union surface_update_flags *update_flags = &u->surface->update_flags;
+ struct pipe_update_bits *update_bits = &u->surface->update_bits;
struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
if (!u->scaling_info)
@@ -2873,26 +2873,26 @@ static struct surface_update_descriptor get_scaling_info_update_type(
|| u->scaling_info->clip_rect.height != u->surface->clip_rect.height
|| u->scaling_info->scaling_quality.integer_scaling !=
u->surface->scaling_quality.integer_scaling) {
- update_flags->bits.scaling_change = 1;
+ update_bits->scaling_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
if (u->scaling_info->src_rect.width > u->surface->src_rect.width
|| u->scaling_info->src_rect.height > u->surface->src_rect.height)
/* Making src rect bigger requires a bandwidth change */
- update_flags->bits.clock_change = 1;
+ update_bits->clock_change = 1;
if ((u->scaling_info->dst_rect.width < u->surface->dst_rect.width
|| u->scaling_info->dst_rect.height < u->surface->dst_rect.height)
&& (u->scaling_info->dst_rect.width < u->surface->src_rect.width
|| u->scaling_info->dst_rect.height < u->surface->src_rect.height))
/* Making dst rect smaller requires a bandwidth change */
- update_flags->bits.bandwidth_change = 1;
+ update_bits->bandwidth_change = 1;
if (u->scaling_info->src_rect.width > (int)check_config->max_optimizable_video_width &&
(u->scaling_info->clip_rect.width > u->surface->clip_rect.width ||
u->scaling_info->clip_rect.height > u->surface->clip_rect.height))
/* Changing clip size of a large surface may result in MPC slice count change */
- update_flags->bits.bandwidth_change = 1;
+ update_bits->bandwidth_change = 1;
}
if (u->scaling_info->src_rect.x != u->surface->src_rect.x
@@ -2902,7 +2902,7 @@ static struct surface_update_descriptor get_scaling_info_update_type(
|| u->scaling_info->dst_rect.x != u->surface->dst_rect.x
|| u->scaling_info->dst_rect.y != u->surface->dst_rect.y) {
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
- update_flags->bits.position_change = 1;
+ update_bits->position_change = 1;
}
return update_type;
@@ -2913,15 +2913,15 @@ static struct surface_update_descriptor det_surface_update(
struct dc_surface_update *u)
{
struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
- union surface_update_flags *update_flags = &u->surface->update_flags;
+ struct pipe_update_bits *update_bits = &u->surface->update_bits;
if (u->surface->force_full_update) {
- update_flags->raw = 0xFFFFFFFF;
+ dc_pipe_update_bits_set_full(update_bits);
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
return overall_type;
}
- update_flags->raw = 0; // Reset all flags
+ dc_pipe_update_bits_clear(update_bits);
struct surface_update_descriptor inner_type = get_plane_info_update_type(u);
@@ -2931,87 +2931,112 @@ static struct surface_update_descriptor det_surface_update(
elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor);
if (u->flip_addr) {
- update_flags->bits.addr_update = 1;
+ update_bits->addr_update = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
if (u->flip_addr->address.tmz_surface != u->surface->address.tmz_surface) {
- update_flags->bits.tmz_changed = 1;
+ update_bits->tmz_changed = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
}
if (u->in_transfer_func) {
- update_flags->bits.in_transfer_func_change = 1;
+ update_bits->in_transfer_func_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->input_csc_color_matrix) {
- update_flags->bits.input_csc_change = 1;
+ update_bits->input_csc_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
if (u->cursor_csc_color_matrix) {
- update_flags->bits.cursor_csc_color_matrix_change = 1;
+ update_bits->cursor_csc_color_matrix_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
if (u->coeff_reduction_factor) {
- update_flags->bits.coeff_reduction_change = 1;
+ update_bits->coeff_reduction_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
if (u->gamut_remap_matrix) {
- update_flags->bits.gamut_remap_change = 1;
+ update_bits->gamut_remap_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
- if (u->blend_tf || (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) {
- update_flags->bits.gamma_change = 1;
+ if ((u->cm && u->cm->flags.bits.blend_enable) ||
+ (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) {
+ update_bits->gamma_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
- if (u->lut3d_func || u->func_shaper) {
- update_flags->bits.lut_3d = 1;
+ if (u->cm && (u->cm->flags.bits.lut3d_enable || u->cm->flags.bits.shaper_enable)) {
+ update_bits->lut_3d = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
+ if (u->cm && u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable &&
+ u->cm->flags.bits.lut3d_enable && u->surface->cm.flags.bits.lut3d_enable) {
+ /* Toggling 3DLUT loading between DMA and Host is illegal */
+ BREAK_TO_DEBUGGER();
+ }
+
+ if (u->cm && u->cm->flags.bits.lut3d_enable && !u->cm->flags.bits.lut3d_dma_enable) {
+ /* Host loading 3DLUT requires full update but only stream lock */
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_STREAM);
+ }
+
if (u->hdr_mult.value)
if (u->hdr_mult.value != u->surface->hdr_mult.value) {
// TODO: Should be fast?
- update_flags->bits.hdr_mult = 1;
+ update_bits->hdr_mult = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->sdr_white_level_nits)
if (u->sdr_white_level_nits != u->surface->sdr_white_level_nits) {
// TODO: Should be fast?
- update_flags->bits.sdr_white_level_nits = 1;
+ update_bits->sdr_white_level_nits = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->cm_hist_control) {
- update_flags->bits.cm_hist_change = 1;
+ update_bits->cm_hist_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
- if (u->cm2_params) {
- if (u->cm2_params->component_settings.shaper_3dlut_setting != u->surface->mcm_shaper_3dlut_setting
- || u->cm2_params->component_settings.lut1d_enable != u->surface->mcm_lut1d_enable
- || u->cm2_params->cm2_luts.lut3d_data.lut3d_src != u->surface->mcm_luts.lut3d_data.lut3d_src) {
- update_flags->bits.mcm_transfer_function_enable_change = 1;
+
+ if (u->cm) {
+ const union dc_plane_cm_flags blend_only_flags = {
+ .bits = {
+ .blend_enable = 1,
+ }
+ };
+
+ if (u->cm->flags.bits.shaper_enable != u->surface->cm.flags.bits.shaper_enable
+ || u->cm->flags.bits.blend_enable != u->surface->cm.flags.bits.blend_enable
+ || u->cm->flags.bits.lut3d_enable != u->surface->cm.flags.bits.lut3d_enable
+ || u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable) {
+ update_bits->mcm_transfer_function_enable_change = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
+ }
+
+ if ((u->cm->flags.all != blend_only_flags.all && u->cm->flags.all != 0) ||
+ (u->surface->cm.flags.all != blend_only_flags.all && u->surface->cm.flags.all != 0)) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
}
- if (update_flags->bits.lut_3d &&
- u->surface->mcm_luts.lut3d_data.lut3d_src != DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) {
+ if (update_bits->lut_3d &&
+ !u->surface->cm.flags.bits.lut3d_dma_enable) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (check_config->enable_legacy_fast_update &&
- (update_flags->bits.gamma_change ||
- update_flags->bits.gamut_remap_change ||
- update_flags->bits.input_csc_change ||
- update_flags->bits.cm_hist_change ||
- update_flags->bits.coeff_reduction_change)) {
+ (update_bits->gamma_change ||
+ update_bits->gamut_remap_change ||
+ update_bits->input_csc_change ||
+ update_bits->cm_hist_change ||
+ update_bits->coeff_reduction_change)) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
return overall_type;
@@ -3036,7 +3061,7 @@ static void force_immediate_gsl_plane_flip(struct dc *dc, struct dc_surface_upda
if (has_flip_immediate_plane && surface_count > 1) {
for (i = 0; i < surface_count; i++) {
if (updates[i].surface->flip_immediate)
- updates[i].surface->update_flags.bits.addr_update = 1;
+ updates[i].surface->update_bits.addr_update = 1;
}
}
}
@@ -3191,9 +3216,9 @@ struct surface_update_descriptor dc_check_update_surfaces_for_stream(
struct dc_stream_update *stream_update)
{
if (stream_update)
- stream_update->stream->update_flags.raw = 0;
+ stream_update_flags_clear(&stream_update->stream->update_flags);
for (int i = 0; i < surface_count; i++)
- updates[i].surface->update_flags.raw = 0;
+ dc_pipe_update_bits_clear(&updates[i].surface->update_bits);
return check_update_surfaces_for_stream(check_config, updates, surface_count, stream_update);
}
@@ -3304,24 +3329,55 @@ static void copy_surface_update_to_plane(
sizeof(struct dc_transfer_func_distributed_points));
}
- if (srf_update->cm2_params) {
- surface->mcm_shaper_3dlut_setting = srf_update->cm2_params->component_settings.shaper_3dlut_setting;
- surface->mcm_lut1d_enable = srf_update->cm2_params->component_settings.lut1d_enable;
- surface->mcm_luts = srf_update->cm2_params->cm2_luts;
- }
-
- if (srf_update->func_shaper) {
- memcpy(&surface->in_shaper_func, srf_update->func_shaper,
- sizeof(surface->in_shaper_func));
+ /* Shaper, 3DLUT, 1DLUT */
+ if (srf_update->cm) {
+ struct kref refcount = surface->cm.refcount;
+
+ memcpy(&surface->cm, srf_update->cm, sizeof(surface->cm));
+ surface->cm.refcount = refcount;
+
+#ifndef TRIM_CM2
+ /* Populate mcm_luts from cm for legacy consumers (dml2, hwseq) */
+ surface->mcm_luts.lut1d_func = &surface->cm.blend_func;
+ surface->mcm_luts.shaper = &surface->cm.shaper_func;
+ if (srf_update->cm->flags.bits.lut3d_dma_enable) {
+ surface->mcm_luts.lut3d_data.lut3d_src = DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.addr = surface->cm.lut3d_dma.addr;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.layout =
+ (surface->cm.lut3d_dma.swizzle == CM_LUT_3D_SWIZZLE_LINEAR_RGB) ?
+ DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB :
+ (surface->cm.lut3d_dma.swizzle == CM_LUT_3D_SWIZZLE_LINEAR_BGR) ?
+ DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR :
+ DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.format =
+ (surface->cm.lut3d_dma.format == CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB) ?
+ DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB :
+ (surface->cm.lut3d_dma.format == CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB) ?
+ DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB :
+ DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias =
+ surface->cm.lut3d_dma.bias;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale =
+ surface->cm.lut3d_dma.scale;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.component_order =
+ DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.size = DC_CM2_GPU_MEM_SIZE_TRANSFORMED;
+ surface->mcm_luts.lut3d_data.mpc_3dlut_enable = (srf_update->cm->flags.bits.lut3d_enable != 0);
+ } else {
+ surface->mcm_luts.lut3d_data.lut3d_src = DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM;
+ surface->mcm_luts.lut3d_data.lut3d_func = &surface->cm.lut3d_func;
+ }
- if (surface->mcm_shaper_3dlut_setting >= DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER)
- surface->mcm_luts.shaper = &surface->in_shaper_func;
+ if (srf_update->cm->flags.bits.shaper_enable &&
+ srf_update->cm->flags.bits.lut3d_enable)
+ surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT;
+ else if (srf_update->cm->flags.bits.shaper_enable)
+ surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER;
+ else
+ surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL;
+#endif /* TRIM_CM2 */
}
- if (srf_update->lut3d_func)
- memcpy(&surface->lut3d_func, srf_update->lut3d_func,
- sizeof(surface->lut3d_func));
-
if (srf_update->hdr_mult.value)
surface->hdr_mult =
srf_update->hdr_mult;
@@ -3330,15 +3386,10 @@ static void copy_surface_update_to_plane(
surface->sdr_white_level_nits =
srf_update->sdr_white_level_nits;
- if (srf_update->blend_tf) {
- memcpy(&surface->blend_tf, srf_update->blend_tf,
- sizeof(surface->blend_tf));
-
- if (surface->mcm_lut1d_enable)
- surface->mcm_luts.lut1d_func = &surface->blend_tf;
- }
-
- if (srf_update->cm2_params || srf_update->blend_tf)
+ if (srf_update->cm &&
+ (srf_update->cm->flags.bits.blend_enable ||
+ srf_update->cm->flags.bits.shaper_enable ||
+ srf_update->cm->flags.bits.lut3d_enable))
surface->lut_bank_a = !surface->lut_bank_a;
if (srf_update->input_csc_color_matrix)
@@ -3714,11 +3765,11 @@ static bool update_planes_and_stream_state(struct dc *dc,
if (update_type == UPDATE_TYPE_FULL) {
if (stream_update) {
uint32_t dsc_changed = stream_update->stream->update_flags.bits.dsc_changed;
- stream_update->stream->update_flags.raw = 0xFFFFFFFF;
+ stream_update_flags_set_full(&stream_update->stream->update_flags);
stream_update->stream->update_flags.bits.dsc_changed = dsc_changed;
}
for (i = 0; i < surface_count; i++)
- srf_updates[i].surface->update_flags.raw = 0xFFFFFFFF;
+ dc_pipe_update_bits_set_full(&srf_updates[i].surface->update_bits);
}
if (update_type >= update_surface_trace_level)
@@ -3767,7 +3818,7 @@ static bool update_planes_and_stream_state(struct dc *dc,
if (update_type != UPDATE_TYPE_MED)
continue;
- if (surface->update_flags.bits.position_change) {
+ if (surface->update_bits.position_change) {
for (j = 0; j < dc->res_pool->pipe_count; j++) {
struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
@@ -4205,8 +4256,8 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc,
hwss_add_dc_set_optimized_required(&seq_state, dc, true);
} else {
- if (get_seamless_boot_stream_count(context) == 0)
- hwss_add_prepare_bandwidth(&seq_state, dc, dc->current_state);
+ if (get_seamless_boot_stream_count(context) == 0 && dc->hwss.prepare_bandwidth_sequence)
+ dc->hwss.prepare_bandwidth_sequence(dc, dc->current_state, &seq_state);
hwss_add_link_set_dpms_on(&seq_state, dc->current_state, dpms_pipe_ctx);
}
} else if (pipe_ctx->stream->link->wa_flags.blank_stream_on_ocs_change && stream_update->output_color_space
@@ -4550,14 +4601,23 @@ static void build_dmub_update_dirty_rect(
}
}
-static bool check_address_only_update(union surface_update_flags update_flags)
+/**
+ * dc_check_address_only_update - Check if addr_update is the sole flag set
+ *
+ * @update_bits: The pipe update bits to check
+ *
+ * Determines whether an update contains only an address change with no other
+ * pending updates.
+ *
+ * Return: %true if addr_update is the sole bit set, %false otherwise.
+ */
+bool dc_check_address_only_update(struct pipe_update_bits update_bits)
{
- union surface_update_flags addr_only_update_flags;
- addr_only_update_flags.raw = 0;
- addr_only_update_flags.bits.addr_update = 1;
+ struct pipe_update_bits check = update_bits; /* 1. Copy all flags from input */
- return update_flags.bits.addr_update &&
- !(update_flags.raw & ~addr_only_update_flags.raw);
+ check.addr_update = 0; /* 2. Zero the addr_update bit in the copy */
+ return update_bits.addr_update && /* 3. Check addr_update was set in original */
+ !dc_pipe_update_bits_is_any_set(&check); /* 4. Check no other bits remain in the copy */
}
/**
@@ -4617,7 +4677,7 @@ static void commit_plane_for_stream_offload_fams2_flip(struct dc *dc,
continue;
/* update pipe context for plane */
- if (pipe_ctx->plane_state->update_flags.bits.addr_update)
+ if (pipe_ctx->plane_state->update_bits.addr_update)
dc->hwss.update_plane_addr(dc, pipe_ctx);
}
}
@@ -4655,8 +4715,8 @@ static void commit_planes_for_stream_fast(struct dc *dc,
should_offload_fams2_flip = true;
for (i = 0; i < surface_count; i++) {
if (srf_updates[i].surface &&
- srf_updates[i].surface->update_flags.raw &&
- !check_address_only_update(srf_updates[i].surface->update_flags)) {
+ dc_pipe_update_bits_is_any_set(&srf_updates[i].surface->update_bits) &&
+ !dc_check_address_only_update(srf_updates[i].surface->update_bits)) {
/* more than address update, need to acquire FAMS2 lock */
should_offload_fams2_flip = false;
break;
@@ -4747,7 +4807,7 @@ static void commit_planes_for_stream_fast(struct dc *dc,
* so no need to clear here.
*/
if (top_pipe_to_program->stream)
- top_pipe_to_program->stream->update_flags.raw = 0;
+ stream_update_flags_clear(&top_pipe_to_program->stream->update_flags);
}
static void commit_planes_for_stream(struct dc *dc,
@@ -5073,11 +5133,9 @@ static void commit_planes_for_stream(struct dc *dc,
if (!should_update_pipe_for_plane(context, pipe_ctx, plane_state))
continue;
- if (srf_updates[i].cm2_params &&
- srf_updates[i].cm2_params->cm2_luts.lut3d_data.lut3d_src ==
- DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM &&
- srf_updates[i].cm2_params->component_settings.shaper_3dlut_setting ==
- DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT &&
+ if (srf_updates[i].cm &&
+ srf_updates[i].cm->flags.bits.lut3d_enable &&
+ srf_updates[i].cm->flags.bits.lut3d_dma_enable &&
dc->hwss.trigger_3dlut_dma_load)
dc->hwss.trigger_3dlut_dma_load(dc, pipe_ctx);
@@ -5087,7 +5145,7 @@ static void commit_planes_for_stream(struct dc *dc,
dc->hwss.program_triplebuffer(
dc, pipe_ctx, pipe_ctx->plane_state->triplebuffer_flips);
}
- if (pipe_ctx->plane_state->update_flags.bits.addr_update)
+ if (pipe_ctx->plane_state->update_bits.addr_update)
dc->hwss.update_plane_addr(dc, pipe_ctx);
}
}
@@ -5178,7 +5236,7 @@ static void commit_planes_for_stream(struct dc *dc,
if (pipe_ctx->bottom_pipe || pipe_ctx->next_odm_pipe ||
!pipe_ctx->stream || !should_update_pipe_for_stream(context, pipe_ctx, stream) ||
- !pipe_ctx->plane_state->update_flags.bits.addr_update ||
+ !pipe_ctx->plane_state->update_bits.addr_update ||
pipe_ctx->plane_state->skip_manual_trigger)
continue;
@@ -5617,7 +5675,7 @@ static bool commit_minimal_transition_state(struct dc *dc,
/* force full surface update */
for (i = 0; i < dc->current_state->stream_count; i++) {
for (j = 0; j < (unsigned int)dc->current_state->stream_status[i].plane_count; j++) {
- dc->current_state->stream_status[i].plane_states[j]->update_flags.raw = 0xFFFFFFFF;
+ dc_pipe_update_bits_set_full(&dc->current_state->stream_status[i].plane_states[j]->update_bits);
}
}
@@ -5792,14 +5850,9 @@ static bool full_update_required(
(srf_updates[i].sdr_white_level_nits &&
srf_updates[i].sdr_white_level_nits != srf_updates->surface->sdr_white_level_nits) ||
srf_updates[i].in_transfer_func ||
- srf_updates[i].func_shaper ||
- srf_updates[i].lut3d_func ||
srf_updates[i].surface->force_full_update ||
(srf_updates[i].flip_addr &&
- srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface) ||
- (srf_updates[i].cm2_params &&
- (srf_updates[i].cm2_params->component_settings.shaper_3dlut_setting != srf_updates[i].surface->mcm_shaper_3dlut_setting ||
- srf_updates[i].cm2_params->component_settings.lut1d_enable != srf_updates[i].surface->mcm_lut1d_enable))))
+ srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface)))
return true;
}
@@ -6063,17 +6116,17 @@ static bool update_planes_and_stream_v3(struct dc *dc,
return true;
}
-static void clear_update_flags(struct dc_surface_update *srf_updates,
+static void clear_update_bits(struct dc_surface_update *srf_updates,
int surface_count, struct dc_stream_state *stream)
{
int i;
if (stream)
- stream->update_flags.raw = 0;
+ stream_update_flags_clear(&stream->update_flags);
for (i = 0; i < surface_count; i++)
if (srf_updates[i].surface)
- srf_updates[i].surface->update_flags.raw = 0;
+ dc_pipe_update_bits_clear(&srf_updates[i].surface->update_bits);
}
bool dc_update_planes_and_stream(struct dc *dc,
@@ -6125,7 +6178,7 @@ void dc_commit_updates_for_stream(struct dc *dc,
}
if (ret && dc->ctx->dce_version >= DCN_VERSION_3_2)
- clear_update_flags(srf_updates, surface_count, stream);
+ clear_update_bits(srf_updates, surface_count, stream);
}
uint8_t dc_get_current_stream_count(struct dc *dc)
@@ -7542,7 +7595,7 @@ bool dc_capture_register_software_state(struct dc *dc, struct dc_register_softwa
struct dc_plane_state *plane_state = pipe_ctx->plane_state;
/* MPCC blending tree and mode control - capture actual blend configuration */
- state->mpc.mpcc_mode[i] = (plane_state->blend_tf.type != TF_TYPE_BYPASS) ? 1 : 0;
+ state->mpc.mpcc_mode[i] = (plane_state->cm.blend_func.type != TF_TYPE_BYPASS) ? 1 : 0;
state->mpc.mpcc_alpha_blend_mode[i] = plane_state->per_pixel_alpha ? 1 : 0;
state->mpc.mpcc_alpha_multiplied_mode[i] = plane_state->pre_multiplied_alpha ? 1 : 0;
state->mpc.mpcc_blnd_active_overlap_only[i] = 0; /* Default - no overlap restriction */
@@ -7875,7 +7928,7 @@ struct dc_update_scratch_space {
struct dc_stream_state *stream;
struct dc_stream_update *stream_update;
bool update_v3;
- bool do_clear_update_flags;
+ bool do_clear_update_bits;
enum surface_update_type update_type;
struct dc_state *new_context;
enum update_v3_flow flow;
@@ -7918,8 +7971,8 @@ static bool update_planes_and_stream_cleanup_v2(
const struct dc_update_scratch_space *scratch
)
{
- if (scratch->do_clear_update_flags)
- clear_update_flags(scratch->surface_updates, scratch->surface_count, scratch->stream);
+ if (scratch->do_clear_update_bits)
+ clear_update_bits(scratch->surface_updates, scratch->surface_count, scratch->stream);
return false;
}
@@ -8173,8 +8226,8 @@ static bool update_planes_and_stream_cleanup_v3(
ASSERT(false);
}
- if (scratch->do_clear_update_flags)
- clear_update_flags(scratch->surface_updates, scratch->surface_count, scratch->stream);
+ if (scratch->do_clear_update_bits)
+ clear_update_bits(scratch->surface_updates, scratch->surface_count, scratch->stream);
return false;
}
@@ -8197,7 +8250,7 @@ struct dc_update_scratch_space *dc_update_planes_and_stream_init(
.stream = stream,
.stream_update = stream_update,
.update_v3 = version >= DCN_VERSION_4_01 || version == DCN_VERSION_3_2 || version == DCN_VERSION_3_21,
- .do_clear_update_flags = version >= DCN_VERSION_1_0,
+ .do_clear_update_bits = version >= DCN_VERSION_1_0,
};
return scratch;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
index 88446817a71f..e47c8cf5d036 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
@@ -37,6 +37,7 @@
#include "dchubbub.h"
#include "dccg.h"
#include "abm.h"
+#include "clk_mgr.h"
#include "dcn10/dcn10_hubbub.h"
#include "dce/dmub_hw_lock_mgr.h"
#include "link_service.h"
@@ -1028,20 +1029,20 @@ void hwss_build_fast_sequence(struct dc *dc,
current_mpc_pipe = current_pipe;
while (current_mpc_pipe) {
if (current_mpc_pipe->plane_state) {
- if (dc->hwss.set_flip_control_gsl && current_mpc_pipe->plane_state->update_flags.raw) {
+ if (dc->hwss.set_flip_control_gsl && dc_pipe_update_bits_is_any_set(&current_mpc_pipe->plane_state->update_bits)) {
block_sequence[*num_steps].params.set_flip_control_gsl_params.hubp = current_mpc_pipe->plane_res.hubp;
block_sequence[*num_steps].params.set_flip_control_gsl_params.flip_immediate = current_mpc_pipe->plane_state->flip_immediate;
block_sequence[*num_steps].func = HUBP_SET_FLIP_CONTROL_GSL;
(*num_steps)++;
}
- if (dc->hwss.program_triplebuffer && dc->debug.enable_tri_buf && current_mpc_pipe->plane_state->update_flags.raw) {
+ if (dc->hwss.program_triplebuffer && dc->debug.enable_tri_buf && dc_pipe_update_bits_is_any_set(&current_mpc_pipe->plane_state->update_bits)) {
block_sequence[*num_steps].params.program_triplebuffer_params.dc = dc;
block_sequence[*num_steps].params.program_triplebuffer_params.pipe_ctx = current_mpc_pipe;
block_sequence[*num_steps].params.program_triplebuffer_params.enableTripleBuffer = current_mpc_pipe->plane_state->triplebuffer_flips;
block_sequence[*num_steps].func = HUBP_PROGRAM_TRIPLEBUFFER;
(*num_steps)++;
}
- if (dc->hwss.update_plane_addr && current_mpc_pipe->plane_state->update_flags.bits.addr_update) {
+ if (dc->hwss.update_plane_addr && current_mpc_pipe->plane_state->update_bits.addr_update) {
if (resource_is_pipe_type(current_mpc_pipe, OTG_MASTER) &&
stream_status->mall_stream_config.type == SUBVP_MAIN) {
block_sequence[*num_steps].params.subvp_save_surf_addr.dc_dmub_srv = dc->ctx->dmub_srv;
@@ -1057,7 +1058,7 @@ void hwss_build_fast_sequence(struct dc *dc,
(*num_steps)++;
}
- if (hws->funcs.set_input_transfer_func && current_mpc_pipe->plane_state->update_flags.bits.gamma_change) {
+ if (hws->funcs.set_input_transfer_func && current_mpc_pipe->plane_state->update_bits.gamma_change) {
block_sequence[*num_steps].params.set_input_transfer_func_params.dc = dc;
block_sequence[*num_steps].params.set_input_transfer_func_params.pipe_ctx = current_mpc_pipe;
block_sequence[*num_steps].params.set_input_transfer_func_params.plane_state = current_mpc_pipe->plane_state;
@@ -1066,23 +1067,23 @@ void hwss_build_fast_sequence(struct dc *dc,
}
if (dc->hwss.program_gamut_remap &&
- (current_mpc_pipe->plane_state->update_flags.bits.gamut_remap_change ||
+ (current_mpc_pipe->plane_state->update_bits.gamut_remap_change ||
current_mpc_pipe->stream->update_flags.bits.gamut_remap)) {
block_sequence[*num_steps].params.program_gamut_remap_params.pipe_ctx = current_mpc_pipe;
block_sequence[*num_steps].func = DPP_PROGRAM_GAMUT_REMAP;
(*num_steps)++;
}
- if (current_mpc_pipe->plane_state->update_flags.bits.input_csc_change) {
+ if (current_mpc_pipe->plane_state->update_bits.input_csc_change) {
block_sequence[*num_steps].params.setup_dpp_params.pipe_ctx = current_mpc_pipe;
block_sequence[*num_steps].func = DPP_SETUP_DPP;
(*num_steps)++;
}
- if (current_mpc_pipe->plane_state->update_flags.bits.coeff_reduction_change) {
+ if (current_mpc_pipe->plane_state->update_bits.coeff_reduction_change) {
block_sequence[*num_steps].params.program_bias_and_scale_params.pipe_ctx = current_mpc_pipe;
block_sequence[*num_steps].func = DPP_PROGRAM_BIAS_AND_SCALE;
(*num_steps)++;
}
- if (current_mpc_pipe->plane_state->update_flags.bits.cm_hist_change) {
+ if (current_mpc_pipe->plane_state->update_bits.cm_hist_change) {
block_sequence[*num_steps].params.control_cm_hist_params.dpp
= current_mpc_pipe->plane_res.dpp;
block_sequence[*num_steps].params.control_cm_hist_params.cm_hist_control
@@ -1095,7 +1096,7 @@ void hwss_build_fast_sequence(struct dc *dc,
if (current_mpc_pipe->plane_res.dpp &&
current_mpc_pipe->plane_res.dpp->funcs->set_cursor_matrix &&
- current_mpc_pipe->plane_state->update_flags.bits.cursor_csc_color_matrix_change) {
+ current_mpc_pipe->plane_state->update_bits.cursor_csc_color_matrix_change) {
block_sequence[*num_steps].params.dpp_set_cursor_matrix_params.dpp = current_mpc_pipe->plane_res.dpp;
block_sequence[*num_steps].params.dpp_set_cursor_matrix_params.color_space = current_mpc_pipe->plane_state->color_space;
block_sequence[*num_steps].params.dpp_set_cursor_matrix_params.cursor_csc_color_matrix = &current_mpc_pipe->plane_state->cursor_csc_color_matrix;
@@ -1176,7 +1177,7 @@ void hwss_build_fast_sequence(struct dc *dc,
while (current_mpc_pipe) {
if (!current_mpc_pipe->bottom_pipe && !current_mpc_pipe->next_odm_pipe &&
current_mpc_pipe->stream && current_mpc_pipe->plane_state &&
- current_mpc_pipe->plane_state->update_flags.bits.addr_update &&
+ current_mpc_pipe->plane_state->update_bits.addr_update &&
!current_mpc_pipe->plane_state->skip_manual_trigger) {
if (dc->hwss.program_cursor_offload_now) {
block_sequence[*num_steps].params.program_cursor_update_now_params.dc = dc;
@@ -1668,6 +1669,21 @@ void hwss_execute_sequence(struct dc *dc,
case LINK_SET_DPMS_ON:
hwss_link_set_dpms_on(params);
break;
+ case CLK_MGR_SET_MAX_MEMCLK:
+ hwss_clk_mgr_set_max_memclk(params);
+ break;
+ case CLK_MGR_UPDATE_CLOCKS:
+ hwss_clk_mgr_update_clocks(params);
+ break;
+ case HUBBUB_PROGRAM_WATERMARKS:
+ hwss_hubbub_program_watermarks(params);
+ break;
+ case HUBBUB_PROGRAM_ARBITER:
+ hwss_hubbub_program_arbiter(params);
+ break;
+ case HUBBUB_PROGRAM_COMPBUF_SEGMENTS:
+ hwss_hubbub_program_compbuf_segments(params);
+ break;
default:
ASSERT(false);
break;
@@ -3849,6 +3865,70 @@ void hwss_dsc_set_config_simple(union block_sequence_params *params)
dsc->funcs->dsc_set_config(dsc, dsc_cfg, dsc_optc_cfg);
}
+/*
+ * Clock manager executor functions
+ */
+void hwss_clk_mgr_set_max_memclk(union block_sequence_params *params)
+{
+ struct clk_mgr *clk_mgr = params->clk_mgr_set_max_memclk_params.clk_mgr;
+ unsigned int memclk_mhz = params->clk_mgr_set_max_memclk_params.memclk_mhz;
+
+ if (clk_mgr && clk_mgr->funcs && clk_mgr->funcs->set_max_memclk)
+ clk_mgr->funcs->set_max_memclk(clk_mgr, memclk_mhz);
+}
+
+void hwss_clk_mgr_update_clocks(union block_sequence_params *params)
+{
+ struct clk_mgr *clk_mgr = params->clk_mgr_update_clocks_params.clk_mgr;
+
+ if (clk_mgr && clk_mgr->funcs && clk_mgr->funcs->execute_clk_mgr_block_sequence)
+ clk_mgr->funcs->execute_clk_mgr_block_sequence(clk_mgr);
+}
+
+/*
+ * Hubbub executor functions
+ */
+void hwss_hubbub_program_watermarks(union block_sequence_params *params)
+{
+ struct dc *dc = params->hubbub_program_watermarks_params.dc;
+ struct hubbub *hubbub = params->hubbub_program_watermarks_params.hubbub;
+ union dcn_watermark_set *watermarks = params->hubbub_program_watermarks_params.watermarks;
+ unsigned int refclk_mhz = params->hubbub_program_watermarks_params.refclk_mhz;
+ bool safe_to_lower = params->hubbub_program_watermarks_params.safe_to_lower;
+
+ if (hubbub && hubbub->funcs && hubbub->funcs->program_watermarks) {
+ bool wm_changed = hubbub->funcs->program_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower);
+
+ if (dc && !safe_to_lower)
+ dc->optimized_required |= wm_changed;
+ }
+}
+
+void hwss_hubbub_program_arbiter(union block_sequence_params *params)
+{
+ struct dc *dc = params->hubbub_program_arbiter_params.dc;
+ struct hubbub *hubbub = params->hubbub_program_arbiter_params.hubbub;
+ struct dml2_display_arb_regs *arb_regs = params->hubbub_program_arbiter_params.arb_regs;
+ bool safe_to_lower = params->hubbub_program_arbiter_params.safe_to_lower;
+
+ if (hubbub && hubbub->funcs && hubbub->funcs->program_arbiter) {
+ bool arb_changed = hubbub->funcs->program_arbiter(hubbub, arb_regs, safe_to_lower);
+
+ if (dc && !safe_to_lower)
+ dc->optimized_required |= arb_changed;
+ }
+}
+
+void hwss_hubbub_program_compbuf_segments(union block_sequence_params *params)
+{
+ struct hubbub *hubbub = params->hubbub_program_compbuf_segments_params.hubbub;
+ unsigned int compbuf_size = params->hubbub_program_compbuf_segments_params.compbuf_size;
+ bool safe_to_lower = params->hubbub_program_compbuf_segments_params.safe_to_lower;
+
+ if (hubbub && hubbub->funcs && hubbub->funcs->program_compbuf_segments)
+ hubbub->funcs->program_compbuf_segments(hubbub, compbuf_size, safe_to_lower);
+}
+
void hwss_add_dccg_set_dto_dscclk(struct block_sequence_state *seq_state,
struct dccg *dccg, int inst, int num_slices_h)
{
@@ -4909,6 +4989,9 @@ void hwss_add_hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num(struct block
}
}
+/*
+ * Clock manager helper functions
+ */
void hwss_add_hpo_dp_stream_enc_update_dp_info_packets(struct block_sequence_state *seq_state,
struct pipe_ctx *pipe_ctx)
{
@@ -4919,6 +5002,28 @@ void hwss_add_hpo_dp_stream_enc_update_dp_info_packets(struct block_sequence_sta
}
}
+void hwss_add_clk_mgr_set_max_memclk(struct block_sequence_state *seq_state,
+ struct clk_mgr *clk_mgr,
+ unsigned int memclk_mhz)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = CLK_MGR_SET_MAX_MEMCLK;
+ seq_state->steps[*seq_state->num_steps].params.clk_mgr_set_max_memclk_params.clk_mgr = clk_mgr;
+ seq_state->steps[*seq_state->num_steps].params.clk_mgr_set_max_memclk_params.memclk_mhz = memclk_mhz;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_clk_mgr_update_clocks(struct block_sequence_state *seq_state,
+ struct clk_mgr *clk_mgr)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = CLK_MGR_UPDATE_CLOCKS;
+ seq_state->steps[*seq_state->num_steps].params.clk_mgr_update_clocks_params.clk_mgr = clk_mgr;
+ (*seq_state->num_steps)++;
+ }
+}
+
void hwss_add_stream_enc_update_dp_info_packets_sdp_line_num(struct block_sequence_state *seq_state,
struct pipe_ctx *pipe_ctx)
{
@@ -5022,6 +5127,26 @@ void hwss_add_setup_periodic_interrupt(struct block_sequence_state *seq_state,
(*seq_state->num_steps)++;
}
}
+/*
+ * Hubbub helper functions
+ */
+void hwss_add_hubbub_program_watermarks(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct hubbub *hubbub,
+ union dcn_watermark_set *watermarks,
+ unsigned int refclk_mhz,
+ bool safe_to_lower)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBBUB_PROGRAM_WATERMARKS;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.hubbub = hubbub;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.watermarks = watermarks;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.refclk_mhz = refclk_mhz;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.safe_to_lower = safe_to_lower;
+ (*seq_state->num_steps)++;
+ }
+}
void hwss_add_dp_trace_source_sequence(struct block_sequence_state *seq_state,
struct dc_link *link,
@@ -5035,6 +5160,22 @@ void hwss_add_dp_trace_source_sequence(struct block_sequence_state *seq_state,
}
}
+void hwss_add_hubbub_program_arbiter(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct hubbub *hubbub,
+ struct dml2_display_arb_regs *arb_regs,
+ bool safe_to_lower)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBBUB_PROGRAM_ARBITER;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params.hubbub = hubbub;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params.arb_regs = arb_regs;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params.safe_to_lower = safe_to_lower;
+ (*seq_state->num_steps)++;
+ }
+}
+
void hwss_add_set_dmdata_attributes(struct block_sequence_state *seq_state,
struct pipe_ctx *pipe_ctx)
{
@@ -5119,6 +5260,19 @@ void hwss_add_disable_audio_stream(struct block_sequence_state *seq_state,
(*seq_state->num_steps)++;
}
}
+void hwss_add_hubbub_program_compbuf_segments(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ unsigned int compbuf_size,
+ bool safe_to_lower)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_compbuf_segments_params.hubbub = hubbub;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_compbuf_segments_params.compbuf_size = compbuf_size;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_program_compbuf_segments_params.safe_to_lower = safe_to_lower;
+ (*seq_state->num_steps)++;
+ }
+}
void hwss_add_prepare_bandwidth(struct block_sequence_state *seq_state,
struct dc *dc,
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index 5f6cc1b1f788..b21d41df0fab 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -4412,14 +4412,13 @@ enum dc_status dc_validate_with_context(struct dc *dc,
struct dc_stream_state *unchanged_streams[MAX_PIPES] = { 0 };
struct dc_stream_state *del_streams[MAX_PIPES] = { 0 };
struct dc_stream_state *add_streams[MAX_PIPES] = { 0 };
- int old_stream_count = context->stream_count;
+ unsigned int old_stream_count = context->stream_count;
enum dc_status res = DC_ERROR_UNEXPECTED;
- int unchanged_streams_count = 0;
- int del_streams_count = 0;
- int add_streams_count = 0;
+ unsigned int unchanged_streams_count = 0;
+ unsigned int del_streams_count = 0;
+ unsigned int add_streams_count = 0;
bool found = false;
- int i, j;
- unsigned int k;
+ unsigned int i, j, k;
DC_LOGGER_INIT(dc->ctx->logger);
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c
index 72845fc788f3..88e825a6582c 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c
@@ -45,14 +45,13 @@ void dc_plane_construct(struct dc_context *ctx, struct dc_plane_state *plane_sta
plane_state->in_transfer_func.type = TF_TYPE_BYPASS;
- plane_state->in_shaper_func.type = TF_TYPE_BYPASS;
-
- plane_state->lut3d_func.state.raw = 0;
-
- plane_state->blend_tf.type = TF_TYPE_BYPASS;
-
plane_state->pre_multiplied_alpha = true;
+ /* CM */
+ plane_state->cm.shaper_func.type = TF_TYPE_BYPASS;
+ plane_state->cm.blend_func.type = TF_TYPE_BYPASS;
+ plane_state->cm.lut3d_func.state.raw = 0;
+ plane_state->cm.flags.all = 0;
}
void dc_plane_destruct(struct dc_plane_state *plane_state)
@@ -282,6 +281,39 @@ void dc_3dlut_func_retain(struct dc_3dlut *lut)
kref_get(&lut->refcount);
}
+static void dc_plane_cm_free(struct kref *kref)
+{
+ struct dc_plane_cm *cm = container_of(kref, struct dc_plane_cm, refcount);
+
+ kvfree(cm);
+}
+
+struct dc_plane_cm *dc_plane_cm_create(void)
+{
+ struct dc_plane_cm *cm = kvzalloc(sizeof(*cm), GFP_KERNEL);
+
+ if (cm == NULL)
+ goto alloc_fail;
+
+ kref_init(&cm->refcount);
+
+ return cm;
+
+alloc_fail:
+ return NULL;
+
+}
+
+void dc_plane_cm_release(struct dc_plane_cm *cm)
+{
+ kref_put(&cm->refcount, dc_plane_cm_free);
+}
+
+void dc_plane_cm_retain(struct dc_plane_cm *cm)
+{
+ kref_get(&cm->refcount);
+}
+
void dc_plane_force_dcc_and_tiling_disable(struct dc_plane_state *plane_state,
bool clear_tiling)
{
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 82d02ebbd829..13c1f7cd9d7d 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -65,7 +65,7 @@ struct dcn_dsc_reg_state;
struct dcn_optc_reg_state;
struct dcn_dccg_reg_state;
-#define DC_VER "3.2.384"
+#define DC_VER "3.2.388"
/**
* MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC
@@ -591,7 +591,6 @@ struct dc_config {
bool enable_mipi_converter_optimization;
bool enable_frl;
bool force_hdmi21_frl_enc_enable;
- bool skip_frl_pretraining;
bool use_default_clock_table;
bool force_bios_enable_lttpr;
uint8_t force_bios_fixed_vs;
@@ -1129,8 +1128,6 @@ struct dc_debug_options {
unsigned int force_fclk_khz;
bool enable_tri_buf;
bool ips_disallow_entry;
- bool dmub_offload_enabled;
- bool dmcub_emulation;
bool disable_idle_power_optimizations;
unsigned int mall_size_override;
unsigned int mall_additional_timer_percent;
@@ -1152,7 +1149,6 @@ struct dc_debug_options {
bool validate_dml_output;
bool enable_dmcub_surface_flip;
bool usbc_combo_phy_reset_wa;
- bool force_vrr;
bool force_fva;
int max_frl_rate;
unsigned int force_frl_rate;
@@ -1290,6 +1286,8 @@ struct dc_debug_options {
unsigned int min_deep_sleep_dcfclk_khz;
unsigned int force_odm2to1_for_edp_pixclk_mhz;
bool enable_replay_esd_recovery;
+ uint8_t iommu_mismatch_temp_wka;
+ bool disable_dynamic_expansion_for_test_pattern;
};
@@ -1332,7 +1330,6 @@ struct dc_init_data {
enum dce_environment dce_environment;
struct dmub_offload_funcs *dmub_if;
- struct dc_reg_helper_state *dmub_offload;
struct dc_config flags;
uint64_t log_mask;
@@ -1488,6 +1485,47 @@ struct dc_3dlut {
struct fixed31_32 hdr_multiplier;
union dc_3dlut_state state;
};
+
+/* 3DLUT DMA (Fast Load) params */
+struct dc_3dlut_dma {
+ struct dc_plane_address addr;
+ enum dc_cm_lut_swizzle swizzle;
+ enum dc_cm_lut_pixel_format format;
+ uint16_t bias; /* FP1.5.10 */
+ uint16_t scale; /* FP1.5.10 */
+ enum dc_cm_lut_size size;
+};
+
+/* color manager */
+union dc_plane_cm_flags {
+ unsigned int all;
+ struct {
+ unsigned int shaper_enable : 1;
+ unsigned int lut3d_enable : 1;
+ unsigned int blend_enable : 1;
+ /* whether legacy (lut3d_func) or DMA is valid */
+ unsigned int lut3d_dma_enable : 1;
+#if defined(CONFIG_DRM_AMD_DC_DCN4_2)
+ /* RMCM lut to be used instead of MCM */
+ unsigned int rmcm_enable : 1;
+ unsigned int reserved: 27;
+#else
+ unsigned int reserved: 28;
+#endif
+ } bits;
+};
+
+struct dc_plane_cm {
+ struct kref refcount;
+ struct dc_transfer_func shaper_func;
+ union {
+ struct dc_3dlut lut3d_func;
+ struct dc_3dlut_dma lut3d_dma;
+ };
+ struct dc_transfer_func blend_func;
+ union dc_plane_cm_flags flags;
+};
+
/*
* This structure is filled in by dc_surface_get_status and contains
* the last requested address and the currently active address so the called
@@ -1501,47 +1539,120 @@ struct dc_plane_status {
struct cm_hist cm_hist;
};
-union surface_update_flags {
-
- struct {
- uint32_t addr_update:1;
- /* Medium updates */
- uint32_t dcc_change:1;
- uint32_t color_space_change:1;
- uint32_t horizontal_mirror_change:1;
- uint32_t per_pixel_alpha_change:1;
- uint32_t global_alpha_change:1;
- uint32_t hdr_mult:1;
- uint32_t rotation_change:1;
- uint32_t swizzle_change:1;
- uint32_t scaling_change:1;
- uint32_t position_change:1;
- uint32_t in_transfer_func_change:1;
- uint32_t input_csc_change:1;
- uint32_t coeff_reduction_change:1;
- uint32_t pixel_format_change:1;
- uint32_t plane_size_change:1;
- uint32_t gamut_remap_change:1;
- uint32_t cursor_csc_color_matrix_change:1;
-
- /* Full updates */
- uint32_t new_plane:1;
- uint32_t bpp_change:1;
- uint32_t gamma_change:1;
- uint32_t bandwidth_change:1;
- uint32_t clock_change:1;
- uint32_t stereo_format_change:1;
- uint32_t lut_3d:1;
- uint32_t tmz_changed:1;
- uint32_t mcm_transfer_function_enable_change:1; /* disable or enable MCM transfer func */
- uint32_t full_update:1;
- uint32_t sdr_white_level_nits:1;
- uint32_t cm_hist_change:1;
- } bits;
-
- uint32_t raw;
+struct pipe_update_bits {
+ uint32_t addr_update:1;
+ uint32_t dcc_change:1;
+ uint32_t color_space_change:1;
+ uint32_t horizontal_mirror_change:1;
+ uint32_t per_pixel_alpha_change:1;
+ uint32_t global_alpha_change:1;
+ uint32_t hdr_mult:1;
+ uint32_t rotation_change:1;
+ uint32_t swizzle_change:1;
+ uint32_t scaling_change:1;
+ uint32_t position_change:1;
+ uint32_t in_transfer_func_change:1;
+ uint32_t input_csc_change:1;
+ uint32_t coeff_reduction_change:1;
+ uint32_t pixel_format_change:1;
+ uint32_t plane_size_change:1;
+ uint32_t gamut_remap_change:1;
+ uint32_t cursor_csc_color_matrix_change:1;
+ uint32_t new_plane:1;
+ uint32_t bpp_change:1;
+ uint32_t gamma_change:1;
+ uint32_t bandwidth_change:1;
+ uint32_t clock_change:1;
+ uint32_t stereo_format_change:1;
+ uint32_t lut_3d:1;
+ uint32_t tmz_changed:1;
+ uint32_t mcm_transfer_function_enable_change:1; /* disable or enable MCM transfer func */
+ uint32_t full_update:1;
+ uint32_t sdr_white_level_nits:1;
+ uint32_t cm_hist_change:1;
+ /* NOTE: When adding a new field, also update:
+ * - dc_pipe_update_bits_set_full()
+ * - dc_pipe_update_bits_is_any_set()
+ */
};
+static inline void dc_pipe_update_bits_clear(struct pipe_update_bits *flags)
+{
+ /* memset ensures padding bits are zeroed */
+ memset(flags, 0, sizeof(*flags));
+}
+
+static inline void dc_pipe_update_bits_set_full(struct pipe_update_bits *flags)
+{
+ dc_pipe_update_bits_clear(flags);
+ flags->addr_update = 1;
+ flags->dcc_change = 1;
+ flags->color_space_change = 1;
+ flags->horizontal_mirror_change = 1;
+ flags->per_pixel_alpha_change = 1;
+ flags->global_alpha_change = 1;
+ flags->hdr_mult = 1;
+ flags->rotation_change = 1;
+ flags->swizzle_change = 1;
+ flags->scaling_change = 1;
+ flags->position_change = 1;
+ flags->in_transfer_func_change = 1;
+ flags->input_csc_change = 1;
+ flags->coeff_reduction_change = 1;
+ flags->pixel_format_change = 1;
+ flags->plane_size_change = 1;
+ flags->gamut_remap_change = 1;
+ flags->cursor_csc_color_matrix_change = 1;
+ flags->new_plane = 1;
+ flags->bpp_change = 1;
+ flags->gamma_change = 1;
+ flags->bandwidth_change = 1;
+ flags->clock_change = 1;
+ flags->stereo_format_change = 1;
+ flags->lut_3d = 1;
+ flags->tmz_changed = 1;
+ flags->mcm_transfer_function_enable_change = 1;
+ flags->full_update = 1;
+ flags->sdr_white_level_nits = 1;
+ flags->cm_hist_change = 1;
+}
+
+static inline bool dc_pipe_update_bits_is_any_set(const struct pipe_update_bits *flags)
+{
+ return flags->addr_update ||
+ flags->dcc_change ||
+ flags->color_space_change ||
+ flags->horizontal_mirror_change ||
+ flags->per_pixel_alpha_change ||
+ flags->global_alpha_change ||
+ flags->hdr_mult ||
+ flags->rotation_change ||
+ flags->swizzle_change ||
+ flags->scaling_change ||
+ flags->position_change ||
+ flags->in_transfer_func_change ||
+ flags->input_csc_change ||
+ flags->coeff_reduction_change ||
+ flags->pixel_format_change ||
+ flags->plane_size_change ||
+ flags->gamut_remap_change ||
+ flags->cursor_csc_color_matrix_change ||
+ flags->new_plane ||
+ flags->bpp_change ||
+ flags->gamma_change ||
+ flags->bandwidth_change ||
+ flags->clock_change ||
+ flags->stereo_format_change ||
+ flags->lut_3d ||
+ flags->tmz_changed ||
+ flags->mcm_transfer_function_enable_change ||
+ flags->full_update ||
+ flags->sdr_white_level_nits ||
+ flags->cm_hist_change;
+}
+
+bool dc_check_address_only_update(struct pipe_update_bits update_bits);
+
#define DC_REMOVE_PLANE_POINTERS 1
struct dc_plane_state {
@@ -1566,14 +1677,22 @@ struct dc_plane_state {
struct fixed31_32 hdr_mult;
struct colorspace_transform gamut_remap_matrix;
+ enum dc_color_space color_space;
+
+#ifndef TRIM_CM2
// TODO: No longer used, remove
struct dc_hdr_static_metadata hdr_static_ctx;
- enum dc_color_space color_space;
-
struct dc_3dlut lut3d_func;
struct dc_transfer_func in_shaper_func;
struct dc_transfer_func blend_tf;
+ enum dc_cm2_shaper_3dlut_setting mcm_shaper_3dlut_setting;
+ bool mcm_lut1d_enable;
+ struct dc_cm2_func_luts mcm_luts;
+#endif /* TRIM_CM2 */
+ bool lut_bank_a;
+ enum mpcc_movable_cm_location mcm_location;
+ struct dc_plane_cm cm;
struct dc_transfer_func *gamcor_tf;
enum surface_pixel_format format;
@@ -1590,7 +1709,7 @@ struct dc_plane_state {
bool horizontal_mirror;
unsigned int layer_index;
- union surface_update_flags update_flags;
+ struct pipe_update_bits update_bits;
bool flip_int_enabled;
bool skip_manual_trigger;
@@ -1610,11 +1729,6 @@ struct dc_plane_state {
bool is_statically_allocated;
enum chroma_cositing cositing;
- enum dc_cm2_shaper_3dlut_setting mcm_shaper_3dlut_setting;
- bool mcm_lut1d_enable;
- struct dc_cm2_func_luts mcm_luts;
- bool lut_bank_a;
- enum mpcc_movable_cm_location mcm_location;
struct dc_csc_transform cursor_csc_color_matrix;
bool adaptive_sharpness_en;
int adaptive_sharpness_policy;
@@ -1728,6 +1842,10 @@ struct dc_scratch_space {
* of ddc_pin to know which aux instance is associated with link.
*/
bool no_ddc_pin;
+ /** When set, forces all native I2C communication on this DP connector
+ * to use the I2C-over-AUX protocol instead of native I2C signaling.
+ */
+ bool force_to_use_aux;
enum gpio_ddc_line aux_hw_inst;
enum gpio_ddc_line ddc_hw_inst;
@@ -1978,17 +2096,7 @@ struct dc_surface_update {
const struct dc_csc_transform *input_csc_color_matrix;
const struct fixed31_32 *coeff_reduction_factor;
- const struct dc_transfer_func *func_shaper;
- const struct dc_3dlut *lut3d_func;
- const struct dc_transfer_func *blend_tf;
const struct colorspace_transform *gamut_remap_matrix;
- /*
- * Color Transformations for pre-blend MCM (Shaper, 3DLUT, 1DLUT)
- *
- * change cm2_params.component_settings: Full update
- * change cm2_params.cm2_luts: Fast update
- */
- const struct dc_cm2_parameters *cm2_params;
const struct dc_plane_cm *cm;
const struct dc_csc_transform *cursor_csc_color_matrix;
unsigned int sdr_white_level_nits;
@@ -2034,6 +2142,10 @@ struct dc_3dlut *dc_create_3dlut_func(void);
void dc_3dlut_func_release(struct dc_3dlut *lut);
void dc_3dlut_func_retain(struct dc_3dlut *lut);
+struct dc_plane_cm *dc_plane_cm_create(void);
+void dc_plane_cm_release(struct dc_plane_cm *cm);
+void dc_plane_cm_retain(struct dc_plane_cm *cm);
+
void dc_post_update_surfaces_to_stream(
struct dc *dc);
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
index 0ee5c0c5545c..68ed0e16639d 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
@@ -518,9 +518,6 @@ void dc_dmub_srv_query_caps_cmd(struct dc_dmub_srv *dc_dmub_srv)
{
union dmub_rb_cmd cmd = { 0 };
- if (dc_dmub_srv->ctx->dc->debug.dmcub_emulation)
- return;
-
memset(&cmd, 0, sizeof(cmd));
/* Prepare fw command */
@@ -1302,9 +1299,6 @@ bool dc_dmub_srv_is_hw_pwr_up(struct dc_dmub_srv *dc_dmub_srv, bool wait)
if (!dc_dmub_srv || !dc_dmub_srv->dmub)
return true;
- if (dc_dmub_srv->ctx->dc->debug.dmcub_emulation)
- return true;
-
dc_ctx = dc_dmub_srv->ctx;
if (wait) {
@@ -1345,9 +1339,6 @@ static void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle)
struct dc_dmub_srv *dc_dmub_srv;
union dmub_rb_cmd cmd = {0};
- if (dc->debug.dmcub_emulation)
- return;
-
if (!dc->ctx->dmub_srv || !dc->ctx->dmub_srv->dmub)
return;
@@ -1466,9 +1457,6 @@ static void dc_dmub_srv_exit_low_power_state(const struct dc *dc)
struct dc_dmub_srv *dc_dmub_srv;
uint32_t rcg_exit_count = 0, ips1_exit_count = 0, ips2_exit_count = 0, ips1z8_exit_count = 0;
- if (dc->debug.dmcub_emulation)
- return;
-
if (!dc->ctx->dmub_srv || !dc->ctx->dmub_srv->dmub)
return;
@@ -2137,9 +2125,7 @@ bool dmub_lsdma_init(struct dc_dmub_srv *dc_dmub_srv)
bool dmub_lsdma_send_linear_copy_command(
struct dc_dmub_srv *dc_dmub_srv,
- uint64_t src_addr,
- uint64_t dst_addr,
- uint32_t count
+ struct lsdma_linear_copy_params copy_data
)
{
struct dc_context *dc_ctx = dc_dmub_srv->ctx;
@@ -2154,11 +2140,20 @@ bool dmub_lsdma_send_linear_copy_command(
cmd.cmd_common.header.sub_type = DMUB_CMD__LSDMA_LINEAR_COPY;
wait_type = DM_DMUB_WAIT_TYPE_NO_WAIT;
- lsdma_data->u.linear_copy_data.count = count - 1; // LSDMA controller expects bytes to copy -1
- lsdma_data->u.linear_copy_data.src_lo = src_addr & 0xFFFFFFFF;
- lsdma_data->u.linear_copy_data.src_hi = (src_addr >> 32) & 0xFFFFFFFF;
- lsdma_data->u.linear_copy_data.dst_lo = dst_addr & 0xFFFFFFFF;
- lsdma_data->u.linear_copy_data.dst_hi = (dst_addr >> 32) & 0xFFFFFFFF;
+ lsdma_data->u.linear_copy_data.count = copy_data.count;
+ lsdma_data->u.linear_copy_data.src_lo = copy_data.src_lo;
+ lsdma_data->u.linear_copy_data.src_hi = copy_data.src_hi;
+ lsdma_data->u.linear_copy_data.dst_lo = copy_data.dst_lo;
+ lsdma_data->u.linear_copy_data.dst_hi = copy_data.dst_hi;
+ lsdma_data->u.linear_copy_data.tmz = copy_data.tmz;
+ lsdma_data->u.linear_copy_data.data_format = copy_data.data_format;
+ lsdma_data->u.linear_copy_data.num_type = copy_data.num_type;
+ lsdma_data->u.linear_copy_data.read_compress = copy_data.read_compress;
+ lsdma_data->u.linear_copy_data.write_compress = copy_data.write_compress;
+ lsdma_data->u.linear_copy_data.max_com = copy_data.max_com;
+ lsdma_data->u.linear_copy_data.max_uncom = copy_data.max_uncom;
+ lsdma_data->u.linear_copy_data.cache_policy_src = copy_data.cache_policy_src;
+ lsdma_data->u.linear_copy_data.cache_policy_dst = copy_data.cache_policy_dst;
result = dc_wake_and_execute_dmub_cmd(dc_ctx, &cmd, wait_type);
@@ -2203,6 +2198,12 @@ bool dmub_lsdma_send_linear_sub_window_copy_command(
lsdma_data->u.linear_sub_window_copy_data.rect_y = copy_data.rect_y;
lsdma_data->u.linear_sub_window_copy_data.src_cache_policy = copy_data.src_cache_policy;
lsdma_data->u.linear_sub_window_copy_data.dst_cache_policy = copy_data.dst_cache_policy;
+ lsdma_data->u.linear_sub_window_copy_data.data_format = copy_data.data_format;
+ lsdma_data->u.linear_sub_window_copy_data.num_type = copy_data.num_type;
+ lsdma_data->u.linear_sub_window_copy_data.read_compress = copy_data.read_compress;
+ lsdma_data->u.linear_sub_window_copy_data.write_compress = copy_data.write_compress;
+ lsdma_data->u.linear_sub_window_copy_data.max_com = copy_data.max_com;
+ lsdma_data->u.linear_sub_window_copy_data.max_uncom = copy_data.max_uncom;
result = dc_wake_and_execute_dmub_cmd(dc_ctx, &cmd, wait_type);
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
index ebcaf49e5961..8bdaac0b0f98 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
@@ -37,17 +37,8 @@ struct dc_crtc_timing;
struct dc_state;
struct dc_surface_update;
-struct dc_reg_helper_state {
- bool gather_in_progress;
- uint32_t same_addr_count;
- bool should_burst_write;
- union dmub_rb_cmd cmd_data;
- unsigned int reg_seq_count;
-};
-
struct dc_dmub_srv {
struct dmub_srv *dmub;
- struct dc_reg_helper_state reg_helper_offload;
struct dc_context *ctx;
void *dm;
@@ -212,11 +203,31 @@ void dc_dmub_srv_fams2_passthrough_flip(
int surface_count);
bool dmub_lsdma_init(struct dc_dmub_srv *dc_dmub_srv);
+
+struct lsdma_linear_copy_params {
+ uint32_t src_lo;
+ uint32_t src_hi;
+
+ uint32_t dst_lo;
+ uint32_t dst_hi;
+
+ uint32_t count : 30;
+ uint32_t read_compress : 2;
+
+ uint32_t tmz : 4;
+ uint32_t cache_policy_src : 3;
+ uint32_t cache_policy_dst : 3;
+ uint32_t data_format : 6;
+ uint32_t num_type : 3;
+ uint32_t write_compress : 2;
+ uint32_t max_com : 2;
+ uint32_t max_uncom : 1;
+ uint32_t reserved0 : 8;
+};
+
bool dmub_lsdma_send_linear_copy_command(
struct dc_dmub_srv *dc_dmub_srv,
- uint64_t src_addr,
- uint64_t dst_addr,
- uint32_t count);
+ struct lsdma_linear_copy_params copy_data);
struct lsdma_linear_sub_window_copy_params {
uint32_t src_lo;
@@ -244,7 +255,13 @@ struct lsdma_linear_sub_window_copy_params {
uint32_t element_size : 3;
uint32_t src_cache_policy : 3;
uint32_t dst_cache_policy : 3;
- uint32_t padding : 19;
+ uint32_t data_format : 6;
+ uint32_t num_type : 3;
+ uint32_t read_compress : 2;
+ uint32_t write_compress : 2;
+ uint32_t max_com : 2;
+ uint32_t max_uncom : 1;
+ uint32_t reserved0 : 3;
};
bool dmub_lsdma_send_linear_sub_window_copy_command(
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h
index fbef0dc743ff..d0ba9ad67a3e 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h
@@ -1146,6 +1146,7 @@ struct edp_psr_info {
union edp_psr_dpcd_caps psr_dpcd_caps;
uint8_t psr2_su_y_granularity_cap;
uint8_t force_psrsu_cap;
+ uint8_t psr_active_vtotal_control_cap;
};
struct replay_info {
diff --git a/drivers/gpu/drm/amd/display/dc/dc_helper.c b/drivers/gpu/drm/amd/display/dc/dc_helper.c
index 0e0165764a57..cc7fea613d9e 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_helper.c
@@ -39,53 +39,6 @@
#define DC_LOGGER \
ctx->logger
-static inline void submit_dmub_read_modify_write(
- struct dc_reg_helper_state *offload,
- const struct dc_context *ctx)
-{
- struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write;
-
- offload->should_burst_write =
- (offload->same_addr_count == (DMUB_READ_MODIFY_WRITE_SEQ__MAX - 1));
- cmd_buf->header.payload_bytes =
- sizeof(struct dmub_cmd_read_modify_write_sequence) * offload->reg_seq_count;
-
- dc_wake_and_execute_dmub_cmd(ctx, &offload->cmd_data, DM_DMUB_WAIT_TYPE_NO_WAIT);
-
- memset(cmd_buf, 0, sizeof(*cmd_buf));
-
- offload->reg_seq_count = 0;
- offload->same_addr_count = 0;
-}
-
-static inline void submit_dmub_burst_write(
- struct dc_reg_helper_state *offload,
- const struct dc_context *ctx)
-{
- struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write;
-
- cmd_buf->header.payload_bytes =
- sizeof(uint32_t) * offload->reg_seq_count;
-
- dc_wake_and_execute_dmub_cmd(ctx, &offload->cmd_data, DM_DMUB_WAIT_TYPE_NO_WAIT);
-
- memset(cmd_buf, 0, sizeof(*cmd_buf));
-
- offload->reg_seq_count = 0;
-}
-
-static inline void submit_dmub_reg_wait(
- struct dc_reg_helper_state *offload,
- const struct dc_context *ctx)
-{
- struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait;
-
- dc_wake_and_execute_dmub_cmd(ctx, &offload->cmd_data, DM_DMUB_WAIT_TYPE_NO_WAIT);
-
- memset(cmd_buf, 0, sizeof(*cmd_buf));
- offload->reg_seq_count = 0;
-}
-
struct dc_reg_value_masks {
uint32_t value;
uint32_t mask;
@@ -127,98 +80,6 @@ static void set_reg_field_values(struct dc_reg_value_masks *field_value_mask,
}
}
-static void dmub_flush_buffer_execute(
- struct dc_reg_helper_state *offload,
- const struct dc_context *ctx)
-{
- submit_dmub_read_modify_write(offload, ctx);
-}
-
-static void dmub_flush_burst_write_buffer_execute(
- struct dc_reg_helper_state *offload,
- const struct dc_context *ctx)
-{
- submit_dmub_burst_write(offload, ctx);
-}
-
-static bool dmub_reg_value_burst_set_pack(const struct dc_context *ctx, uint32_t addr,
- uint32_t reg_val)
-{
- struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
- struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write;
-
- /* flush command if buffer is full */
- if (offload->reg_seq_count == DMUB_BURST_WRITE_VALUES__MAX)
- dmub_flush_burst_write_buffer_execute(offload, ctx);
-
- if (offload->cmd_data.cmd_common.header.type == DMUB_CMD__REG_SEQ_BURST_WRITE &&
- addr != cmd_buf->addr) {
- dmub_flush_burst_write_buffer_execute(offload, ctx);
- return false;
- }
-
- cmd_buf->header.type = DMUB_CMD__REG_SEQ_BURST_WRITE;
- cmd_buf->header.sub_type = 0;
- cmd_buf->addr = addr;
- cmd_buf->write_values[offload->reg_seq_count] = reg_val;
- offload->reg_seq_count++;
-
- return true;
-}
-
-static uint32_t dmub_reg_value_pack(const struct dc_context *ctx, uint32_t addr,
- struct dc_reg_value_masks *field_value_mask)
-{
- struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
- struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write;
- struct dmub_cmd_read_modify_write_sequence *seq;
-
- /* flush command if buffer is full */
- if (offload->cmd_data.cmd_common.header.type != DMUB_CMD__REG_SEQ_BURST_WRITE &&
- offload->reg_seq_count == DMUB_READ_MODIFY_WRITE_SEQ__MAX)
- dmub_flush_buffer_execute(offload, ctx);
-
- if (offload->should_burst_write) {
- if (dmub_reg_value_burst_set_pack(ctx, addr, field_value_mask->value))
- return field_value_mask->value;
- else
- offload->should_burst_write = false;
- }
-
- /* pack commands */
- cmd_buf->header.type = DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE;
- cmd_buf->header.sub_type = 0;
- seq = &cmd_buf->seq[offload->reg_seq_count];
-
- if (offload->reg_seq_count) {
- if (cmd_buf->seq[offload->reg_seq_count - 1].addr == addr)
- offload->same_addr_count++;
- else
- offload->same_addr_count = 0;
- }
-
- seq->addr = addr;
- seq->modify_mask = field_value_mask->mask;
- seq->modify_value = field_value_mask->value;
- offload->reg_seq_count++;
-
- return field_value_mask->value;
-}
-
-static void dmub_reg_wait_done_pack(const struct dc_context *ctx, uint32_t addr,
- uint32_t mask, uint32_t shift, uint32_t condition_value, uint32_t time_out_us)
-{
- struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
- struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait;
-
- cmd_buf->header.type = DMUB_CMD__REG_REG_WAIT;
- cmd_buf->header.sub_type = 0;
- cmd_buf->reg_wait.addr = addr;
- cmd_buf->reg_wait.condition_field_value = mask & (condition_value << shift);
- cmd_buf->reg_wait.mask = mask;
- cmd_buf->reg_wait.time_out_us = time_out_us;
-}
-
uint32_t generic_reg_update_ex(const struct dc_context *ctx,
uint32_t addr, int n,
uint8_t shift1, uint32_t mask1, uint32_t field_value1,
@@ -235,11 +96,6 @@ uint32_t generic_reg_update_ex(const struct dc_context *ctx,
va_end(ap);
- if (ctx->dmub_srv &&
- ctx->dmub_srv->reg_helper_offload.gather_in_progress)
- return dmub_reg_value_pack(ctx, addr, &field_value_mask);
- /* todo: return void so we can decouple code running in driver from register states */
-
/* mmio write directly */
reg_val = dm_read_reg(ctx, addr);
reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value;
@@ -265,12 +121,6 @@ uint32_t generic_reg_set_ex(const struct dc_context *ctx,
/* mmio write directly */
reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value;
- if (ctx->dmub_srv &&
- ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
- return dmub_reg_value_burst_set_pack(ctx, addr, reg_val);
- /* todo: return void so we can decouple code running in driver from register states */
- }
-
dm_write_reg(ctx, addr, reg_val);
return reg_val;
}
@@ -434,13 +284,6 @@ void generic_reg_wait(const struct dc_context *ctx,
uint32_t reg_val;
unsigned int i;
- if (ctx->dmub_srv &&
- ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
- dmub_reg_wait_done_pack(ctx, addr, mask, shift, condition_value,
- delay_between_poll_us * time_out_num_tries);
- return;
- }
-
/*
* Something is terribly wrong if time out is > 3000ms.
* 3000ms is the maximum time needed for SMU to pass values back.
@@ -491,12 +334,6 @@ uint32_t generic_read_indirect_reg(const struct dc_context *ctx,
{
uint32_t value = 0;
- // when reg read, there should not be any offload.
- if (ctx->dmub_srv &&
- ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
- ASSERT(false);
- }
-
dm_write_reg(ctx, addr_index, index);
value = dm_read_reg(ctx, addr_data);
@@ -624,69 +461,6 @@ uint32_t generic_indirect_reg_get_sync(const struct dc_context *ctx,
return value;
}
-void reg_sequence_start_gather(const struct dc_context *ctx)
-{
- /* if reg sequence is supported and enabled, set flag to
- * indicate we want to have REG_SET, REG_UPDATE macro build
- * reg sequence command buffer rather than MMIO directly.
- */
-
- if (ctx->dmub_srv && ctx->dc->debug.dmub_offload_enabled) {
- struct dc_reg_helper_state *offload =
- &ctx->dmub_srv->reg_helper_offload;
-
- /* caller sequence mismatch. need to debug caller. offload will not work!!! */
- ASSERT(!offload->gather_in_progress);
-
- offload->gather_in_progress = true;
- }
-}
-
-void reg_sequence_start_execute(const struct dc_context *ctx)
-{
- struct dc_reg_helper_state *offload;
-
- if (!ctx->dmub_srv)
- return;
-
- offload = &ctx->dmub_srv->reg_helper_offload;
-
- if (offload && offload->gather_in_progress) {
- offload->gather_in_progress = false;
- offload->should_burst_write = false;
- switch (offload->cmd_data.cmd_common.header.type) {
- case DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE:
- submit_dmub_read_modify_write(offload, ctx);
- break;
- case DMUB_CMD__REG_REG_WAIT:
- submit_dmub_reg_wait(offload, ctx);
- break;
- case DMUB_CMD__REG_SEQ_BURST_WRITE:
- submit_dmub_burst_write(offload, ctx);
- break;
- default:
- return;
- }
- }
-}
-
-void reg_sequence_wait_done(const struct dc_context *ctx)
-{
- /* callback to DM to poll for last submission done*/
- struct dc_reg_helper_state *offload;
-
- if (!ctx->dmub_srv)
- return;
-
- offload = &ctx->dmub_srv->reg_helper_offload;
-
- if (offload &&
- ctx->dc->debug.dmub_offload_enabled &&
- !ctx->dc->debug.dmcub_emulation) {
- dc_dmub_srv_wait_for_idle(ctx->dmub_srv, DM_DMUB_WAIT_TYPE_WAIT, NULL);
- }
-}
-
char *dce_version_to_string(const int version)
{
switch (version) {
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index 4154cd059562..8b164edc9c51 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -128,6 +128,35 @@ union stream_update_flags {
uint32_t raw;
};
+static inline void stream_update_flags_clear(union stream_update_flags *flags)
+{
+ flags->raw = 0;
+}
+
+static inline void stream_update_flags_set_full(union stream_update_flags *flags)
+{
+ stream_update_flags_clear(flags);
+ flags->bits.scaling = 1;
+ flags->bits.out_tf = 1;
+ flags->bits.out_csc = 1;
+ flags->bits.abm_level = 1;
+ flags->bits.dpms_off = 1;
+ flags->bits.gamut_remap = 1;
+ flags->bits.wb_update = 1;
+ flags->bits.dsc_changed = 1;
+ flags->bits.mst_bw = 1;
+ flags->bits.crtc_timing_adjust = 1;
+ flags->bits.fams_changed = 1;
+ flags->bits.scaler_sharpener = 1;
+ flags->bits.sharpening_required = 1;
+ flags->bits.cursor_attr = 1;
+ flags->bits.cursor_pos = 1;
+ flags->bits.periodic_interrupt = 1;
+ flags->bits.info_frame = 1;
+ flags->bits.dmdata = 1;
+ flags->bits.dither = 1;
+}
+
struct test_pattern {
enum dp_test_pattern type;
enum dp_test_pattern_color_space color_space;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h
index 4ed1efa17270..90dd1ae7e953 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_types.h
@@ -183,6 +183,7 @@ struct dc_panel_patch {
unsigned int force_frl;
unsigned int vsdb_rcc_wa;
unsigned int delay_hdmi_link_training;
+ unsigned int skip_frl_pre_training;
unsigned int skip_avmute;
unsigned int skip_audio_sab_check;
unsigned int mst_start_top_delay;
@@ -1397,6 +1398,39 @@ enum dc_hpd_enable_select {
HPD_EN_FOR_SECONDARY_EDP_ONLY,
};
+enum dc_cm_lut_swizzle {
+ CM_LUT_3D_SWIZZLE_LINEAR_RGB,
+ CM_LUT_3D_SWIZZLE_LINEAR_BGR,
+ CM_LUT_1D_PACKED_LINEAR
+};
+
+enum dc_cm_lut_pixel_format {
+ CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB,
+#if defined(CONFIG_DRM_AMD_DC_DCN4_2)
+ CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12MSB,
+#endif
+ CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB,
+#if defined(CONFIG_DRM_AMD_DC_DCN4_2)
+ CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12LSB,
+#endif
+ CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10,
+#if defined(CONFIG_DRM_AMD_DC_DCN4_2)
+ CM_LUT_PIXEL_FORMAT_BGRA16161616_FLOAT_FP1_5_10
+#endif
+};
+
+enum dc_cm_lut_size {
+ CM_LUT_SIZE_NONE,
+ CM_LUT_SIZE_999,
+ CM_LUT_SIZE_171717,
+#if defined(CONFIG_DRM_AMD_DC_DCN4_2)
+ CM_LUT_SIZE_333333,
+ CM_LUT_SIZE_454545,
+ CM_LUT_SIZE_656565,
+#endif
+};
+
+#ifndef TRIM_CM2
enum dc_cm2_shaper_3dlut_setting {
DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL,
DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER,
@@ -1421,6 +1455,16 @@ enum dc_cm2_gpu_mem_format {
DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10
};
+enum dc_cm2_gpu_mem_size {
+ DC_CM2_GPU_MEM_SIZE_171717,
+ DC_CM2_GPU_MEM_SIZE_333333,
+ DC_CM2_GPU_MEM_SIZE_454545,
+ DC_CM2_GPU_MEM_SIZE_656565,
+ DC_CM2_GPU_MEM_SIZE_TRANSFORMED,
+};
+#endif /* TRIM_CM2 */
+
+#ifndef TRIM_CM2
struct dc_cm2_gpu_mem_format_parameters {
enum dc_cm2_gpu_mem_format format;
union {
@@ -1432,14 +1476,6 @@ struct dc_cm2_gpu_mem_format_parameters {
};
};
-enum dc_cm2_gpu_mem_size {
- DC_CM2_GPU_MEM_SIZE_171717,
- DC_CM2_GPU_MEM_SIZE_333333,
- DC_CM2_GPU_MEM_SIZE_454545,
- DC_CM2_GPU_MEM_SIZE_656565,
- DC_CM2_GPU_MEM_SIZE_TRANSFORMED,
-};
-
struct dc_cm2_gpu_mem_parameters {
struct dc_plane_address addr;
enum dc_cm2_gpu_mem_layout layout;
@@ -1448,17 +1484,16 @@ struct dc_cm2_gpu_mem_parameters {
enum dc_cm2_gpu_mem_size size;
uint16_t bit_depth;
};
+#endif /* TRIM_CM2 */
+#ifndef TRIM_CM2
enum dc_cm2_transfer_func_source {
DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM,
DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM
};
+#endif /* TRIM_CM2 */
-struct dc_cm2_component_settings {
- enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting;
- bool lut1d_enable;
-};
-
+#ifndef TRIM_CM2
/*
* All pointers in this struct must remain valid for as long as the 3DLUTs are used
*/
@@ -1478,11 +1513,7 @@ struct dc_cm2_func_luts {
} lut3d_data;
const struct dc_transfer_func *lut1d_func;
};
-
-struct dc_cm2_parameters {
- struct dc_cm2_component_settings component_settings;
- struct dc_cm2_func_luts cm2_luts;
-};
+#endif /* TRIM_CM2 */
enum mall_stream_type {
SUBVP_NONE, // subvp not in use
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c
index e57242f8bc12..616a896f0782 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c
@@ -77,13 +77,26 @@ void dccg42_otg_drop_pixel(struct dccg *dccg,
}
}
-void dccg42_enable_global_fgcg(struct dccg *dccg, bool value)
+void dccg42_enable_global_fgcg(struct dccg *dccg, bool enable)
{
struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
- if (dccg->ctx->dc->debug.disable_clock_gate)
- value = false;
- REG_UPDATE(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, !value);
+ /* Temporary workaround for IOMMU mismatch issue.
+ * Fine grain control via bit2 of debug flag.
+ */
+ if (dccg->ctx->dc->debug.disable_clock_gate || (dccg->ctx->dc->debug.iommu_mismatch_temp_wka & 0x4))
+ enable = false;
+
+ REG_UPDATE(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, !enable);
+}
+
+bool dccg42_get_global_fgcg_status(struct dccg *dccg)
+{
+ struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
+ uint32_t disabled = 0;
+
+ REG_GET(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, &disabled);
+ return disabled & 0x1;
}
void dccg42_set_physymclk(
@@ -265,40 +278,35 @@ void dccg42_trigger_dio_fifo_resync(struct dccg *dccg)
static void dccg42_init(struct dccg *dccg)
{
- int otg_inst;
- struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
+ unsigned int i;
+ struct resource_pool *res_pool = dccg->ctx->dc->res_pool;
/* Set HPO stream encoder to use refclk to avoid case where PHY is
* disabled and SYMCLK32 for HPO SE is sourced from PHYD32CLK which
* will cause DCN to hang.
*/
- for (otg_inst = 0; otg_inst < 4; otg_inst++)
- dccg35_disable_symclk32_se(dccg, otg_inst);
+ for (i = 0; i < res_pool->hpo_dp_stream_enc_count; i++)
+ dccg35_disable_symclk32_se(dccg, i);
if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) {
- dccg401_disable_symclk32_le(dccg, 0);
- dccg401_disable_symclk32_le(dccg, 1);
- dccg401_disable_symclk32_le(dccg, 2);
- dccg401_disable_symclk32_le(dccg, 3);
+ for (i = 0; i < res_pool->hpo_dp_link_enc_count; i++)
+ dccg401_disable_symclk32_le(dccg, i);
}
if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpstream) {
- dccg401_disable_dpstreamclk(dccg, 0);
- dccg401_disable_dpstreamclk(dccg, 1);
- dccg401_disable_dpstreamclk(dccg, 2);
- dccg401_disable_dpstreamclk(dccg, 3);
- }
- if (!dccg->ctx->dc->debug.root_clock_optimization.bits.physymclk) {
- REG_UPDATE_5(DCCG_GATE_DISABLE_CNTL2,
- PHYASYMCLK_ROOT_GATE_DISABLE, 1,
- PHYBSYMCLK_ROOT_GATE_DISABLE, 1,
- PHYCSYMCLK_ROOT_GATE_DISABLE, 1,
- PHYDSYMCLK_ROOT_GATE_DISABLE, 1,
- PHYESYMCLK_ROOT_GATE_DISABLE, 1);
+ for (i = 0; i < res_pool->hpo_dp_stream_enc_count; i++)
+ dccg401_disable_dpstreamclk(dccg, i);
}
+
dccg42_disable_hdmistreamclk(dccg);
if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar)
dccg42_disable_hdmicharclk(dccg, 0);
+
+ if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpp) {
+ for (i = 0; i < res_pool->pipe_count; i++) {
+ dccg35_dpp_root_clock_control(dccg, i, true);
+ }
+ }
}
@@ -340,7 +348,8 @@ static const struct dccg_funcs dccg42_funcs = {
.dccg_root_gate_disable_control = dccg35_root_gate_disable_control,
.dccg_read_reg_state = dccg31_read_reg_state,
.dccg_enable_global_fgcg = dccg42_enable_global_fgcg,
- .allow_clock_gating = dccg2_allow_clock_gating
+ .allow_clock_gating = dccg2_allow_clock_gating,
+ .dccg_get_global_fgcg_status = dccg42_get_global_fgcg_status,
};
struct dccg *dccg42_create(
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h
index a2b17ed11bdb..ebd3cec1a977 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h
@@ -247,6 +247,7 @@ void dccg42_otg_add_pixel(struct dccg *dccg,
void dccg42_otg_drop_pixel(struct dccg *dccg,
uint32_t otg_inst);
void dccg42_enable_global_fgcg(struct dccg *dccg, bool value);
+bool dccg42_get_global_fgcg_status(struct dccg *dccg);
void dccg42_set_physymclk(
struct dccg *dccg,
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c
index 72ad3ee3d6a5..6cb5e8152cf1 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c
@@ -26,6 +26,7 @@
#include "dm_services.h"
#include "core_types.h"
#include "dce_aux.h"
+#include "dce/dce_11_0_d.h"
#include "dce/dce_11_0_sh_mask.h"
#include "dm_event_log.h"
#include "dm_helpers.h"
@@ -529,7 +530,7 @@ static uint32_t dce_aux_configure_timeout(struct ddc_service *ddc,
uint32_t prev_timeout_val = 0;
struct ddc *ddc_pin = ddc->ddc_pin;
- if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin)
+ if (ddc->link->force_to_use_aux)
return dce_aux_configure_timeout_without_ddc_pin(ddc, timeout_in_us);
struct dce_aux *aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en];
@@ -652,7 +653,7 @@ int dce_aux_transfer_raw(struct ddc_service *ddc,
struct aux_payload *payload,
enum aux_return_code_type *operation_result)
{
- if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) {
+ if (ddc->link->force_to_use_aux) {
/* Check whether aux to be processed via dmub or dcn directly */
if (ddc->ctx->dc->debug.enable_dmub_aux_for_legacy_ddc) {
return dce_aux_transfer_dmub_raw(ddc, payload, operation_result);
@@ -795,7 +796,7 @@ int dce_aux_transfer_dmub_raw(struct ddc_service *ddc,
release_engine(aux_engine);
}
- if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) {
+ if (ddc->link->force_to_use_aux) {
struct dce_aux *aux_engine = ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst];
if (!acquire_aux_engine_without_ddc_pin(aux_engine, ddc_pin)) {
@@ -893,7 +894,7 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc,
aux110 = FROM_AUX_ENGINE(aux_engine);
}
- if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) {
+ if (ddc->link->force_to_use_aux) {
aux_engine = ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst];
aux110 = FROM_AUX_ENGINE(aux_engine);
}
diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c
index 9be578ff8c88..140c66081492 100644
--- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c
+++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c
@@ -27,8 +27,6 @@
#include "dce/dce_11_0_d.h"
#include "dce/dce_11_0_sh_mask.h"
-#include "gmc/gmc_8_2_sh_mask.h"
-#include "gmc/gmc_8_2_d.h"
#include "include/logger_interface.h"
diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c
index b265a72eeb70..095869912c09 100644
--- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c
+++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c
@@ -27,8 +27,6 @@
#include "dce/dce_11_0_d.h"
#include "dce/dce_11_0_sh_mask.h"
/* TODO: this needs to be looked at, used by Stella's workaround*/
-#include "gmc/gmc_8_2_d.h"
-#include "gmc/gmc_8_2_sh_mask.h"
#include "include/logger_interface.h"
#include "inc/dce_calcs.h"
diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c
index fe97d3946cab..4b273762f07a 100644
--- a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c
+++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c
@@ -27,8 +27,12 @@
#include "dce/dce_11_2_d.h"
#include "dce/dce_11_2_sh_mask.h"
-#include "gmc/gmc_8_1_sh_mask.h"
-#include "gmc/gmc_8_1_d.h"
+
+#ifndef mmGMCON_LPT_TARGET
+#define mmGMCON_LPT_TARGET 0x0D53
+#define GMCON_LPT_TARGET__STCTRL_LPT_TARGET__SHIFT 0x00000000
+#define GMCON_LPT_TARGET__STCTRL_LPT_TARGET_MASK 0xffffffffL
+#endif
#include "include/logger_interface.h"
diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
index bfd5515c2f4f..66fe7f313ea3 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
@@ -303,6 +303,190 @@ bool cm3_helper_translate_curve_to_hw_format(struct dc_context *ctx,
return true;
}
+/* Linear interpolation of tf_pts entries, where (i >> 4) is the integer tf_pts
+ * index, (i & 0xf) is the 1/16 sub-position.
+ */
+static struct fixed31_32 interp_tf_pts(const struct fixed31_32 *output_tf_channel, int i)
+{
+ struct fixed31_32 in_plus_one, in, value;
+ uint32_t t = i & 0xf;
+
+ in_plus_one = output_tf_channel[(i >> 4) + 1];
+ in = output_tf_channel[i >> 4];
+ value = dc_fixpt_sub(in_plus_one, in);
+ value = dc_fixpt_shr(dc_fixpt_mul_int(value, t), 4);
+ value = dc_fixpt_add(in, value);
+
+ return value;
+}
+
+bool cm3_helper_translate_curve_to_degamma_hw_format(
+ const struct dc_transfer_func *output_tf,
+ struct pwl_params *lut_params)
+{
+ struct curve_points3 *corner_points;
+ struct pwl_result_data *rgb_resulted;
+ struct pwl_result_data *rgb;
+ struct pwl_result_data *rgb_plus_1;
+
+ int32_t region_start, region_end;
+ int32_t i;
+ uint32_t j, k, seg_distr[MAX_REGIONS_NUMBER], increment, start_index, hw_points;
+
+ if (output_tf == NULL || lut_params == NULL || output_tf->type == TF_TYPE_BYPASS)
+ return false;
+
+ corner_points = lut_params->corner_points;
+ rgb_resulted = lut_params->rgb_resulted;
+ hw_points = 0;
+
+ memset(lut_params, 0, sizeof(struct pwl_params));
+ memset(seg_distr, 0, sizeof(seg_distr));
+
+ if (output_tf->tf == TRANSFER_FUNCTION_PQ ||
+ output_tf->tf == TRANSFER_FUNCTION_SRGB) {
+ /* 9 segments
+ * segments are from 2^-9 to 0
+ */
+ const uint8_t SEG_COUNT = 9;
+ seg_distr[0] = 0; // Since we only have one point in darkest region
+ for (k = 1; k < SEG_COUNT; k++)
+ seg_distr[k] = k - 1; // 2^(k-1) points per region; halves as k decreases
+
+ region_start = -SEG_COUNT;
+ region_end = 0;
+ } else {
+ /* 12 segments
+ * segments are from 2^-12 to 2^0
+ * There are less than 256 points, for optimization
+ */
+ const uint8_t SEG_COUNT = 12;
+
+ for (i = 0; i < SEG_COUNT; i++)
+ seg_distr[i] = 4;
+
+ region_start = -SEG_COUNT;
+ region_end = 0;
+ }
+
+ for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++)
+ seg_distr[i] = -1;
+
+ for (k = 0; k < MAX_REGIONS_NUMBER; k++) {
+ if (seg_distr[k] != -1)
+ hw_points += (1 << seg_distr[k]);
+ }
+
+ j = 0;
+ for (k = 0; k < (region_end - region_start); k++) {
+ increment = (NUMBER_SW_SEGMENTS << 4) / (1 << seg_distr[k]);
+ start_index = (region_start + k + MAX_LOW_POINT) *
+ NUMBER_SW_SEGMENTS;
+ for (i = (start_index << 4);
+ i < (start_index << 4) + (NUMBER_SW_SEGMENTS << 4);
+ i += increment) {
+ if (j == hw_points - 1)
+ break;
+ if ((i >> 4) + 1 >= TRANSFER_FUNC_POINTS)
+ return false;
+
+ rgb_resulted[j].red = interp_tf_pts(output_tf->tf_pts.red, i);
+ rgb_resulted[j].green = interp_tf_pts(output_tf->tf_pts.green, i);
+ rgb_resulted[j].blue = interp_tf_pts(output_tf->tf_pts.blue, i);
+ j++;
+ }
+ }
+
+ /* last point */
+ start_index = (region_end + MAX_LOW_POINT) * NUMBER_SW_SEGMENTS;
+ rgb_resulted[hw_points - 1].red = output_tf->tf_pts.red[start_index];
+ rgb_resulted[hw_points - 1].green = output_tf->tf_pts.green[start_index];
+ rgb_resulted[hw_points - 1].blue = output_tf->tf_pts.blue[start_index];
+
+ corner_points[0].red.x = dc_fixpt_pow(dc_fixpt_from_int(2),
+ dc_fixpt_from_int(region_start));
+ corner_points[0].green.x = corner_points[0].red.x;
+ corner_points[0].blue.x = corner_points[0].red.x;
+ corner_points[1].red.x = dc_fixpt_pow(dc_fixpt_from_int(2),
+ dc_fixpt_from_int(region_end));
+ corner_points[1].green.x = corner_points[1].red.x;
+ corner_points[1].blue.x = corner_points[1].red.x;
+
+ corner_points[0].red.y = rgb_resulted[0].red;
+ corner_points[0].green.y = rgb_resulted[0].green;
+ corner_points[0].blue.y = rgb_resulted[0].blue;
+
+ /* see comment above, m_arrPoints[1].y should be the Y value for the
+ * region end (m_numOfHwPoints), not last HW point(m_numOfHwPoints - 1)
+ */
+ corner_points[1].red.y = rgb_resulted[hw_points - 1].red;
+ corner_points[1].green.y = rgb_resulted[hw_points - 1].green;
+ corner_points[1].blue.y = rgb_resulted[hw_points - 1].blue;
+ corner_points[1].red.slope = dc_fixpt_zero;
+ corner_points[1].green.slope = dc_fixpt_zero;
+ corner_points[1].blue.slope = dc_fixpt_zero;
+
+ if (output_tf->tf == TRANSFER_FUNCTION_PQ) {
+ /* for PQ, we want to have a straight line from last HW X point,
+ * and the slope to be such that we hit 1.0 at 10000 nits.
+ */
+ const struct fixed31_32 end_value =
+ dc_fixpt_from_int(125);
+
+ corner_points[1].red.slope = dc_fixpt_div(
+ dc_fixpt_sub(dc_fixpt_one, corner_points[1].red.y),
+ dc_fixpt_sub(end_value, corner_points[1].red.x));
+ corner_points[1].green.slope = dc_fixpt_div(
+ dc_fixpt_sub(dc_fixpt_one, corner_points[1].green.y),
+ dc_fixpt_sub(end_value, corner_points[1].green.x));
+ corner_points[1].blue.slope = dc_fixpt_div(
+ dc_fixpt_sub(dc_fixpt_one, corner_points[1].blue.y),
+ dc_fixpt_sub(end_value, corner_points[1].blue.x));
+ }
+
+ lut_params->hw_points_num = hw_points;
+
+ k = 0;
+ for (i = 1; i < MAX_REGIONS_NUMBER; i++) {
+ if (seg_distr[k] != -1) {
+ lut_params->arr_curve_points[k].segments_num =
+ seg_distr[k];
+ lut_params->arr_curve_points[i].offset =
+ lut_params->arr_curve_points[k].offset + (1 << seg_distr[k]);
+ }
+ k++;
+ }
+
+ if (seg_distr[k] != -1)
+ lut_params->arr_curve_points[k].segments_num = seg_distr[k];
+
+ rgb = rgb_resulted;
+ rgb_plus_1 = rgb_resulted + 1;
+
+ i = 1;
+ while (i != hw_points + 1) {
+ if (dc_fixpt_lt(rgb_plus_1->red, rgb->red))
+ rgb_plus_1->red = rgb->red;
+ if (dc_fixpt_lt(rgb_plus_1->green, rgb->green))
+ rgb_plus_1->green = rgb->green;
+ if (dc_fixpt_lt(rgb_plus_1->blue, rgb->blue))
+ rgb_plus_1->blue = rgb->blue;
+
+ rgb->delta_red = dc_fixpt_sub(rgb_plus_1->red, rgb->red);
+ rgb->delta_green = dc_fixpt_sub(rgb_plus_1->green, rgb->green);
+ rgb->delta_blue = dc_fixpt_sub(rgb_plus_1->blue, rgb->blue);
+
+ ++rgb_plus_1;
+ ++rgb;
+ ++i;
+ }
+ cm3_helper_convert_to_custom_float(rgb_resulted,
+ lut_params->corner_points,
+ hw_points, false);
+
+ return true;
+}
+
bool cm3_helper_convert_to_custom_float(
struct pwl_result_data *rgb_resulted,
struct curve_points3 *corner_points,
diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c
index bcb791d74189..f57f3ba68a02 100644
--- a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c
@@ -516,6 +516,8 @@ void dcn31_link_encoder_construct_minimal(
struct dc_context *ctx,
const struct encoder_feature_support *enc_features,
const struct dcn10_link_enc_registers *link_regs,
+ const struct dcn10_link_enc_shift *link_shift,
+ const struct dcn10_link_enc_mask *link_mask,
enum engine_id eng_id)
{
struct dcn10_link_encoder *enc10 = &enc20->enc10;
@@ -529,6 +531,8 @@ void dcn31_link_encoder_construct_minimal(
enc10->base.features = *enc_features;
enc10->base.transmitter = TRANSMITTER_UNKNOWN;
enc10->link_regs = link_regs;
+ enc10->link_shift = link_shift;
+ enc10->link_mask = link_mask;
enc10->base.output_signals =
SIGNAL_TYPE_DISPLAY_PORT |
diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h
index 3cf587527991..88ab9684e207 100644
--- a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h
+++ b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h
@@ -246,6 +246,8 @@ void dcn31_link_encoder_construct_minimal(
struct dc_context *ctx,
const struct encoder_feature_support *enc_features,
const struct dcn10_link_enc_registers *link_regs,
+ const struct dcn10_link_enc_shift *link_shift,
+ const struct dcn10_link_enc_mask *link_mask,
enum engine_id eng_id);
void dcn31_link_encoder_set_dio_phy_mux(
diff --git a/drivers/gpu/drm/amd/display/dc/dm_services.h b/drivers/gpu/drm/amd/display/dc/dm_services.h
index 8b062b011fc6..2cf4bcb03cb0 100644
--- a/drivers/gpu/drm/amd/display/dc/dm_services.h
+++ b/drivers/gpu/drm/amd/display/dc/dm_services.h
@@ -127,10 +127,6 @@ uint32_t generic_reg_update_ex(const struct dc_context *ctx,
struct dc_dmub_srv *dc_dmub_srv_create(struct dc *dc, struct dmub_srv *dmub);
void dc_dmub_srv_destroy(struct dc_dmub_srv **dmub_srv);
-void reg_sequence_start_gather(const struct dc_context *ctx);
-void reg_sequence_start_execute(const struct dc_context *ctx);
-void reg_sequence_wait_done(const struct dc_context *ctx);
-
#define FD(reg_field) reg_field ## __SHIFT, \
reg_field ## _MASK
diff --git a/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c b/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c
index dcca23d53261..2ad4a2635683 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c
@@ -1237,7 +1237,7 @@ bool dcn_validate_bandwidth(
if (pipe->plane_state) {
struct pipe_ctx *hsplit_pipe = pipe->bottom_pipe;
- pipe->plane_state->update_flags.bits.full_update = 1;
+ pipe->plane_state->update_bits.full_update = 1;
if (v->dpp_per_plane[input_idx] == 2 ||
((pipe->stream->view_format ==
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
index bd14ebea1111..8064c4b3fd25 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c
@@ -5337,7 +5337,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l
for (j = 0; j <= 1; ++j) {
double VMDataOnlyReturnBWPerState;
double HostVMInefficiencyFactor = 1;
- int NextPrefetchModeState = MinPrefetchMode;
+ unsigned int NextPrefetchModeState = MinPrefetchMode;
bool UnboundedRequestEnabledThisState = false;
unsigned int CompressedBufferSizeInkByteThisState = 0;
double dummy;
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
index 2ea5cf37f273..bf2dde26b98b 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
@@ -5421,7 +5421,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_
for (j = 0; j <= 1; ++j) {
double VMDataOnlyReturnBWPerState;
double HostVMInefficiencyFactor = 1;
- int NextPrefetchModeState = MinPrefetchMode;
+ unsigned int NextPrefetchModeState = MinPrefetchMode;
bool UnboundedRequestEnabledThisState = false;
unsigned int CompressedBufferSizeInkByteThisState = 0;
double dummy;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c
index de40d7bae252..11fc0b1cd152 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c
@@ -118,16 +118,16 @@ static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_sta
context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.dispclk_khz;
if (in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values > 1) {
context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz =
- in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values] * 1000;
+ in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values - 1];
} else {
- context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[0] * 1000;
+ context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[0];
}
if (in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values > 1) {
context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz =
- in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values] * 1000;
+ in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values - 1];
} else {
- context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[0] * 1000;
+ context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[0];
}
/* get global mall allocation */
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h
index ce4025591b87..60ef56419846 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h
@@ -75,7 +75,7 @@ static const struct dml2_soc_bb dml2_socbb_dcn42b = {
.clk_values_khz = {2},
},
.uclk = {
- .clk_values_khz = {400000},
+ .clk_values_khz = {2400000},
.num_clk_values = 1,
},
.fclk = {
@@ -224,4 +224,42 @@ static const struct dml2_soc_bb dml2_socbb_dcn42b = {
.max_fclk_for_uclk_dpm_khz = 2200 * 1000,
};
+static const struct dml2_ip_capabilities dml2_dcn42b_max_ip_caps = {
+ .pipe_count = 4,
+ .otg_count = 3,
+ .num_dsc = 3,
+ .max_num_dp2p0_streams = 3,
+ .max_num_hdmi_frl_outputs = 0,
+ .max_num_dp2p0_outputs = 2,
+ .rob_buffer_size_kbytes = 64,
+ .config_return_buffer_size_in_kbytes = 1792,
+ .config_return_buffer_segment_size_in_kbytes = 64,
+ .meta_fifo_size_in_kentries = 32,
+ .compressed_buffer_segment_size_in_kbytes = 64,
+ .cursor_buffer_size = 24,
+ .max_flip_time_us = 110,
+ .max_flip_time_lines = 50,
+ .hostvm_mode = 0,
+ .subvp_drr_scheduling_margin_us = 100,
+ .subvp_prefetch_end_to_mall_start_us = 15,
+ .subvp_fw_processing_delay = 15,
+ .max_vactive_det_fill_delay_us = 400,
+
+ .fams2 = {
+ .max_allow_delay_us = 100 * 1000,
+ .scheduling_delay_us = 550,
+ .vertical_interrupt_ack_delay_us = 40,
+ .allow_programming_delay_us = 18,
+ .min_allow_width_us = 20,
+ .subvp_df_throttle_delay_us = 100,
+ .subvp_programming_delay_us = 200,
+ .subvp_prefetch_to_mall_delay_us = 18,
+ .drr_programming_delay_us = 35,
+
+ .lock_timeout_us = 5000,
+ .recovery_timeout_us = 5000,
+ .flip_programming_delay_us = 300,
+ },
+};
+
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h
index 6152155d6073..672b96a3da74 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h
@@ -71,8 +71,21 @@ enum dml2_qos_param_type {
dml2_qos_param_type_dcn4x
};
+//Indicies mapped to DPM level
+// Unpopulated indicies should fallback to the global derate value.
+struct dml2_soc_derate_values_per_dpm {
+ unsigned int dram_derate_percent_pixel[DML_MAX_CLK_TABLE_SIZE];
+ unsigned int fclk_derate_percent[DML_MAX_CLK_TABLE_SIZE];
+ unsigned int dcfclk_derate_percent[DML_MAX_CLK_TABLE_SIZE];
+};
+
+struct dml2_soc_derates_per_dpm {
+ struct dml2_soc_derate_values_per_dpm system_active_derates_per_dpm;
+};
+
struct dml2_soc_qos_parameters {
struct dml2_soc_derates derate_table;
+ struct dml2_soc_derates_per_dpm derate_table_per_dpm;
struct {
unsigned int base_latency_us;
unsigned int scaling_factor_us;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
index f338e733318e..51a66e1be7a1 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
@@ -2701,7 +2701,8 @@ static double dml_get_return_bandwidth_available(
bool is_hvm_only,
double dcfclk_mhz,
double fclk_mhz,
- double dram_bw_mbps)
+ double dram_bw_mbps,
+ unsigned int uclk_dpm_level)
{
double return_bw_mbps = 0.;
double ideal_sdp_bandwidth = (double)soc->return_bus_width_bytes * dcfclk_mhz;
@@ -2722,9 +2723,16 @@ static double dml_get_return_bandwidth_available(
derate_fabric_factor = soc->qos_parameters.derate_table.dcn_mall_prefetch_average.fclk_derate_percent / 100.0;
derate_dram_factor = soc->qos_parameters.derate_table.dcn_mall_prefetch_average.dram_derate_percent_pixel / 100.0;
} else { // just assume sys_active
- derate_sdp_factor = soc->qos_parameters.derate_table.system_active_average.dcfclk_derate_percent / 100.0;
- derate_fabric_factor = soc->qos_parameters.derate_table.system_active_average.fclk_derate_percent / 100.0;
- derate_dram_factor = soc->qos_parameters.derate_table.system_active_average.dram_derate_percent_pixel / 100.0;
+ // use per dpm derates if the values are populated. Otherwise use global derates
+ derate_sdp_factor = soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.dcfclk_derate_percent[uclk_dpm_level] != 0 ?
+ soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.dcfclk_derate_percent[uclk_dpm_level] / 100.0 :
+ soc->qos_parameters.derate_table.system_active_average.dcfclk_derate_percent / 100.0;
+ derate_fabric_factor = soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.fclk_derate_percent[uclk_dpm_level] != 0 ?
+ soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.fclk_derate_percent[uclk_dpm_level] / 100.0 :
+ soc->qos_parameters.derate_table.system_active_average.fclk_derate_percent / 100.0;
+ derate_dram_factor = soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.dram_derate_percent_pixel[uclk_dpm_level] != 0 ?
+ soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.dram_derate_percent_pixel[uclk_dpm_level] / 100.0 :
+ soc->qos_parameters.derate_table.system_active_average.dram_derate_percent_pixel / 100.0;
}
} else { // urgent bw
if (state_type == dml2_core_internal_soc_state_svp_prefetch) {
@@ -2778,6 +2786,7 @@ static double dml_get_return_bandwidth_available(
DML_LOG_VERBOSE("DML::%s: derate_fabric_bandwidth = %f (derate %f)\n", __func__, derate_fabric_bandwidth, derate_fabric_factor);
DML_LOG_VERBOSE("DML::%s: derate_dram_bandwidth = %f (derate %f)\n", __func__, derate_dram_bandwidth, derate_dram_factor);
DML_LOG_VERBOSE("DML::%s: return_bw_mbps = %f\n", __func__, return_bw_mbps);
+ DML_LOG_VERBOSE("DML::%s: uclk_dpm_level = %u\n", __func__, uclk_dpm_level);
return return_bw_mbps;
}
@@ -2793,7 +2802,8 @@ static noinline_for_stack void calculate_bandwidth_available(
bool HostVMEnable,
double dcfclk_mhz,
double fclk_mhz,
- double dram_bw_mbps)
+ double dram_bw_mbps,
+ unsigned int uclk_dpm_level)
{
unsigned int n, m;
@@ -2812,9 +2822,10 @@ static noinline_for_stack void calculate_bandwidth_available(
0, // hvm_only
dcfclk_mhz,
fclk_mhz,
- dram_bw_mbps);
+ dram_bw_mbps,
+ uclk_dpm_level);
- urg_bandwidth_available[m][n] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 0, dcfclk_mhz, fclk_mhz, dram_bw_mbps);
+ urg_bandwidth_available[m][n] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 0, dcfclk_mhz, fclk_mhz, dram_bw_mbps, uclk_dpm_level);
#ifdef __DML_VBA_DEBUG__
@@ -2824,8 +2835,8 @@ static noinline_for_stack void calculate_bandwidth_available(
// urg_bandwidth_available_vm_only is indexed by soc_state
if (n == dml2_core_internal_bw_dram) {
- urg_bandwidth_available_vm_only[m] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 1, dcfclk_mhz, fclk_mhz, dram_bw_mbps);
- urg_bandwidth_available_pixel_and_vm[m] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 0, dcfclk_mhz, fclk_mhz, dram_bw_mbps);
+ urg_bandwidth_available_vm_only[m] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 1, dcfclk_mhz, fclk_mhz, dram_bw_mbps, uclk_dpm_level);
+ urg_bandwidth_available_pixel_and_vm[m] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 0, dcfclk_mhz, fclk_mhz, dram_bw_mbps, uclk_dpm_level);
}
}
@@ -9483,7 +9494,8 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
display_cfg->hostvm_enable,
mode_lib->ms.DCFCLK,
mode_lib->ms.FabricClock,
- mode_lib->ms.dram_bw_mbps);
+ mode_lib->ms.dram_bw_mbps,
+ mode_lib->ms.active_min_uclk_dpm_index);
calculate_bandwidth_available(
mode_lib->ms.support.avg_bandwidth_available_min,
@@ -9498,10 +9510,12 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
mode_lib->ms.MaxDCFCLK,
mode_lib->ms.MaxFabricClock,
#ifdef DML_MODE_SUPPORT_USE_DPM_DRAM_BW
- mode_lib->ms.dram_bw_mbps);
+ mode_lib->ms.dram_bw_mbps,
#else
- mode_lib->ms.max_dram_bw_mbps);
+ mode_lib->ms.max_dram_bw_mbps,
#endif
+ mode_lib->ms.active_min_uclk_dpm_index);
+
// Average BW support check
calculate_avg_bandwidth_required(
@@ -10958,7 +10972,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex
display_cfg->hostvm_enable,
mode_lib->mp.Dcfclk,
mode_lib->mp.FabricClock,
- mode_lib->mp.dram_bw_mbps);
+ mode_lib->mp.dram_bw_mbps,
+ mode_lib->mp.active_min_uclk_dpm_index);
calculate_hostvm_inefficiency_factor(
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c
index 67e307fa4310..9f1222f5a835 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c
@@ -15,8 +15,6 @@ bool dml2_core_create(enum dml2_project_id project_id, struct dml2_core_instance
memset(out, 0, sizeof(struct dml2_core_instance));
- out->project_id = project_id;
-
switch (project_id) {
case dml2_project_dcn4x_stage1:
result = false;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
index 11e295253f72..e9f970794488 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
@@ -2329,7 +2329,6 @@ struct dml2_core_calcs_mode_support_ex {
const struct dml2_display_cfg *in_display_cfg;
const struct dml2_mcg_min_clock_table *min_clk_table;
int min_clk_index;
- enum dml2_project_id project_id;
//unsigned int in_state_index;
struct dml2_core_internal_mode_support_info *out_evaluation_info;
};
@@ -2342,7 +2341,6 @@ struct dml2_core_calcs_mode_programming_ex {
const struct dml2_mcg_min_clock_table *min_clk_table;
const struct core_display_cfg_support_info *cfg_support_info;
int min_clk_index;
- enum dml2_project_id project_id;
struct dml2_display_cfg_programming *programming;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h
index d328d92240b4..3ae817ea2aad 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h
@@ -489,7 +489,6 @@ struct dml2_core_scratch {
};
struct dml2_core_instance {
- enum dml2_project_id project_id;
struct dml2_mcg_min_clock_table *minimum_clock_table;
struct dml2_core_internal_state_inputs inputs;
struct dml2_core_internal_state_intermediates intermediates;
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c
index 53b21adc6267..9788628cf0ad 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c
@@ -397,8 +397,6 @@ void dpp1_cm_program_regamma_lut(struct dpp *dpp_base,
uint32_t i;
struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base);
- REG_SEQ_START();
-
for (i = 0 ; i < num; i++) {
REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].red_reg);
REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].green_reg);
@@ -408,9 +406,6 @@ void dpp1_cm_program_regamma_lut(struct dpp *dpp_base,
REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].delta_green_reg);
REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].delta_blue_reg);
}
-
- REG_SEQ_SUBMIT();
- REG_SEQ_WAIT_DONE();
}
void dpp1_cm_configure_regamma_lut(
diff --git a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h
index 95f9318a54ef..c23dc1bb29bf 100644
--- a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h
+++ b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h
@@ -63,6 +63,10 @@ bool cm3_helper_translate_curve_to_hw_format(struct dc_context *ctx,
const struct dc_transfer_func *output_tf,
struct pwl_params *lut_params, bool fixpoint);
+bool cm3_helper_translate_curve_to_degamma_hw_format(
+ const struct dc_transfer_func *output_tf,
+ struct pwl_params *lut_params);
+
bool cm3_helper_convert_to_custom_float(
struct pwl_result_data *rgb_resulted,
struct curve_points3 *corner_points,
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c
index fabb9da504be..19d148a85f12 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c
@@ -35,7 +35,10 @@
#include "dce/dce_8_0_d.h"
#include "dce/dce_8_0_sh_mask.h"
-#include "smu/smu_7_0_1_d.h"
+
+#ifndef mmGPIOPAD_A
+#define mmGPIOPAD_A 0x0183
+#endif
/*
* @brief
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c
index fecc8688048d..000f603def58 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c
@@ -58,146 +58,180 @@
/* macros to expend register list macro defined in HW object header file
* end *********************/
+static const struct gpio_id_offset_entry gpio_offsets[] = {
+ /* GENERIC */
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_B),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_C),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_D),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_E),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_F),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_G),
+ /* HPD */
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_1),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_2),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_3),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_4),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_5),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_6),
+ /* SYNCA */
+ GPIO_MASK_ENTRY(DC_GPIO_SYNCA_A,
+ DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK,
+ GPIO_ID_SYNC, GPIO_SYNC_HSYNC_A),
+ GPIO_MASK_ENTRY(DC_GPIO_SYNCA_A,
+ DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK,
+ GPIO_ID_SYNC, GPIO_SYNC_VSYNC_A),
+ /* GSL */
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B),
+};
+
+/* DDC */
+static const struct gpio_ddc_offset_entry ddc_offset_map[] = {
+ { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 },
+ { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 },
+ { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 },
+ { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 },
+ { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 },
+ { REG(DC_GPIO_DDC6_A), GPIO_DDC_LINE_DDC6 },
+ { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA },
+ { REG(DC_GPIO_I2CPAD_A), GPIO_DDC_LINE_I2C_PAD },
+};
+
+static const struct gpio_pin_entry gpio_pins[] = {
+ /* DDC */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC6,
+ DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_I2C_PAD,
+ DC_GPIO_I2CPAD_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC6,
+ DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_I2C_PAD,
+ DC_GPIO_I2CPAD_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ /* GENERIC */
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK),
+ /* HPD */
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK),
+ /* SYNCA */
+ GPIO_PIN_ENTRY(GPIO_ID_SYNC, GPIO_SYNC_HSYNC_A,
+ DC_GPIO_SYNCA_A, DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_SYNC, GPIO_SYNC_VSYNC_A,
+ DC_GPIO_SYNCA_A, DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK),
+ /* GSL */
+ GPIO_PIN_ENTRY(GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK,
+ DC_GPIO_GENLK_A, DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC,
+ DC_GPIO_GENLK_A, DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A,
+ DC_GPIO_GENLK_A, DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B,
+ DC_GPIO_GENLK_A, DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK),
+};
+
static bool offset_to_id(
uint32_t offset,
uint32_t mask,
enum gpio_id *id,
uint32_t *en)
{
- switch (offset) {
- /* GENERIC */
- case REG(DC_GPIO_GENERIC_A):
- *id = GPIO_ID_GENERIC;
- switch (mask) {
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK:
- *en = GPIO_GENERIC_A;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK:
- *en = GPIO_GENERIC_B;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK:
- *en = GPIO_GENERIC_C;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK:
- *en = GPIO_GENERIC_D;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK:
- *en = GPIO_GENERIC_E;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK:
- *en = GPIO_GENERIC_F;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK:
- *en = GPIO_GENERIC_G;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* HPD */
- case REG(DC_GPIO_HPD_A):
- *id = GPIO_ID_HPD;
- switch (mask) {
- case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK:
- *en = GPIO_HPD_1;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK:
- *en = GPIO_HPD_2;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK:
- *en = GPIO_HPD_3;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK:
- *en = GPIO_HPD_4;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK:
- *en = GPIO_HPD_5;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK:
- *en = GPIO_HPD_6;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* SYNCA */
- case REG(DC_GPIO_SYNCA_A):
- *id = GPIO_ID_SYNC;
- switch (mask) {
- case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK:
- *en = GPIO_SYNC_HSYNC_A;
- return true;
- case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK:
- *en = GPIO_SYNC_VSYNC_A;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* REG(DC_GPIO_GENLK_MASK */
- case REG(DC_GPIO_GENLK_A):
- *id = GPIO_ID_GSL;
- switch (mask) {
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK:
- *en = GPIO_GSL_GENLOCK_CLOCK;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK:
- *en = GPIO_GSL_GENLOCK_VSYNC;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_A;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_B;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* DDC */
- /* we don't care about the GPIO_ID for DDC
- * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
- * directly in the create method */
- case REG(DC_GPIO_DDC1_A):
- *en = GPIO_DDC_LINE_DDC1;
- return true;
- case REG(DC_GPIO_DDC2_A):
- *en = GPIO_DDC_LINE_DDC2;
- return true;
- case REG(DC_GPIO_DDC3_A):
- *en = GPIO_DDC_LINE_DDC3;
- return true;
- case REG(DC_GPIO_DDC4_A):
- *en = GPIO_DDC_LINE_DDC4;
- return true;
- case REG(DC_GPIO_DDC5_A):
- *en = GPIO_DDC_LINE_DDC5;
- return true;
- case REG(DC_GPIO_DDC6_A):
- *en = GPIO_DDC_LINE_DDC6;
+ if (dal_hw_translate_gpio_ddc_offset_to_id(
+ ddc_offset_map,
+ ARRAY_SIZE(ddc_offset_map),
+ offset, en))
return true;
- case REG(DC_GPIO_DDCVGA_A):
- *en = GPIO_DDC_LINE_DDC_VGA;
- return true;
- /* GPIO_I2CPAD */
- case REG(DC_GPIO_I2CPAD_A):
- *en = GPIO_DDC_LINE_I2C_PAD;
+
+ if (dal_hw_translate_gpio_offset_to_id(
+ gpio_offsets,
+ ARRAY_SIZE(gpio_offsets),
+ offset, mask, id, en))
return true;
- /* Not implemented */
- case REG(DC_GPIO_PWRSEQ_A):
- case REG(DC_GPIO_PAD_STRENGTH_1):
- case REG(DC_GPIO_PAD_STRENGTH_2):
- case REG(DC_GPIO_DEBUG):
- return false;
- /* UNEXPECTED */
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
+
+ ASSERT_CRITICAL(false);
+ return false;
}
static bool id_to_offset(
@@ -205,186 +239,14 @@ static bool id_to_offset(
uint32_t en,
struct gpio_pin_info *info)
{
- bool result = true;
-
- switch (id) {
- case GPIO_ID_DDC_DATA:
- info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC6:
- info->offset = REG(DC_GPIO_DDC6_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- info->offset = REG(DC_GPIO_I2CPAD_A);
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_DDC_CLOCK:
- info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC6:
- info->offset = REG(DC_GPIO_DDC6_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- info->offset = REG(DC_GPIO_I2CPAD_A);
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GENERIC:
- info->offset = REG(DC_GPIO_GENERIC_A);
- switch (en) {
- case GPIO_GENERIC_A:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK;
- break;
- case GPIO_GENERIC_B:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK;
- break;
- case GPIO_GENERIC_C:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK;
- break;
- case GPIO_GENERIC_D:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK;
- break;
- case GPIO_GENERIC_E:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK;
- break;
- case GPIO_GENERIC_F:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK;
- break;
- case GPIO_GENERIC_G:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_HPD:
- info->offset = REG(DC_GPIO_HPD_A);
- switch (en) {
- case GPIO_HPD_1:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK;
- break;
- case GPIO_HPD_2:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK;
- break;
- case GPIO_HPD_3:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK;
- break;
- case GPIO_HPD_4:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK;
- break;
- case GPIO_HPD_5:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK;
- break;
- case GPIO_HPD_6:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_SYNC:
- switch (en) {
- case GPIO_SYNC_HSYNC_A:
- info->offset = REG(DC_GPIO_SYNCA_A);
- info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK;
- break;
- case GPIO_SYNC_VSYNC_A:
- info->offset = REG(DC_GPIO_SYNCA_A);
- info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK;
- break;
- case GPIO_SYNC_HSYNC_B:
- case GPIO_SYNC_VSYNC_B:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GSL:
- switch (en) {
- case GPIO_GSL_GENLOCK_CLOCK:
- info->offset = REG(DC_GPIO_GENLK_A);
- info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK;
- break;
- case GPIO_GSL_GENLOCK_VSYNC:
- info->offset = REG(DC_GPIO_GENLK_A);
- info->mask =
- DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK;
- break;
- case GPIO_GSL_SWAPLOCK_A:
- info->offset = REG(DC_GPIO_GENLK_A);
- info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK;
- break;
- case GPIO_GSL_SWAPLOCK_B:
- info->offset = REG(DC_GPIO_GENLK_A);
- info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_VIP_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
-
- if (result) {
- info->offset_y = info->offset + 2;
- info->offset_en = info->offset + 1;
- info->offset_mask = info->offset - 1;
-
- info->mask_y = info->mask;
- info->mask_en = info->mask;
- info->mask_mask = info->mask;
- }
+ if (dal_hw_translate_id_to_offset(
+ gpio_pins,
+ ARRAY_SIZE(gpio_pins),
+ id, en, info))
+ return true;
- return result;
+ ASSERT_CRITICAL(false);
+ return false;
}
/* function table */
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c
index 3005ee7751a0..a21df8668266 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c
@@ -62,131 +62,161 @@
* end *********************/
+static const struct gpio_id_offset_entry gpio_offsets[] = {
+ /* GENERIC */
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_B),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_C),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_D),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_E),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_F),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_G),
+ /* HPD */
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_1),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_2),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_3),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_4),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_5),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_6),
+ /* GSL */
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B),
+};
+
+
+/* DDC */
+static const struct gpio_ddc_offset_entry ddc_offset_map[] = {
+ { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 },
+ { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 },
+ { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 },
+ { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 },
+ { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 },
+ { REG(DC_GPIO_DDC6_A), GPIO_DDC_LINE_DDC6 },
+ { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA },
+};
+
+
+/*
+ * GSL is intentionally omitted here.
+ * id_to_offset() for GSL is not implemented on this ASIC.
+ */
+static const struct gpio_pin_entry gpio_pins[] = {
+ /* DDC */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC6,
+ DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC6,
+ DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ /* GENERIC */
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK),
+ /* HPD */
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK),
+};
+
+
static bool offset_to_id(
uint32_t offset,
uint32_t mask,
enum gpio_id *id,
uint32_t *en)
{
- switch (offset) {
- /* GENERIC */
- case REG(DC_GPIO_GENERIC_A):
- *id = GPIO_ID_GENERIC;
- switch (mask) {
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK:
- *en = GPIO_GENERIC_A;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK:
- *en = GPIO_GENERIC_B;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK:
- *en = GPIO_GENERIC_C;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK:
- *en = GPIO_GENERIC_D;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK:
- *en = GPIO_GENERIC_E;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK:
- *en = GPIO_GENERIC_F;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK:
- *en = GPIO_GENERIC_G;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* HPD */
- case REG(DC_GPIO_HPD_A):
- *id = GPIO_ID_HPD;
- switch (mask) {
- case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK:
- *en = GPIO_HPD_1;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK:
- *en = GPIO_HPD_2;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK:
- *en = GPIO_HPD_3;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK:
- *en = GPIO_HPD_4;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK:
- *en = GPIO_HPD_5;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK:
- *en = GPIO_HPD_6;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* REG(DC_GPIO_GENLK_MASK */
- case REG(DC_GPIO_GENLK_A):
- *id = GPIO_ID_GSL;
- switch (mask) {
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK:
- *en = GPIO_GSL_GENLOCK_CLOCK;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK:
- *en = GPIO_GSL_GENLOCK_VSYNC;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_A;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_B;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* DDC */
- /* we don't care about the GPIO_ID for DDC
- * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
- * directly in the create method
- */
- case REG(DC_GPIO_DDC1_A):
- *en = GPIO_DDC_LINE_DDC1;
- return true;
- case REG(DC_GPIO_DDC2_A):
- *en = GPIO_DDC_LINE_DDC2;
- return true;
- case REG(DC_GPIO_DDC3_A):
- *en = GPIO_DDC_LINE_DDC3;
- return true;
- case REG(DC_GPIO_DDC4_A):
- *en = GPIO_DDC_LINE_DDC4;
- return true;
- case REG(DC_GPIO_DDC5_A):
- *en = GPIO_DDC_LINE_DDC5;
- return true;
- case REG(DC_GPIO_DDC6_A):
- *en = GPIO_DDC_LINE_DDC6;
+ if (dal_hw_translate_gpio_ddc_offset_to_id(
+ ddc_offset_map,
+ ARRAY_SIZE(ddc_offset_map),
+ offset, en))
return true;
- case REG(DC_GPIO_DDCVGA_A):
- *en = GPIO_DDC_LINE_DDC_VGA;
+
+ if (dal_hw_translate_gpio_offset_to_id(
+ gpio_offsets,
+ ARRAY_SIZE(gpio_offsets),
+ offset, mask, id, en))
return true;
-/*
- * case REG(DC_GPIO_I2CPAD_A): not exit
- * case REG(DC_GPIO_PWRSEQ_A):
- * case REG(DC_GPIO_PAD_STRENGTH_1):
- * case REG(DC_GPIO_PAD_STRENGTH_2):
- * case REG(DC_GPIO_DEBUG):
- */
- /* UNEXPECTED */
- default:
-/* case REG(DC_GPIO_SYNCA_A): not exist */
- ASSERT_CRITICAL(false);
- return false;
- }
+ ASSERT_CRITICAL(false);
+ return false;
}
static bool id_to_offset(
@@ -194,170 +224,14 @@ static bool id_to_offset(
uint32_t en,
struct gpio_pin_info *info)
{
- bool result = true;
-
- switch (id) {
- case GPIO_ID_DDC_DATA:
- info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC6:
- info->offset = REG(DC_GPIO_DDC6_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_DDC_CLOCK:
- info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC6:
- info->offset = REG(DC_GPIO_DDC6_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GENERIC:
- info->offset = REG(DC_GPIO_GENERIC_A);
- switch (en) {
- case GPIO_GENERIC_A:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK;
- break;
- case GPIO_GENERIC_B:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK;
- break;
- case GPIO_GENERIC_C:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK;
- break;
- case GPIO_GENERIC_D:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK;
- break;
- case GPIO_GENERIC_E:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK;
- break;
- case GPIO_GENERIC_F:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK;
- break;
- case GPIO_GENERIC_G:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_HPD:
- info->offset = REG(DC_GPIO_HPD_A);
- switch (en) {
- case GPIO_HPD_1:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK;
- break;
- case GPIO_HPD_2:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK;
- break;
- case GPIO_HPD_3:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK;
- break;
- case GPIO_HPD_4:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK;
- break;
- case GPIO_HPD_5:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK;
- break;
- case GPIO_HPD_6:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GSL:
- switch (en) {
- case GPIO_GSL_GENLOCK_CLOCK:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_GENLOCK_VSYNC:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_A:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_B:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
-
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_SYNC:
- case GPIO_ID_VIP_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
-
- if (result) {
- info->offset_y = info->offset + 2;
- info->offset_en = info->offset + 1;
- info->offset_mask = info->offset - 1;
-
- info->mask_y = info->mask;
- info->mask_en = info->mask;
- info->mask_mask = info->mask;
- }
+ if (dal_hw_translate_id_to_offset(
+ gpio_pins,
+ ARRAY_SIZE(gpio_pins),
+ id, en, info))
+ return true;
- return result;
+ ASSERT_CRITICAL(false);
+ return false;
}
/* function table */
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c
index e3b11b3c1daa..18bd4d4e32d0 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c
@@ -60,6 +60,135 @@
/* macros to expend register list macro defined in HW object header file
* end *********************/
+static const struct gpio_id_offset_entry gpio_offsets[] = {
+ /* GENERIC */
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_B),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_C),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_D),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_E),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_F),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_G),
+ /* HPD */
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_1),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_2),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_3),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_4),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_5),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_6),
+ /* GSL */
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B),
+};
+
+
+/* DDC */
+static const struct gpio_ddc_offset_entry ddc_offset_map[] = {
+ { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 },
+ { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 },
+ { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 },
+ { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 },
+ { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 },
+ { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA },
+};
+
+
+/*
+ * GSL is intentionally omitted here.
+ * id_to_offset() for GSL is not implemented on this ASIC.
+ */
+static const struct gpio_pin_entry gpio_pins[] = {
+ /* DDC */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK),
+ /* GENERIC */
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK),
+ /* HPD */
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK),
+};
+
static bool offset_to_id(
uint32_t offset,
@@ -67,122 +196,20 @@ static bool offset_to_id(
enum gpio_id *id,
uint32_t *en)
{
- switch (offset) {
- /* GENERIC */
- case REG(DC_GPIO_GENERIC_A):
- *id = GPIO_ID_GENERIC;
- switch (mask) {
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK:
- *en = GPIO_GENERIC_A;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK:
- *en = GPIO_GENERIC_B;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK:
- *en = GPIO_GENERIC_C;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK:
- *en = GPIO_GENERIC_D;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK:
- *en = GPIO_GENERIC_E;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK:
- *en = GPIO_GENERIC_F;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK:
- *en = GPIO_GENERIC_G;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* HPD */
- case REG(DC_GPIO_HPD_A):
- *id = GPIO_ID_HPD;
- switch (mask) {
- case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK:
- *en = GPIO_HPD_1;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK:
- *en = GPIO_HPD_2;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK:
- *en = GPIO_HPD_3;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK:
- *en = GPIO_HPD_4;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK:
- *en = GPIO_HPD_5;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK:
- *en = GPIO_HPD_6;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* REG(DC_GPIO_GENLK_MASK */
- case REG(DC_GPIO_GENLK_A):
- *id = GPIO_ID_GSL;
- switch (mask) {
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK:
- *en = GPIO_GSL_GENLOCK_CLOCK;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK:
- *en = GPIO_GSL_GENLOCK_VSYNC;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_A;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_B;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* DDC */
- /* we don't care about the GPIO_ID for DDC
- * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
- * directly in the create method
- */
- case REG(DC_GPIO_DDC1_A):
- *en = GPIO_DDC_LINE_DDC1;
- return true;
- case REG(DC_GPIO_DDC2_A):
- *en = GPIO_DDC_LINE_DDC2;
+ if (dal_hw_translate_gpio_ddc_offset_to_id(
+ ddc_offset_map,
+ ARRAY_SIZE(ddc_offset_map),
+ offset, en))
return true;
- case REG(DC_GPIO_DDC3_A):
- *en = GPIO_DDC_LINE_DDC3;
- return true;
- case REG(DC_GPIO_DDC4_A):
- *en = GPIO_DDC_LINE_DDC4;
- return true;
- case REG(DC_GPIO_DDC5_A):
- *en = GPIO_DDC_LINE_DDC5;
- return true;
- case REG(DC_GPIO_DDCVGA_A):
- *en = GPIO_DDC_LINE_DDC_VGA;
+
+ if (dal_hw_translate_gpio_offset_to_id(
+ gpio_offsets,
+ ARRAY_SIZE(gpio_offsets),
+ offset, mask, id, en))
return true;
-/*
- * case REG(DC_GPIO_I2CPAD_A): not exit
- * case REG(DC_GPIO_PWRSEQ_A):
- * case REG(DC_GPIO_PAD_STRENGTH_1):
- * case REG(DC_GPIO_PAD_STRENGTH_2):
- * case REG(DC_GPIO_DEBUG):
- */
- /* UNEXPECTED */
- default:
-/* case REG(DC_GPIO_SYNCA_A): not exist */
- ASSERT_CRITICAL(false);
- return false;
- }
+ ASSERT_CRITICAL(false);
+ return false;
}
static bool id_to_offset(
@@ -190,164 +217,14 @@ static bool id_to_offset(
uint32_t en,
struct gpio_pin_info *info)
{
- bool result = true;
-
- switch (id) {
- case GPIO_ID_DDC_DATA:
- info->mask = DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_DDC_CLOCK:
- info->mask = DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GENERIC:
- info->offset = REG(DC_GPIO_GENERIC_A);
- switch (en) {
- case GPIO_GENERIC_A:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK;
- break;
- case GPIO_GENERIC_B:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK;
- break;
- case GPIO_GENERIC_C:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK;
- break;
- case GPIO_GENERIC_D:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK;
- break;
- case GPIO_GENERIC_E:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK;
- break;
- case GPIO_GENERIC_F:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK;
- break;
- case GPIO_GENERIC_G:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_HPD:
- info->offset = REG(DC_GPIO_HPD_A);
- switch (en) {
- case GPIO_HPD_1:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK;
- break;
- case GPIO_HPD_2:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK;
- break;
- case GPIO_HPD_3:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK;
- break;
- case GPIO_HPD_4:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK;
- break;
- case GPIO_HPD_5:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK;
- break;
- case GPIO_HPD_6:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GSL:
- switch (en) {
- case GPIO_GSL_GENLOCK_CLOCK:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_GENLOCK_VSYNC:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_A:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_B:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
-
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_SYNC:
- case GPIO_ID_VIP_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
-
- if (result) {
- info->offset_y = info->offset + 2;
- info->offset_en = info->offset + 1;
- info->offset_mask = info->offset - 1;
-
- info->mask_y = info->mask;
- info->mask_en = info->mask;
- info->mask_mask = info->mask;
- }
+ if (dal_hw_translate_id_to_offset(
+ gpio_pins,
+ ARRAY_SIZE(gpio_pins),
+ id, en, info))
+ return true;
- return result;
+ ASSERT_CRITICAL(false);
+ return false;
}
/* function table */
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c
index 49d6250037a9..c4225231f725 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c
@@ -67,131 +67,161 @@
* end *********************/
+static const struct gpio_id_offset_entry gpio_offsets[] = {
+ /* GENERIC */
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_B),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_C),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_D),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_E),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_F),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_G),
+ /* HPD */
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_1),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_2),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_3),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_4),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_5),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_6),
+ /* GSL */
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B),
+};
+
+
+/* DDC */
+static const struct gpio_ddc_offset_entry ddc_offset_map[] = {
+ { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 },
+ { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 },
+ { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 },
+ { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 },
+ { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 },
+ { REG(DC_GPIO_DDC6_A), GPIO_DDC_LINE_DDC6 },
+ { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA },
+};
+
+
+/*
+ * GSL is intentionally omitted here.
+ * id_to_offset() for GSL is not implemented on this ASIC.
+ */
+static const struct gpio_pin_entry gpio_pins[] = {
+ /* DDC */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC6,
+ DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC6,
+ DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK),
+ /* GENERIC */
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK),
+ /* HPD */
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK),
+};
+
+
static bool offset_to_id(
uint32_t offset,
uint32_t mask,
enum gpio_id *id,
uint32_t *en)
{
- switch (offset) {
- /* GENERIC */
- case REG(DC_GPIO_GENERIC_A):
- *id = GPIO_ID_GENERIC;
- switch (mask) {
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK:
- *en = GPIO_GENERIC_A;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK:
- *en = GPIO_GENERIC_B;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK:
- *en = GPIO_GENERIC_C;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK:
- *en = GPIO_GENERIC_D;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK:
- *en = GPIO_GENERIC_E;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK:
- *en = GPIO_GENERIC_F;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK:
- *en = GPIO_GENERIC_G;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* HPD */
- case REG(DC_GPIO_HPD_A):
- *id = GPIO_ID_HPD;
- switch (mask) {
- case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK:
- *en = GPIO_HPD_1;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK:
- *en = GPIO_HPD_2;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK:
- *en = GPIO_HPD_3;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK:
- *en = GPIO_HPD_4;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK:
- *en = GPIO_HPD_5;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK:
- *en = GPIO_HPD_6;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* REG(DC_GPIO_GENLK_MASK */
- case REG(DC_GPIO_GENLK_A):
- *id = GPIO_ID_GSL;
- switch (mask) {
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK:
- *en = GPIO_GSL_GENLOCK_CLOCK;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK:
- *en = GPIO_GSL_GENLOCK_VSYNC;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_A;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_B;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* DDC */
- /* we don't care about the GPIO_ID for DDC
- * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
- * directly in the create method
- */
- case REG(DC_GPIO_DDC1_A):
- *en = GPIO_DDC_LINE_DDC1;
- return true;
- case REG(DC_GPIO_DDC2_A):
- *en = GPIO_DDC_LINE_DDC2;
- return true;
- case REG(DC_GPIO_DDC3_A):
- *en = GPIO_DDC_LINE_DDC3;
- return true;
- case REG(DC_GPIO_DDC4_A):
- *en = GPIO_DDC_LINE_DDC4;
- return true;
- case REG(DC_GPIO_DDC5_A):
- *en = GPIO_DDC_LINE_DDC5;
- return true;
- case REG(DC_GPIO_DDC6_A):
- *en = GPIO_DDC_LINE_DDC6;
+ if (dal_hw_translate_gpio_ddc_offset_to_id(
+ ddc_offset_map,
+ ARRAY_SIZE(ddc_offset_map),
+ offset, en))
return true;
- case REG(DC_GPIO_DDCVGA_A):
- *en = GPIO_DDC_LINE_DDC_VGA;
+
+ if (dal_hw_translate_gpio_offset_to_id(
+ gpio_offsets,
+ ARRAY_SIZE(gpio_offsets),
+ offset, mask, id, en))
return true;
-/*
- * case REG(DC_GPIO_I2CPAD_A): not exit
- * case REG(DC_GPIO_PWRSEQ_A):
- * case REG(DC_GPIO_PAD_STRENGTH_1):
- * case REG(DC_GPIO_PAD_STRENGTH_2):
- * case REG(DC_GPIO_DEBUG):
- */
- /* UNEXPECTED */
- default:
-/* case REG(DC_GPIO_SYNCA_A): not exist */
- ASSERT_CRITICAL(false);
- return false;
- }
+ ASSERT_CRITICAL(false);
+ return false;
}
static bool id_to_offset(
@@ -199,170 +229,14 @@ static bool id_to_offset(
uint32_t en,
struct gpio_pin_info *info)
{
- bool result = true;
-
- switch (id) {
- case GPIO_ID_DDC_DATA:
- info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC6:
- info->offset = REG(DC_GPIO_DDC6_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_DDC_CLOCK:
- info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC6:
- info->offset = REG(DC_GPIO_DDC6_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GENERIC:
- info->offset = REG(DC_GPIO_GENERIC_A);
- switch (en) {
- case GPIO_GENERIC_A:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK;
- break;
- case GPIO_GENERIC_B:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK;
- break;
- case GPIO_GENERIC_C:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK;
- break;
- case GPIO_GENERIC_D:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK;
- break;
- case GPIO_GENERIC_E:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK;
- break;
- case GPIO_GENERIC_F:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK;
- break;
- case GPIO_GENERIC_G:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_HPD:
- info->offset = REG(DC_GPIO_HPD_A);
- switch (en) {
- case GPIO_HPD_1:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK;
- break;
- case GPIO_HPD_2:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK;
- break;
- case GPIO_HPD_3:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK;
- break;
- case GPIO_HPD_4:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK;
- break;
- case GPIO_HPD_5:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK;
- break;
- case GPIO_HPD_6:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GSL:
- switch (en) {
- case GPIO_GSL_GENLOCK_CLOCK:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_GENLOCK_VSYNC:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_A:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_B:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
-
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_SYNC:
- case GPIO_ID_VIP_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
-
- if (result) {
- info->offset_y = info->offset + 2;
- info->offset_en = info->offset + 1;
- info->offset_mask = info->offset - 1;
-
- info->mask_y = info->mask;
- info->mask_en = info->mask;
- info->mask_mask = info->mask;
- }
+ if (dal_hw_translate_id_to_offset(
+ gpio_pins,
+ ARRAY_SIZE(gpio_pins),
+ id, en, info))
+ return true;
- return result;
+ ASSERT_CRITICAL(false);
+ return false;
}
/* function table */
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c
index fbdaba57f718..aa507f7f4ef9 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c
@@ -62,128 +62,156 @@
* end *********************/
+static const struct gpio_id_offset_entry gpio_offsets[] = {
+ /* GENERIC */
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_B),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_C),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_D),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_E),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_F),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_G),
+ /* HPD */
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_1),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_2),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_3),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_4),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_5),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_6),
+ /* GSL */
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B),
+};
+
+
+/* DDC */
+static const struct gpio_ddc_offset_entry ddc_offset_map[] = {
+ { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 },
+ { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 },
+ { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 },
+ { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 },
+ { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 },
+ { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA },
+};
+
+
+/*
+ * GSL is intentionally omitted here.
+ * id_to_offset() for GSL is not implemented on this ASIC.
+ */
+static const struct gpio_pin_entry gpio_pins[] = {
+ /* DDC */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ /* GENERIC */
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK),
+ /* HPD */
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK),
+};
+
+
static bool offset_to_id(
uint32_t offset,
uint32_t mask,
enum gpio_id *id,
uint32_t *en)
{
- switch (offset) {
- /* GENERIC */
- case REG(DC_GPIO_GENERIC_A):
- *id = GPIO_ID_GENERIC;
- switch (mask) {
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK:
- *en = GPIO_GENERIC_A;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK:
- *en = GPIO_GENERIC_B;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK:
- *en = GPIO_GENERIC_C;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK:
- *en = GPIO_GENERIC_D;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK:
- *en = GPIO_GENERIC_E;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK:
- *en = GPIO_GENERIC_F;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK:
- *en = GPIO_GENERIC_G;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* HPD */
- case REG(DC_GPIO_HPD_A):
- *id = GPIO_ID_HPD;
- switch (mask) {
- case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK:
- *en = GPIO_HPD_1;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK:
- *en = GPIO_HPD_2;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK:
- *en = GPIO_HPD_3;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK:
- *en = GPIO_HPD_4;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK:
- *en = GPIO_HPD_5;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK:
- *en = GPIO_HPD_6;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* REG(DC_GPIO_GENLK_MASK */
- case REG(DC_GPIO_GENLK_A):
- *id = GPIO_ID_GSL;
- switch (mask) {
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK:
- *en = GPIO_GSL_GENLOCK_CLOCK;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK:
- *en = GPIO_GSL_GENLOCK_VSYNC;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_A;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_B;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* DDC */
- /* we don't care about the GPIO_ID for DDC
- * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
- * directly in the create method
- */
- case REG(DC_GPIO_DDC1_A):
- *en = GPIO_DDC_LINE_DDC1;
- return true;
- case REG(DC_GPIO_DDC2_A):
- *en = GPIO_DDC_LINE_DDC2;
+ if (dal_hw_translate_gpio_ddc_offset_to_id(
+ ddc_offset_map,
+ ARRAY_SIZE(ddc_offset_map),
+ offset, en))
return true;
- case REG(DC_GPIO_DDC3_A):
- *en = GPIO_DDC_LINE_DDC3;
- return true;
- case REG(DC_GPIO_DDC4_A):
- *en = GPIO_DDC_LINE_DDC4;
- return true;
- case REG(DC_GPIO_DDC5_A):
- *en = GPIO_DDC_LINE_DDC5;
- return true;
- case REG(DC_GPIO_DDCVGA_A):
- *en = GPIO_DDC_LINE_DDC_VGA;
+
+ if (dal_hw_translate_gpio_offset_to_id(
+ gpio_offsets,
+ ARRAY_SIZE(gpio_offsets),
+ offset, mask, id, en))
return true;
-/*
- * case REG(DC_GPIO_I2CPAD_A): not exit
- * case REG(DC_GPIO_PWRSEQ_A):
- * case REG(DC_GPIO_PAD_STRENGTH_1):
- * case REG(DC_GPIO_PAD_STRENGTH_2):
- * case REG(DC_GPIO_DEBUG):
- */
- /* UNEXPECTED */
- default:
-/* case REG(DC_GPIO_SYNCA_A): not exist */
- ASSERT_CRITICAL(false);
- return false;
- }
+ ASSERT_CRITICAL(false);
+ return false;
}
static bool id_to_offset(
@@ -191,164 +219,14 @@ static bool id_to_offset(
uint32_t en,
struct gpio_pin_info *info)
{
- bool result = true;
-
- switch (id) {
- case GPIO_ID_DDC_DATA:
- info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_DDC_CLOCK:
- info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GENERIC:
- info->offset = REG(DC_GPIO_GENERIC_A);
- switch (en) {
- case GPIO_GENERIC_A:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK;
- break;
- case GPIO_GENERIC_B:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK;
- break;
- case GPIO_GENERIC_C:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK;
- break;
- case GPIO_GENERIC_D:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK;
- break;
- case GPIO_GENERIC_E:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK;
- break;
- case GPIO_GENERIC_F:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK;
- break;
- case GPIO_GENERIC_G:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_HPD:
- info->offset = REG(DC_GPIO_HPD_A);
- switch (en) {
- case GPIO_HPD_1:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK;
- break;
- case GPIO_HPD_2:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK;
- break;
- case GPIO_HPD_3:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK;
- break;
- case GPIO_HPD_4:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK;
- break;
- case GPIO_HPD_5:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK;
- break;
- case GPIO_HPD_6:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GSL:
- switch (en) {
- case GPIO_GSL_GENLOCK_CLOCK:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_GENLOCK_VSYNC:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_A:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_B:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
-
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_SYNC:
- case GPIO_ID_VIP_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
-
- if (result) {
- info->offset_y = info->offset + 2;
- info->offset_en = info->offset + 1;
- info->offset_mask = info->offset - 1;
-
- info->mask_y = info->mask;
- info->mask_en = info->mask;
- info->mask_mask = info->mask;
- }
+ if (dal_hw_translate_id_to_offset(
+ gpio_pins,
+ ARRAY_SIZE(gpio_pins),
+ id, en, info))
+ return true;
- return result;
+ ASSERT_CRITICAL(false);
+ return false;
}
/* function table */
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c
index 8493b9981f9e..71067a8da121 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c
@@ -60,111 +60,145 @@
* end *********************/
+static const struct gpio_id_offset_entry gpio_offsets[] = {
+ /* GENERIC */
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_B),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_C),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_D),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_E),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_F),
+ /* HPD */
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_1),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_2),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_3),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_4),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_5),
+ /* GSL */
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B),
+};
+
+
+/* DDC */
+static const struct gpio_ddc_offset_entry ddc_offset_map[] = {
+ { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 },
+ { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 },
+ { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 },
+ { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 },
+ { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 },
+ { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA },
+};
+
+/*
+ * GSL is intentionally omitted here.
+ * id_to_offset() for GSL is not implemented on this ASIC.
+ */
+static const struct gpio_pin_entry gpio_pins[] = {
+ /* DDC */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ /* GENERIC */
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK),
+ /* HPD */
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK),
+};
+
+
static bool offset_to_id(
uint32_t offset,
uint32_t mask,
enum gpio_id *id,
uint32_t *en)
{
- switch (offset) {
- /* GENERIC */
- case REG(DC_GPIO_GENERIC_A):
- *id = GPIO_ID_GENERIC;
- switch (mask) {
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK:
- *en = GPIO_GENERIC_A;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK:
- *en = GPIO_GENERIC_B;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK:
- *en = GPIO_GENERIC_C;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK:
- *en = GPIO_GENERIC_D;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK:
- *en = GPIO_GENERIC_E;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK:
- *en = GPIO_GENERIC_F;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* HPD */
- case REG(DC_GPIO_HPD_A):
- *id = GPIO_ID_HPD;
- switch (mask) {
- case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK:
- *en = GPIO_HPD_1;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK:
- *en = GPIO_HPD_2;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK:
- *en = GPIO_HPD_3;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK:
- *en = GPIO_HPD_4;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK:
- *en = GPIO_HPD_5;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* REG(DC_GPIO_GENLK_MASK */
- case REG(DC_GPIO_GENLK_A):
- *id = GPIO_ID_GSL;
- switch (mask) {
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK:
- *en = GPIO_GSL_GENLOCK_CLOCK;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK:
- *en = GPIO_GSL_GENLOCK_VSYNC;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_A;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_B;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* DDC */
- /* we don't care about the GPIO_ID for DDC
- * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
- * directly in the create method */
- case REG(DC_GPIO_DDC1_A):
- *en = GPIO_DDC_LINE_DDC1;
- return true;
- case REG(DC_GPIO_DDC2_A):
- *en = GPIO_DDC_LINE_DDC2;
- return true;
- case REG(DC_GPIO_DDC3_A):
- *en = GPIO_DDC_LINE_DDC3;
- return true;
- case REG(DC_GPIO_DDC4_A):
- *en = GPIO_DDC_LINE_DDC4;
- return true;
- case REG(DC_GPIO_DDC5_A):
- *en = GPIO_DDC_LINE_DDC5;
+ if (dal_hw_translate_gpio_ddc_offset_to_id(
+ ddc_offset_map,
+ ARRAY_SIZE(ddc_offset_map),
+ offset, en))
return true;
- case REG(DC_GPIO_DDCVGA_A):
- *en = GPIO_DDC_LINE_DDC_VGA;
+
+ if (dal_hw_translate_gpio_offset_to_id(
+ gpio_offsets,
+ ARRAY_SIZE(gpio_offsets),
+ offset, mask, id, en))
return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
+
+ ASSERT_CRITICAL(false);
+ return false;
}
static bool id_to_offset(
@@ -172,158 +206,14 @@ static bool id_to_offset(
uint32_t en,
struct gpio_pin_info *info)
{
- bool result = true;
-
- switch (id) {
- case GPIO_ID_DDC_DATA:
- info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_DDC_CLOCK:
- info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GENERIC:
- info->offset = REG(DC_GPIO_GENERIC_A);
- switch (en) {
- case GPIO_GENERIC_A:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK;
- break;
- case GPIO_GENERIC_B:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK;
- break;
- case GPIO_GENERIC_C:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK;
- break;
- case GPIO_GENERIC_D:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK;
- break;
- case GPIO_GENERIC_E:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK;
- break;
- case GPIO_GENERIC_F:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_HPD:
- info->offset = REG(DC_GPIO_HPD_A);
- switch (en) {
- case GPIO_HPD_1:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK;
- break;
- case GPIO_HPD_2:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK;
- break;
- case GPIO_HPD_3:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK;
- break;
- case GPIO_HPD_4:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK;
- break;
- case GPIO_HPD_5:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GSL:
- switch (en) {
- case GPIO_GSL_GENLOCK_CLOCK:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_GENLOCK_VSYNC:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_A:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_B:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
-
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_SYNC:
- case GPIO_ID_VIP_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
-
- if (result) {
- info->offset_y = info->offset + 2;
- info->offset_en = info->offset + 1;
- info->offset_mask = info->offset - 1;
-
- info->mask_y = info->mask;
- info->mask_en = info->mask;
- info->mask_mask = info->mask;
- }
+ if (dal_hw_translate_id_to_offset(
+ gpio_pins,
+ ARRAY_SIZE(gpio_pins),
+ id, en, info))
+ return true;
- return result;
+ ASSERT_CRITICAL(false);
+ return false;
}
/* function table */
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c
index ea416f01f888..7aa97f09955c 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c
@@ -35,119 +35,145 @@
* end *********************/
+static const struct gpio_id_offset_entry gpio_offsets[] = {
+ /* GENERIC */
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_B),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_C),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_D),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_E),
+ GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK,
+ GPIO_ID_GENERIC, GPIO_GENERIC_F),
+ /* HPD */
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_1),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_2),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_3),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_4),
+ GPIO_MASK_ENTRY(DC_GPIO_HPD_A,
+ DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK,
+ GPIO_ID_HPD, GPIO_HPD_5),
+ /* GSL */
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A),
+ GPIO_MASK_ENTRY(DC_GPIO_GENLK_A,
+ DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK,
+ GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B),
+};
+
+
+/* DDC */
+static const struct gpio_ddc_offset_entry ddc_offset_map[] = {
+ { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 },
+ { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 },
+ { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 },
+ { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 },
+ { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA },
+};
+
+
+/*
+ * GSL is intentionally omitted here.
+ * id_to_offset() for GSL is not implemented on this ASIC.
+ */
+static const struct gpio_pin_entry gpio_pins[] = {
+ /* DDC */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ /* GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ /* GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ /* GENERIC */
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F,
+ DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK),
+ /* HPD */
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5,
+ DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK),
+};
+
+
static bool offset_to_id(
uint32_t offset,
uint32_t mask,
enum gpio_id *id,
uint32_t *en)
{
- switch (offset) {
- /* GENERIC */
- case REG(DC_GPIO_GENERIC_A):
- *id = GPIO_ID_GENERIC;
- switch (mask) {
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK:
- *en = GPIO_GENERIC_A;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK:
- *en = GPIO_GENERIC_B;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK:
- *en = GPIO_GENERIC_C;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK:
- *en = GPIO_GENERIC_D;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK:
- *en = GPIO_GENERIC_E;
- return true;
- case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK:
- *en = GPIO_GENERIC_F;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* HPD */
- case REG(DC_GPIO_HPD_A):
- *id = GPIO_ID_HPD;
- switch (mask) {
- case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK:
- *en = GPIO_HPD_1;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK:
- *en = GPIO_HPD_2;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK:
- *en = GPIO_HPD_3;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK:
- *en = GPIO_HPD_4;
- return true;
- case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK:
- *en = GPIO_HPD_5;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* REG(DC_GPIO_GENLK_MASK */
- case REG(DC_GPIO_GENLK_A):
- *id = GPIO_ID_GSL;
- switch (mask) {
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK:
- *en = GPIO_GSL_GENLOCK_CLOCK;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK:
- *en = GPIO_GSL_GENLOCK_VSYNC;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_A;
- return true;
- case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK:
- *en = GPIO_GSL_SWAPLOCK_B;
- return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
- break;
- /* DDC */
- /* we don't care about the GPIO_ID for DDC
- * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
- * directly in the create method
- */
- case REG(DC_GPIO_DDC1_A):
- *en = GPIO_DDC_LINE_DDC1;
+ if (dal_hw_translate_gpio_ddc_offset_to_id(
+ ddc_offset_map,
+ ARRAY_SIZE(ddc_offset_map),
+ offset, en))
return true;
- case REG(DC_GPIO_DDC2_A):
- *en = GPIO_DDC_LINE_DDC2;
- return true;
- case REG(DC_GPIO_DDC3_A):
- *en = GPIO_DDC_LINE_DDC3;
- return true;
- case REG(DC_GPIO_DDC4_A):
- *en = GPIO_DDC_LINE_DDC4;
- return true;
- case REG(DC_GPIO_DDCVGA_A):
- *en = GPIO_DDC_LINE_DDC_VGA;
+
+ if (dal_hw_translate_gpio_offset_to_id(
+ gpio_offsets,
+ ARRAY_SIZE(gpio_offsets),
+ offset, mask, id, en))
return true;
-/*
- * case REG(DC_GPIO_I2CPAD_A): not exit
- * case REG(DC_GPIO_PWRSEQ_A):
- * case REG(DC_GPIO_PAD_STRENGTH_1):
- * case REG(DC_GPIO_PAD_STRENGTH_2):
- * case REG(DC_GPIO_DEBUG):
- */
- /* UNEXPECTED */
- default:
-/* case REG(DC_GPIO_SYNCA_A): not exist */
- ASSERT_CRITICAL(false);
- return false;
- }
+ ASSERT_CRITICAL(false);
+ return false;
}
@@ -156,158 +182,14 @@ static bool id_to_offset(
uint32_t en,
struct gpio_pin_info *info)
{
- bool result = true;
-
- switch (id) {
- case GPIO_ID_DDC_DATA:
- info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
-/* case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break; */
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_DDC_CLOCK:
- info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
-/* case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break; */
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GENERIC:
- info->offset = REG(DC_GPIO_GENERIC_A);
- switch (en) {
- case GPIO_GENERIC_A:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK;
- break;
- case GPIO_GENERIC_B:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK;
- break;
- case GPIO_GENERIC_C:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK;
- break;
- case GPIO_GENERIC_D:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK;
- break;
- case GPIO_GENERIC_E:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK;
- break;
- case GPIO_GENERIC_F:
- info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_HPD:
- info->offset = REG(DC_GPIO_HPD_A);
- switch (en) {
- case GPIO_HPD_1:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK;
- break;
- case GPIO_HPD_2:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK;
- break;
- case GPIO_HPD_3:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK;
- break;
- case GPIO_HPD_4:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK;
- break;
- case GPIO_HPD_5:
- info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK;
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_GSL:
- switch (en) {
- case GPIO_GSL_GENLOCK_CLOCK:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_GENLOCK_VSYNC:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_A:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
- break;
- case GPIO_GSL_SWAPLOCK_B:
- /*not implmented*/
- ASSERT_CRITICAL(false);
- result = false;
-
- break;
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_SYNC:
- case GPIO_ID_VIP_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
-
- if (result) {
- info->offset_y = info->offset + 2;
- info->offset_en = info->offset + 1;
- info->offset_mask = info->offset - 1;
-
- info->mask_y = info->mask;
- info->mask_en = info->mask;
- info->mask_mask = info->mask;
- }
+ if (dal_hw_translate_id_to_offset(
+ gpio_pins,
+ ARRAY_SIZE(gpio_pins),
+ id, en, info))
+ return true;
- return result;
+ ASSERT_CRITICAL(false);
+ return false;
}
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c
index e7e1d9979876..7b2c4cd42450 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c
@@ -39,62 +39,76 @@
* end *********************/
+static const struct gpio_id_offset_entry gpio_offsets[] = {
+ /* HPD */
+ GPIO_ENTRY(HPD0_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_1),
+ GPIO_ENTRY(HPD1_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_2),
+ GPIO_ENTRY(HPD2_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_3),
+ GPIO_ENTRY(HPD3_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_4),
+ GPIO_ENTRY(HPD4_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_5),
+};
+
+
+/* DDC */
+static const struct gpio_ddc_offset_entry ddc_offset_map[] = {
+ { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 },
+ { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 },
+ { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 },
+ { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 },
+ { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 },
+ { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA },
+};
+
+
+static const struct gpio_pin_entry gpio_pins[] = {
+ /* DDC */
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1,
+ DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2,
+ DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3,
+ DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4,
+ DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5,
+ DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+ GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA,
+ DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK),
+};
+
+
static bool offset_to_id(
uint32_t offset,
uint32_t mask,
enum gpio_id *id,
uint32_t *en)
{
- (void)mask;
- switch (offset) {
- /* HPD */
- case REG(HPD0_DC_HPD_INT_STATUS):
- *id = GPIO_ID_HPD;
- *en = GPIO_HPD_1;
- return true;
- case REG(HPD1_DC_HPD_INT_STATUS):
- *id = GPIO_ID_HPD;
- *en = GPIO_HPD_2;
+ if (dal_hw_translate_gpio_ddc_offset_to_id(
+ ddc_offset_map,
+ ARRAY_SIZE(ddc_offset_map),
+ offset, en))
return true;
- case REG(HPD2_DC_HPD_INT_STATUS):
- *id = GPIO_ID_HPD;
- *en = GPIO_HPD_3;
- return true;
- case REG(HPD3_DC_HPD_INT_STATUS):
- *id = GPIO_ID_HPD;
- *en = GPIO_HPD_4;
- return true;
- case REG(HPD4_DC_HPD_INT_STATUS):
- *id = GPIO_ID_HPD;
- *en = GPIO_HPD_5;
- return true;
- /* DDC */
- /* we don't care about the GPIO_ID for DDC
- * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
- * directly in the create method
- */
- case REG(DC_GPIO_DDC1_A):
- *en = GPIO_DDC_LINE_DDC1;
- return true;
- case REG(DC_GPIO_DDC2_A):
- *en = GPIO_DDC_LINE_DDC2;
- return true;
- case REG(DC_GPIO_DDC3_A):
- *en = GPIO_DDC_LINE_DDC3;
- return true;
- case REG(DC_GPIO_DDC4_A):
- *en = GPIO_DDC_LINE_DDC4;
- return true;
- case REG(DC_GPIO_DDC5_A):
- *en = GPIO_DDC_LINE_DDC5;
- return true;
- case REG(DC_GPIO_DDCVGA_A):
- *en = GPIO_DDC_LINE_DDC_VGA;
+
+ if (dal_hw_translate_gpio_offset_to_id(
+ gpio_offsets,
+ ARRAY_SIZE(gpio_offsets),
+ offset, mask, id, en))
return true;
- default:
- ASSERT_CRITICAL(false);
- return false;
- }
+
+ ASSERT_CRITICAL(false);
+ return false;
}
@@ -103,81 +117,14 @@ static bool id_to_offset(
uint32_t en,
struct gpio_pin_info *info)
{
- bool result = true;
-
- switch (id) {
- case GPIO_ID_DDC_DATA:
- info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_DDC_CLOCK:
- info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK;
- switch (en) {
- case GPIO_DDC_LINE_DDC1:
- info->offset = REG(DC_GPIO_DDC1_A);
- break;
- case GPIO_DDC_LINE_DDC2:
- info->offset = REG(DC_GPIO_DDC2_A);
- break;
- case GPIO_DDC_LINE_DDC3:
- info->offset = REG(DC_GPIO_DDC3_A);
- break;
- case GPIO_DDC_LINE_DDC4:
- info->offset = REG(DC_GPIO_DDC4_A);
- break;
- case GPIO_DDC_LINE_DDC5:
- info->offset = REG(DC_GPIO_DDC5_A);
- break;
- case GPIO_DDC_LINE_DDC_VGA:
- info->offset = REG(DC_GPIO_DDCVGA_A);
- break;
- case GPIO_DDC_LINE_I2C_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
- break;
- case GPIO_ID_SYNC:
- case GPIO_ID_VIP_PAD:
- default:
- ASSERT_CRITICAL(false);
- result = false;
- }
-
- if (result) {
- info->offset_y = info->offset + 2;
- info->offset_en = info->offset + 1;
- info->offset_mask = info->offset - 1;
-
- info->mask_y = info->mask;
- info->mask_en = info->mask;
- info->mask_mask = info->mask;
- }
-
- return result;
+ if (dal_hw_translate_id_to_offset(
+ gpio_pins,
+ ARRAY_SIZE(gpio_pins),
+ id, en, info))
+ return true;
+
+ ASSERT_CRITICAL(false);
+ return false;
}
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c
index 64a5e11fce5c..b58af86dee10 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c
+++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c
@@ -133,3 +133,89 @@ bool dal_hw_translate_init(
return false;
}
}
+
+bool dal_hw_translate_gpio_offset_to_id(
+ const struct gpio_id_offset_entry *table,
+ uint32_t table_size,
+ uint32_t offset,
+ uint32_t mask,
+ enum gpio_id *id,
+ uint32_t *en)
+{
+ uint32_t i;
+
+ for (i = 0; i < table_size; i++) {
+ const struct gpio_id_offset_entry *entry = &table[i];
+
+ if (entry->offset != offset)
+ continue;
+
+ if (entry->check_mask && entry->mask != mask)
+ continue;
+
+ *id = entry->id;
+ *en = entry->en;
+
+ return true;
+ }
+
+ return false;
+}
+
+/* we don't care about the GPIO_ID for DDC
+ * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK
+ * directly in the create method
+ */
+bool dal_hw_translate_gpio_ddc_offset_to_id(
+ const struct gpio_ddc_offset_entry *table,
+ uint32_t table_size,
+ uint32_t offset,
+ uint32_t *en)
+{
+ uint32_t i;
+
+ for (i = 0; i < table_size; i++) {
+ const struct gpio_ddc_offset_entry *entry = &table[i];
+
+ if (entry->offset != offset)
+ continue;
+
+ *en = entry->en;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool dal_hw_translate_id_to_offset(
+ const struct gpio_pin_entry *table,
+ uint32_t table_size,
+ enum gpio_id id,
+ uint32_t en,
+ struct gpio_pin_info *info)
+{
+ uint32_t i;
+
+ for (i = 0; i < table_size; i++) {
+ const struct gpio_pin_entry *entry = &table[i];
+
+ if (entry->id != id || entry->en != en)
+ continue;
+
+ info->offset = entry->offset;
+ info->mask = entry->mask;
+
+ info->offset_y = info->offset + 2;
+ info->offset_en = info->offset + 1;
+ info->offset_mask = info->offset - 1;
+
+ info->mask_y = info->mask;
+ info->mask_en = info->mask;
+ info->mask_mask = info->mask;
+
+ return true;
+ }
+
+ return false;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h
index 3a7d89ca1605..339e381f8fde 100644
--- a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h
+++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h
@@ -47,4 +47,25 @@ bool dal_hw_translate_init(
enum dce_version dce_version,
enum dce_environment dce_environment);
+bool dal_hw_translate_gpio_offset_to_id(
+ const struct gpio_id_offset_entry *table,
+ uint32_t table_size,
+ uint32_t offset,
+ uint32_t mask,
+ enum gpio_id *id,
+ uint32_t *en);
+
+bool dal_hw_translate_gpio_ddc_offset_to_id(
+ const struct gpio_ddc_offset_entry *table,
+ uint32_t table_size,
+ uint32_t offset,
+ uint32_t *en);
+
+bool dal_hw_translate_id_to_offset(
+ const struct gpio_pin_entry *table,
+ uint32_t table_size,
+ enum gpio_id id,
+ uint32_t en,
+ struct gpio_pin_info *info);
+
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c
index 79cb506be5cb..cbcd22789013 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c
@@ -138,10 +138,10 @@ static void dcn31_program_compbuf_size(struct hubbub *hubbub, unsigned int compb
if (safe_to_increase || compbuf_size_segments <= hubbub2->compbuf_size_segments) {
if (compbuf_size_segments > hubbub2->compbuf_size_segments) {
- REG_WAIT(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, hubbub2->det0_size, 1, 100);
- REG_WAIT(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, hubbub2->det1_size, 1, 100);
- REG_WAIT(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, hubbub2->det2_size, 1, 100);
- REG_WAIT(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, hubbub2->det3_size, 1, 100);
+ dcn31_wait_for_det_apply(hubbub, 0);
+ dcn31_wait_for_det_apply(hubbub, 1);
+ dcn31_wait_for_det_apply(hubbub, 2);
+ dcn31_wait_for_det_apply(hubbub, 3);
}
/* Should never be hit, if it is we have an erroneous hw config*/
ASSERT(hubbub2->det0_size + hubbub2->det1_size + hubbub2->det2_size
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c
index 82d4e3e0e5e8..5e5a7a74346d 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c
@@ -571,7 +571,7 @@ void dcn35_dchvm_init(struct hubbub *hubbub)
if (riommu_active) {
// Disable gating and memory power requests
- REG_UPDATE(DCHVM_MEM_CTRL, HVM_GPUVMRET_PWR_REQ_DIS, 1);
+ REG_UPDATE_2(DCHVM_MEM_CTRL, HVM_GPUVMRET_PWR_REQ_DIS, 1, HVM_GPUVMRET_FORCE_REQ, 0);
REG_UPDATE_4(DCHVM_CLK_CTRL,
HVM_DISPCLK_R_GATE_DIS, 1,
HVM_DISPCLK_G_GATE_DIS, 1,
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c
index 302515128358..9965cf572354 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c
@@ -136,7 +136,7 @@ void hubp401_program_3dlut_fl_config(
uint32_t mpc_width = {(cfg->width == 17) ? 0 : 1};
uint32_t width = {cfg->width};
- if (cfg->layout == DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR)
+ if (cfg->layout == CM_LUT_1D_PACKED_LINEAR)
width = (cfg->width == 17) ? 4916 : 35940;
REG_UPDATE_2(_3DLUT_FL_CONFIG,
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c
index e4602c3ddc66..57de98444f6c 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c
@@ -20,6 +20,12 @@ static void hubp42_set_fgcg(struct hubp *hubp, bool enable)
{
struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp);
+ /* Temporary workaround for IOMMU mismatch issue.
+ * Fine grain control via bit1 of debug flag.
+ */
+ if (hubp->ctx->dc->debug.iommu_mismatch_temp_wka & 0x2)
+ enable = false;
+
REG_UPDATE(HUBP_CLK_CNTL, HUBP_FGCG_REP_DIS, !enable);
}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
index 042602c50e35..c9691974bf72 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
@@ -3166,12 +3166,12 @@ static void dce110_program_front_end_for_pipe(
plane_state->rotation);
/* Moved programming gamma from dc to hwss */
- if (pipe_ctx->plane_state->update_flags.bits.full_update ||
- pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
- pipe_ctx->plane_state->update_flags.bits.gamma_change)
+ if (pipe_ctx->plane_state->update_bits.full_update ||
+ pipe_ctx->plane_state->update_bits.in_transfer_func_change ||
+ pipe_ctx->plane_state->update_bits.gamma_change)
hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);
- if (pipe_ctx->plane_state->update_flags.bits.full_update)
+ if (pipe_ctx->plane_state->update_bits.full_update)
hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream);
DC_LOG_SURFACE(
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c
index a08e9f9eec17..26aa303b8237 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c
@@ -332,12 +332,12 @@ dce60_program_front_end_for_pipe(
plane_state->rotation);
/* Moved programming gamma from dc to hwss */
- if (pipe_ctx->plane_state->update_flags.bits.full_update ||
- pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
- pipe_ctx->plane_state->update_flags.bits.gamma_change)
+ if (pipe_ctx->plane_state->update_bits.full_update ||
+ pipe_ctx->plane_state->update_bits.in_transfer_func_change ||
+ pipe_ctx->plane_state->update_bits.gamma_change)
hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);
- if (pipe_ctx->plane_state->update_flags.bits.full_update)
+ if (pipe_ctx->plane_state->update_bits.full_update)
hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream);
DC_LOG_SURFACE(
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
index 7112b71af977..541cd908b341 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
@@ -2981,7 +2981,7 @@ void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx)
mpcc_id = hubp->inst;
/* If there is no full update, don't need to touch MPC tree*/
- if (!pipe_ctx->plane_state->update_flags.bits.full_update) {
+ if (!pipe_ctx->plane_state->update_bits.full_update) {
mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id);
dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id);
return;
@@ -3041,7 +3041,7 @@ static void dcn10_update_dchubp_dpp(
/* If request max dpp clk is lower than current dispclk, no need to
* divided by 2
*/
- if (plane_state->update_flags.bits.full_update) {
+ if (plane_state->update_bits.full_update) {
/* new calculated dispclk, dppclk are stored in
* context->bw_ctx.bw.dcn.clk.dispclk_khz / dppclk_khz. current
@@ -3096,7 +3096,7 @@ static void dcn10_update_dchubp_dpp(
* VTG is within DCHUBBUB which is commond block share by each pipe HUBP.
* VTG is 1:1 mapping with OTG. Each pipe HUBP will select which VTG
*/
- if (plane_state->update_flags.bits.full_update) {
+ if (plane_state->update_bits.full_update) {
hubp->funcs->hubp_vtg_sel(hubp, pipe_ctx->stream_res.tg->inst);
hubp->funcs->hubp_setup(
@@ -3113,26 +3113,26 @@ static void dcn10_update_dchubp_dpp(
size.surface_size = pipe_ctx->plane_res.scl_data.viewport;
- if (plane_state->update_flags.bits.full_update ||
- plane_state->update_flags.bits.bpp_change)
+ if (plane_state->update_bits.full_update ||
+ plane_state->update_bits.bpp_change)
dcn10_update_dpp(dpp, plane_state);
- if (plane_state->update_flags.bits.full_update ||
- plane_state->update_flags.bits.per_pixel_alpha_change ||
- plane_state->update_flags.bits.global_alpha_change)
+ if (plane_state->update_bits.full_update ||
+ plane_state->update_bits.per_pixel_alpha_change ||
+ plane_state->update_bits.global_alpha_change)
hws->funcs.update_mpcc(dc, pipe_ctx);
- if (plane_state->update_flags.bits.full_update ||
- plane_state->update_flags.bits.per_pixel_alpha_change ||
- plane_state->update_flags.bits.global_alpha_change ||
- plane_state->update_flags.bits.scaling_change ||
- plane_state->update_flags.bits.position_change) {
+ if (plane_state->update_bits.full_update ||
+ plane_state->update_bits.per_pixel_alpha_change ||
+ plane_state->update_bits.global_alpha_change ||
+ plane_state->update_bits.scaling_change ||
+ plane_state->update_bits.position_change) {
update_scaler(pipe_ctx);
}
- if (plane_state->update_flags.bits.full_update ||
- plane_state->update_flags.bits.scaling_change ||
- plane_state->update_flags.bits.position_change) {
+ if (plane_state->update_bits.full_update ||
+ plane_state->update_bits.scaling_change ||
+ plane_state->update_bits.position_change) {
hubp->funcs->mem_program_viewport(
hubp,
&pipe_ctx->plane_res.scl_data.viewport,
@@ -3150,7 +3150,7 @@ static void dcn10_update_dchubp_dpp(
dc->hwss.set_cursor_sdr_white_level(pipe_ctx);
}
- if (plane_state->update_flags.bits.full_update) {
+ if (plane_state->update_bits.full_update) {
/*gamut remap*/
dc->hwss.program_gamut_remap(pipe_ctx);
@@ -3161,15 +3161,15 @@ static void dcn10_update_dchubp_dpp(
pipe_ctx->stream_res.opp->inst);
}
- if (plane_state->update_flags.bits.full_update ||
- plane_state->update_flags.bits.pixel_format_change ||
- plane_state->update_flags.bits.horizontal_mirror_change ||
- plane_state->update_flags.bits.rotation_change ||
- plane_state->update_flags.bits.swizzle_change ||
- plane_state->update_flags.bits.dcc_change ||
- plane_state->update_flags.bits.bpp_change ||
- plane_state->update_flags.bits.scaling_change ||
- plane_state->update_flags.bits.plane_size_change) {
+ if (plane_state->update_bits.full_update ||
+ plane_state->update_bits.pixel_format_change ||
+ plane_state->update_bits.horizontal_mirror_change ||
+ plane_state->update_bits.rotation_change ||
+ plane_state->update_bits.swizzle_change ||
+ plane_state->update_bits.dcc_change ||
+ plane_state->update_bits.bpp_change ||
+ plane_state->update_bits.scaling_change ||
+ plane_state->update_bits.plane_size_change) {
hubp->funcs->hubp_program_surface_config(
hubp,
plane_state->format,
@@ -3278,16 +3278,16 @@ void dcn10_program_pipe(
hws->funcs.blank_pixel_data(dc, pipe_ctx, blank);
}
- if (pipe_ctx->plane_state->update_flags.bits.full_update)
+ if (pipe_ctx->plane_state->update_bits.full_update)
dcn10_enable_plane(dc, pipe_ctx, context);
dcn10_update_dchubp_dpp(dc, pipe_ctx, context);
hws->funcs.set_hdr_multiplier(pipe_ctx);
- if (pipe_ctx->plane_state->update_flags.bits.full_update ||
- pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
- pipe_ctx->plane_state->update_flags.bits.gamma_change)
+ if (pipe_ctx->plane_state->update_bits.full_update ||
+ pipe_ctx->plane_state->update_bits.in_transfer_func_change ||
+ pipe_ctx->plane_state->update_bits.gamma_change)
hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);
/* dcn10_translate_regamma_to_hw_format takes 750us to finish
@@ -3296,7 +3296,7 @@ void dcn10_program_pipe(
* Always call this for now since it does memcmp inside before
* doing heavy calculation and programming
*/
- if (pipe_ctx->plane_state->update_flags.bits.full_update)
+ if (pipe_ctx->plane_state->update_bits.full_update)
hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream);
}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c
index e6a8206f8ce0..95e5b6a6ba0f 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c
@@ -1066,11 +1066,11 @@ bool dcn20_set_blend_lut(
bool result = true;
const struct pwl_params *blend_lut = NULL;
- if (plane_state->blend_tf.type == TF_TYPE_HWPWL)
- blend_lut = &plane_state->blend_tf.pwl;
- else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL)
+ blend_lut = &plane_state->cm.blend_func.pwl;
+ else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
cm_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->blend_tf,
+ &plane_state->cm.blend_func,
&dpp_base->regamma_params, false);
blend_lut = &dpp_base->regamma_params;
}
@@ -1086,19 +1086,19 @@ bool dcn20_set_shaper_3dlut(
bool result = true;
const struct pwl_params *shaper_lut = NULL;
- if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL)
- shaper_lut = &plane_state->in_shaper_func.pwl;
- else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (plane_state->cm.shaper_func.type == TF_TYPE_HWPWL)
+ shaper_lut = &plane_state->cm.shaper_func.pwl;
+ else if (plane_state->cm.shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
cm_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->in_shaper_func,
+ &plane_state->cm.shaper_func,
&dpp_base->shaper_params, true);
shaper_lut = &dpp_base->shaper_params;
}
result = dpp_base->funcs->dpp_program_shaper_lut(dpp_base, shaper_lut);
- if (plane_state->lut3d_func.state.bits.initialized == 1)
+ if (plane_state->cm.lut3d_func.state.bits.initialized == 1)
result = dpp_base->funcs->dpp_program_3dlut(dpp_base,
- &plane_state->lut3d_func.lut_3d);
+ &plane_state->cm.lut3d_func.lut_3d);
else
result = dpp_base->funcs->dpp_program_3dlut(dpp_base, NULL);
@@ -1733,10 +1733,10 @@ void dcn20_update_dchubp_dpp(
if (pipe_ctx->update_flags.bits.enable ||
pipe_ctx->update_flags.bits.plane_changed ||
- plane_state->update_flags.bits.bpp_change ||
- plane_state->update_flags.bits.input_csc_change ||
- plane_state->update_flags.bits.color_space_change ||
- plane_state->update_flags.bits.coeff_reduction_change) {
+ plane_state->update_bits.bpp_change ||
+ plane_state->update_bits.input_csc_change ||
+ plane_state->update_bits.color_space_change ||
+ plane_state->update_bits.coeff_reduction_change) {
struct dc_bias_and_scale bns_params = plane_state->bias_and_scale;
// program the input csc
@@ -1760,16 +1760,16 @@ void dcn20_update_dchubp_dpp(
if (pipe_ctx->update_flags.bits.mpcc
|| pipe_ctx->update_flags.bits.plane_changed
- || plane_state->update_flags.bits.global_alpha_change
- || plane_state->update_flags.bits.per_pixel_alpha_change) {
+ || plane_state->update_bits.global_alpha_change
+ || plane_state->update_bits.per_pixel_alpha_change) {
// MPCC inst is equal to pipe index in practice
hws->funcs.update_mpcc(dc, pipe_ctx);
}
if (pipe_ctx->update_flags.bits.scaler ||
- plane_state->update_flags.bits.scaling_change ||
- plane_state->update_flags.bits.position_change ||
- plane_state->update_flags.bits.per_pixel_alpha_change ||
+ plane_state->update_bits.scaling_change ||
+ plane_state->update_bits.position_change ||
+ plane_state->update_bits.per_pixel_alpha_change ||
pipe_ctx->stream->update_flags.bits.scaling) {
pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha;
ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_36BPP);
@@ -1779,8 +1779,8 @@ void dcn20_update_dchubp_dpp(
}
if (pipe_ctx->update_flags.bits.viewport ||
- (context == dc->current_state && plane_state->update_flags.bits.position_change) ||
- (context == dc->current_state && plane_state->update_flags.bits.scaling_change) ||
+ (context == dc->current_state && plane_state->update_bits.position_change) ||
+ (context == dc->current_state && plane_state->update_bits.scaling_change) ||
(context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) {
hubp->funcs->mem_program_viewport(
@@ -1812,7 +1812,7 @@ void dcn20_update_dchubp_dpp(
if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed
|| pipe_ctx->update_flags.bits.plane_changed
|| pipe_ctx->stream->update_flags.bits.gamut_remap
- || plane_state->update_flags.bits.gamut_remap_change
+ || plane_state->update_bits.gamut_remap_change
|| pipe_ctx->stream->update_flags.bits.out_csc) {
/* dpp/cm gamut remap*/
dc->hwss.program_gamut_remap(pipe_ctx);
@@ -1828,14 +1828,14 @@ void dcn20_update_dchubp_dpp(
if (pipe_ctx->update_flags.bits.enable ||
pipe_ctx->update_flags.bits.plane_changed ||
pipe_ctx->update_flags.bits.opp_changed ||
- plane_state->update_flags.bits.pixel_format_change ||
- plane_state->update_flags.bits.horizontal_mirror_change ||
- plane_state->update_flags.bits.rotation_change ||
- plane_state->update_flags.bits.swizzle_change ||
- plane_state->update_flags.bits.dcc_change ||
- plane_state->update_flags.bits.bpp_change ||
- plane_state->update_flags.bits.scaling_change ||
- plane_state->update_flags.bits.plane_size_change) {
+ plane_state->update_bits.pixel_format_change ||
+ plane_state->update_bits.horizontal_mirror_change ||
+ plane_state->update_bits.rotation_change ||
+ plane_state->update_bits.swizzle_change ||
+ plane_state->update_bits.dcc_change ||
+ plane_state->update_bits.bpp_change ||
+ plane_state->update_bits.scaling_change ||
+ plane_state->update_bits.plane_size_change) {
struct plane_size size = plane_state->plane_size;
size.surface_size = pipe_ctx->plane_res.scl_data.viewport;
@@ -1853,7 +1853,7 @@ void dcn20_update_dchubp_dpp(
if (pipe_ctx->update_flags.bits.enable ||
pipe_ctx->update_flags.bits.plane_changed ||
- plane_state->update_flags.bits.addr_update) {
+ plane_state->update_bits.addr_update) {
if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) &&
pipe_mall_type == SUBVP_MAIN) {
union block_sequence_params params;
@@ -1969,18 +1969,18 @@ static void dcn20_program_pipe(
}
if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw ||
- pipe_ctx->plane_state->update_flags.raw ||
+ dc_pipe_update_bits_is_any_set(&pipe_ctx->plane_state->update_bits) ||
pipe_ctx->stream->update_flags.raw))
dcn20_update_dchubp_dpp(dc, pipe_ctx, context);
if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable ||
- pipe_ctx->plane_state->update_flags.bits.hdr_mult))
+ pipe_ctx->plane_state->update_bits.hdr_mult))
hws->funcs.set_hdr_multiplier(pipe_ctx);
if (pipe_ctx->plane_state &&
- (pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
- pipe_ctx->plane_state->update_flags.bits.gamma_change ||
- pipe_ctx->plane_state->update_flags.bits.lut_3d ||
+ (pipe_ctx->plane_state->update_bits.in_transfer_func_change ||
+ pipe_ctx->plane_state->update_bits.gamma_change ||
+ pipe_ctx->plane_state->update_bits.lut_3d ||
pipe_ctx->update_flags.bits.enable))
hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);
@@ -2186,7 +2186,7 @@ void dcn20_program_front_end_for_ctx(
pipe = &context->res_ctx.pipe_ctx[i];
if (!pipe->top_pipe && !pipe->prev_odm_pipe
&& pipe->stream && pipe->stream->num_wb_info > 0
- && (pipe->update_flags.raw || (pipe->plane_state && pipe->plane_state->update_flags.raw)
+ && (pipe->update_flags.raw || (pipe->plane_state && dc_pipe_update_bits_is_any_set(&pipe->plane_state->update_bits))
|| pipe->stream->update_flags.raw)
&& hws->funcs.program_all_writeback_pipes_in_tree)
hws->funcs.program_all_writeback_pipes_in_tree(dc, pipe->stream, context);
@@ -2998,7 +2998,7 @@ void dcn20_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx)
mpcc_id = hubp->inst;
/* If there is no full update, don't need to touch MPC tree*/
- if (!pipe_ctx->plane_state->update_flags.bits.full_update &&
+ if (!pipe_ctx->plane_state->update_bits.full_update &&
!pipe_ctx->update_flags.bits.mpcc) {
mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id);
dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c
index ce18d75fd991..7b820bdae55b 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c
@@ -485,7 +485,7 @@ void dcn201_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx)
mpcc_id = dpp_id;
/* If there is no full update, don't need to touch MPC tree*/
- if (!pipe_ctx->plane_state->update_flags.bits.full_update) {
+ if (!pipe_ctx->plane_state->update_bits.full_update) {
dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id);
mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id);
return;
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
index a7c85a2302ab..aed9d06ec538 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
@@ -239,11 +239,13 @@ bool dcn30_set_blend_lut(
bool result = true;
const struct pwl_params *blend_lut = NULL;
- if (plane_state->blend_tf.type == TF_TYPE_HWPWL)
- blend_lut = &plane_state->blend_tf.pwl;
- else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL)
+ blend_lut = &plane_state->cm.blend_func.pwl;
+ else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
result = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->blend_tf, &dpp_base->regamma_params, false);
+ &plane_state->cm.blend_func,
+ &dpp_base->regamma_params,
+ false);
if (!result)
return result;
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
index a3242e7521a4..c2ea25927765 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
@@ -490,12 +490,12 @@ bool dcn32_set_mcm_luts(
const struct pwl_params *lut_params = NULL;
// 1D LUT
- if (plane_state->blend_tf.type == TF_TYPE_HWPWL)
- lut_params = &plane_state->blend_tf.pwl;
- else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) {
- result = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->blend_tf,
- &dpp_base->regamma_params, false);
+ if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL)
+ lut_params = &plane_state->cm.blend_func.pwl;
+ else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ result = cm3_helper_translate_curve_to_degamma_hw_format(
+ &plane_state->cm.blend_func,
+ &dpp_base->regamma_params);
if (!result)
return result;
@@ -505,21 +505,22 @@ bool dcn32_set_mcm_luts(
lut_params = NULL;
// Shaper
- if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL)
- lut_params = &plane_state->in_shaper_func.pwl;
- else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (plane_state->cm.shaper_func.type == TF_TYPE_HWPWL)
+ lut_params = &plane_state->cm.shaper_func.pwl;
+ else if (plane_state->cm.shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
// TODO: dpp_base replace
rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->in_shaper_func,
- &dpp_base->shaper_params, true);
+ &plane_state->cm.shaper_func,
+ &dpp_base->shaper_params,
+ true);
lut_params = rval ? &dpp_base->shaper_params : NULL;
}
mpc->funcs->program_shaper(mpc, lut_params, mpcc_id);
// 3D
- if (plane_state->lut3d_func.state.bits.initialized == 1)
- result = mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id);
+ if (plane_state->cm.lut3d_func.state.bits.initialized == 1)
+ result = mpc->funcs->program_3dlut(mpc, &plane_state->cm.lut3d_func.lut_3d, mpcc_id);
else
result = mpc->funcs->program_3dlut(mpc, NULL, mpcc_id);
@@ -551,9 +552,8 @@ bool dcn32_set_input_transfer_func(struct dc *dc,
if (plane_state->in_transfer_func.type == TF_TYPE_HWPWL)
params = &plane_state->in_transfer_func.pwl;
else if (plane_state->in_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS &&
- cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->in_transfer_func,
- &dpp_base->degamma_params, false))
+ cm3_helper_translate_curve_to_degamma_hw_format(&plane_state->in_transfer_func,
+ &dpp_base->degamma_params))
params = &dpp_base->degamma_params;
dpp_base->funcs->dpp_program_gamcor_lut(dpp_base, params);
@@ -1463,7 +1463,7 @@ void dcn32_update_phantom_vp_position(struct dc *dc,
if (pipe->stream && dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_MAIN &&
dc_state_get_paired_subvp_stream(context, pipe->stream) == phantom_pipe->stream) {
- if (pipe->plane_state && pipe->plane_state->update_flags.bits.position_change) {
+ if (pipe->plane_state && pipe->plane_state->update_bits.position_change) {
phantom_plane->src_rect.x = pipe->plane_state->src_rect.x;
phantom_plane->src_rect.y = pipe->plane_state->src_rect.y;
@@ -1471,7 +1471,7 @@ void dcn32_update_phantom_vp_position(struct dc *dc,
phantom_plane->dst_rect.x = pipe->plane_state->dst_rect.x;
phantom_plane->dst_rect.y = pipe->plane_state->dst_rect.y;
- phantom_pipe->plane_state->update_flags.bits.position_change = 1;
+ phantom_pipe->plane_state->update_bits.position_change = 1;
resource_build_scaling_params(phantom_pipe);
return;
}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
index 8f9038fec0f7..01027d120cb0 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
@@ -581,9 +581,6 @@ void dcn35_power_down_on_boot(struct dc *dc)
bool dcn35_apply_idle_power_optimizations(struct dc *dc, bool enable)
{
- if (dc->debug.dmcub_emulation)
- return true;
-
if (enable) {
uint32_t num_active_edp = 0;
int i;
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
index 96815a92a629..0336d118e77e 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
@@ -321,7 +321,7 @@ void dcn401_init_hw(struct dc *dc)
user_level = link->panel_cntl->stored_backlight_registers.USER_LEVEL;
}
- if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
+ if (link->force_to_use_aux) {
struct graphics_object_i2c_info i2c_info;
struct ddc *ddc_pin;
struct gpio_ddc_hw_info hw_info;
@@ -410,37 +410,27 @@ static void dcn401_get_mcm_lut_xable_from_pipe_ctx(struct dc *dc, struct pipe_ct
enum MCM_LUT_XABLE *lut3d_xable,
enum MCM_LUT_XABLE *lut1d_xable)
{
- enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL;
- bool lut1d_enable = false;
struct mpc *mpc = dc->res_pool->mpc;
int mpcc_id = pipe_ctx->plane_res.hubp->inst;
if (!pipe_ctx->plane_state)
return;
- shaper_3dlut_setting = pipe_ctx->plane_state->mcm_shaper_3dlut_setting;
- lut1d_enable = pipe_ctx->plane_state->mcm_lut1d_enable;
+
mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id);
pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE;
- *lut1d_xable = lut1d_enable ? MCM_LUT_ENABLE : MCM_LUT_DISABLE;
-
- switch (shaper_3dlut_setting) {
- case DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL:
- *lut3d_xable = *shaper_xable = MCM_LUT_DISABLE;
- break;
- case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER:
- *lut3d_xable = MCM_LUT_DISABLE;
- *shaper_xable = MCM_LUT_ENABLE;
- break;
- case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT:
- *lut3d_xable = *shaper_xable = MCM_LUT_ENABLE;
- break;
- }
+ *lut1d_xable = pipe_ctx->plane_state->cm.flags.bits.blend_enable ?
+ MCM_LUT_ENABLE : MCM_LUT_DISABLE;
+ *shaper_xable = pipe_ctx->plane_state->cm.flags.bits.shaper_enable ?
+ MCM_LUT_ENABLE : MCM_LUT_DISABLE;
+ *lut3d_xable = (pipe_ctx->plane_state->cm.flags.bits.shaper_enable &&
+ pipe_ctx->plane_state->cm.flags.bits.lut3d_enable) ?
+ MCM_LUT_ENABLE : MCM_LUT_DISABLE;
}
void dcn401_populate_mcm_luts(struct dc *dc,
struct pipe_ctx *pipe_ctx,
- struct dc_cm2_func_luts mcm_luts,
+ const struct dc_plane_cm *cm,
bool lut_bank_a)
{
struct dpp *dpp_base = pipe_ctx->plane_res.dpp;
@@ -448,14 +438,17 @@ void dcn401_populate_mcm_luts(struct dc *dc,
int mpcc_id = hubp->inst;
struct mpc *mpc = dc->res_pool->mpc;
union mcm_lut_params m_lut_params;
- enum dc_cm2_transfer_func_source lut3d_src = mcm_luts.lut3d_data.lut3d_src;
+ const bool lut3d_dma = !!cm->flags.bits.lut3d_dma_enable;
enum hubp_3dlut_fl_format format = 0;
enum hubp_3dlut_fl_mode mode;
- enum hubp_3dlut_fl_width width = 0;
+ /* Width was previously hard-coded to TRANSFORMED via local_mcm build,
+ * preserve identical behavior.
+ */
+ enum hubp_3dlut_fl_width width = hubp_3dlut_fl_width_transformed;
enum hubp_3dlut_fl_addressing_mode addr_mode;
- enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g = 0;
- enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b = 0;
- enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r = 0;
+ enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g;
+ enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b;
+ enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r;
enum MCM_LUT_XABLE shaper_xable = MCM_LUT_DISABLE;
enum MCM_LUT_XABLE lut3d_xable = MCM_LUT_DISABLE;
enum MCM_LUT_XABLE lut1d_xable = MCM_LUT_DISABLE;
@@ -464,13 +457,13 @@ void dcn401_populate_mcm_luts(struct dc *dc,
dcn401_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable);
/* 1D LUT */
- if (mcm_luts.lut1d_func) {
+ {
memset(&m_lut_params, 0, sizeof(m_lut_params));
- if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL)
- m_lut_params.pwl = &mcm_luts.lut1d_func->pwl;
- else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (cm->blend_func.type == TF_TYPE_HWPWL)
+ m_lut_params.pwl = &cm->blend_func.pwl;
+ else if (cm->blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx,
- mcm_luts.lut1d_func,
+ &cm->blend_func,
&dpp_base->regamma_params, false);
m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL;
}
@@ -483,14 +476,14 @@ void dcn401_populate_mcm_luts(struct dc *dc,
}
/* Shaper */
- if (mcm_luts.shaper && mcm_luts.lut3d_data.mpc_3dlut_enable) {
+ if (cm->flags.bits.lut3d_enable) {
memset(&m_lut_params, 0, sizeof(m_lut_params));
- if (mcm_luts.shaper->type == TF_TYPE_HWPWL)
- m_lut_params.pwl = &mcm_luts.shaper->pwl;
- else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (cm->shaper_func.type == TF_TYPE_HWPWL)
+ m_lut_params.pwl = &cm->shaper_func.pwl;
+ else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
ASSERT(false);
rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx,
- mcm_luts.shaper,
+ &cm->shaper_func,
&dpp_base->regamma_params, true);
m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL;
}
@@ -503,42 +496,43 @@ void dcn401_populate_mcm_luts(struct dc *dc,
}
/* 3DLUT */
- switch (lut3d_src) {
- case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM:
+ if (!lut3d_dma) {
+ /* SYSMEM (legacy lut3d_func) */
memset(&m_lut_params, 0, sizeof(m_lut_params));
if (hubp->funcs->hubp_enable_3dlut_fl)
hubp->funcs->hubp_enable_3dlut_fl(hubp, false);
- if (mcm_luts.lut3d_data.lut3d_func && mcm_luts.lut3d_data.lut3d_func->state.bits.initialized) {
- m_lut_params.lut3d = &mcm_luts.lut3d_data.lut3d_func->lut_3d;
+ if (cm->lut3d_func.state.bits.initialized) {
+ m_lut_params.lut3d = &cm->lut3d_func.lut_3d;
if (mpc->funcs->populate_lut)
mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, m_lut_params, lut_bank_a, mpcc_id);
if (mpc->funcs->program_lut_mode)
mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a,
mpcc_id);
}
- break;
- case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM:
- switch (mcm_luts.lut3d_data.gpu_mem_params.size) {
- case DC_CM2_GPU_MEM_SIZE_333333:
+ } else {
+ /* VIDMEM (3DLUT DMA Fast Load) */
+
+ /* Select width based on the requested LUT size */
+ switch (cm->lut3d_dma.size) {
+#if defined(CONFIG_DRM_AMD_DC_DCN4_2)
+ case CM_LUT_SIZE_333333:
if (dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33)
width = hubp_3dlut_fl_width_33;
break;
- case DC_CM2_GPU_MEM_SIZE_171717:
+#endif // CONFIG_DRM_AMD_DC_DCN4_2
+ case CM_LUT_SIZE_171717:
width = hubp_3dlut_fl_width_17;
break;
- case DC_CM2_GPU_MEM_SIZE_TRANSFORMED:
- width = hubp_3dlut_fl_width_transformed;
- break;
default:
- //TODO: handle default case
+ /* keep default hubp_3dlut_fl_width_transformed */
break;
}
//check for support
if (mpc->funcs->mcm.is_config_supported &&
!mpc->funcs->mcm.is_config_supported(width))
- break;
+ return;
if (mpc->funcs->program_lut_read_write_control)
mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, mpcc_id);
@@ -546,21 +540,24 @@ void dcn401_populate_mcm_luts(struct dc *dc,
mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id);
if (hubp->funcs->hubp_program_3dlut_fl_addr)
- hubp->funcs->hubp_program_3dlut_fl_addr(hubp, mcm_luts.lut3d_data.gpu_mem_params.addr);
+ hubp->funcs->hubp_program_3dlut_fl_addr(hubp, cm->lut3d_dma.addr);
+ /* bit_depth was previously zero-initialized in local_mcm,
+ * preserve identical behavior.
+ */
if (mpc->funcs->mcm.program_bit_depth)
- mpc->funcs->mcm.program_bit_depth(mpc, mcm_luts.lut3d_data.gpu_mem_params.bit_depth, mpcc_id);
+ mpc->funcs->mcm.program_bit_depth(mpc, 0, mpcc_id);
- switch (mcm_luts.lut3d_data.gpu_mem_params.layout) {
- case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB:
+ switch (cm->lut3d_dma.swizzle) {
+ case CM_LUT_3D_SWIZZLE_LINEAR_RGB:
mode = hubp_3dlut_fl_mode_native_1;
addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
break;
- case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR:
+ case CM_LUT_3D_SWIZZLE_LINEAR_BGR:
mode = hubp_3dlut_fl_mode_native_2;
addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
break;
- case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR:
+ case CM_LUT_1D_PACKED_LINEAR:
mode = hubp_3dlut_fl_mode_transform;
addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear;
break;
@@ -575,40 +572,38 @@ void dcn401_populate_mcm_luts(struct dc *dc,
if (hubp->funcs->hubp_program_3dlut_fl_addressing_mode)
hubp->funcs->hubp_program_3dlut_fl_addressing_mode(hubp, addr_mode);
- switch (mcm_luts.lut3d_data.gpu_mem_params.format_params.format) {
- case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB:
+ switch (cm->lut3d_dma.format) {
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB:
format = hubp_3dlut_fl_format_unorm_12msb_bitslice;
break;
- case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB:
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB:
format = hubp_3dlut_fl_format_unorm_12lsb_bitslice;
break;
- case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10:
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10:
format = hubp_3dlut_fl_format_float_fp1_5_10;
break;
+ default:
+ break;
}
if (hubp->funcs->hubp_program_3dlut_fl_format)
hubp->funcs->hubp_program_3dlut_fl_format(hubp, format);
if (hubp->funcs->hubp_update_3dlut_fl_bias_scale &&
mpc->funcs->mcm.program_bias_scale) {
mpc->funcs->mcm.program_bias_scale(mpc,
- mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias,
- mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale,
+ cm->lut3d_dma.bias,
+ cm->lut3d_dma.scale,
mpcc_id);
hubp->funcs->hubp_update_3dlut_fl_bias_scale(hubp,
- mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias,
- mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale);
+ cm->lut3d_dma.bias,
+ cm->lut3d_dma.scale);
}
- //navi 4x has a bug and r and blue are swapped and need to be worked around here in
- //TODO: need to make a method for get_xbar per asic OR do the workaround in program_crossbar for 4x
- switch (mcm_luts.lut3d_data.gpu_mem_params.component_order) {
- case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA:
- default:
- crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15;
- crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31;
- crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47;
- break;
- }
+ /* component_order was previously hard-coded to RGBA in local_mcm,
+ * preserve identical behavior.
+ */
+ crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15;
+ crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31;
+ crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47;
if (hubp->funcs->hubp_program_3dlut_fl_crossbar)
hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp,
@@ -634,8 +629,6 @@ void dcn401_populate_mcm_luts(struct dc *dc,
mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id);
}
}
- break;
-
}
}
@@ -660,19 +653,19 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx,
const struct pwl_params *lut_params = NULL;
bool rval;
- if (plane_state->mcm_luts.lut3d_data.lut3d_src == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) {
- dcn401_populate_mcm_luts(dc, pipe_ctx, plane_state->mcm_luts, plane_state->lut_bank_a);
+ if (plane_state->cm.flags.bits.lut3d_dma_enable) {
+ dcn401_populate_mcm_luts(dc, pipe_ctx, &plane_state->cm, plane_state->lut_bank_a);
return true;
}
mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id);
pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE;
// 1D LUT
- if (plane_state->blend_tf.type == TF_TYPE_HWPWL)
- lut_params = &plane_state->blend_tf.pwl;
- else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL)
+ lut_params = &plane_state->cm.blend_func.pwl;
+ else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->blend_tf,
+ &plane_state->cm.blend_func,
&dpp_base->regamma_params, false);
lut_params = rval ? &dpp_base->regamma_params : NULL;
}
@@ -680,12 +673,12 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx,
lut_params = NULL;
// Shaper
- if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL)
- lut_params = &plane_state->in_shaper_func.pwl;
- else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (plane_state->cm.shaper_func.type == TF_TYPE_HWPWL)
+ lut_params = &plane_state->cm.shaper_func.pwl;
+ else if (plane_state->cm.shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
// TODO: dpp_base replace
rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->in_shaper_func,
+ &plane_state->cm.shaper_func,
&dpp_base->shaper_params, true);
lut_params = rval ? &dpp_base->shaper_params : NULL;
}
@@ -693,8 +686,8 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx,
// 3D
if (mpc->funcs->program_3dlut) {
- if (plane_state->lut3d_func.state.bits.initialized == 1)
- result &= mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id);
+ if (plane_state->cm.lut3d_func.state.bits.initialized == 1)
+ result &= mpc->funcs->program_3dlut(mpc, &plane_state->cm.lut3d_func.lut_3d, mpcc_id);
else
result &= mpc->funcs->program_3dlut(mpc, NULL, mpcc_id);
}
@@ -1428,7 +1421,7 @@ void dcn401_wait_for_dcc_meta_propagation(const struct dc *dc,
if (pipe_ctx->plane_state &&
pipe_ctx->plane_state->dcc.enable &&
pipe_ctx->plane_state->flip_immediate &&
- pipe_ctx->plane_state->update_flags.bits.addr_update) {
+ pipe_ctx->plane_state->update_bits.addr_update) {
is_wait_needed = true;
break;
}
@@ -1503,6 +1496,57 @@ void dcn401_prepare_bandwidth(struct dc *dc,
}
}
+void dcn401_prepare_bandwidth_sequence(struct dc *dc,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state)
+{
+ struct hubbub *hubbub = dc->res_pool->hubbub;
+ bool p_state_change_support = context->bw_ctx.bw.dcn.clk.p_state_change_support;
+ unsigned int compbuf_size = 0;
+
+ /* Any transition into P-State support should disable MCLK switching first to avoid hangs */
+ if (p_state_change_support) {
+ dc->optimized_required = true;
+ context->bw_ctx.bw.dcn.clk.p_state_change_support = false;
+ }
+
+ if (dc->clk_mgr->dc_mode_softmax_enabled)
+ if (dc->clk_mgr->clks.dramclk_khz <= (int)dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&
+ context->bw_ctx.bw.dcn.clk.dramclk_khz > (int)dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)
+ hwss_add_clk_mgr_set_max_memclk(seq_state, dc->clk_mgr,
+ dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz);
+
+ /* Build bandwidth and display clocks back-to-back (SW calc + append BLS steps) */
+ if (dc->clk_mgr->funcs->build_clock_update_for_bls)
+ dc->clk_mgr->funcs->build_clock_update_for_bls(
+ dc->clk_mgr, context, false, seq_state);
+
+ hwss_add_hubbub_program_watermarks(seq_state, dc, hubbub,
+ &context->bw_ctx.bw.dcn.watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
+ false);
+
+ if (hubbub->funcs->program_arbiter)
+ hwss_add_hubbub_program_arbiter(seq_state, dc, hubbub,
+ &context->bw_ctx.bw.dcn.arb_regs, false);
+
+ if (hubbub->funcs->program_compbuf_segments) {
+ compbuf_size = context->bw_ctx.bw.dcn.arb_regs.compbuf_size;
+ dc->optimized_required |= (compbuf_size != dc->current_state->bw_ctx.bw.dcn.arb_regs.compbuf_size);
+
+ hwss_add_hubbub_program_compbuf_segments(seq_state, hubbub, compbuf_size, false);
+ }
+
+ if (dc->debug.fams2_config.bits.enable) {
+ dcn401_dmub_hw_control_lock(dc, context, true);
+ dcn401_fams2_update_config(dc, context, false);
+ dcn401_dmub_hw_control_lock(dc, context, false);
+ }
+
+ if (p_state_change_support != context->bw_ctx.bw.dcn.clk.p_state_change_support)
+ context->bw_ctx.bw.dcn.clk.p_state_change_support = p_state_change_support;
+}
+
void dcn401_optimize_bandwidth(
struct dc *dc,
struct dc_state *context)
@@ -1556,6 +1600,50 @@ void dcn401_optimize_bandwidth(
}
}
+/*
+ * optimize_bandwidth_sequence is unused for now. It will be used when
+ * dc_commit_state_no_check is moved into block sequence pattern, similar
+ * to how commit_planes_do_stream_update_sequence replaces
+ * commit_planes_do_stream_update.
+ */
+void dcn401_optimize_bandwidth_sequence(struct dc *dc,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state)
+{
+ struct hubbub *hubbub = dc->res_pool->hubbub;
+
+ /* enable fams2 if needed */
+ if (dc->debug.fams2_config.bits.enable) {
+ dcn401_dmub_hw_control_lock(dc, context, true);
+ dcn401_fams2_update_config(dc, context, true);
+ dcn401_dmub_hw_control_lock(dc, context, false);
+ }
+
+ hwss_add_hubbub_program_watermarks(seq_state, dc, hubbub,
+ &context->bw_ctx.bw.dcn.watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
+ true);
+
+ if (hubbub->funcs->program_arbiter)
+ hwss_add_hubbub_program_arbiter(seq_state, dc, hubbub,
+ &context->bw_ctx.bw.dcn.arb_regs, true);
+
+ if (dc->clk_mgr->dc_mode_softmax_enabled)
+ if (dc->clk_mgr->clks.dramclk_khz > (int)dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 &&
+ context->bw_ctx.bw.dcn.clk.dramclk_khz <= (int)dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000)
+ hwss_add_clk_mgr_set_max_memclk(seq_state, dc->clk_mgr,
+ dc->clk_mgr->bw_params->dc_mode_softmax_memclk);
+
+ if (hubbub->funcs->program_compbuf_segments)
+ hwss_add_hubbub_program_compbuf_segments(seq_state, hubbub,
+ context->bw_ctx.bw.dcn.arb_regs.compbuf_size, true);
+
+ /* Build bandwidth and display clocks (SW calc + append BLS steps) */
+ if (dc->clk_mgr->funcs->build_clock_update_for_bls)
+ dc->clk_mgr->funcs->build_clock_update_for_bls(
+ dc->clk_mgr, context, true, seq_state);
+}
+
void dcn401_dmub_hw_control_lock(struct dc *dc,
struct dc_state *context,
bool lock)
@@ -1999,10 +2087,9 @@ void dcn401_perform_3dlut_wa_unlock(struct pipe_ctx *pipe_ctx)
for (odm_pipe = pipe_ctx; odm_pipe != NULL; odm_pipe = odm_pipe->next_odm_pipe) {
for (mpc_pipe = odm_pipe; mpc_pipe != NULL; mpc_pipe = mpc_pipe->bottom_pipe) {
- if (mpc_pipe->plane_state && mpc_pipe->plane_state->mcm_luts.lut3d_data.lut3d_src
- == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM
- && mpc_pipe->plane_state->mcm_shaper_3dlut_setting
- == DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT) {
+ if (mpc_pipe->plane_state &&
+ mpc_pipe->plane_state->cm.flags.bits.lut3d_enable &&
+ mpc_pipe->plane_state->cm.flags.bits.lut3d_dma_enable) {
wa_pipes[wa_pipe_ct++] = mpc_pipe;
}
}
@@ -2276,18 +2363,18 @@ void dcn401_program_pipe(
}
if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw ||
- pipe_ctx->plane_state->update_flags.raw ||
+ dc_pipe_update_bits_is_any_set(&pipe_ctx->plane_state->update_bits) ||
pipe_ctx->stream->update_flags.raw))
dc->hwss.update_dchubp_dpp(dc, pipe_ctx, context);
if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable ||
- pipe_ctx->plane_state->update_flags.bits.hdr_mult))
+ pipe_ctx->plane_state->update_bits.hdr_mult))
hws->funcs.set_hdr_multiplier(pipe_ctx);
if (pipe_ctx->plane_state &&
- (pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
- pipe_ctx->plane_state->update_flags.bits.gamma_change ||
- pipe_ctx->plane_state->update_flags.bits.lut_3d ||
+ (pipe_ctx->plane_state->update_bits.in_transfer_func_change ||
+ pipe_ctx->plane_state->update_bits.gamma_change ||
+ pipe_ctx->plane_state->update_bits.lut_3d ||
pipe_ctx->update_flags.bits.enable))
hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);
@@ -2346,7 +2433,7 @@ void dcn401_program_pipe(
pipe_ctx->stream_res.test_pattern_params.offset);
}
if (pipe_ctx->plane_state
- && pipe_ctx->plane_state->update_flags.bits.cm_hist_change
+ && pipe_ctx->plane_state->update_bits.cm_hist_change
&& hws->funcs.program_cm_hist)
hws->funcs.program_cm_hist(dc, pipe_ctx, pipe_ctx->plane_state);
}
@@ -2427,7 +2514,7 @@ void dcn401_program_pipe_sequence(
}
if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw ||
- pipe_ctx->plane_state->update_flags.raw ||
+ dc_pipe_update_bits_is_any_set(&pipe_ctx->plane_state->update_bits) ||
pipe_ctx->stream->update_flags.raw)) {
if (dc->hwss.update_dchubp_dpp_sequence)
@@ -2435,15 +2522,15 @@ void dcn401_program_pipe_sequence(
}
if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable ||
- pipe_ctx->plane_state->update_flags.bits.hdr_mult)) {
+ pipe_ctx->plane_state->update_bits.hdr_mult)) {
hws->funcs.set_hdr_multiplier_sequence(pipe_ctx, seq_state);
}
if (pipe_ctx->plane_state &&
- (pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
- pipe_ctx->plane_state->update_flags.bits.gamma_change ||
- pipe_ctx->plane_state->update_flags.bits.lut_3d ||
+ (pipe_ctx->plane_state->update_bits.in_transfer_func_change ||
+ pipe_ctx->plane_state->update_bits.gamma_change ||
+ pipe_ctx->plane_state->update_bits.lut_3d ||
pipe_ctx->update_flags.bits.enable)) {
hwss_add_dpp_set_input_transfer_func(seq_state, dc, pipe_ctx, pipe_ctx->plane_state);
@@ -2501,7 +2588,7 @@ void dcn401_program_pipe_sequence(
}
if (pipe_ctx->plane_state
- && pipe_ctx->plane_state->update_flags.bits.cm_hist_change
+ && pipe_ctx->plane_state->update_bits.cm_hist_change
&& hws->funcs.program_cm_hist) {
hwss_add_dpp_program_cm_hist(seq_state, pipe_ctx->plane_res.dpp,
@@ -2655,7 +2742,7 @@ void dcn401_program_front_end_for_ctx(
pipe = &context->res_ctx.pipe_ctx[i];
if (!pipe->top_pipe && !pipe->prev_odm_pipe
&& pipe->stream && pipe->stream->num_wb_info > 0
- && (pipe->update_flags.raw || (pipe->plane_state && pipe->plane_state->update_flags.raw)
+ && (pipe->update_flags.raw || (pipe->plane_state && dc_pipe_update_bits_is_any_set(&pipe->plane_state->update_bits))
|| pipe->stream->update_flags.raw)
&& hws->funcs.program_all_writeback_pipes_in_tree)
hws->funcs.program_all_writeback_pipes_in_tree(dc, pipe->stream, context);
@@ -3741,10 +3828,10 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
/* Step 7: DPP setup - input CSC and format setup */
if (pipe_ctx->update_flags.bits.enable ||
pipe_ctx->update_flags.bits.plane_changed ||
- plane_state->update_flags.bits.bpp_change ||
- plane_state->update_flags.bits.input_csc_change ||
- plane_state->update_flags.bits.color_space_change ||
- plane_state->update_flags.bits.coeff_reduction_change) {
+ plane_state->update_bits.bpp_change ||
+ plane_state->update_bits.input_csc_change ||
+ plane_state->update_bits.color_space_change ||
+ plane_state->update_bits.coeff_reduction_change) {
hwss_add_dpp_setup_dpp(seq_state, pipe_ctx);
/* Step 8: DPP cursor matrix setup */
@@ -3761,8 +3848,8 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
/* Step 10: MPCC updates */
if (pipe_ctx->update_flags.bits.mpcc ||
pipe_ctx->update_flags.bits.plane_changed ||
- plane_state->update_flags.bits.global_alpha_change ||
- plane_state->update_flags.bits.per_pixel_alpha_change) {
+ plane_state->update_bits.global_alpha_change ||
+ plane_state->update_bits.per_pixel_alpha_change) {
/* Check if update_mpcc_sequence is implemented and prefer it over single MPC_UPDATE_MPCC step */
if (hws->funcs.update_mpcc_sequence)
@@ -3771,9 +3858,9 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
/* Step 11: DPP scaler setup */
if (pipe_ctx->update_flags.bits.scaler ||
- plane_state->update_flags.bits.scaling_change ||
- plane_state->update_flags.bits.position_change ||
- plane_state->update_flags.bits.per_pixel_alpha_change ||
+ plane_state->update_bits.scaling_change ||
+ plane_state->update_bits.position_change ||
+ plane_state->update_bits.per_pixel_alpha_change ||
pipe_ctx->stream->update_flags.bits.scaling) {
pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha;
ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_36BPP);
@@ -3782,8 +3869,8 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
/* Step 12: HUBP viewport programming */
if (pipe_ctx->update_flags.bits.viewport ||
- (context == dc->current_state && plane_state->update_flags.bits.position_change) ||
- (context == dc->current_state && plane_state->update_flags.bits.scaling_change) ||
+ (context == dc->current_state && plane_state->update_bits.position_change) ||
+ (context == dc->current_state && plane_state->update_bits.scaling_change) ||
(context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) {
hwss_add_hubp_mem_program_viewport(seq_state, hubp,
&pipe_ctx->plane_res.scl_data.viewport, &pipe_ctx->plane_res.scl_data.viewport_c);
@@ -3815,7 +3902,7 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed ||
pipe_ctx->update_flags.bits.plane_changed ||
pipe_ctx->stream->update_flags.bits.gamut_remap ||
- plane_state->update_flags.bits.gamut_remap_change ||
+ plane_state->update_bits.gamut_remap_change ||
pipe_ctx->stream->update_flags.bits.out_csc) {
/* Gamut remap */
@@ -3830,14 +3917,14 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
if (pipe_ctx->update_flags.bits.enable ||
pipe_ctx->update_flags.bits.plane_changed ||
pipe_ctx->update_flags.bits.opp_changed ||
- plane_state->update_flags.bits.pixel_format_change ||
- plane_state->update_flags.bits.horizontal_mirror_change ||
- plane_state->update_flags.bits.rotation_change ||
- plane_state->update_flags.bits.swizzle_change ||
- plane_state->update_flags.bits.dcc_change ||
- plane_state->update_flags.bits.bpp_change ||
- plane_state->update_flags.bits.scaling_change ||
- plane_state->update_flags.bits.plane_size_change) {
+ plane_state->update_bits.pixel_format_change ||
+ plane_state->update_bits.horizontal_mirror_change ||
+ plane_state->update_bits.rotation_change ||
+ plane_state->update_bits.swizzle_change ||
+ plane_state->update_bits.dcc_change ||
+ plane_state->update_bits.bpp_change ||
+ plane_state->update_bits.scaling_change ||
+ plane_state->update_bits.plane_size_change) {
struct plane_size size = plane_state->plane_size;
size.surface_size = pipe_ctx->plane_res.scl_data.viewport;
@@ -3851,7 +3938,7 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
/* Step 19: Update plane address (with SubVP support) */
if (pipe_ctx->update_flags.bits.enable ||
pipe_ctx->update_flags.bits.plane_changed ||
- plane_state->update_flags.bits.addr_update) {
+ plane_state->update_bits.addr_update) {
/* SubVP save surface address if needed */
if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && pipe_mall_type == SUBVP_MAIN) {
@@ -3924,7 +4011,7 @@ void dcn401_update_mpcc_sequence(struct dc *dc,
mpcc_id = hubp->inst;
/* Step 1: Update blending if no full update needed */
- if (!pipe_ctx->plane_state->update_flags.bits.full_update &&
+ if (!pipe_ctx->plane_state->update_bits.full_update &&
!pipe_ctx->update_flags.bits.mpcc) {
/* Update blending configuration */
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h
index f78162ab859b..a760050eea8c 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h
@@ -52,7 +52,7 @@ enum dc_status dcn401_enable_stream_timing(
void dcn401_enable_stream(struct pipe_ctx *pipe_ctx);
void dcn401_populate_mcm_luts(struct dc *dc,
struct pipe_ctx *pipe_ctx,
- struct dc_cm2_func_luts mcm_luts,
+ const struct dc_plane_cm *cm,
bool lut_bank_a);
void dcn401_setup_hpo_hw_control(const struct dce_hwseq *hws, bool enable);
@@ -70,10 +70,20 @@ void dcn401_wait_for_dcc_meta_propagation(const struct dc *dc,
void dcn401_prepare_bandwidth(struct dc *dc,
struct dc_state *context);
+struct block_sequence_state;
+
+void dcn401_prepare_bandwidth_sequence(struct dc *dc,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
+
void dcn401_optimize_bandwidth(
struct dc *dc,
struct dc_state *context);
+void dcn401_optimize_bandwidth_sequence(struct dc *dc,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
+
void dcn401_dmub_hw_control_lock(struct dc *dc,
struct dc_state *context,
bool lock);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
index 33b2cf344f1e..f206e221f926 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
@@ -44,7 +44,9 @@ static const struct hw_sequencer_funcs dcn401_funcs = {
.interdependent_update_lock = dcn401_interdependent_update_lock,
.cursor_lock = dcn10_cursor_lock,
.prepare_bandwidth = dcn401_prepare_bandwidth,
+ .prepare_bandwidth_sequence = dcn401_prepare_bandwidth_sequence,
.optimize_bandwidth = dcn401_optimize_bandwidth,
+ .optimize_bandwidth_sequence = dcn401_optimize_bandwidth_sequence,
.update_bandwidth = dcn401_update_bandwidth,
.set_drr = dcn10_set_drr,
.get_position = dcn10_get_position,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c
index 664004cadf10..f415473517d4 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c
@@ -70,6 +70,7 @@ void dcn42_init_hw(struct dc *dc)
uint32_t user_level = MAX_BACKLIGHT_LEVEL;
bool dchub_ref_freq_changed;
int current_dchub_ref_freq = 0;
+ uint8_t dcfclk_gate_dis_value = 0;
if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) {
dc->clk_mgr->funcs->init_clocks(dc->clk_mgr);
@@ -243,7 +244,13 @@ void dcn42_init_hw(struct dc *dc)
/* enable all DCN clock gating */
REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0);
- REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0);
+ /* Temporary workaround for IOMMU mismatch issue.
+ * Fine grain control via bit0 of debug flag.
+ */
+ if (dc->debug.iommu_mismatch_temp_wka & 0x1)
+ dcfclk_gate_dis_value = 1;
+
+ REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, dcfclk_gate_dis_value);
}
dcn401_setup_hpo_hw_control(hws, true);
@@ -348,7 +355,7 @@ void dcn42_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx)
mpcc_id = hubp->inst;
/* If there is no full update, don't need to touch MPC tree*/
- if (!pipe_ctx->plane_state->update_flags.bits.full_update &&
+ if (!pipe_ctx->plane_state->update_bits.full_update &&
!pipe_ctx->update_flags.bits.mpcc) {
mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id);
dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id);
@@ -394,40 +401,33 @@ void dcn42_program_cm_hist(
}
static void dc_get_lut_xbar(
- enum dc_cm2_gpu_mem_pixel_component_order order,
enum hubp_3dlut_fl_crossbar_bit_slice *cr_r,
enum hubp_3dlut_fl_crossbar_bit_slice *y_g,
enum hubp_3dlut_fl_crossbar_bit_slice *cb_b)
{
- switch (order) {
- case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA:
- *cr_r = hubp_3dlut_fl_crossbar_bit_slice_32_47;
- *y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31;
- *cb_b = hubp_3dlut_fl_crossbar_bit_slice_0_15;
- break;
- case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_BGRA:
- *cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15;
- *y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31;
- *cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47;
- break;
- }
+ /* component_order was previously hard-coded to RGBA in local_mcm,
+ * preserve identical behavior.
+ */
+ *cr_r = hubp_3dlut_fl_crossbar_bit_slice_32_47;
+ *y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31;
+ *cb_b = hubp_3dlut_fl_crossbar_bit_slice_0_15;
}
static void dc_get_lut_mode(
- enum dc_cm2_gpu_mem_layout layout,
+ enum dc_cm_lut_swizzle swizzle,
enum hubp_3dlut_fl_mode *mode,
enum hubp_3dlut_fl_addressing_mode *addr_mode)
{
- switch (layout) {
- case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB:
+ switch (swizzle) {
+ case CM_LUT_3D_SWIZZLE_LINEAR_RGB:
*mode = hubp_3dlut_fl_mode_native_1;
*addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
break;
- case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR:
+ case CM_LUT_3D_SWIZZLE_LINEAR_BGR:
*mode = hubp_3dlut_fl_mode_native_2;
*addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
break;
- case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR:
+ case CM_LUT_1D_PACKED_LINEAR:
*mode = hubp_3dlut_fl_mode_transform;
*addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear;
break;
@@ -439,19 +439,22 @@ static void dc_get_lut_mode(
}
static void dc_get_lut_format(
- enum dc_cm2_gpu_mem_format dc_format,
+ enum dc_cm_lut_pixel_format dc_format,
enum hubp_3dlut_fl_format *format)
{
switch (dc_format) {
- case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB:
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB:
*format = hubp_3dlut_fl_format_unorm_12msb_bitslice;
break;
- case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB:
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB:
*format = hubp_3dlut_fl_format_unorm_12lsb_bitslice;
break;
- case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10:
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10:
*format = hubp_3dlut_fl_format_float_fp1_5_10;
break;
+ default:
+ *format = hubp_3dlut_fl_format_unorm_12msb_bitslice;
+ break;
}
}
@@ -465,16 +468,17 @@ static bool dc_is_rmcm_3dlut_supported(struct hubp *hubp, struct mpc *mpc)
return false;
}
-static bool is_rmcm_3dlut_fl_supported(struct dc *dc, enum dc_cm2_gpu_mem_size size)
+#if defined(CONFIG_DRM_AMD_DC_DCN4_2)
+static bool is_rmcm_3dlut_fl_supported(struct dc *dc)
{
+ /* size was previously hard-coded to TRANSFORMED in local_mcm,
+ * which mapped to dim_17. Preserve identical behavior.
+ */
if (!dc->caps.color.mpc.rmcm_3d_lut_caps.dma_3d_lut)
return false;
- if (size == DC_CM2_GPU_MEM_SIZE_171717)
- return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_17 != 0u;
- else if (size == DC_CM2_GPU_MEM_SIZE_333333)
- return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33 != 0u;
- return false;
+ return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_17 != 0u;
}
+#endif
static void dcn42_set_mcm_location_post_blend(struct dc *dc, struct pipe_ctx *pipe_ctx, bool bPostBlend)
{
@@ -495,56 +499,45 @@ static void dcn42_get_mcm_lut_xable_from_pipe_ctx(struct dc *dc, struct pipe_ctx
enum MCM_LUT_XABLE *lut3d_xable,
enum MCM_LUT_XABLE *lut1d_xable)
{
- enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL;
- bool lut1d_enable = false;
struct mpc *mpc = dc->res_pool->mpc;
int mpcc_id = pipe_ctx->plane_res.hubp->inst;
if (!pipe_ctx->plane_state)
return;
- shaper_3dlut_setting = pipe_ctx->plane_state->mcm_shaper_3dlut_setting;
- lut1d_enable = pipe_ctx->plane_state->mcm_lut1d_enable;
+
mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id);
pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE;
- *lut1d_xable = lut1d_enable ? MCM_LUT_ENABLE : MCM_LUT_DISABLE;
-
- switch (shaper_3dlut_setting) {
- case DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL:
- *lut3d_xable = *shaper_xable = MCM_LUT_DISABLE;
- break;
- case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER:
- *lut3d_xable = MCM_LUT_DISABLE;
- *shaper_xable = MCM_LUT_ENABLE;
- break;
- case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT:
- *lut3d_xable = *shaper_xable = MCM_LUT_ENABLE;
- break;
- }
+ *lut1d_xable = pipe_ctx->plane_state->cm.flags.bits.blend_enable ?
+ MCM_LUT_ENABLE : MCM_LUT_DISABLE;
+ *shaper_xable = pipe_ctx->plane_state->cm.flags.bits.shaper_enable ?
+ MCM_LUT_ENABLE : MCM_LUT_DISABLE;
+ *lut3d_xable = (pipe_ctx->plane_state->cm.flags.bits.shaper_enable &&
+ pipe_ctx->plane_state->cm.flags.bits.lut3d_enable) ?
+ MCM_LUT_ENABLE : MCM_LUT_DISABLE;
}
static void fl_get_lut_mode(
- enum dc_cm2_gpu_mem_layout layout,
- enum dc_cm2_gpu_mem_size size,
+ enum dc_cm_lut_swizzle swizzle,
enum hubp_3dlut_fl_mode *mode,
enum hubp_3dlut_fl_addressing_mode *addr_mode,
enum hubp_3dlut_fl_width *width)
{
+ /* size was previously hard-coded to TRANSFORMED in local_mcm,
+ * preserve identical behavior (transformed width).
+ */
*width = hubp_3dlut_fl_width_17;
- if (size == DC_CM2_GPU_MEM_SIZE_333333)
- *width = hubp_3dlut_fl_width_33;
-
- switch (layout) {
- case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB:
+ switch (swizzle) {
+ case CM_LUT_3D_SWIZZLE_LINEAR_RGB:
*mode = hubp_3dlut_fl_mode_native_1;
*addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
break;
- case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR:
+ case CM_LUT_3D_SWIZZLE_LINEAR_BGR:
*mode = hubp_3dlut_fl_mode_native_2;
*addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
break;
- case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR:
+ case CM_LUT_1D_PACKED_LINEAR:
*mode = hubp_3dlut_fl_mode_transform;
*addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear;
break;
@@ -558,8 +551,7 @@ static void fl_get_lut_mode(
bool dcn42_program_rmcm_luts(
struct hubp *hubp,
struct pipe_ctx *pipe_ctx,
- enum dc_cm2_transfer_func_source lut3d_src,
- struct dc_cm2_func_luts *mcm_luts,
+ const struct dc_plane_cm *cm,
struct mpc *mpc,
bool lut_bank_a,
int mpcc_id)
@@ -589,21 +581,24 @@ bool dcn42_program_rmcm_luts(
if (!rmcm_3dlut)
return false;
- rmcm_3dlut->protection_bits = mcm_luts->lut3d_data.rmcm_tmz;
+ /* rmcm_tmz was previously zero-initialized in local_mcm,
+ * preserve identical behavior.
+ */
+ rmcm_3dlut->protection_bits = 0;
dcn42_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable);
/* Shaper */
- if (mcm_luts->shaper) {
+ {
memset(&m_lut_params, 0, sizeof(m_lut_params));
- if (mcm_luts->shaper->type == TF_TYPE_HWPWL) {
- m_lut_params.pwl = &mcm_luts->shaper->pwl;
- } else if (mcm_luts->shaper->type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (cm->shaper_func.type == TF_TYPE_HWPWL) {
+ m_lut_params.pwl = &cm->shaper_func.pwl;
+ } else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
ASSERT(false);
cm_helper_translate_curve_to_hw_format(
dc->ctx,
- mcm_luts->shaper,
+ &cm->shaper_func,
&dpp_base->shaper_params, true);
m_lut_params.pwl = &dpp_base->shaper_params;
}
@@ -619,15 +614,16 @@ bool dcn42_program_rmcm_luts(
}
/* 3DLUT */
- switch (lut3d_src) {
- case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM:
+ if (!cm->flags.bits.lut3d_dma_enable) {
+ /* SYSMEM path — no DMA 3DLUT available.
+ * Previously this was treated as a no-op for the DMA/VIDMEM
+ * programming, preserve identical behavior.
+ */
memset(&m_lut_params, 0, sizeof(m_lut_params));
- // Don't know what to do in this case.
- //case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM:
- break;
- case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM:
- fl_get_lut_mode(mcm_luts->lut3d_data.gpu_mem_params.layout,
- mcm_luts->lut3d_data.gpu_mem_params.size,
+ } else {
+ /* VIDMEM (3DLUT DMA Fast Load) */
+
+ fl_get_lut_mode(cm->lut3d_dma.swizzle,
&mode,
&addr_mode,
&width);
@@ -639,20 +635,19 @@ bool dcn42_program_rmcm_luts(
return false;
// setting native or transformed mode,
- dc_get_lut_mode(mcm_luts->lut3d_data.gpu_mem_params.layout, &mode, &addr_mode);
+ dc_get_lut_mode(cm->lut3d_dma.swizzle, &mode, &addr_mode);
//seems to be only for the MCM
- dc_get_lut_format(mcm_luts->lut3d_data.gpu_mem_params.format_params.format, &format);
+ dc_get_lut_format(cm->lut3d_dma.format, &format);
dc_get_lut_xbar(
- mcm_luts->lut3d_data.gpu_mem_params.component_order,
&crossbar_bit_slice_cr_r,
&crossbar_bit_slice_y_g,
&crossbar_bit_slice_cb_b);
fl_config.mode = mode;
fl_config.enabled = lut3d_xable != MCM_LUT_DISABLE;
- fl_config.address = mcm_luts->lut3d_data.gpu_mem_params.addr;
+ fl_config.address = cm->lut3d_dma.addr;
fl_config.format = format;
fl_config.crossbar_bit_slice_y_g = crossbar_bit_slice_y_g;
fl_config.crossbar_bit_slice_cb_b = crossbar_bit_slice_cb_b;
@@ -660,17 +655,20 @@ bool dcn42_program_rmcm_luts(
fl_config.width = width;
fl_config.protection_bits = rmcm_3dlut->protection_bits;
fl_config.addr_mode = addr_mode;
- fl_config.layout = mcm_luts->lut3d_data.gpu_mem_params.layout;
- fl_config.bias = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.bias;
- fl_config.scale = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.scale;
+ fl_config.layout = cm->lut3d_dma.swizzle;
+ fl_config.bias = cm->lut3d_dma.bias;
+ fl_config.scale = cm->lut3d_dma.scale;
mpc_fl_config.enabled = fl_config.enabled;
mpc_fl_config.width = width;
mpc_fl_config.select_lut_bank_a = lut_bank_a;
- mpc_fl_config.bit_depth = mcm_luts->lut3d_data.gpu_mem_params.bit_depth;
+ /* bit_depth was previously zero-initialized in local_mcm,
+ * preserve identical behavior.
+ */
+ mpc_fl_config.bit_depth = 0;
mpc_fl_config.hubp_index = hubp->inst;
- mpc_fl_config.bias = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.bias;
- mpc_fl_config.scale = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.scale;
+ mpc_fl_config.bias = cm->lut3d_dma.bias;
+ mpc_fl_config.scale = cm->lut3d_dma.scale;
//1. power down the block
mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, false);
@@ -682,10 +680,6 @@ bool dcn42_program_rmcm_luts(
//3. power on the block
mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, true);
-
- break;
- default:
- return false;
}
return true;
@@ -693,7 +687,7 @@ bool dcn42_program_rmcm_luts(
void dcn42_populate_mcm_luts(struct dc *dc,
struct pipe_ctx *pipe_ctx,
- struct dc_cm2_func_luts mcm_luts,
+ const struct dc_plane_cm *cm,
bool lut_bank_a)
{
struct dpp *dpp_base = pipe_ctx->plane_res.dpp;
@@ -701,14 +695,17 @@ void dcn42_populate_mcm_luts(struct dc *dc,
int mpcc_id = hubp->inst;
struct mpc *mpc = dc->res_pool->mpc;
union mcm_lut_params m_lut_params;
- enum dc_cm2_transfer_func_source lut3d_src = mcm_luts.lut3d_data.lut3d_src;
+ const bool lut3d_dma = !!cm->flags.bits.lut3d_dma_enable;
enum hubp_3dlut_fl_format format = 0;
enum hubp_3dlut_fl_mode mode;
- enum hubp_3dlut_fl_width width = 0;
+ /* Width was previously hard-coded to TRANSFORMED via local_mcm build,
+ * preserve identical behavior.
+ */
+ enum hubp_3dlut_fl_width width = hubp_3dlut_fl_width_transformed;
enum hubp_3dlut_fl_addressing_mode addr_mode;
- enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g = 0;
- enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b = 0;
- enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r = 0;
+ enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g;
+ enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b;
+ enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r;
enum MCM_LUT_XABLE shaper_xable = MCM_LUT_DISABLE;
enum MCM_LUT_XABLE lut3d_xable = MCM_LUT_DISABLE;
enum MCM_LUT_XABLE lut1d_xable = MCM_LUT_DISABLE;
@@ -717,33 +714,35 @@ void dcn42_populate_mcm_luts(struct dc *dc,
dcn42_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable);
//MCM - setting its location (Before/After) blender
- //set to post blend (true)
+ //mpc_mcm_post_blend was previously zero-initialized in local_mcm,
+ //preserve identical behavior.
dcn42_set_mcm_location_post_blend(
dc,
pipe_ctx,
- mcm_luts.lut3d_data.mpc_mcm_post_blend);
+ false);
//RMCM - 3dLUT+Shaper
- if (mcm_luts.lut3d_data.rmcm_3dlut_enable &&
- is_rmcm_3dlut_fl_supported(dc, mcm_luts.lut3d_data.gpu_mem_params.size)) {
+#if defined(CONFIG_DRM_AMD_DC_DCN4_2)
+ if (cm->flags.bits.rmcm_enable &&
+ is_rmcm_3dlut_fl_supported(dc)) {
dcn42_program_rmcm_luts(
hubp,
pipe_ctx,
- lut3d_src,
- &mcm_luts,
+ cm,
mpc,
lut_bank_a,
mpcc_id);
}
+#endif /* CONFIG_DRM_AMD_DC_DCN4_2 */
/* 1D LUT */
- if (mcm_luts.lut1d_func) {
+ {
memset(&m_lut_params, 0, sizeof(m_lut_params));
- if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL)
- m_lut_params.pwl = &mcm_luts.lut1d_func->pwl;
- else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (cm->blend_func.type == TF_TYPE_HWPWL)
+ m_lut_params.pwl = &cm->blend_func.pwl;
+ else if (cm->blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx,
- mcm_luts.lut1d_func,
+ &cm->blend_func,
&dpp_base->regamma_params, false);
m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL;
}
@@ -756,14 +755,14 @@ void dcn42_populate_mcm_luts(struct dc *dc,
}
/* Shaper */
- if (mcm_luts.shaper && mcm_luts.lut3d_data.mpc_3dlut_enable) {
+ if (cm->flags.bits.lut3d_enable) {
memset(&m_lut_params, 0, sizeof(m_lut_params));
- if (mcm_luts.shaper->type == TF_TYPE_HWPWL)
- m_lut_params.pwl = &mcm_luts.shaper->pwl;
- else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (cm->shaper_func.type == TF_TYPE_HWPWL)
+ m_lut_params.pwl = &cm->shaper_func.pwl;
+ else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
ASSERT(false);
rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx,
- mcm_luts.shaper,
+ &cm->shaper_func,
&dpp_base->regamma_params, true);
m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL;
}
@@ -776,41 +775,27 @@ void dcn42_populate_mcm_luts(struct dc *dc,
}
/* 3DLUT */
- switch (lut3d_src) {
- case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM:
+ if (!lut3d_dma) {
+ /* SYSMEM (legacy lut3d_func) */
memset(&m_lut_params, 0, sizeof(m_lut_params));
if (hubp->funcs->hubp_enable_3dlut_fl)
hubp->funcs->hubp_enable_3dlut_fl(hubp, false);
- if (mcm_luts.lut3d_data.lut3d_func && mcm_luts.lut3d_data.lut3d_func->state.bits.initialized) {
- m_lut_params.lut3d = &mcm_luts.lut3d_data.lut3d_func->lut_3d;
+ if (cm->lut3d_func.state.bits.initialized) {
+ m_lut_params.lut3d = &cm->lut3d_func.lut_3d;
if (mpc->funcs->populate_lut)
mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, m_lut_params, lut_bank_a, mpcc_id);
if (mpc->funcs->program_lut_mode)
mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a,
mpcc_id);
}
- break;
- case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM:
- switch (mcm_luts.lut3d_data.gpu_mem_params.size) {
- case DC_CM2_GPU_MEM_SIZE_333333:
- width = hubp_3dlut_fl_width_33;
- break;
- case DC_CM2_GPU_MEM_SIZE_171717:
- width = hubp_3dlut_fl_width_17;
- break;
- case DC_CM2_GPU_MEM_SIZE_TRANSFORMED:
- width = hubp_3dlut_fl_width_transformed;
- break;
- default:
- //TODO: Handle default case
- break;
- }
+ } else {
+ /* VIDMEM (3DLUT DMA Fast Load) */
//check for support
if (mpc->funcs->mcm.is_config_supported &&
!mpc->funcs->mcm.is_config_supported(width))
- break;
+ return;
if (mpc->funcs->program_lut_read_write_control)
mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, mpcc_id);
@@ -818,49 +803,70 @@ void dcn42_populate_mcm_luts(struct dc *dc,
mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id);
if (hubp->funcs->hubp_program_3dlut_fl_addr)
- hubp->funcs->hubp_program_3dlut_fl_addr(hubp, mcm_luts.lut3d_data.gpu_mem_params.addr);
+ hubp->funcs->hubp_program_3dlut_fl_addr(hubp, cm->lut3d_dma.addr);
+ /* bit_depth was previously zero-initialized in local_mcm,
+ * preserve identical behavior.
+ */
if (mpc->funcs->mcm.program_bit_depth)
- mpc->funcs->mcm.program_bit_depth(mpc, mcm_luts.lut3d_data.gpu_mem_params.bit_depth, mpcc_id);
+ mpc->funcs->mcm.program_bit_depth(mpc, 0, mpcc_id);
- dc_get_lut_mode(mcm_luts.lut3d_data.gpu_mem_params.layout, &mode, &addr_mode);
+ switch (cm->lut3d_dma.swizzle) {
+ case CM_LUT_3D_SWIZZLE_LINEAR_RGB:
+ mode = hubp_3dlut_fl_mode_native_1;
+ addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
+ break;
+ case CM_LUT_3D_SWIZZLE_LINEAR_BGR:
+ mode = hubp_3dlut_fl_mode_native_2;
+ addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
+ break;
+ case CM_LUT_1D_PACKED_LINEAR:
+ mode = hubp_3dlut_fl_mode_transform;
+ addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear;
+ break;
+ default:
+ mode = hubp_3dlut_fl_mode_disable;
+ addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear;
+ break;
+ }
if (hubp->funcs->hubp_program_3dlut_fl_mode)
hubp->funcs->hubp_program_3dlut_fl_mode(hubp, mode);
if (hubp->funcs->hubp_program_3dlut_fl_addressing_mode)
hubp->funcs->hubp_program_3dlut_fl_addressing_mode(hubp, addr_mode);
- switch (mcm_luts.lut3d_data.gpu_mem_params.format_params.format) {
- case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB:
+ switch (cm->lut3d_dma.format) {
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB:
format = hubp_3dlut_fl_format_unorm_12msb_bitslice;
break;
- case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB:
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB:
format = hubp_3dlut_fl_format_unorm_12lsb_bitslice;
break;
- case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10:
+ case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10:
format = hubp_3dlut_fl_format_float_fp1_5_10;
break;
+ default:
+ break;
}
if (hubp->funcs->hubp_program_3dlut_fl_format)
hubp->funcs->hubp_program_3dlut_fl_format(hubp, format);
if (hubp->funcs->hubp_update_3dlut_fl_bias_scale &&
mpc->funcs->mcm.program_bias_scale) {
mpc->funcs->mcm.program_bias_scale(mpc,
- mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias,
- mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale,
+ cm->lut3d_dma.bias,
+ cm->lut3d_dma.scale,
mpcc_id);
hubp->funcs->hubp_update_3dlut_fl_bias_scale(hubp,
- mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias,
- mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale);
+ cm->lut3d_dma.bias,
+ cm->lut3d_dma.scale);
}
- //navi 4x has a bug and r and blue are swapped and need to be worked around here in
- //TODO: need to make a method for get_xbar per asic OR do the workaround in program_crossbar for 4x
- dc_get_lut_xbar(
- mcm_luts.lut3d_data.gpu_mem_params.component_order,
- &crossbar_bit_slice_cr_r,
- &crossbar_bit_slice_y_g,
- &crossbar_bit_slice_cb_b);
+ /* component_order was previously hard-coded to RGBA in local_mcm,
+ * preserve identical behavior.
+ */
+ crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15;
+ crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31;
+ crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47;
if (hubp->funcs->hubp_program_3dlut_fl_crossbar)
hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp,
@@ -886,7 +892,6 @@ void dcn42_populate_mcm_luts(struct dc *dc,
mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id);
}
}
- break;
}
}
@@ -901,19 +906,19 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx,
const struct pwl_params *lut_params = NULL;
bool rval;
- if (plane_state->mcm_luts.lut3d_data.lut3d_src == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) {
- dcn42_populate_mcm_luts(dc, pipe_ctx, plane_state->mcm_luts, plane_state->lut_bank_a);
+ if (plane_state->cm.flags.bits.lut3d_dma_enable) {
+ dcn42_populate_mcm_luts(dc, pipe_ctx, &plane_state->cm, plane_state->lut_bank_a);
return true;
}
mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id);
pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE;
// 1D LUT
- if (plane_state->blend_tf.type == TF_TYPE_HWPWL)
- lut_params = &plane_state->blend_tf.pwl;
- else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL)
+ lut_params = &plane_state->cm.blend_func.pwl;
+ else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->blend_tf,
+ &plane_state->cm.blend_func,
&dpp_base->regamma_params, false);
lut_params = rval ? &dpp_base->regamma_params : NULL;
}
@@ -921,12 +926,12 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx,
lut_params = NULL;
// Shaper
- if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL)
- lut_params = &plane_state->in_shaper_func.pwl;
- else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
+ if (plane_state->cm.shaper_func.type == TF_TYPE_HWPWL)
+ lut_params = &plane_state->cm.shaper_func.pwl;
+ else if (plane_state->cm.shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) {
// TODO: dpp_base replace
rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
- &plane_state->in_shaper_func,
+ &plane_state->cm.shaper_func,
&dpp_base->shaper_params, true);
lut_params = rval ? &dpp_base->shaper_params : NULL;
}
@@ -934,8 +939,8 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx,
// 3D
if (mpc->funcs->program_3dlut) {
- if (plane_state->lut3d_func.state.bits.initialized == 1)
- result &= mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id);
+ if (plane_state->cm.lut3d_func.state.bits.initialized == 1)
+ result &= mpc->funcs->program_3dlut(mpc, &plane_state->cm.lut3d_func.lut_3d, mpcc_id);
else
result &= mpc->funcs->program_3dlut(mpc, NULL, mpcc_id);
}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h
index 0539ee0ffaee..c469e7535114 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h
@@ -20,14 +20,13 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx,
void dcn42_populate_mcm_luts(struct dc *dc,
struct pipe_ctx *pipe_ctx,
- struct dc_cm2_func_luts mcm_luts,
+ const struct dc_plane_cm *cm,
bool lut_bank_a);
bool dcn42_program_rmcm_luts(
struct hubp *hubp,
struct pipe_ctx *pipe_ctx,
- enum dc_cm2_transfer_func_source lut3d_src,
- struct dc_cm2_func_luts *mcm_luts,
+ const struct dc_plane_cm *cm,
struct mpc *mpc,
bool lut_bank_a,
int mpcc_id);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
index dfb278a9fc3e..65df8002d3d7 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -894,6 +894,36 @@ struct disable_audio_stream_params {
struct pipe_ctx *pipe_ctx;
};
+struct clk_mgr_set_max_memclk_params {
+ struct clk_mgr *clk_mgr;
+ unsigned int memclk_mhz;
+};
+
+struct clk_mgr_update_clocks_params {
+ struct clk_mgr *clk_mgr;
+};
+
+struct hubbub_program_watermarks_params {
+ struct dc *dc;
+ struct hubbub *hubbub;
+ union dcn_watermark_set *watermarks;
+ unsigned int refclk_mhz;
+ bool safe_to_lower;
+};
+
+struct hubbub_program_arbiter_params {
+ struct dc *dc;
+ struct hubbub *hubbub;
+ struct dml2_display_arb_regs *arb_regs;
+ bool safe_to_lower;
+};
+
+struct hubbub_program_compbuf_segments_params {
+ struct hubbub *hubbub;
+ unsigned int compbuf_size;
+ bool safe_to_lower;
+};
+
struct prepare_bandwidth_params {
struct dc *dc;
struct dc_state *context;
@@ -1057,6 +1087,11 @@ union block_sequence_params {
struct disable_audio_stream_params disable_audio_stream_params;
struct prepare_bandwidth_params prepare_bandwidth_params;
struct link_set_dpms_on_params link_set_dpms_on_params;
+ struct clk_mgr_set_max_memclk_params clk_mgr_set_max_memclk_params;
+ struct clk_mgr_update_clocks_params clk_mgr_update_clocks_params;
+ struct hubbub_program_watermarks_params hubbub_program_watermarks_params;
+ struct hubbub_program_arbiter_params hubbub_program_arbiter_params;
+ struct hubbub_program_compbuf_segments_params hubbub_program_compbuf_segments_params;
};
enum block_sequence_func {
@@ -1209,6 +1244,11 @@ enum block_sequence_func {
DISABLE_AUDIO_STREAM,
PREPARE_BANDWIDTH,
LINK_SET_DPMS_ON,
+ CLK_MGR_SET_MAX_MEMCLK,
+ CLK_MGR_UPDATE_CLOCKS,
+ HUBBUB_PROGRAM_WATERMARKS,
+ HUBBUB_PROGRAM_ARBITER,
+ HUBBUB_PROGRAM_COMPBUF_SEGMENTS,
/* This must be the last value in this enum, add new ones above */
HWSS_BLOCK_SEQUENCE_FUNC_COUNT
};
@@ -1316,8 +1356,14 @@ struct hw_sequencer_funcs {
/* Bandwidth Related */
void (*prepare_bandwidth)(struct dc *dc, struct dc_state *context);
+ void (*prepare_bandwidth_sequence)(struct dc *dc,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
bool (*update_bandwidth)(struct dc *dc, struct dc_state *context);
void (*optimize_bandwidth)(struct dc *dc, struct dc_state *context);
+ void (*optimize_bandwidth_sequence)(struct dc *dc,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
/* Infopacket Related */
void (*set_avmute)(struct pipe_ctx *pipe_ctx, bool enable);
@@ -2475,4 +2521,40 @@ void hwss_add_link_set_dpms_on(struct block_sequence_state *seq_state,
struct dc_state *state,
struct pipe_ctx *pipe_ctx);
+/* Clock manager BLS executor functions */
+void hwss_clk_mgr_set_max_memclk(union block_sequence_params *params);
+void hwss_clk_mgr_update_clocks(union block_sequence_params *params);
+
+void hwss_hubbub_program_watermarks(union block_sequence_params *params);
+
+void hwss_hubbub_program_arbiter(union block_sequence_params *params);
+
+void hwss_hubbub_program_compbuf_segments(union block_sequence_params *params);
+
+/* Clock manager BLS add-helper functions */
+void hwss_add_clk_mgr_set_max_memclk(struct block_sequence_state *seq_state,
+ struct clk_mgr *clk_mgr,
+ unsigned int memclk_mhz);
+
+void hwss_add_clk_mgr_update_clocks(struct block_sequence_state *seq_state,
+ struct clk_mgr *clk_mgr);
+
+void hwss_add_hubbub_program_watermarks(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct hubbub *hubbub,
+ union dcn_watermark_set *watermarks,
+ unsigned int refclk_mhz,
+ bool safe_to_lower);
+
+void hwss_add_hubbub_program_arbiter(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct hubbub *hubbub,
+ struct dml2_display_arb_regs *arb_regs,
+ bool safe_to_lower);
+
+void hwss_add_hubbub_program_compbuf_segments(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ unsigned int compbuf_size,
+ bool safe_to_lower);
+
#endif /* __DC_HW_SEQUENCER_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h
index 63c6c841c681..b4956893ae9a 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h
@@ -58,6 +58,7 @@ struct dc_state;
struct dc_stream_status;
struct dc_writeback_info;
struct dchub_init_data;
+struct dc_plane_cm;
struct dc_static_screen_params;
struct resource_pool;
struct resource_context;
@@ -219,7 +220,7 @@ struct hwseq_private_funcs {
struct dc_state *context);
void (*populate_mcm_luts)(struct dc *dc,
struct pipe_ctx *pipe_ctx,
- struct dc_cm2_func_luts mcm_luts,
+ const struct dc_plane_cm *cm,
bool lut_bank_a);
void (*perform_3dlut_wa_unlock)(struct pipe_ctx *pipe_ctx);
void (*wait_for_pipe_update_if_needed)(struct dc *dc, struct pipe_ctx *pipe_ctx, bool is_surface_update_only);
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h
index f829ce3f70e5..68dc2d4ba7ca 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h
@@ -30,6 +30,7 @@
#include "dc.h"
#include "core_types.h"
#include "dm_pp_smu.h"
+struct utm_qos_model;
/* Constants */
#define DDR4_DRAM_WIDTH 64
@@ -311,6 +312,7 @@ struct clk_bw_params {
struct wm_table wm_table;
struct dummy_pstate_entry dummy_pstate_table[4];
struct clk_limit_table_entry dc_mode_limit;
+ const struct utm_qos_model *utm_qos_model;
};
/* Public interfaces */
@@ -318,6 +320,8 @@ struct clk_states {
uint32_t dprefclk_khz;
};
+struct block_sequence_state;
+
struct clk_mgr_funcs {
/*
* This function should set new clocks based on the input "safe_to_lower".
@@ -337,6 +341,8 @@ struct clk_mgr_funcs {
void (*exit_low_power_state)(struct clk_mgr *clk_mgr);
bool (*is_ips_supported)(struct clk_mgr *clk_mgr);
+ void (*set_idle_power_optimizations)(struct clk_mgr *clk_mgr, bool enable);
+
void (*init_clocks)(struct clk_mgr *clk_mgr);
void (*dump_clk_registers)(struct clk_state_registers_and_bypass *regs_and_bypass,
@@ -405,6 +411,12 @@ struct clk_mgr_funcs {
void (*get_requested_memory_qos)(
struct clk_mgr *clk_mgr,
struct dc_requested_memory_qos *qos);
+
+ void (*build_clock_update_for_bls)(struct clk_mgr *clk_mgr,
+ struct dc_state *context, bool safe_to_lower,
+ struct block_sequence_state *seq_state);
+
+ void (*execute_clk_mgr_block_sequence)(struct clk_mgr *clk_mgr);
};
struct clk_mgr {
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h
index 6db7c8753081..e756719308ab 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h
@@ -348,6 +348,7 @@ struct dccg_funcs {
void (*dccg_root_gate_disable_control)(struct dccg *dccg, uint32_t pipe_idx, uint32_t disable_clock_gating);
void (*dccg_read_reg_state)(struct dccg *dccg, struct dcn_dccg_reg_state *dccg_reg_state);
void (*dccg_enable_global_fgcg)(struct dccg *dccg, bool enable);
+ bool (*dccg_get_global_fgcg_status)(struct dccg *dccg);
};
#endif //__DAL_DCCG_H__
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h
index 1c18898aa475..6d6eda0e7e9d 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h
@@ -108,7 +108,7 @@ struct hubp_fl_3dlut_config {
uint16_t scale;
struct dc_plane_address address;
enum hubp_3dlut_fl_addressing_mode addr_mode;
- enum dc_cm2_gpu_mem_layout layout;
+ enum dc_cm_lut_swizzle layout;
uint8_t protection_bits;
enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g;
enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b;
diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_service.h b/drivers/gpu/drm/amd/display/dc/inc/link_service.h
index 23202c2114bb..addeb3e3b25a 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/link_service.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/link_service.h
@@ -193,6 +193,7 @@ struct link_service {
struct aux_payload *payload);
bool (*is_in_aux_transaction_mode)(struct ddc_service *ddc);
uint32_t (*get_aux_defer_delay)(struct ddc_service *ddc);
+ uint8_t (*get_ddc_aux_inst)(const struct dc_link *link);
/*************************** DP Capability ****************************/
diff --git a/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h b/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h
index 7a1ecb8d986f..6d15ccdc7f87 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h
@@ -536,23 +536,4 @@ uint32_t generic_indirect_reg_update_ex_sync(const struct dc_context *ctx,
uint8_t shift1, uint32_t mask1, uint32_t field_value1,
...);
-/* register offload macros
- *
- * instead of MMIO to register directly, in some cases we want
- * to gather register sequence and execute the register sequence
- * from another thread so we optimize time required for lengthy ops
- */
-
-/* start gathering register sequence */
-#define REG_SEQ_START() \
- reg_sequence_start_gather(CTX)
-
-/* start execution of register sequence gathered since REG_SEQ_START */
-#define REG_SEQ_SUBMIT() \
- reg_sequence_start_execute(CTX)
-
-/* wait for the last REG_SEQ_SUBMIT to finish */
-#define REG_SEQ_WAIT_DONE() \
- reg_sequence_wait_done(CTX)
-
#endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_REG_HELPER_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h
index 6a97a3e28bd2..5dcb9f8f4daf 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h
@@ -8,26 +8,12 @@
#include "dc.h"
#include "dml_top_soc_parameter_types.h"
-/* Forward declarations — callers that dereference these structs must include
- * the full UTM model headers themselves. */
-struct utm_qos_model;
-struct utm_qos_model_dchub_v2;
-
struct soc_and_ip_translator_funcs {
void (*get_soc_bb)(
struct dml2_soc_bb *soc_bb,
const struct dc *dc,
const struct dml2_configuration_options *config);
void (*get_ip_caps)(struct dml2_ip_capabilities *dml_ip_caps);
- /**
- * get_utm_qos_model - Return the static UTM QoS model for this DCN
- * generation. Caller provides storage for @qos_model and @dchub.
- * @qos_model: output — populated with SoC bounding box and SOP table
- * @dchub: output — populated with DCHUB client extension data
- */
- void (*get_utm_qos_model)(
- struct utm_qos_model *qos_model,
- struct utm_qos_model_dchub_v2 *dchub);
};
struct soc_and_ip_translator {
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
index 7d8951fecd57..281a7c5acaca 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
@@ -933,7 +933,7 @@ static bool should_verify_link_capability_destructively(struct dc_link *link,
destrictive = true;
if (is_hdmi_frl_in_use(link)) {
destrictive = false;
- } else if (link->dc->config.skip_frl_pretraining) {
+ } else if (link->local_sink->edid_caps.panel_patch.skip_frl_pre_training) {
for (i = 0; i < MAX_PIPES; i++) {
if (pipes[i].stream != NULL &&
pipes[i].stream->link == link) {
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c
index b6262e43ca02..67ce8d95bbd6 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c
@@ -143,6 +143,7 @@ static void construct_link_service_ddc(struct link_service *link_srv)
link_aux_transfer_with_retries_no_mutex;
link_srv->is_in_aux_transaction_mode = link_is_in_aux_transaction_mode;
link_srv->get_aux_defer_delay = link_get_aux_defer_delay;
+ link_srv->get_ddc_aux_inst = link_get_ddc_aux_inst;
}
/* link dp capability implements dp specific link capability retrieval sequence.
@@ -441,7 +442,7 @@ static enum channel_id get_ddc_line(struct dc_link *link)
channel = CHANNEL_ID_UNKNOWN;
- if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
+ if (link->force_to_use_aux) {
channel = link->aux_hw_inst + 1;
} else {
ddc = get_ddc_pin(link->ddc);
@@ -576,6 +577,8 @@ static bool construct_phy(struct dc_link *link,
link->is_internal_display = (disp_connect_caps_info.INTERNAL_DISPLAY != 0);
DC_LOG_DC("BIOS object table - is_internal_display: %d", link->is_internal_display);
link->no_ddc_pin = disp_connect_caps_info.NO_DDC_PIN != 0;
+ link->force_to_use_aux = link->dc->config.dp_connector_no_native_i2c
+ && link->no_ddc_pin;
}
if (link->link_id.type != OBJECT_TYPE_CONNECTOR) {
@@ -598,7 +601,7 @@ static bool construct_phy(struct dc_link *link,
goto ddc_create_fail;
}
- if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
+ if (link->force_to_use_aux) {
link->ddc_hw_inst = link->aux_hw_inst;
} else {
/* Embedded display connectors such as LVDS may not have DDC. */
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
index ead71f6d116d..f9d5a2441e38 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
@@ -120,8 +120,7 @@ static void ddc_service_construct(
ddc_service->link = init_data->link;
ddc_service->ctx = init_data->ctx;
- if (ddc_service->link && ddc_service->ctx->dc->config.dp_connector_no_native_i2c &&
- ddc_service->link->no_ddc_pin) {
+ if (ddc_service->link && ddc_service->link->force_to_use_aux) {
// Obtain aux instance info from i2c_info without GPIO DDC pin info
if (dcb->funcs->get_connector_aux_info(dcb, init_data->id, &i2c_info) == BP_RESULT_OK)
ddc_service->link->aux_hw_inst = (uint8_t)i2c_info.i2c_line;
@@ -252,6 +251,21 @@ static uint32_t defer_delay_converter_wa(
#define DP_TRANSLATOR_DELAY 5
+/**
+ * link_get_ddc_aux_inst - Return the AUX/DDC hardware instance for a link.
+ * @link: the link to query
+ *
+ * Return: aux_hw_inst when I2C is forced over AUX, otherwise the DDC pin
+ * channel index.
+ */
+uint8_t link_get_ddc_aux_inst(const struct dc_link *link)
+{
+ if (link->force_to_use_aux)
+ return link->aux_hw_inst;
+ ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF);
+ return (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel;
+}
+
uint32_t link_get_aux_defer_delay(struct ddc_service *ddc)
{
uint32_t defer_delay = 0;
@@ -526,7 +540,7 @@ bool try_to_configure_aux_timeout(struct ddc_service *ddc,
if (ddc->link->ep_type != DISPLAY_ENDPOINT_PHY)
return true;
- if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) {
+ if (ddc->link->force_to_use_aux) {
if (ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst]->funcs->configure_timeout) {
ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst]->funcs->configure_timeout(ddc, timeout);
result = true;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h
index f2a80e12494b..fdd8a3dce97f 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h
@@ -46,6 +46,8 @@ void set_ddc_transaction_type(
struct ddc_service *ddc,
enum ddc_transaction_type type);
+uint8_t link_get_ddc_aux_inst(const struct dc_link *link);
+
uint32_t link_get_aux_defer_delay(struct ddc_service *ddc);
bool link_is_in_aux_transaction_mode(struct ddc_service *ddc);
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
index 47abb4066709..d2329714408a 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
@@ -181,6 +181,12 @@ uint32_t link_bw_kbps_from_raw_frl_link_rate_data(uint8_t bw)
return 40000000;
case 0b110:
return 48000000;
+ case 0b111:
+ return 64000000;
+ case 0b1000:
+ return 80000000;
+ case 0b1001:
+ return 96000000;
}
return 0;
@@ -2242,10 +2248,14 @@ void detect_edp_sink_caps(struct dc_link *link)
/*
* ALPM is only valid for eDP v1.4 or higher.
*/
- if (link->dpcd_caps.dpcd_rev.raw >= DP_EDP_14)
+ if (link->dpcd_caps.dpcd_rev.raw >= DP_EDP_14) {
core_link_read_dpcd(link, DP_RECEIVER_ALPM_CAP,
&link->dpcd_caps.alpm_caps.raw,
sizeof(link->dpcd_caps.alpm_caps.raw));
+ core_link_read_dpcd(link, DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_CAP,
+ &link->dpcd_caps.psr_info.psr_active_vtotal_control_cap,
+ sizeof(link->dpcd_caps.psr_info.psr_active_vtotal_control_cap));
+ }
/*
* Read REPLAY info
@@ -2573,7 +2583,7 @@ bool dp_is_sink_present(struct dc_link *link)
/* We can't perform the step below for ASICs with no Native
* I2C signaling support on DP connectors, so skip it.
*/
- if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin)
+ if (link->force_to_use_aux)
return present;
ddc = get_ddc_pin(link->ddc);
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c
index d87f87a02d63..0d4f88ff844d 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c
@@ -25,6 +25,7 @@
#include "link_dp_panel_replay.h"
#include "link_edp_panel_control.h"
+#include "link_ddc.h"
#include "link_dpcd.h"
#include "dm_helpers.h"
#include "dc/dc_dmub_srv.h"
@@ -119,7 +120,7 @@ static bool dp_setup_panel_replay(struct dc_link *link, const struct dc_stream_s
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
- replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel;
+ replay_context.aux_inst = (enum channel_id) link_get_ddc_aux_inst(link);
replay_context.digbe_inst = link->link_enc->transmitter;
replay_context.digfe_inst = link->link_enc->preferred_engine;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
index 80a372ceaa51..baf57692bbb5 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
@@ -29,6 +29,7 @@
*/
#include "link_edp_panel_control.h"
+#include "link_ddc.h"
#include "link_dpcd.h"
#include "link_dp_capability.h"
#include "dm_helpers.h"
@@ -788,10 +789,7 @@ bool edp_setup_psr(struct dc_link *link,
}
}
- if (dc->config.dp_connector_no_native_i2c && link->no_ddc_pin)
- psr_context->channel = (enum channel_id)link->aux_hw_inst;
- else
- psr_context->channel = link->ddc->ddc_pin->hw_info.ddc_channel;
+ psr_context->channel = link_get_ddc_aux_inst(link);
psr_context->transmitterId = link->link_enc->transmitter;
psr_context->engineId = link->link_enc->preferred_engine;
@@ -1024,7 +1022,7 @@ bool edp_setup_freesync_replay(struct dc_link *link, const struct dc_stream_stat
if (!dp_pr_get_panel_inst(dc, link, &panel_inst))
return false;
- replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel;
+ replay_context.aux_inst = link_get_ddc_aux_inst(link);
replay_context.digbe_inst = link->link_enc->transmitter;
replay_context.digfe_inst = link->link_enc->preferred_engine;
diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c
index fa600593f4c1..0e09d073ab29 100644
--- a/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c
+++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c
@@ -380,7 +380,6 @@ static void mpc20_program_ogam_pwl(
struct dcn20_mpc *mpc20 = TO_DCN20_MPC(mpc);
PERF_TRACE();
- REG_SEQ_START();
for (i = 0 ; i < num; i++) {
REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, rgb[i].red_reg);
@@ -395,9 +394,6 @@ static void mpc20_program_ogam_pwl(
MPCC_OGAM_LUT_DATA, rgb[i].delta_blue_reg);
}
- REG_SEQ_SUBMIT();
- PERF_TRACE();
- REG_SEQ_WAIT_DONE();
PERF_TRACE();
}
diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c b/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c
index 83730bbe26a8..881b8da656b2 100644
--- a/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c
+++ b/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c
@@ -89,6 +89,24 @@ void opp2_set_disp_pattern_generator(
break;
}
+ if (opp->ctx->dc->debug.disable_dynamic_expansion_for_test_pattern) {
+ switch (test_pattern) {
+ case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES:
+ case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA:
+ case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS:
+ case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS:
+ case CONTROLLER_DP_TEST_PATTERN_COLORRAMP:
+ if (color_depth == COLOR_DEPTH_121212)
+ REG_UPDATE(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, 0);
+ break;
+ case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE:
+ REG_UPDATE(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, 1);
+ break;
+ default:
+ break;
+ }
+ }
+
/* set DPG dimentions */
REG_SET_2(DPG_DIMENSIONS, 0,
DPG_ACTIVE_WIDTH, width,
@@ -149,6 +167,9 @@ void opp2_set_disp_pattern_generator(
case TEST_PATTERN_COLOR_FORMAT_BPC_10:
dst_bpc = 10;
break;
+ case TEST_PATTERN_COLOR_FORMAT_BPC_12:
+ dst_bpc = 12;
+ break;
default:
dst_bpc = 8;
break;
@@ -192,22 +213,25 @@ void opp2_set_disp_pattern_generator(
case CONTROLLER_DP_TEST_PATTERN_COLORRAMP:
{
- mode = (bit_depth ==
- TEST_PATTERN_COLOR_FORMAT_BPC_10 ?
- TEST_PATTERN_MODE_DUALRAMP_RGB :
- TEST_PATTERN_MODE_SINGLERAMP_RGB);
-
switch (bit_depth) {
case TEST_PATTERN_COLOR_FORMAT_BPC_6:
+ mode = TEST_PATTERN_MODE_SINGLERAMP_RGB;
dst_bpc = 6;
break;
case TEST_PATTERN_COLOR_FORMAT_BPC_8:
+ mode = TEST_PATTERN_MODE_SINGLERAMP_RGB;
dst_bpc = 8;
break;
case TEST_PATTERN_COLOR_FORMAT_BPC_10:
+ mode = TEST_PATTERN_MODE_DUALRAMP_RGB;
dst_bpc = 10;
break;
+ case TEST_PATTERN_COLOR_FORMAT_BPC_12:
+ mode = TEST_PATTERN_MODE_DUALRAMP_RGB;
+ dst_bpc = 12;
+ break;
default:
+ mode = TEST_PATTERN_MODE_SINGLERAMP_RGB;
dst_bpc = 8;
break;
}
@@ -244,9 +268,20 @@ void opp2_set_disp_pattern_generator(
case TEST_PATTERN_COLOR_FORMAT_BPC_10:
{
REG_SET_3(DPG_RAMP_CONTROL, 0,
- DPG_RAMP0_OFFSET, 384 << 6,
- DPG_INC0, inc_base,
- DPG_INC1, inc_base + 2);
+ DPG_RAMP0_OFFSET, 384 << inc_base, // 384 start point
+ DPG_INC0, inc_base, // step size of 1
+ DPG_INC1, inc_base + 2); // step size of 4 (1 << 2)
+ REG_UPDATE_2(DPG_CONTROL,
+ DPG_VRES, 5,
+ DPG_HRES, 8);
+ }
+ break;
+ case TEST_PATTERN_COLOR_FORMAT_BPC_12:
+ {
+ REG_SET_3(DPG_RAMP_CONTROL, 0,
+ DPG_RAMP0_OFFSET, 1920 << inc_base, // 1920 start point
+ DPG_INC0, inc_base, // step size of 1
+ DPG_INC1, inc_base + 4); // step size of 16 (1 << 4)
REG_UPDATE_2(DPG_CONTROL,
DPG_VRES, 5,
DPG_HRES, 8);
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c
index e6426ccee2d8..cf8e22289d6a 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c
@@ -539,16 +539,11 @@ static bool optc1_enable_crtc(struct timing_generator *optc)
REG_UPDATE(CONTROL,
VTG0_ENABLE, 1);
- REG_SEQ_START();
-
/* Enable CRTC */
REG_UPDATE_2(OTG_CONTROL,
OTG_DISABLE_POINT_CNTL, 3,
OTG_MASTER_EN, 1);
- REG_SEQ_SUBMIT();
- REG_SEQ_WAIT_DONE();
-
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c
index c558b1d633f3..73cc8a713556 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c
@@ -63,16 +63,11 @@ bool optc2_enable_crtc(struct timing_generator *optc)
REG_UPDATE(CONTROL,
VTG0_ENABLE, 1);
- REG_SEQ_START();
-
/* Enable CRTC */
REG_UPDATE_2(OTG_CONTROL,
OTG_DISABLE_POINT_CNTL, 3,
OTG_MASTER_EN, 1);
- REG_SEQ_SUBMIT();
- REG_SEQ_WAIT_DONE();
-
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
index 98aaa22ce81c..3ace83e1b50f 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
@@ -105,16 +105,11 @@ static bool optc31_enable_crtc(struct timing_generator *optc)
REG_UPDATE(CONTROL,
VTG0_ENABLE, 1);
- REG_SEQ_START();
-
/* Enable CRTC */
REG_UPDATE_2(OTG_CONTROL,
OTG_DISABLE_POINT_CNTL, 2,
OTG_MASTER_EN, 1);
- REG_SEQ_SUBMIT();
- REG_SEQ_WAIT_DONE();
-
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
index a7cf34937b2f..7250478a5092 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
@@ -115,16 +115,11 @@ static bool optc314_enable_crtc(struct timing_generator *optc)
REG_UPDATE(CONTROL,
VTG0_ENABLE, 1);
- REG_SEQ_START();
-
/* Enable CRTC */
REG_UPDATE_2(OTG_CONTROL,
OTG_DISABLE_POINT_CNTL, 2,
OTG_MASTER_EN, 1);
- REG_SEQ_SUBMIT();
- REG_SEQ_WAIT_DONE();
-
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
index 07895d5f4dfa..f9e05efcad98 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
@@ -155,16 +155,11 @@ static bool optc32_enable_crtc(struct timing_generator *optc)
REG_UPDATE(CONTROL,
VTG0_ENABLE, 1);
- REG_SEQ_START();
-
/* Enable CRTC */
REG_UPDATE_2(OTG_CONTROL,
OTG_DISABLE_POINT_CNTL, 2,
OTG_MASTER_EN, 1);
- REG_SEQ_SUBMIT();
- REG_SEQ_WAIT_DONE();
-
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
index 62f45c156c32..9b7f9d5bbfb3 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
@@ -122,16 +122,11 @@ static bool optc35_enable_crtc(struct timing_generator *optc)
REG_UPDATE(CONTROL,
VTG0_ENABLE, 1);
- REG_SEQ_START();
-
/* Enable CRTC */
REG_UPDATE_2(OTG_CONTROL,
OTG_DISABLE_POINT_CNTL, 2,
OTG_MASTER_EN, 1);
- REG_SEQ_SUBMIT();
- REG_SEQ_WAIT_DONE();
-
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
index a6d76f451cf8..5fcdd74eb4a0 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
@@ -189,16 +189,11 @@ bool optc401_enable_crtc(struct timing_generator *optc)
REG_UPDATE(CONTROL,
VTG0_ENABLE, 1);
- REG_SEQ_START();
-
/* Enable CRTC */
REG_UPDATE_2(OTG_CONTROL,
OTG_DISABLE_POINT_CNTL, 2,
OTG_MASTER_EN, 1);
- REG_SEQ_SUBMIT();
- REG_SEQ_WAIT_DONE();
-
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c
index 729c2b653161..78b33b2dbae8 100644
--- a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c
+++ b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c
@@ -22,6 +22,60 @@
#define DC_LOGGER \
pg_cntl->ctx->logger
+/*
+ * ONO PG Workoaround: Saved FGCG repeaters states captured before powering up an ONO
+ * domain so it can be restored once the domain is powered up.
+ */
+struct dcn42_global_fgcg_rep_state {
+ uint32_t dmu_rep_fgcg;
+ uint32_t dccg_global_ono_rep_fgcg;
+ uint32_t az_rep_fgcg;
+};
+
+/* Save and disable FGCG repeaters before powering up the ONO domain. */
+static void pg_cntl42_save_and_disable_global_fgcg_rep(struct pg_cntl *pg_cntl,
+ struct dcn42_global_fgcg_rep_state *state)
+{
+ struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
+
+ REG_GET(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, &state->dmu_rep_fgcg);
+ if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_get_global_fgcg_status)
+ state->dccg_global_ono_rep_fgcg = pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_get_global_fgcg_status(pg_cntl->ctx->dc->res_pool->dccg);
+ REG_GET(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, &state->az_rep_fgcg);
+
+ REG_UPDATE(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, 1);
+ if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
+ pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false);
+ REG_UPDATE(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, 1);
+}
+
+/* Restore FGCG repeaters after the ONO domains are powered up. */
+static void pg_cntl42_restore_global_fgcg_rep(struct pg_cntl *pg_cntl,
+ struct dcn42_global_fgcg_rep_state *state)
+{
+ struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
+
+ REG_UPDATE(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, state->dmu_rep_fgcg);
+ if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
+ pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, state->dccg_global_ono_rep_fgcg);
+ REG_UPDATE(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, state->az_rep_fgcg);
+}
+
+static bool should_skip_pg_control(bool dc_in_idle_opt, bool power_on, bool block_enabled)
+{
+ if (dc_in_idle_opt)
+ return true;
+
+ if (power_on && block_enabled)
+ return true;
+
+ if (!power_on && !block_enabled)
+ return true;
+
+ return false;
+}
+
+
static bool pg_cntl42_dsc_pg_status(struct pg_cntl *pg_cntl, unsigned int dsc_inst)
{
struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl);
@@ -54,37 +108,23 @@ void pg_cntl42_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
uint32_t org_ip_request_cntl = 0;
- bool block_enabled;
-
- /*need to enable dscclk regardless DSC_PG*/
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->enable_dsc && power_on)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->enable_dsc(
- pg_cntl->ctx->dc->res_pool->dccg, dsc_inst);
+ struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0};
+ bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || pg_cntl->ctx->dc->debug.disable_dsc_power_gate;
- bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
- pg_cntl->ctx->dc->debug.disable_dsc_power_gate ||
- pg_cntl->ctx->dc->idle_optimizations_allowed;
-
- if (skip_pg && !power_on)
+ if (block_pg_disabled && !power_on)
return;
- block_enabled = pg_cntl42_dsc_pg_status(pg_cntl, dsc_inst);
- if (power_on) {
- if (block_enabled)
- return;
- } else {
- if (!block_enabled)
- return;
- }
+ bool block_enabled = pg_cntl42_dsc_pg_status(pg_cntl, dsc_inst);
+ if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
+ return;
REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
if (org_ip_request_cntl == 0)
REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
- if (power_on) {
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false);
- }
+ if (power_on)
+ pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
+
switch (dsc_inst) {
case 0: /* DSC0 */
REG_UPDATE(DOMAIN16_PG_CONFIG,
@@ -123,19 +163,11 @@ void pg_cntl42_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo
break;
}
- if (power_on) {
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, true);
- }
+ if (power_on)
+ pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
if (dsc_inst < MAX_PIPES)
pg_cntl->pg_pipe_res_enable[PG_DSC][dsc_inst] = power_on;
-
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->disable_dsc && !power_on) {
- /*this is to disable dscclk*/
- pg_cntl->ctx->dc->res_pool->dccg->funcs->disable_dsc(
- pg_cntl->ctx->dc->res_pool->dccg, dsc_inst);
- }
}
static bool pg_cntl42_hubp_dpp_pg_status(struct pg_cntl *pg_cntl, unsigned int hubp_dpp_inst)
@@ -174,32 +206,24 @@ void pg_cntl42_hubp_dpp_pg_control(struct pg_cntl *pg_cntl, unsigned int hubp_dp
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
uint32_t org_ip_request_cntl;
- bool block_enabled;
- bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
- pg_cntl->ctx->dc->debug.disable_hubp_power_gate ||
- pg_cntl->ctx->dc->debug.disable_dpp_power_gate ||
- pg_cntl->ctx->dc->idle_optimizations_allowed;
+ struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0};
+ bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
+ pg_cntl->ctx->dc->debug.disable_hubp_power_gate ||
+ pg_cntl->ctx->dc->debug.disable_dpp_power_gate;
- if (skip_pg && !power_on)
+ if (block_pg_disabled && !power_on)
return;
- block_enabled = pg_cntl42_hubp_dpp_pg_status(pg_cntl, hubp_dpp_inst);
- if (power_on) {
- if (block_enabled)
- return;
- } else {
- if (!block_enabled)
- return;
- }
+ bool block_enabled = pg_cntl42_hubp_dpp_pg_status(pg_cntl, hubp_dpp_inst);
+ if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
+ return;
REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
if (org_ip_request_cntl == 0)
REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
- if (power_on) {
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false);
- }
+ if (power_on)
+ pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
switch (hubp_dpp_inst) {
case 0:
@@ -227,10 +251,9 @@ void pg_cntl42_hubp_dpp_pg_control(struct pg_cntl *pg_cntl, unsigned int hubp_dp
break;
}
- if (power_on) {
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, true);
- }
+ if (power_on)
+ pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
+
DC_LOG_DEBUG("HUBP DPP instance %d, power %s", hubp_dpp_inst,
power_on ? "ON" : "OFF");
@@ -258,22 +281,18 @@ void pg_cntl42_hpo_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t pwr_status = power_on ? 0 : 2;
uint32_t org_ip_request_cntl;
uint32_t power_forceon;
- bool block_enabled;
+ struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0};
- bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
- pg_cntl->ctx->dc->debug.disable_hpo_power_gate ||
- pg_cntl->ctx->dc->idle_optimizations_allowed;
+ bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
+ pg_cntl->ctx->dc->debug.disable_hpo_power_gate;
- if (skip_pg && !power_on)
+ if (block_pg_disabled && !power_on)
+ return;
+
+ bool block_enabled = pg_cntl42_hpo_pg_status(pg_cntl);
+
+ if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
return;
- block_enabled = pg_cntl42_hpo_pg_status(pg_cntl);
- if (power_on) {
- if (block_enabled)
- return;
- } else {
- if (!block_enabled)
- return;
- }
REG_GET(DOMAIN25_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon);
if (power_forceon)
@@ -282,17 +301,15 @@ void pg_cntl42_hpo_pg_control(struct pg_cntl *pg_cntl, bool power_on)
REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
if (org_ip_request_cntl == 0)
REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
- if (power_on) {
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false);
- }
+ if (power_on)
+ pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
+
REG_UPDATE(DOMAIN25_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
REG_WAIT(DOMAIN25_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
- if (power_on) {
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, true);
- }
+ if (power_on)
+ pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
+
pg_cntl->pg_res_enable[PG_HPO] = power_on;
}
@@ -314,23 +331,17 @@ void pg_cntl42_io_clk_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t pwr_status = power_on ? 0 : 2;
uint32_t org_ip_request_cntl;
uint32_t power_forceon;
- bool block_enabled;
- bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
- pg_cntl->ctx->dc->idle_optimizations_allowed ||
+ bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->debug.disable_io_clk_power_gate;
- if (skip_pg && !power_on)
+ if (block_pg_disabled && !power_on)
return;
- block_enabled = pg_cntl42_io_clk_status(pg_cntl);
- if (power_on) {
- if (block_enabled)
- return;
- } else {
- if (!block_enabled)
- return;
- }
+ bool block_enabled = pg_cntl42_io_clk_status(pg_cntl);
+
+ if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
+ return;
REG_GET(DOMAIN22_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon);
if (power_forceon)
@@ -412,24 +423,16 @@ void pg_cntl42_mem_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t pwr_status = power_on ? 0 : 2;
uint32_t org_ip_request_cntl;
uint32_t power_forceon;
- bool block_enabled;
- bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
- pg_cntl->ctx->dc->idle_optimizations_allowed ||
+ bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->debug.disable_mem_power_gate;
- if (skip_pg && !power_on)
+ if (block_pg_disabled && !power_on)
return;
- block_enabled = pg_cntl42_mem_status(pg_cntl);
- if (power_on) {
- if (block_enabled)
- return;
- } else {
- if (!block_enabled)
- return;
- }
-
+ bool block_enabled = pg_cntl42_mem_status(pg_cntl);
+ if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
+ return;
REG_GET(DOMAIN23_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon);
if (power_forceon)
return;
@@ -466,38 +469,31 @@ void pg_cntl42_dio_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
uint32_t org_ip_request_cntl;
- bool block_enabled;
+ struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0};
- bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg ||
- pg_cntl->ctx->dc->idle_optimizations_allowed ||
+ bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
pg_cntl->ctx->dc->debug.disable_dio_power_gate;
- if (skip_pg && !power_on)
+
+ if (block_pg_disabled && !power_on)
return;
- block_enabled = pg_cntl42_dio_pg_status(pg_cntl);
- if (power_on) {
- if (block_enabled)
- return;
- } else {
- if (!block_enabled)
- return;
- }
+ bool block_enabled = pg_cntl42_dio_pg_status(pg_cntl);
+ if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
+ return;
REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
if (org_ip_request_cntl == 0)
REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1);
- if (power_on) {
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false);
- }
+ if (power_on)
+ pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
+
/* DIO */
REG_UPDATE(DOMAIN26_PG_CONFIG, DOMAIN_POWER_GATE, power_gate);
REG_WAIT(DOMAIN26_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000);
- if (power_on) {
- if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg)
- pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, true);
- }
+ if (power_on)
+ pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state);
+
pg_cntl->pg_res_enable[PG_DIO] = power_on;
}
@@ -509,23 +505,19 @@ void pg_cntl42_plane_otg_pg_control(struct pg_cntl *pg_cntl, bool power_on)
uint32_t pwr_status = power_on ? 0 : 2;
uint32_t org_ip_request_cntl;
unsigned int i;
- bool block_enabled;
bool all_mpcc_disabled = true, all_opp_disabled = true;
bool all_optc_disabled = true, all_stream_disabled = true;
- if (pg_cntl->ctx->dc->debug.ignore_pg ||
- pg_cntl->ctx->dc->debug.disable_optc_power_gate ||
- pg_cntl->ctx->dc->idle_optimizations_allowed)
+ bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg ||
+ pg_cntl->ctx->dc->debug.disable_optc_power_gate;
+
+ if (block_pg_disabled && !power_on)
return;
- block_enabled = pg_cntl42_plane_otg_status(pg_cntl);
- if (power_on) {
- if (block_enabled)
- return;
- } else {
- if (!block_enabled)
- return;
- }
+ bool block_enabled = pg_cntl42_plane_otg_status(pg_cntl);
+
+ if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled))
+ return;
for (i = 0; i < pg_cntl->ctx->dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe_ctx = &pg_cntl->ctx->dc->current_state->res_ctx.pipe_ctx[i];
diff --git a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h
index 7e8f4f03ae0e..813fa5c81172 100644
--- a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h
+++ b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h
@@ -34,7 +34,9 @@
SR(DOMAIN24_PG_STATUS), \
SR(DOMAIN25_PG_STATUS), \
SR(DOMAIN26_PG_STATUS), \
- SR(DC_IP_REQUEST_CNTL)
+ SR(DC_IP_REQUEST_CNTL), \
+ SR(DMU_CLK_CNTL), \
+ SR(AZ_CLOCK_CNTL)
#define PG_CNTL_REG_LIST_DCN42B()\
SR(DOMAIN0_PG_CONFIG), \
@@ -63,7 +65,9 @@
SR(DOMAIN24_PG_STATUS), \
SR(DOMAIN25_PG_STATUS), \
SR(DOMAIN26_PG_STATUS), \
- SR(DC_IP_REQUEST_CNTL)
+ SR(DC_IP_REQUEST_CNTL), \
+ SR(DMU_CLK_CNTL), \
+ SR(AZ_CLOCK_CNTL)
#define PG_CNTL_SF(reg_name, field_name, post_fix)\
.field_name = reg_name ## __ ## field_name ## post_fix
@@ -121,7 +125,9 @@
PG_CNTL_SF(DOMAIN25_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, mask_sh), \
PG_CNTL_SF(DOMAIN26_PG_STATUS, DOMAIN_DESIRED_PWR_STATE, mask_sh), \
PG_CNTL_SF(DOMAIN26_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, mask_sh), \
- PG_CNTL_SF(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh)
+ PG_CNTL_SF(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh), \
+ PG_CNTL_SF(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, mask_sh), \
+ PG_CNTL_SF(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, mask_sh)
/* Not in DCN42B:
* PG_CNTL_SF(DOMAIN19_PG_CONFIG, DOMAIN_POWER_FORCEON, mask_sh),
@@ -178,7 +184,9 @@
PG_CNTL_SF(DOMAIN25_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, mask_sh), \
PG_CNTL_SF(DOMAIN26_PG_STATUS, DOMAIN_DESIRED_PWR_STATE, mask_sh), \
PG_CNTL_SF(DOMAIN26_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, mask_sh), \
- PG_CNTL_SF(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh)
+ PG_CNTL_SF(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh), \
+ PG_CNTL_SF(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, mask_sh), \
+ PG_CNTL_SF(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, mask_sh)
struct pg_cntl_shift {
uint8_t IP_REQUEST_EN;
@@ -186,6 +194,8 @@ struct pg_cntl_shift {
uint8_t DOMAIN_POWER_GATE;
uint8_t DOMAIN_DESIRED_PWR_STATE;
uint8_t DOMAIN_PGFSM_PWR_STATUS;
+ uint8_t LONO_FGCG_REP_DIS;
+ uint8_t AZ_GLOBAL_FGCG_REP_DIS;
};
struct pg_cntl_mask {
uint32_t IP_REQUEST_EN;
@@ -193,6 +203,8 @@ struct pg_cntl_mask {
uint32_t DOMAIN_POWER_GATE;
uint32_t DOMAIN_DESIRED_PWR_STATE;
uint32_t DOMAIN_PGFSM_PWR_STATUS;
+ uint32_t LONO_FGCG_REP_DIS;
+ uint32_t AZ_GLOBAL_FGCG_REP_DIS;
};
struct pg_cntl_registers {
@@ -224,6 +236,8 @@ struct pg_cntl_registers {
uint32_t DOMAIN24_PG_STATUS;
uint32_t DOMAIN25_PG_STATUS;
uint32_t DOMAIN26_PG_STATUS;
+ uint32_t DMU_CLK_CNTL;
+ uint32_t AZ_CLOCK_CNTL;
};
struct dcn_pg_cntl {
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
index 01770df63d0e..d11ab57afcdd 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
@@ -2479,7 +2479,6 @@ static bool dcn30_resource_construct(
dc->caps.post_blend_color_processing = true;
dc->caps.force_dp_tps4_for_cp2520 = true;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.extended_aux_timeout_support = true;
dc->caps.dmcub_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
index 58add1071bc1..ae8918a4ad3e 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
@@ -1378,7 +1378,6 @@ static bool dcn302_resource_construct(
dc->caps.post_blend_color_processing = true;
dc->caps.force_dp_tps4_for_cp2520 = true;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.extended_aux_timeout_support = true;
dc->caps.dmcub_support = true;
dc->caps.max_v_total = (1 << 15) - 1;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
index 6cb297cd08fd..75e6f4e46f60 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
@@ -1322,7 +1322,6 @@ static bool dcn303_resource_construct(
dc->caps.post_blend_color_processing = true;
dc->caps.force_dp_tps4_for_cp2520 = true;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.extended_aux_timeout_support = true;
dc->caps.dmcub_support = true;
dc->caps.max_v_total = (1 << 15) - 1;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
index 7c6a6872688b..e29efa452c87 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
@@ -1189,7 +1189,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc_obj(struct dcn20_link_encoder);
@@ -1201,6 +1201,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
@@ -2077,7 +2079,6 @@ static bool dcn31_resource_construct(
if (dc->config.forceHBR2CP2520)
dc->caps.force_dp_tps4_for_cp2520 = false;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.dp_hpo = true;
dc->caps.dp_hdmi21_pcon_support = true;
dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
index d8096f11fb77..f50b3250dcba 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
@@ -1246,7 +1246,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc_obj(struct dcn20_link_encoder);
@@ -1258,6 +1258,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
index 3b2e57c6970f..8297f2f04c16 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
@@ -1188,7 +1188,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc_obj(struct dcn20_link_encoder);
@@ -1200,6 +1200,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
@@ -2053,7 +2055,6 @@ static bool dcn315_resource_construct(
if (dc->config.forceHBR2CP2520)
dc->caps.force_dp_tps4_for_cp2520 = false;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.dp_hpo = true;
dc->caps.dp_hdmi21_pcon_support = true;
dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
index 924b167bcd74..046566ad1afe 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
@@ -1181,7 +1181,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc_obj(struct dcn20_link_encoder);
@@ -1193,6 +1193,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
@@ -1927,7 +1929,6 @@ static bool dcn316_resource_construct(
if (dc->config.forceHBR2CP2520)
dc->caps.force_dp_tps4_for_cp2520 = false;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.dp_hpo = true;
dc->caps.dp_hdmi21_pcon_support = true;
dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
index a11110e304fc..004c5690f876 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
@@ -2405,7 +2405,6 @@ static bool dcn32_resource_construct(
if (dc->config.forceHBR2CP2520)
dc->caps.force_dp_tps4_for_cp2520 = false;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.dp_hpo = true;
dc->caps.dp_hdmi21_pcon_support = true;
dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
index d1dbcc8ddb71..53fd32249310 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
@@ -1897,7 +1897,6 @@ static bool dcn321_resource_construct(
dc->caps.post_blend_color_processing = true;
dc->caps.force_dp_tps4_for_cp2520 = true;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.dp_hpo = true;
dc->caps.dp_hdmi21_pcon_support = true;
dc->caps.edp_dsc_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
index 53596e790eb4..5541b89b1350 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
@@ -819,7 +819,6 @@ static const struct dc_debug_options debug_defaults_drv = {
.enable_hpo_pg_support = false,
.enable_single_display_2to1_odm_policy = true,
.disable_idle_power_optimizations = false,
- .dmcub_emulation = false,
.disable_boot_optimizations = false,
.disable_unbounded_requesting = false,
.disable_mem_low_power = false,
@@ -1189,7 +1188,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc_obj(struct dcn20_link_encoder);
@@ -1201,6 +1200,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
@@ -2029,7 +2030,6 @@ static bool dcn35_resource_construct(
if (dc->config.forceHBR2CP2520)
dc->caps.force_dp_tps4_for_cp2520 = false;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.dp_hpo = true;
dc->caps.dp_hdmi21_pcon_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
index 3e2c9cfd555d..053b4380f57e 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
@@ -799,7 +799,6 @@ static const struct dc_debug_options debug_defaults_drv = {
.enable_hpo_pg_support = false,
.enable_single_display_2to1_odm_policy = true,
.disable_idle_power_optimizations = false,
- .dmcub_emulation = false,
.disable_boot_optimizations = false,
.disable_unbounded_requesting = false,
.disable_mem_low_power = false,
@@ -1169,7 +1168,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc_obj(struct dcn20_link_encoder);
@@ -1181,6 +1180,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
@@ -2002,7 +2003,6 @@ static bool dcn351_resource_construct(
if (dc->config.forceHBR2CP2520)
dc->caps.force_dp_tps4_for_cp2520 = false;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.dp_hpo = true;
dc->caps.dp_hdmi21_pcon_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
index 9e795130eb89..592000cf9250 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
@@ -806,7 +806,6 @@ static const struct dc_debug_options debug_defaults_drv = {
.enable_hpo_pg_support = false,
.enable_single_display_2to1_odm_policy = true,
.disable_idle_power_optimizations = false,
- .dmcub_emulation = false,
.disable_boot_optimizations = false,
.disable_unbounded_requesting = false,
.disable_mem_low_power = false,
@@ -1176,7 +1175,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc_obj(struct dcn20_link_encoder);
@@ -1188,6 +1187,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
@@ -1999,7 +2000,6 @@ static bool dcn36_resource_construct(
if (dc->config.forceHBR2CP2520)
dc->caps.force_dp_tps4_for_cp2520 = false;
dc->caps.hdmi_hpo = true;
- dc->config.skip_frl_pretraining = true;
dc->caps.dp_hpo = true;
dc->caps.dp_hdmi21_pcon_support = true;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
index 7de12b16d7ad..7620da96ffc1 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c
@@ -801,6 +801,7 @@ static const struct dc_debug_options debug_defaults_drv = {
.replay_skip_crtc_disabled = true,
.psr_skip_crtc_disable = true,
.force_odm2to1_for_edp_pixclk_mhz = 0, // disable the policy for now
+ .iommu_mismatch_temp_wka = 0x7,
};
static const struct dc_check_config config_defaults = {
@@ -1881,7 +1882,7 @@ static struct link_encoder *dcn42_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if ((unsigned int)(eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if ((unsigned int)(eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL);
@@ -1893,6 +1894,8 @@ static struct link_encoder *dcn42_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
index 527d17f29f3b..60cbaf4f6fdf 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
@@ -757,7 +757,7 @@ static const struct dc_debug_options debug_defaults_drv = {
.underflow_assert_delay_us = 0xFFFFFFFF,
.dwb_fi_phase = -1, // -1 = disable,
.dmub_command_table = true,
- .pstate_enabled = false,
+ .pstate_enabled = true,
.enable_mem_low_power = {
.bits = {
.vga = false,
@@ -1824,7 +1824,7 @@ static struct link_encoder *dcn42b_link_enc_create_minimal(
{
struct dcn20_link_encoder *enc20;
- if ((unsigned int)(eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if ((unsigned int)(eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL);
@@ -1836,6 +1836,8 @@ static struct link_encoder *dcn42b_link_enc_create_minimal(
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile
index d168fb1eacf7..8a9bb0aef9b7 100644
--- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile
@@ -9,13 +9,16 @@ soc_and_ip_translator_rcflags := $(CC_FLAGS_NO_FPU)
CFLAGS_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.o := $(soc_and_ip_translator_ccflags)
CFLAGS_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.o := $(soc_and_ip_translator_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.o := $(soc_and_ip_translator_ccflags)
CFLAGS_REMOVE_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.o := $(soc_and_ip_translator_rcflags)
CFLAGS_REMOVE_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.o := $(soc_and_ip_translator_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.o := $(soc_and_ip_translator_rcflags)
soc_and_ip_translator := soc_and_ip_translator.o
soc_and_ip_translator += dcn401/dcn401_soc_and_ip_translator.o
soc_and_ip_translator += dcn42/dcn42_soc_and_ip_translator.o
+soc_and_ip_translator += dcn42b/dcn42b_soc_and_ip_translator.o
AMD_DAL_soc_and_ip_translator := $(addprefix $(AMDDALPATH)/dc/soc_and_ip_translator/, $(soc_and_ip_translator))
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c
index ae2c6a2f3f75..c6c1b19b7370 100644
--- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c
@@ -5,22 +5,16 @@
#include "dcn42_soc_and_ip_translator.h"
#include "../dcn401/dcn401_soc_and_ip_translator.h"
#include "bounding_boxes/dcn42_soc_bb.h"
-#include "bounding_boxes/dcn42b_soc_bb.h"
/* soc_and_ip_translator component used to get up-to-date values for bounding box.
* Bounding box values are stored in several locations and locations can vary with DCN revision.
* This component provides an interface to get DCN-specific bounding box values.
*/
-static void get_default_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc)
+static void get_default_soc_bb(struct dml2_soc_bb *soc_bb)
{
- if (dc->ctx->dce_version == DCN_VERSION_4_2B) {
- memcpy(soc_bb, &dml2_socbb_dcn42b, sizeof(struct dml2_soc_bb));
- memcpy(&soc_bb->qos_parameters, &dml_dcn42b_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters));
- } else {
- memcpy(soc_bb, &dml2_socbb_dcn42, sizeof(struct dml2_soc_bb));
- memcpy(&soc_bb->qos_parameters, &dml_dcn42_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters));
- }
+ memcpy(soc_bb, &dml2_socbb_dcn42, sizeof(struct dml2_soc_bb));
+ memcpy(&soc_bb->qos_parameters, &dml_dcn42_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters));
}
/*
@@ -165,7 +159,7 @@ static void dcn42_update_soc_bb_with_values_from_clk_mgr(struct dml2_soc_bb *soc
}
}
-static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config)
+void dcn42_apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config)
{
(void)config;
/* Individual modification can be overwritten even if it was obtained by a previous function.
@@ -181,9 +175,9 @@ static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc
void dcn42_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config)
{
//get default soc_bb with static values
- get_default_soc_bb(soc_bb, dc);
+ get_default_soc_bb(soc_bb);
//update soc_bb values with more accurate values
- apply_soc_bb_updates(soc_bb, dc, config);
+ dcn42_apply_soc_bb_updates(soc_bb, dc, config);
}
static void dcn42_get_ip_caps(struct dml2_ip_capabilities *ip_caps)
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h
index 1dded5426152..8ac90655f276 100644
--- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h
@@ -13,5 +13,6 @@
void dcn42_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator);
void dcn42_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config);
+void dcn42_apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config);
#endif /* _DCN42_SOC_AND_IP_TRANSLATOR_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.c
new file mode 100644
index 000000000000..50669f458e23
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: MIT
+//
+// Copyright 2026 Advanced Micro Devices, Inc.
+
+#include "../dcn42/dcn42_soc_and_ip_translator.h"
+#include "dcn42b_soc_and_ip_translator.h"
+#include "../dcn401/dcn401_soc_and_ip_translator.h"
+#include "bounding_boxes/dcn42b_soc_bb.h"
+
+/* soc_and_ip_translator component used to get up-to-date values for bounding box.
+ * Bounding box values are stored in several locations and locations can vary with DCN revision.
+ * This component provides an interface to get DCN-specific bounding box values.
+ */
+
+static void get_default_soc_bb(struct dml2_soc_bb *soc_bb)
+{
+ memcpy(soc_bb, &dml2_socbb_dcn42b, sizeof(struct dml2_soc_bb));
+ memcpy(&soc_bb->qos_parameters, &dml_dcn42b_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters));
+}
+
+void dcn42b_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config)
+{
+ //get default soc_bb with static values
+ get_default_soc_bb(soc_bb);
+ //update soc_bb values with more accurate values
+ dcn42_apply_soc_bb_updates(soc_bb, dc, config);
+}
+
+static void dcn42b_get_ip_caps(struct dml2_ip_capabilities *ip_caps)
+{
+ *ip_caps = dml2_dcn42b_max_ip_caps;
+}
+
+static struct soc_and_ip_translator_funcs dcn42b_translator_funcs = {
+ .get_soc_bb = dcn42b_get_soc_bb,
+ .get_ip_caps = dcn42b_get_ip_caps,
+};
+
+void dcn42b_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator)
+{
+ soc_and_ip_translator->translator_funcs = &dcn42b_translator_funcs;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h
new file mode 100644
index 000000000000..0d4ea613431a
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: MIT
+//
+// Copyright 2026 Advanced Micro Devices, Inc.
+
+#ifndef _DCN42B_SOC_AND_IP_TRANSLATOR_H_
+#define _DCN42B_SOC_AND_IP_TRANSLATOR_H_
+
+#include "core_types.h"
+#include "dc.h"
+#include "clk_mgr.h"
+#include "dml_top_soc_parameter_types.h"
+#include "soc_and_ip_translator.h"
+
+void dcn42b_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator);
+void dcn42b_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config);
+
+#endif /* _DCN42B_SOC_AND_IP_TRANSLATOR_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c
index f99afb22d7da..1e3ee25732fa 100644
--- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c
@@ -5,6 +5,7 @@
#include "soc_and_ip_translator.h"
#include "soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h"
#include "soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h"
+#include "soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h"
static void dc_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator,
enum dce_version dc_version)
@@ -14,9 +15,11 @@ static void dc_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc
dcn401_construct_soc_and_ip_translator(soc_and_ip_translator);
break;
case DCN_VERSION_4_2:
- case DCN_VERSION_4_2B:
dcn42_construct_soc_and_ip_translator(soc_and_ip_translator);
break;
+ case DCN_VERSION_4_2B:
+ dcn42b_construct_soc_and_ip_translator(soc_and_ip_translator);
+ break;
default:
break;
}
diff --git a/drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h b/drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h
index a6f6132df241..a0e9df382582 100644
--- a/drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h
+++ b/drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h
@@ -5,7 +5,7 @@
#ifndef SPL_DEBUG_H
#define SPL_DEBUG_H
-#if defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB)
+#ifdef CONFIG_KGDB
#define SPL_ASSERT_CRITICAL(expr) do { \
if (WARN_ON(!(expr))) { \
kgdb_breakpoint(); \
@@ -17,7 +17,7 @@
; \
} \
} while (0)
-#endif /* CONFIG_HAVE_KGDB || CONFIG_KGDB */
+#endif /* CONFIG_KGDB */
#if defined(CONFIG_DEBUG_KERNEL_DC)
#define SPL_ASSERT(expr) SPL_ASSERT_CRITICAL(expr)
diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
index 6f6a59a23495..e7879bd81f83 100644
--- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
+++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
@@ -246,14 +246,14 @@
* OS/FW agnostic memcpy
*/
#ifndef dmub_memcpy
-#define dmub_memcpy(dest, source, bytes) memcpy((dest), (source), (bytes))
+#define dmub_memcpy(dest, source, bytes) ((void)memcpy((dest), (source), (bytes)))
#endif
/**
* OS/FW agnostic memset
*/
#ifndef dmub_memset
-#define dmub_memset(dest, val, bytes) memset((dest), (val), (bytes))
+#define dmub_memset(dest, val, bytes) ((void)memset((dest), (val), (bytes)))
#endif
/**
@@ -1702,6 +1702,17 @@ enum dmub_gpint_command {
* ARGS: 1 - Power off
*/
DMUB_GPINT__PANEL_POWER_OFF_SEQ = 138,
+ /**
+ * DESC: Gets panel polarity bias.
+ * ARGS: 0 - Get panel polarity bias
+ */
+ DMUB_GPINT__PANEL_POLARITY_GET_BIAS = 139,
+ /**
+ * DESC: Enables panel polarity.
+ * ARGS: 0 - Disable panel polarity
+ * 1 - Enable panel polarity
+ */
+ DMUB_GPINT__PANEL_POLARITY_DEBUG_ENABLE = 140,
};
/**
@@ -1800,6 +1811,7 @@ enum dmub_inbox0_command {
*
* Command IDs should be treated as stable ABI.
* Do not reuse or modify IDs.
+ * Note that command IDs 1-4 have been deprecated.
*/
enum dmub_cmd_type {
/**
@@ -1807,22 +1819,6 @@ enum dmub_cmd_type {
*/
DMUB_CMD__NULL = 0,
/**
- * Read modify write register sequence offload.
- */
- DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE = 1,
- /**
- * Field update register sequence offload.
- */
- DMUB_CMD__REG_SEQ_FIELD_UPDATE_SEQ = 2,
- /**
- * Burst write sequence offload.
- */
- DMUB_CMD__REG_SEQ_BURST_WRITE = 3,
- /**
- * Reg wait sequence offload.
- */
- DMUB_CMD__REG_REG_WAIT = 4,
- /**
* Workaround to avoid HUBP underflow during NV12 playback.
*/
DMUB_CMD__PLAT_54186_WA = 5,
@@ -1972,6 +1968,11 @@ enum dmub_cmd_type {
DMUB_CMD__BOOT_TIME_CRC = 96,
/**
+ * Command type use for all Panel Polarity commands.
+ */
+ DMUB_CMD__PANEL_POLARITY = 97,
+
+ /**
* Command type use for VBIOS shared commands.
*/
DMUB_CMD__VBIOS = 128,
@@ -2041,98 +2042,6 @@ struct dmub_cmd_header {
unsigned int reserved1 : 2; /**< reserved bits */
};
-/*
- * struct dmub_cmd_read_modify_write_sequence - Read modify write
- *
- * 60 payload bytes can hold up to 5 sets of read modify writes,
- * each take 3 dwords.
- *
- * number of sequences = header.payload_bytes / sizeof(struct dmub_cmd_read_modify_write_sequence)
- *
- * modify_mask = 0xffff'ffff means all fields are going to be updated. in this case
- * command parser will skip the read and we can use modify_mask = 0xffff'ffff as reg write
- */
-struct dmub_cmd_read_modify_write_sequence {
- uint32_t addr; /**< register address */
- uint32_t modify_mask; /**< modify mask */
- uint32_t modify_value; /**< modify value */
-};
-
-/**
- * Maximum number of ops in read modify write sequence.
- */
-#define DMUB_READ_MODIFY_WRITE_SEQ__MAX 5
-
-/**
- * struct dmub_cmd_read_modify_write_sequence - Read modify write command.
- */
-struct dmub_rb_cmd_read_modify_write {
- struct dmub_cmd_header header; /**< command header */
- /**
- * Read modify write sequence.
- */
- struct dmub_cmd_read_modify_write_sequence seq[DMUB_READ_MODIFY_WRITE_SEQ__MAX];
-};
-
-/*
- * Update a register with specified masks and values sequeunce
- *
- * 60 payload bytes can hold address + up to 7 sets of mask/value combo, each take 2 dword
- *
- * number of field update sequence = (header.payload_bytes - sizeof(addr)) / sizeof(struct read_modify_write_sequence)
- *
- *
- * USE CASE:
- * 1. auto-increment register where additional read would update pointer and produce wrong result
- * 2. toggle a bit without read in the middle
- */
-
-struct dmub_cmd_reg_field_update_sequence {
- uint32_t modify_mask; /**< 0xffff'ffff to skip initial read */
- uint32_t modify_value; /**< value to update with */
-};
-
-/**
- * Maximum number of ops in field update sequence.
- */
-#define DMUB_REG_FIELD_UPDATE_SEQ__MAX 7
-
-/**
- * struct dmub_rb_cmd_reg_field_update_sequence - Field update command.
- */
-struct dmub_rb_cmd_reg_field_update_sequence {
- struct dmub_cmd_header header; /**< command header */
- uint32_t addr; /**< register address */
- /**
- * Field update sequence.
- */
- struct dmub_cmd_reg_field_update_sequence seq[DMUB_REG_FIELD_UPDATE_SEQ__MAX];
-};
-
-
-/**
- * Maximum number of burst write values.
- */
-#define DMUB_BURST_WRITE_VALUES__MAX 14
-
-/*
- * struct dmub_rb_cmd_burst_write - Burst write
- *
- * support use case such as writing out LUTs.
- *
- * 60 payload bytes can hold up to 14 values to write to given address
- *
- * number of payload = header.payload_bytes / sizeof(struct read_modify_write_sequence)
- */
-struct dmub_rb_cmd_burst_write {
- struct dmub_cmd_header header; /**< command header */
- uint32_t addr; /**< register start address */
- /**
- * Burst write register values.
- */
- uint32_t write_values[DMUB_BURST_WRITE_VALUES__MAX];
-};
-
/**
* struct dmub_rb_cmd_common - Common command header
*/
@@ -2145,24 +2054,6 @@ struct dmub_rb_cmd_common {
};
/**
- * struct dmub_cmd_reg_wait_data - Register wait data
- */
-struct dmub_cmd_reg_wait_data {
- uint32_t addr; /**< Register address */
- uint32_t mask; /**< Mask for register bits */
- uint32_t condition_field_value; /**< Value to wait for */
- uint32_t time_out_us; /**< Time out for reg wait in microseconds */
-};
-
-/**
- * struct dmub_rb_cmd_reg_wait - Register wait command
- */
-struct dmub_rb_cmd_reg_wait {
- struct dmub_cmd_header header; /**< Command header */
- struct dmub_cmd_reg_wait_data reg_wait; /**< Register wait data */
-};
-
-/**
* struct dmub_cmd_PLAT_54186_wa - Underflow workaround
*
* Reprograms surface parameters to avoid underflow.
@@ -4491,6 +4382,15 @@ enum dmub_cmd_replay_type {
};
/*
+ * Panel Polarity sub-types
+ */
+enum dmub_cmd_panel_polarity_type {
+ DMUB_CMD__PANEL_POLARITY_ENABLE = 0,
+ DMUB_CMD__PANEL_POLARITY_GET_BIAS = 1,
+ DMUB_CMD__PANEL_POLARITY_RESET = 2,
+};
+
+/*
* Panel Replay sub-types
*/
enum dmub_cmd_panel_replay_type {
@@ -6440,9 +6340,42 @@ struct dmub_cmd_cacp_set_backlight_data {
uint8_t panel_mask;
/**
+ * AUX HW Instance.
+ */
+ uint8_t aux_inst;
+
+ /**
* Explicit padding to 4 byte boundary.
*/
- uint8_t pad[2];
+ uint8_t pad[1];
+
+ /**
+ * Backlight control type.
+ * Value 0 is PWM backlight control.
+ * Value 1 is VAUX backlight control.
+ * Value 2 is AMD DPCD AUX backlight control.
+ */
+ enum dmub_backlight_control_type backlight_control_type;
+
+ /**
+ * Minimum luminance in nits.
+ */
+ uint32_t min_luminance;
+
+ /**
+ * Maximum luminance in nits.
+ */
+ uint32_t max_luminance;
+
+ /**
+ * Minimum backlight in pwm.
+ */
+ uint32_t min_backlight_pwm;
+
+ /**
+ * Maximum backlight in pwm.
+ */
+ uint32_t max_backlight_pwm;
};
/**
@@ -7123,6 +7056,80 @@ struct dmub_cmd_pr_enable_data {
uint8_t pad[2];
};
+struct dmub_cmd_panel_polarity_enable_data {
+ /**
+ * Panel Polarity enable or disable.
+ */
+ uint8_t enable;
+ /**
+ * OTG instance
+ */
+ uint8_t otg_inst;
+ /**
+ * @pad: Align structure to 4 byte boundary.
+ */
+ uint8_t pad[2];
+};
+
+struct dmub_cmd_panel_polarity_reset_data {
+ /**
+ * OTG instance
+ */
+ uint8_t otg_inst;
+ /**
+ * @pad: Align structure to 4 byte boundary.
+ */
+ uint8_t pad[3];
+};
+
+struct dmub_cmd_panel_polarity_get_bias_input {
+ /**
+ * OTG instance
+ */
+ uint8_t otg_inst;
+ uint8_t pad[3];
+};
+
+struct dmub_cmd_panel_polarity_get_bias_output {
+ /**
+ * Accumulated Polarity Bias
+ */
+ int32_t accumulated_bias;
+};
+
+struct dmub_rb_cmd_panel_polarity_enable {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+
+ struct dmub_cmd_panel_polarity_enable_data data;
+};
+
+
+struct dmub_rb_cmd_panel_polarity_get_bias {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+
+ union dmub_cmd_panel_polarity_get_bias_data {
+ struct dmub_cmd_panel_polarity_get_bias_input input; /**< Input */
+ struct dmub_cmd_panel_polarity_get_bias_output output; /**< Output */
+ uint32_t output_raw; /**< Raw data output */
+ } data;
+};
+
+struct dmub_rb_cmd_panel_polarity_reset {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+
+ struct dmub_cmd_panel_polarity_reset_data data;
+};
+
+
/**
* Definition of a DMUB_CMD__PR_ENABLE command.
* Panel Replay enable/disable is controlled using action in data.
@@ -7363,22 +7370,6 @@ union dmub_rb_cmd {
*/
struct dmub_rb_cmd_common cmd_common;
/**
- * Definition of a DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE command.
- */
- struct dmub_rb_cmd_read_modify_write read_modify_write;
- /**
- * Definition of a DMUB_CMD__REG_SEQ_FIELD_UPDATE_SEQ command.
- */
- struct dmub_rb_cmd_reg_field_update_sequence reg_field_update_seq;
- /**
- * Definition of a DMUB_CMD__REG_SEQ_BURST_WRITE command.
- */
- struct dmub_rb_cmd_burst_write burst_write;
- /**
- * Definition of a DMUB_CMD__REG_REG_WAIT command.
- */
- struct dmub_rb_cmd_reg_wait reg_wait;
- /**
* Definition of a DMUB_CMD__VBIOS_DIGX_ENCODER_CONTROL command.
*/
struct dmub_rb_cmd_digx_encoder_control digx_encoder_control;
@@ -7754,6 +7745,7 @@ union dmub_rb_cmd {
struct dmub_rb_cmd_pr_update_state pr_update_state;
struct dmub_rb_cmd_pr_general_cmd pr_general_cmd;
+
/**
* Definition of a DMUB_CMD__IHC command.
*/
@@ -7762,6 +7754,13 @@ union dmub_rb_cmd {
* Definition of a DMUB_CMD__BOOT_TIME_CRC_INIT command.
*/
struct dmub_rb_cmd_boot_time_crc_init boot_time_crc_init;
+
+ /**
+ * Definition of a DMUB_CMD__PANEL_POLARITY_ENABLE command.
+ */
+ struct dmub_rb_cmd_panel_polarity_enable panel_polarity_enable;
+ struct dmub_rb_cmd_panel_polarity_get_bias panel_polarity_get_bias;
+ struct dmub_rb_cmd_panel_polarity_reset panel_polarity_reset;
};
/**
diff --git a/drivers/gpu/drm/amd/display/include/ddc_service_types.h b/drivers/gpu/drm/amd/display/include/ddc_service_types.h
index 53210e3aa0e0..827e9bd7c5cf 100644
--- a/drivers/gpu/drm/amd/display/include/ddc_service_types.h
+++ b/drivers/gpu/drm/amd/display/include/ddc_service_types.h
@@ -45,6 +45,7 @@
#define DP_DEVICE_ID_BA4159 0xBA4159
#define DP_FORCE_PSRSU_CAPABILITY 0x40F
+#define DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_CAP 0x370
#define DP_SINK_PSR_ACTIVE_VTOTAL 0x373
#define DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_MODE 0x375
#define DP_SOURCE_PSR_ACTIVE_VTOTAL 0x376
diff --git a/drivers/gpu/drm/amd/display/include/gpio_types.h b/drivers/gpu/drm/amd/display/include/gpio_types.h
index 8dd46ed799e5..afd3fc73a911 100644
--- a/drivers/gpu/drm/amd/display/include/gpio_types.h
+++ b/drivers/gpu/drm/amd/display/include/gpio_types.h
@@ -277,6 +277,49 @@ enum gpio_config_type {
GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE
};
+struct gpio_id_offset_entry {
+ uint32_t offset;
+ uint32_t mask;
+
+ bool check_mask;
+
+ enum gpio_id id;
+ uint32_t en;
+};
+
+#define GPIO_ENTRY(_offset, _id, _en) \
+ { \
+ .offset = REG(_offset), \
+ .check_mask = false, \
+ .id = (_id), \
+ .en = (_en), \
+ }
+
+#define GPIO_MASK_ENTRY(_offset, _mask, _id, _en) \
+ { \
+ .offset = REG(_offset), \
+ .mask = (_mask), \
+ .check_mask = true, \
+ .id = (_id), \
+ .en = (_en), \
+ }
+
+struct gpio_pin_entry {
+ enum gpio_id id;
+ uint32_t en;
+
+ uint32_t offset;
+ uint32_t mask;
+};
+
+#define GPIO_PIN_ENTRY(_id, _en, _offset, _mask) \
+ { \
+ .id = (_id), \
+ .en = (_en), \
+ .offset = REG(_offset), \
+ .mask = (_mask), \
+ }
+
/* DDC configuration */
enum gpio_ddc_config_type {
@@ -293,6 +336,11 @@ struct gpio_ddc_config {
bool clock_en_bit_present;
};
+struct gpio_ddc_offset_entry {
+ uint32_t offset;
+ uint32_t en;
+};
+
/* HPD configuration */
struct gpio_hpd_config {
diff --git a/drivers/gpu/drm/amd/display/modules/power/Makefile b/drivers/gpu/drm/amd/display/modules/power/Makefile
index 3000f392bdbc..0746f671eb4d 100644
--- a/drivers/gpu/drm/amd/display/modules/power/Makefile
+++ b/drivers/gpu/drm/amd/display/modules/power/Makefile
@@ -23,7 +23,7 @@
# Makefile for the 'power' sub-module of DAL.
#
-MOD_POWER = power_helpers.o power.o power_abm.o power_psr.o power_replay.o
+MOD_POWER = power.o power_abm.o power_psr.o power_replay.o
AMD_DAL_MOD_POWER = $(addprefix $(AMDDALPATH)/modules/power/,$(MOD_POWER))
#$(info ************ DAL POWER MODULE MAKEFILE ************)
diff --git a/drivers/gpu/drm/amd/display/modules/power/power.c b/drivers/gpu/drm/amd/display/modules/power/power.c
index 5659a38b3366..db101fdb11f0 100644
--- a/drivers/gpu/drm/amd/display/modules/power/power.c
+++ b/drivers/gpu/drm/amd/display/modules/power/power.c
@@ -483,12 +483,7 @@ bool mod_power_notify_mode_change(struct mod_power *mod_power,
link = dc_stream_get_link(stream);
if (link != NULL && dc_get_edp_link_panel_inst(dc, link, &panel_inst)) {
- if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
- aux_inst = (uint8_t)link->aux_hw_inst;
- } else {
- ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF);
- aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel;
- }
+ aux_inst = link->dc->link_srv->get_ddc_aux_inst(link);
mod_power_update_backlight_on_mode_change(core_power, link, panel_inst, aux_inst, is_hdr);
@@ -501,3 +496,8 @@ bool mod_power_notify_mode_change(struct mod_power *mod_power,
return true;
}
+
+bool mod_power_only_edp(const struct dc_state *context, const struct dc_stream_state *stream)
+{
+ return context && context->stream_count == 1 && dc_is_embedded_signal(stream->signal);
+}
diff --git a/drivers/gpu/drm/amd/display/modules/power/power_abm.c b/drivers/gpu/drm/amd/display/modules/power/power_abm.c
index a1a0563598b5..b9447cb7485b 100644
--- a/drivers/gpu/drm/amd/display/modules/power/power_abm.c
+++ b/drivers/gpu/drm/amd/display/modules/power/power_abm.c
@@ -849,12 +849,7 @@ bool mod_power_set_backlight_nits(struct mod_power *mod_power,
core_power = MOD_POWER_TO_CORE(mod_power);
link = dc_stream_get_link(stream);
- if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
- aux_inst = (uint8_t)link->aux_hw_inst;
- } else {
- ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF);
- aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel;
- }
+ aux_inst = link->dc->link_srv->get_ddc_aux_inst(link);
if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &panel_inst))
return false;
@@ -941,12 +936,7 @@ bool mod_power_set_backlight_percent(struct mod_power *mod_power,
core_power = MOD_POWER_TO_CORE(mod_power);
link = dc_stream_get_link(stream);
- if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) {
- aux_inst = (uint8_t)link->aux_hw_inst;
- } else {
- ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF);
- aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel;
- }
+ aux_inst = link->dc->link_srv->get_ddc_aux_inst(link);
if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &panel_inst))
return false;
diff --git a/drivers/gpu/drm/amd/display/modules/power/power_replay.c b/drivers/gpu/drm/amd/display/modules/power/power_replay.c
index 983be9759e74..e782501442c4 100644
--- a/drivers/gpu/drm/amd/display/modules/power/power_replay.c
+++ b/drivers/gpu/drm/amd/display/modules/power/power_replay.c
@@ -175,11 +175,10 @@ static bool mod_power_update_replay_active_status(unsigned int active_replay_eve
if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS])
*coasting_vtotal =
link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS];
- if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) {
- ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF);
- *frame_skip_number =
- (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
- }
+
+ ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF);
+ *frame_skip_number =
+ (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS];
/* During the ultra sleep mode testing, disable the timing sync in short vblank mode */
if (active_replay_events & (replay_event_test_harness_enable_replay)) {
diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h
index 3fd38323a88b..10396018afb3 100644
--- a/drivers/gpu/drm/amd/include/amd_shared.h
+++ b/drivers/gpu/drm/amd/include/amd_shared.h
@@ -471,10 +471,7 @@ struct amd_ip_funcs {
void (*complete)(struct amdgpu_ip_block *ip_block);
bool (*is_idle)(struct amdgpu_ip_block *ip_block);
int (*wait_for_idle)(struct amdgpu_ip_block *ip_block);
- bool (*check_soft_reset)(struct amdgpu_ip_block *ip_block);
- int (*pre_soft_reset)(struct amdgpu_ip_block *ip_block);
int (*soft_reset)(struct amdgpu_ip_block *ip_block);
- int (*post_soft_reset)(struct amdgpu_ip_block *ip_block);
int (*set_clockgating_state)(struct amdgpu_ip_block *ip_block,
enum amd_clockgating_state state);
int (*set_powergating_state)(struct amdgpu_ip_block *ip_block,
diff --git a/drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h b/drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h
index ead81aeffd67..11c32e4274fa 100644
--- a/drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h
+++ b/drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h
@@ -493,6 +493,10 @@
#define regSDMA_RLC0_MIDCMD_DATA10_BASE_IDX 0
#define regSDMA_RLC0_MIDCMD_CNTL 0x017b
#define regSDMA_RLC0_MIDCMD_CNTL_BASE_IDX 0
+#define regSDMA_RLC0_UTILIZATION_LO 0x017c
+#define regSDMA_RLC0_UTILIZATION_LO_BASE_IDX 0
+#define regSDMA_RLC0_UTILIZATION_HI 0x017d
+#define regSDMA_RLC0_UTILIZATION_HI_BASE_IDX 0
#define regSDMA_RLC1_RB_CNTL 0x0188
#define regSDMA_RLC1_RB_CNTL_BASE_IDX 0
#define regSDMA_RLC1_RB_BASE 0x0189
diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h
index 44e225e097d0..965b50c8ca30 100644
--- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h
@@ -339,6 +339,9 @@ struct kfd2kgd_calls {
uint32_t *ptl_state,
enum amdgpu_ptl_fmt *fmt1,
enum amdgpu_ptl_fmt *fmt2);
+ int (*hqd_sdma_get_counter)(struct amdgpu_device *adev,
+ void *mqd, uint32_t num_sdma_queues_per_eng,
+ uint64_t *val);
};
#endif /* KGD_KFD_INTERFACE_H_INCLUDED */
diff --git a/drivers/gpu/drm/amd/include/mes_v11_api_def.h b/drivers/gpu/drm/amd/include/mes_v11_api_def.h
index 7808147ada38..b06412ac8583 100644
--- a/drivers/gpu/drm/amd/include/mes_v11_api_def.h
+++ b/drivers/gpu/drm/amd/include/mes_v11_api_def.h
@@ -238,8 +238,9 @@ union MESAPI_SET_HW_RESOURCES {
uint32_t enable_mes_sch_stb_log : 1;
uint32_t limit_single_process : 1;
uint32_t is_strix_tmz_wa_enabled :1;
- uint32_t enable_lr_compute_wa : 1;
- uint32_t reserved : 12;
+ uint32_t enable_lr_compute_wa : 2;
+ uint32_t enable_compute_pipe_reset : 1;
+ uint32_t reserved : 10;
};
uint32_t uint32_t_all;
};
diff --git a/drivers/gpu/drm/amd/include/mes_v12_api_def.h b/drivers/gpu/drm/amd/include/mes_v12_api_def.h
index e541a43714a1..cb7ebdfffeeb 100644
--- a/drivers/gpu/drm/amd/include/mes_v12_api_def.h
+++ b/drivers/gpu/drm/amd/include/mes_v12_api_def.h
@@ -294,8 +294,9 @@ union MESAPI_SET_HW_RESOURCES {
uint32_t limit_single_process : 1;
uint32_t unmapped_doorbell_handling: 2;
uint32_t enable_mes_fence_int: 1;
- uint32_t enable_lr_compute_wa : 1;
- uint32_t reserved : 9;
+ uint32_t enable_lr_compute_wa : 2;
+ uint32_t enable_compute_pipe_reset : 1;
+ uint32_t reserved : 7;
};
uint32_t uint32_all;
};
diff --git a/drivers/gpu/drm/amd/include/soc15_hw_ip.h b/drivers/gpu/drm/amd/include/soc15_hw_ip.h
index a20e59584dde..60f588dd0130 100644
--- a/drivers/gpu/drm/amd/include/soc15_hw_ip.h
+++ b/drivers/gpu/drm/amd/include/soc15_hw_ip.h
@@ -44,6 +44,7 @@
#define SDPMUX_HWID 19
#define NTB_HWID 20
#define VPE_HWID 21
+#define UMSCH_HWID 22
#define IOHC_HWID 24
#define L2IMU_HWID 28
#define VCE_HWID 32
diff --git a/drivers/gpu/drm/amd/include/v9_structs.h b/drivers/gpu/drm/amd/include/v9_structs.h
index a2f81b9c38af..e0d387f08576 100644
--- a/drivers/gpu/drm/amd/include/v9_structs.h
+++ b/drivers/gpu/drm/amd/include/v9_structs.h
@@ -69,8 +69,8 @@ struct v9_sdma_mqd {
uint32_t sdmax_rlcx_midcmd_cntl;
uint32_t reserved_42;
uint32_t reserved_43;
- uint32_t reserved_44;
- uint32_t reserved_45;
+ uint32_t sdmax_rlcx_utilization_lo;
+ uint32_t sdmax_rlcx_utilization_hi;
uint32_t reserved_46;
uint32_t reserved_47;
uint32_t reserved_48;
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index 97da01aff76c..f5a5d72b4108 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -100,6 +100,37 @@ const char * const amdgpu_pp_profile_name[] = {
"UNCAPPED",
};
+static int amdgpu_pm_parse_long_params(char *str, long *params,
+ uint32_t max_params,
+ uint32_t *num_params)
+{
+ const char delimiter[] = { ' ', '\n', '\0' };
+ uint32_t count = 0;
+ char *sub_str;
+ int ret;
+
+ if (!params || !num_params)
+ return -EINVAL;
+
+ while ((sub_str = strsep(&str, delimiter)) != NULL) {
+ if (strlen(sub_str) == 0)
+ continue;
+ if (count >= max_params)
+ return -EINVAL;
+ ret = kstrtol(sub_str, 0, &params[count]);
+ if (ret)
+ return -EINVAL;
+ count++;
+ if (!str)
+ break;
+ while (isspace(*str))
+ str++;
+ }
+ *num_params = count;
+
+ return 0;
+}
+
/**
* amdgpu_pm_dev_state_check - Check if device can be accessed.
* @adev: Target device.
@@ -769,8 +800,6 @@ static ssize_t amdgpu_set_pp_od_clk_voltage(struct device *dev,
long parameter[64];
char buf_cpy[128];
char *tmp_str;
- char *sub_str;
- const char delimiter[3] = {' ', '\n', '\0'};
uint32_t type;
if (count > 127 || count == 0)
@@ -805,22 +834,10 @@ static ssize_t amdgpu_set_pp_od_clk_voltage(struct device *dev,
tmp_str++;
while (isspace(*++tmp_str));
- while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) {
- if (strlen(sub_str) == 0)
- continue;
- if (parameter_size >= ARRAY_SIZE(parameter))
- return -EINVAL;
- ret = kstrtol(sub_str, 0, &parameter[parameter_size]);
- if (ret)
- return -EINVAL;
- parameter_size++;
-
- if (!tmp_str)
- break;
-
- while (isspace(*tmp_str))
- tmp_str++;
- }
+ ret = amdgpu_pm_parse_long_params(
+ tmp_str, parameter, ARRAY_SIZE(parameter), &parameter_size);
+ if (ret)
+ return ret;
ret = amdgpu_pm_get_access(adev);
if (ret < 0)
@@ -1393,11 +1410,9 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev,
struct amdgpu_device *adev = drm_to_adev(ddev);
uint32_t parameter_size = 0;
long parameter[64];
- char *sub_str, buf_cpy[128];
- char *tmp_str;
+ char buf_cpy[128];
char tmp[2];
long int profile_mode = 0;
- const char delimiter[3] = {' ', '\n', '\0'};
/* Reject empty/whitespace strings - fuzzing found this is not validated */
if (count == 0 || sysfs_streq(buf, ""))
@@ -1415,19 +1430,11 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev,
while (isspace(*buf))
buf++;
strscpy(buf_cpy, buf, sizeof(buf_cpy));
- tmp_str = buf_cpy;
- while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) {
- if (strlen(sub_str) == 0)
- continue;
- ret = kstrtol(sub_str, 0, &parameter[parameter_size]);
- if (ret)
- return -EINVAL;
- parameter_size++;
- if (!tmp_str)
- break;
- while (isspace(*tmp_str))
- tmp_str++;
- }
+ ret = amdgpu_pm_parse_long_params(buf_cpy, parameter,
+ ARRAY_SIZE(parameter) - 1,
+ &parameter_size);
+ if (ret)
+ return ret;
}
parameter[parameter_size] = profile_mode;
@@ -3958,18 +3965,14 @@ out_pm_put:
return size;
}
-static int parse_input_od_command_lines(const char *buf,
- size_t count,
- u32 *type,
- long *params,
- size_t params_max,
+static int parse_input_od_command_lines(const char *buf, size_t count,
+ u32 *type, long *params,
+ uint32_t max_params,
uint32_t *num_of_params)
{
- const char delimiter[3] = {' ', '\n', '\0'};
uint32_t parameter_size = 0;
char buf_cpy[128] = {0};
- char *tmp_str, *sub_str;
- int ret;
+ char *tmp_str;
if (count > sizeof(buf_cpy) - 1)
return -EINVAL;
@@ -3994,28 +3997,8 @@ static int parse_input_od_command_lines(const char *buf,
break;
}
- while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) {
- if (strlen(sub_str) == 0)
- continue;
-
- if (parameter_size >= params_max)
- return -EINVAL;
-
- ret = kstrtol(sub_str, 0, &params[parameter_size]);
- if (ret)
- return -EINVAL;
- parameter_size++;
-
- if (!tmp_str)
- break;
-
- while (isspace(*tmp_str))
- tmp_str++;
- }
-
- *num_of_params = parameter_size;
-
- return 0;
+ return amdgpu_pm_parse_long_params(tmp_str, params, max_params,
+ num_of_params);
}
static int
@@ -4028,10 +4011,7 @@ amdgpu_distribute_custom_od_settings(struct amdgpu_device *adev,
long parameter[64];
int ret;
- ret = parse_input_od_command_lines(in_buf,
- count,
- &cmd_type,
- parameter,
+ ret = parse_input_od_command_lines(in_buf, count, &cmd_type, parameter,
ARRAY_SIZE(parameter),
&parameter_size);
if (ret)
diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
index 6f5c27bdc1e9..7c70e228a5ba 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
@@ -660,25 +660,20 @@ static int amd_powerplay_reset(void *handle)
static int pp_dpm_set_pp_table(void *handle, const char *buf, size_t size)
{
struct pp_hwmgr *hwmgr = handle;
+ void *hardcode_pp_table;
int ret = -ENOMEM;
- if (!hwmgr || !hwmgr->pm_en)
- return -EINVAL;
-
- if (size > hwmgr->soft_pp_table_size)
+ if (!hwmgr || !hwmgr->pm_en || !buf || !size || size > U32_MAX)
return -EINVAL;
- if (!hwmgr->hardcode_pp_table) {
- hwmgr->hardcode_pp_table = kmemdup(hwmgr->soft_pp_table,
- hwmgr->soft_pp_table_size,
- GFP_KERNEL);
- if (!hwmgr->hardcode_pp_table)
- return ret;
- }
-
- memcpy(hwmgr->hardcode_pp_table, buf, size);
+ hardcode_pp_table = kmemdup(buf, size, GFP_KERNEL);
+ if (!hardcode_pp_table)
+ return ret;
+ kfree(hwmgr->hardcode_pp_table);
+ hwmgr->hardcode_pp_table = hardcode_pp_table;
hwmgr->soft_pp_table = hwmgr->hardcode_pp_table;
+ hwmgr->soft_pp_table_size = size;
ret = amd_powerplay_reset(handle);
if (ret)
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c
index ce166a7f8e42..4b796d60b03d 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c
@@ -46,16 +46,22 @@ union voltage_object_info {
static int atomctrl_retrieve_ac_timing(
uint8_t index,
ATOM_INIT_REG_BLOCK *reg_block,
+ u8 *table_end,
pp_atomctrl_mc_reg_table *table)
{
uint32_t i, j;
+ u16 stride = le16_to_cpu(reg_block->usRegDataBlkSize);
uint8_t tmem_id;
ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *)
((uint8_t *)reg_block + (2 * sizeof(uint16_t)) + le16_to_cpu(reg_block->usRegIndexTblSize));
uint8_t num_ranges = 0;
- while (*(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK &&
+ if (stride < sizeof(uint32_t))
+ return -EINVAL;
+
+ while ((uint8_t *)reg_data + sizeof(uint32_t) <= table_end &&
+ *(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK &&
num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES) {
tmem_id = (uint8_t)((*(uint32_t *)reg_data & MEM_ID_MASK) >> MEM_ID_SHIFT);
@@ -67,6 +73,10 @@ static int atomctrl_retrieve_ac_timing(
for (i = 0, j = 1; i < table->last; i++) {
if ((table->mc_reg_address[i].uc_pre_reg_data &
LOW_NIBBLE_MASK) == DATA_FROM_TABLE) {
+ if ((uint8_t *)reg_data +
+ (j + 1) * sizeof(uint32_t) > table_end)
+ return -EINVAL;
+
table->mc_reg_table_entry[num_ranges].mc_data[i] =
(uint32_t)*((uint32_t *)reg_data + j);
j++;
@@ -81,11 +91,13 @@ static int atomctrl_retrieve_ac_timing(
}
reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *)
- ((uint8_t *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)) ;
+ ((uint8_t *)reg_data + stride);
}
- PP_ASSERT_WITH_CODE((*(uint32_t *)reg_data == END_OF_REG_DATA_BLOCK),
- "Invalid VramInfo table.", return -1);
+ if ((uint8_t *)reg_data + sizeof(uint32_t) > table_end ||
+ *(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK)
+ return -EINVAL;
+
table->num_entries = num_ranges;
return 0;
@@ -136,6 +148,7 @@ int atomctrl_initialize_mc_reg_table(
{
ATOM_VRAM_INFO_HEADER_V2_1 *vram_info;
ATOM_INIT_REG_BLOCK *reg_block;
+ u8 *table_end;
int result = 0;
u8 frev, crev;
u16 size;
@@ -157,6 +170,7 @@ int atomctrl_initialize_mc_reg_table(
}
if (0 == result) {
+ table_end = (uint8_t *)vram_info + size;
reg_block = (ATOM_INIT_REG_BLOCK *)
((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset));
result = atomctrl_set_mc_reg_address_table(reg_block, table);
@@ -164,7 +178,7 @@ int atomctrl_initialize_mc_reg_table(
if (0 == result) {
result = atomctrl_retrieve_ac_timing(module_index,
- reg_block, table);
+ reg_block, table_end, table);
}
return result;
@@ -177,6 +191,7 @@ int atomctrl_initialize_mc_reg_table_v2_2(
{
ATOM_VRAM_INFO_HEADER_V2_2 *vram_info;
ATOM_INIT_REG_BLOCK *reg_block;
+ u8 *table_end;
int result = 0;
u8 frev, crev;
u16 size;
@@ -198,6 +213,7 @@ int atomctrl_initialize_mc_reg_table_v2_2(
}
if (0 == result) {
+ table_end = (uint8_t *)vram_info + size;
reg_block = (ATOM_INIT_REG_BLOCK *)
((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset));
result = atomctrl_set_mc_reg_address_table(reg_block, table);
@@ -205,7 +221,7 @@ int atomctrl_initialize_mc_reg_table_v2_2(
if (0 == result) {
result = atomctrl_retrieve_ac_timing(module_index,
- reg_block, table);
+ reg_block, table_end, table);
}
return result;
@@ -268,15 +284,21 @@ static const ATOM_VOLTAGE_OBJECT_V3 *atomctrl_lookup_voltage_type_v3(
unsigned int offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]);
uint8_t *start = (uint8_t *)voltage_object_info_table;
- while (offset < size) {
+ while (offset + sizeof(ATOM_VOLTAGE_OBJECT_HEADER_V3) <= size) {
const ATOM_VOLTAGE_OBJECT_V3 *voltage_object =
(const ATOM_VOLTAGE_OBJECT_V3 *)(start + offset);
+ u16 obj_size;
+
+ obj_size = le16_to_cpu(voltage_object->asGpioVoltageObj.sHeader.usSize);
+ if (obj_size < sizeof(voltage_object->asGpioVoltageObj.sHeader) ||
+ offset + obj_size > size)
+ break;
if (voltage_type == voltage_object->asGpioVoltageObj.sHeader.ucVoltageType &&
voltage_mode == voltage_object->asGpioVoltageObj.sHeader.ucVoltageMode)
return voltage_object;
- offset += le16_to_cpu(voltage_object->asGpioVoltageObj.sHeader.usSize);
+ offset += obj_size;
}
return NULL;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c
index 6120f14caab0..69aee8661d1e 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c
@@ -36,16 +36,21 @@ static const union atom_voltage_object_v4 *pp_atomfwctrl_lookup_voltage_type_v4(
offsetof(struct atom_voltage_objects_info_v4_1, voltage_object[0]);
unsigned long start = (unsigned long)voltage_object_info_table;
- while (offset < size) {
+ while (offset + sizeof(struct atom_voltage_object_header_v4) <= size) {
const union atom_voltage_object_v4 *voltage_object =
(const union atom_voltage_object_v4 *)(start + offset);
+ u16 obj_size;
+
+ obj_size = le16_to_cpu(voltage_object->gpio_voltage_obj.header.object_size);
+ if (obj_size < sizeof(voltage_object->gpio_voltage_obj.header) ||
+ offset + obj_size > size)
+ break;
if (voltage_type == voltage_object->gpio_voltage_obj.header.voltage_type &&
voltage_mode == voltage_object->gpio_voltage_obj.header.voltage_mode)
return voltage_object;
- offset += le16_to_cpu(voltage_object->gpio_voltage_obj.header.object_size);
-
+ offset += obj_size;
}
return NULL;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c
index 6fcca65bd7d4..c5673077c895 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c
@@ -150,6 +150,368 @@ static const void *get_powerplay_table(struct pp_hwmgr *hwmgr)
return table_address;
}
+static bool tonga_pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset,
+ size_t size)
+{
+ size_t table_size = hwmgr->soft_pp_table_size;
+
+ return offset <= table_size && size <= table_size - offset;
+}
+
+static int get_tonga_subtable(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ u16 table_offset, size_t table_size, const void **table)
+{
+ PP_ASSERT_WITH_CODE((table_offset != 0),
+ "Invalid PowerPlay Table!", return -1);
+ PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *table = (const void *)(((unsigned long)powerplay_table) + table_offset);
+
+ return 0;
+}
+
+static int validate_tonga_table_entries(struct pp_hwmgr *hwmgr,
+ u16 table_offset, size_t entries_offset,
+ u8 num_entries, size_t entry_size)
+{
+ size_t table_size;
+
+ PP_ASSERT_WITH_CODE((num_entries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = entries_offset + num_entries * entry_size;
+ PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ return 0;
+}
+
+static int get_tonga_voltage_lookup_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ u16 table_offset, uint32_t max_levels,
+ const ATOM_Tonga_Voltage_Lookup_Table **lookup_table)
+{
+ const ATOM_Tonga_Voltage_Lookup_Table *table;
+ size_t table_size;
+ int ret;
+
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ PP_ASSERT_WITH_CODE((table->ucNumEntries != 0 &&
+ table->ucNumEntries <= max_levels),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = offsetof(ATOM_Tonga_Voltage_Lookup_Table, entries) +
+ table->ucNumEntries * sizeof(ATOM_Tonga_Voltage_Lookup_Record);
+ PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *lookup_table = table;
+
+ return 0;
+}
+
+static int get_tonga_mclk_dependency_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Tonga_MCLK_Dependency_Table **mclk_dep_table)
+{
+ const ATOM_Tonga_MCLK_Dependency_Table *table;
+ u16 table_offset;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usMclkDependencyTableOffset);
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ ret = validate_tonga_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Tonga_MCLK_Dependency_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Tonga_MCLK_Dependency_Record));
+ if (ret)
+ return ret;
+
+ *mclk_dep_table = table;
+
+ return 0;
+}
+
+static int get_tonga_mm_dependency_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Tonga_MM_Dependency_Table **mm_dep_table)
+{
+ const ATOM_Tonga_MM_Dependency_Table *table;
+ u16 table_offset;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usMMDependencyTableOffset);
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ ret = validate_tonga_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Tonga_MM_Dependency_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Tonga_MM_Dependency_Record));
+ if (ret)
+ return ret;
+
+ *mm_dep_table = table;
+
+ return 0;
+}
+
+static int get_tonga_sclk_dependency_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const PPTable_Generic_SubTable_Header **sclk_dep_table)
+{
+ const PPTable_Generic_SubTable_Header *header;
+ u16 table_offset;
+ size_t entries_offset;
+ size_t entry_size;
+ u8 num_entries;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usSclkDependencyTableOffset);
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*header), (const void **)&header);
+ if (ret)
+ return ret;
+
+ if (header->ucRevId < 1) {
+ const ATOM_Tonga_SCLK_Dependency_Table *table =
+ (const ATOM_Tonga_SCLK_Dependency_Table *)header;
+
+ entries_offset = offsetof(ATOM_Tonga_SCLK_Dependency_Table, entries);
+ entry_size = sizeof(ATOM_Tonga_SCLK_Dependency_Record);
+ num_entries = table->ucNumEntries;
+ } else {
+ const ATOM_Polaris_SCLK_Dependency_Table *table =
+ (const ATOM_Polaris_SCLK_Dependency_Table *)header;
+
+ entries_offset = offsetof(ATOM_Polaris_SCLK_Dependency_Table, entries);
+ entry_size = sizeof(ATOM_Polaris_SCLK_Dependency_Record);
+ num_entries = table->ucNumEntries;
+ }
+
+ ret = validate_tonga_table_entries(hwmgr, table_offset, entries_offset,
+ num_entries, entry_size);
+ if (ret)
+ return ret;
+
+ *sclk_dep_table = header;
+
+ return 0;
+}
+
+static int get_tonga_pcie_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const PPTable_Generic_SubTable_Header **pcie_table)
+{
+ const PPTable_Generic_SubTable_Header *header;
+ u16 table_offset;
+ size_t entries_offset;
+ size_t entry_size;
+ u8 num_entries;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usPCIETableOffset);
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*header), (const void **)&header);
+ if (ret)
+ return ret;
+
+ if (header->ucRevId < 1) {
+ const ATOM_Tonga_PCIE_Table *table =
+ (const ATOM_Tonga_PCIE_Table *)header;
+
+ entries_offset = offsetof(ATOM_Tonga_PCIE_Table, entries);
+ entry_size = sizeof(ATOM_Tonga_PCIE_Record);
+ num_entries = table->ucNumEntries;
+ } else {
+ const ATOM_Polaris10_PCIE_Table *table =
+ (const ATOM_Polaris10_PCIE_Table *)header;
+
+ entries_offset = offsetof(ATOM_Polaris10_PCIE_Table, entries);
+ entry_size = sizeof(ATOM_Polaris10_PCIE_Record);
+ num_entries = table->ucNumEntries;
+ }
+
+ ret = validate_tonga_table_entries(hwmgr, table_offset, entries_offset,
+ num_entries, entry_size);
+ if (ret)
+ return ret;
+
+ *pcie_table = header;
+
+ return 0;
+}
+
+static int get_tonga_hard_limit_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Tonga_Hard_Limit_Table **hard_limit_table)
+{
+ const ATOM_Tonga_Hard_Limit_Table *table;
+ u16 table_offset;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usHardLimitTableOffset);
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ ret = validate_tonga_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Tonga_Hard_Limit_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Tonga_Hard_Limit_Record));
+ if (ret)
+ return ret;
+
+ *hard_limit_table = table;
+
+ return 0;
+}
+
+static int get_tonga_thermal_controller_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Tonga_Thermal_Controller **thermal_controller)
+{
+ u16 table_offset;
+
+ table_offset = le16_to_cpu(powerplay_table->usThermalControllerOffset);
+
+ return get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(**thermal_controller),
+ (const void **)thermal_controller);
+}
+
+static int get_tonga_fan_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const PPTable_Generic_SubTable_Header **fan_table)
+{
+ const PPTable_Generic_SubTable_Header *header;
+ u16 table_offset;
+ size_t table_size;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usFanTableOffset);
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*header), (const void **)&header);
+ if (ret)
+ return ret;
+
+ if (header->ucRevId < 8)
+ table_size = sizeof(ATOM_Tonga_Fan_Table);
+ else if (header->ucRevId == 8)
+ table_size = sizeof(ATOM_Fiji_Fan_Table);
+ else
+ table_size = sizeof(ATOM_Polaris_Fan_Table);
+
+ PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *fan_table = header;
+
+ return 0;
+}
+
+static int get_tonga_power_tune_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const PPTable_Generic_SubTable_Header **power_tune_table)
+{
+ const PPTable_Generic_SubTable_Header *header;
+ u16 table_offset;
+ size_t table_size;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usPowerTuneTableOffset);
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*header), (const void **)&header);
+ if (ret)
+ return ret;
+
+ if (header->ucRevId < 3)
+ table_size = sizeof(ATOM_Tonga_PowerTune_Table);
+ else if (header->ucRevId < 4)
+ table_size = sizeof(ATOM_Fiji_PowerTune_Table);
+ else
+ table_size = sizeof(ATOM_Polaris_PowerTune_Table);
+
+ PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *power_tune_table = header;
+
+ return 0;
+}
+
+static int get_tonga_ppm_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Tonga_PPM_Table **ppm_table)
+{
+ u16 table_offset;
+
+ table_offset = le16_to_cpu(powerplay_table->usPPMTableOffset);
+
+ return get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(**ppm_table), (const void **)ppm_table);
+}
+
+static int get_tonga_gpio_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Tonga_GPIO_Table **gpio_table)
+{
+ u16 table_offset;
+
+ table_offset = le16_to_cpu(powerplay_table->usGPIOTableOffset);
+
+ return get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(**gpio_table), (const void **)gpio_table);
+}
+
+static int get_tonga_vce_state_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Tonga_VCE_State_Table **vce_state_table)
+{
+ const ATOM_Tonga_VCE_State_Table *table;
+ u16 table_offset;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usVCEStateTableOffset);
+ ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ ret = validate_tonga_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Tonga_VCE_State_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Tonga_VCE_State_Record));
+ if (ret)
+ return ret;
+
+ *vce_state_table = table;
+
+ return 0;
+}
+
static int get_vddc_lookup_table(
struct pp_hwmgr *hwmgr,
phm_ppt_v1_voltage_lookup_table **lookup_table,
@@ -158,6 +520,7 @@ static int get_vddc_lookup_table(
)
{
uint32_t i;
+ uint32_t num_entries;
phm_ppt_v1_voltage_lookup_table *table;
phm_ppt_v1_voltage_lookup_record *record;
ATOM_Tonga_Voltage_Lookup_Record *atom_record;
@@ -165,13 +528,22 @@ static int get_vddc_lookup_table(
PP_ASSERT_WITH_CODE((0 != vddc_lookup_pp_tables->ucNumEntries),
"Invalid CAC Leakage PowerPlay Table!", return 1);
- table = kzalloc_flex(*table, entries, max_levels);
+ num_entries = min_t(uint32_t, vddc_lookup_pp_tables->ucNumEntries,
+ min_t(uint32_t, max_levels,
+ pp_entries_max(hwmgr, vddc_lookup_pp_tables,
+ sizeof(*vddc_lookup_pp_tables),
+ sizeof(ATOM_Tonga_Voltage_Lookup_Record))));
+ if (num_entries < vddc_lookup_pp_tables->ucNumEntries)
+ pr_warn("amdgpu: VddcLookup table: clamping ucNumEntries %u -> %u\n",
+ vddc_lookup_pp_tables->ucNumEntries, num_entries);
+
+ table = kzalloc_flex(*table, entries, num_entries);
if (!table)
return -ENOMEM;
- table->count = vddc_lookup_pp_tables->ucNumEntries;
+ table->count = num_entries;
- for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) {
+ for (i = 0; i < num_entries; i++) {
record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
phm_ppt_v1_voltage_lookup_record,
entries, table, i);
@@ -198,7 +570,7 @@ static int get_vddc_lookup_table(
*/
static int get_platform_power_management_table(
struct pp_hwmgr *hwmgr,
- ATOM_Tonga_PPM_Table *atom_ppm_table)
+ const ATOM_Tonga_PPM_Table *atom_ppm_table)
{
struct phm_ppm_table *ptr = kzalloc_obj(*ptr);
struct phm_ppt_v1_information *pp_table_information =
@@ -246,7 +618,7 @@ static int init_dpm_2_parameters(
{
int result = 0;
struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable);
- ATOM_Tonga_PPM_Table *atom_ppm_table;
+ const ATOM_Tonga_PPM_Table *atom_ppm_table;
uint32_t disable_ppm = 0;
uint32_t disable_power_control = 0;
@@ -275,30 +647,39 @@ static int init_dpm_2_parameters(
}
if (0 != powerplay_table->usVddcLookupTableOffset) {
- const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable =
- (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usVddcLookupTableOffset));
-
- result = get_vddc_lookup_table(hwmgr,
- &pp_table_information->vddc_lookup_table, pVddcCACTable, 16);
+ const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable;
+
+ result = get_tonga_voltage_lookup_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usVddcLookupTableOffset),
+ 16, &pVddcCACTable);
+ if (!result)
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_information->vddc_lookup_table,
+ pVddcCACTable, 16);
}
- if (0 != powerplay_table->usVddgfxLookupTableOffset) {
- const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable =
- (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset));
+ if (!result && 0 != powerplay_table->usVddgfxLookupTableOffset) {
+ const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable;
- result = get_vddc_lookup_table(hwmgr,
- &pp_table_information->vddgfx_lookup_table, pVddgfxCACTable, 16);
+ result = get_tonga_voltage_lookup_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset),
+ 16, &pVddgfxCACTable);
+ if (!result)
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_information->vddgfx_lookup_table,
+ pVddgfxCACTable, 16);
}
disable_ppm = 0;
if (0 == disable_ppm) {
- atom_ppm_table = (ATOM_Tonga_PPM_Table *)
- (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset));
-
if (0 != powerplay_table->usPPMTableOffset) {
- if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) {
+ int ret;
+
+ ret = get_tonga_ppm_table(hwmgr, powerplay_table,
+ &atom_ppm_table);
+ if (!ret &&
+ get_platform_power_management_table(hwmgr,
+ atom_ppm_table) == 0) {
phm_cap_set(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_EnablePlatformPowerManagement);
}
@@ -363,6 +744,7 @@ static int get_mclk_voltage_dependency_table(
)
{
uint32_t i;
+ uint32_t num_entries;
phm_ppt_v1_clock_voltage_dependency_table *mclk_table;
phm_ppt_v1_clock_voltage_dependency_record *mclk_table_record;
ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record;
@@ -370,14 +752,21 @@ static int get_mclk_voltage_dependency_table(
PP_ASSERT_WITH_CODE((0 != mclk_dep_table->ucNumEntries),
"Invalid PowerPlay Table!", return -1);
- mclk_table = kzalloc_flex(*mclk_table, entries,
- mclk_dep_table->ucNumEntries);
+ num_entries = min_t(uint32_t, mclk_dep_table->ucNumEntries,
+ pp_entries_max(hwmgr, mclk_dep_table,
+ sizeof(*mclk_dep_table),
+ sizeof(ATOM_Tonga_MCLK_Dependency_Record)));
+ if (num_entries < mclk_dep_table->ucNumEntries)
+ pr_warn("amdgpu: MCLK dependency table: clamping ucNumEntries %u -> %u\n",
+ mclk_dep_table->ucNumEntries, num_entries);
+
+ mclk_table = kzalloc_flex(*mclk_table, entries, num_entries);
if (!mclk_table)
return -ENOMEM;
- mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries;
+ mclk_table->count = num_entries;
- for (i = 0; i < mclk_dep_table->ucNumEntries; i++) {
+ for (i = 0; i < num_entries; i++) {
mclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
phm_ppt_v1_clock_voltage_dependency_record,
entries, mclk_table, i);
@@ -403,6 +792,7 @@ static int get_sclk_voltage_dependency_table(
)
{
uint32_t i;
+ uint32_t num_entries;
phm_ppt_v1_clock_voltage_dependency_table *sclk_table;
phm_ppt_v1_clock_voltage_dependency_record *sclk_table_record;
@@ -414,14 +804,21 @@ static int get_sclk_voltage_dependency_table(
PP_ASSERT_WITH_CODE((0 != tonga_table->ucNumEntries),
"Invalid PowerPlay Table!", return -1);
- sclk_table = kzalloc_flex(*sclk_table, entries,
- tonga_table->ucNumEntries);
+ num_entries = min_t(uint32_t, tonga_table->ucNumEntries,
+ pp_entries_max(hwmgr, tonga_table,
+ sizeof(*tonga_table),
+ sizeof(ATOM_Tonga_SCLK_Dependency_Record)));
+ if (num_entries < tonga_table->ucNumEntries)
+ pr_warn("amdgpu: Tonga SCLK dependency table: clamping ucNumEntries %u -> %u\n",
+ tonga_table->ucNumEntries, num_entries);
+
+ sclk_table = kzalloc_flex(*sclk_table, entries, num_entries);
if (!sclk_table)
return -ENOMEM;
- sclk_table->count = (uint32_t)tonga_table->ucNumEntries;
+ sclk_table->count = num_entries;
- for (i = 0; i < tonga_table->ucNumEntries; i++) {
+ for (i = 0; i < num_entries; i++) {
sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
ATOM_Tonga_SCLK_Dependency_Record,
entries, tonga_table, i);
@@ -443,14 +840,21 @@ static int get_sclk_voltage_dependency_table(
PP_ASSERT_WITH_CODE((0 != polaris_table->ucNumEntries),
"Invalid PowerPlay Table!", return -1);
- sclk_table = kzalloc_flex(*sclk_table, entries,
- polaris_table->ucNumEntries);
+ num_entries = min_t(uint32_t, polaris_table->ucNumEntries,
+ pp_entries_max(hwmgr, polaris_table,
+ sizeof(*polaris_table),
+ sizeof(ATOM_Polaris_SCLK_Dependency_Record)));
+ if (num_entries < polaris_table->ucNumEntries)
+ pr_warn("amdgpu: Polaris SCLK dependency table: clamping ucNumEntries %u -> %u\n",
+ polaris_table->ucNumEntries, num_entries);
+
+ sclk_table = kzalloc_flex(*sclk_table, entries, num_entries);
if (!sclk_table)
return -ENOMEM;
- sclk_table->count = (uint32_t)polaris_table->ucNumEntries;
+ sclk_table->count = num_entries;
- for (i = 0; i < polaris_table->ucNumEntries; i++) {
+ for (i = 0; i < num_entries; i++) {
sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
ATOM_Polaris_SCLK_Dependency_Record,
entries, polaris_table, i);
@@ -715,20 +1119,29 @@ static int get_mm_clock_voltage_table(
)
{
uint32_t i;
+ uint32_t num_entries;
const ATOM_Tonga_MM_Dependency_Record *mm_dependency_record;
phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table;
phm_ppt_v1_mm_clock_voltage_dependency_record *mm_table_record;
PP_ASSERT_WITH_CODE((0 != mm_dependency_table->ucNumEntries),
"Invalid PowerPlay Table!", return -1);
- mm_table = kzalloc_flex(*mm_table, entries,
- mm_dependency_table->ucNumEntries);
+
+ num_entries = min_t(uint32_t, mm_dependency_table->ucNumEntries,
+ pp_entries_max(hwmgr, mm_dependency_table,
+ sizeof(*mm_dependency_table),
+ sizeof(ATOM_Tonga_MM_Dependency_Record)));
+ if (num_entries < mm_dependency_table->ucNumEntries)
+ pr_warn("amdgpu: MM dependency table: clamping ucNumEntries %u -> %u\n",
+ mm_dependency_table->ucNumEntries, num_entries);
+
+ mm_table = kzalloc_flex(*mm_table, entries, num_entries);
if (!mm_table)
return -ENOMEM;
- mm_table->count = mm_dependency_table->ucNumEntries;
+ mm_table->count = num_entries;
- for (i = 0; i < mm_dependency_table->ucNumEntries; i++) {
+ for (i = 0; i < num_entries; i++) {
mm_dependency_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
ATOM_Tonga_MM_Dependency_Record,
entries, mm_dependency_table, i);
@@ -789,28 +1202,13 @@ static int init_clock_voltage_dependency(
int result = 0;
struct phm_ppt_v1_information *pp_table_information =
(struct phm_ppt_v1_information *)(hwmgr->pptable);
-
- const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table =
- (const ATOM_Tonga_MM_Dependency_Table *)(((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usMMDependencyTableOffset));
- const PPTable_Generic_SubTable_Header *pPowerTuneTable =
- (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usPowerTuneTableOffset));
- const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
- (const ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
- const PPTable_Generic_SubTable_Header *sclk_dep_table =
- (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
- const ATOM_Tonga_Hard_Limit_Table *pHardLimits =
- (const ATOM_Tonga_Hard_Limit_Table *)(((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usHardLimitTableOffset));
- const PPTable_Generic_SubTable_Header *pcie_table =
- (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usPCIETableOffset));
- const ATOM_Tonga_GPIO_Table *gpio_table =
- (const ATOM_Tonga_GPIO_Table *)(((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usGPIOTableOffset));
+ const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table;
+ const PPTable_Generic_SubTable_Header *pPowerTuneTable;
+ const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table;
+ const PPTable_Generic_SubTable_Header *sclk_dep_table;
+ const ATOM_Tonga_Hard_Limit_Table *pHardLimits;
+ const PPTable_Generic_SubTable_Header *pcie_table;
+ const ATOM_Tonga_GPIO_Table *gpio_table;
pp_table_information->vdd_dep_on_sclk = NULL;
pp_table_information->vdd_dep_on_mclk = NULL;
@@ -818,29 +1216,58 @@ static int init_clock_voltage_dependency(
pp_table_information->pcie_table = NULL;
pp_table_information->gpio_table = NULL;
- if (powerplay_table->usMMDependencyTableOffset != 0)
- result = get_mm_clock_voltage_table(hwmgr,
- &pp_table_information->mm_dep_table, mm_dependency_table);
+ if (powerplay_table->usMMDependencyTableOffset != 0) {
+ result = get_tonga_mm_dependency_table(hwmgr, powerplay_table,
+ &mm_dependency_table);
+ if (!result)
+ result = get_mm_clock_voltage_table(hwmgr,
+ &pp_table_information->mm_dep_table,
+ mm_dependency_table);
+ }
- if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0)
- result = get_cac_tdp_table(hwmgr,
- &pp_table_information->cac_dtp_table, pPowerTuneTable);
+ if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0) {
+ result = get_tonga_power_tune_table(hwmgr, powerplay_table,
+ &pPowerTuneTable);
+ if (!result)
+ result = get_cac_tdp_table(hwmgr,
+ &pp_table_information->cac_dtp_table,
+ pPowerTuneTable);
+ }
- if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0)
- result = get_sclk_voltage_dependency_table(hwmgr,
- &pp_table_information->vdd_dep_on_sclk, sclk_dep_table);
+ if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0) {
+ result = get_tonga_sclk_dependency_table(hwmgr, powerplay_table,
+ &sclk_dep_table);
+ if (!result)
+ result = get_sclk_voltage_dependency_table(hwmgr,
+ &pp_table_information->vdd_dep_on_sclk,
+ sclk_dep_table);
+ }
- if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0)
- result = get_mclk_voltage_dependency_table(hwmgr,
- &pp_table_information->vdd_dep_on_mclk, mclk_dep_table);
+ if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0) {
+ result = get_tonga_mclk_dependency_table(hwmgr, powerplay_table,
+ &mclk_dep_table);
+ if (!result)
+ result = get_mclk_voltage_dependency_table(hwmgr,
+ &pp_table_information->vdd_dep_on_mclk,
+ mclk_dep_table);
+ }
- if (result == 0 && powerplay_table->usPCIETableOffset != 0)
- result = get_pcie_table(hwmgr,
- &pp_table_information->pcie_table, pcie_table);
+ if (result == 0 && powerplay_table->usPCIETableOffset != 0) {
+ result = get_tonga_pcie_table(hwmgr, powerplay_table,
+ &pcie_table);
+ if (!result)
+ result = get_pcie_table(hwmgr,
+ &pp_table_information->pcie_table, pcie_table);
+ }
- if (result == 0 && powerplay_table->usHardLimitTableOffset != 0)
- result = get_hard_limits(hwmgr,
- &pp_table_information->max_clock_voltage_on_dc, pHardLimits);
+ if (result == 0 && powerplay_table->usHardLimitTableOffset != 0) {
+ result = get_tonga_hard_limit_table(hwmgr, powerplay_table,
+ &pHardLimits);
+ if (!result)
+ result = get_hard_limits(hwmgr,
+ &pp_table_information->max_clock_voltage_on_dc,
+ pHardLimits);
+ }
hwmgr->dyn_state.max_clock_voltage_on_dc.sclk =
pp_table_information->max_clock_voltage_on_dc.sclk;
@@ -861,9 +1288,13 @@ static int init_clock_voltage_dependency(
result = get_valid_clk(hwmgr, &pp_table_information->valid_sclk_values,
pp_table_information->vdd_dep_on_sclk);
- if (!result && gpio_table)
- result = get_gpio_table(hwmgr, &pp_table_information->gpio_table,
- gpio_table);
+ if (!result && powerplay_table->usGPIOTableOffset) {
+ result = get_tonga_gpio_table(hwmgr, powerplay_table,
+ &gpio_table);
+ if (!result)
+ result = get_gpio_table(hwmgr,
+ &pp_table_information->gpio_table, gpio_table);
+ }
return result;
}
@@ -908,14 +1339,17 @@ static int init_thermal_controller(
)
{
const PPTable_Generic_SubTable_Header *fan_table;
- ATOM_Tonga_Thermal_Controller *thermal_controller;
+ const ATOM_Tonga_Thermal_Controller *thermal_controller;
+ int ret;
- thermal_controller = (ATOM_Tonga_Thermal_Controller *)
- (((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usThermalControllerOffset));
PP_ASSERT_WITH_CODE((0 != powerplay_table->usThermalControllerOffset),
"Thermal controller table not set!", return -1);
+ ret = get_tonga_thermal_controller_table(hwmgr, powerplay_table,
+ &thermal_controller);
+ if (ret)
+ return ret;
+
hwmgr->thermal_controller.ucType = thermal_controller->ucType;
hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine;
hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress;
@@ -943,12 +1377,13 @@ static int init_thermal_controller(
return 0;
}
- fan_table = (const PPTable_Generic_SubTable_Header *)
- (((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usFanTableOffset));
-
PP_ASSERT_WITH_CODE((0 != powerplay_table->usFanTableOffset),
"Fan table not set!", return -1);
+
+ ret = get_tonga_fan_table(hwmgr, powerplay_table, &fan_table);
+ if (ret)
+ return ret;
+
PP_ASSERT_WITH_CODE((0 < fan_table->ucRevId),
"Unsupported fan table format!", return -1);
@@ -1104,21 +1539,24 @@ static int init_thermal_controller(
}
/**
- * check_powerplay_tables - Private Function used during initialization.
- * Inspect the PowerPlay table for obvious signs of corruption.
+ * get_tonga_state_array - Get the Tonga state array from the PowerPlay table.
* @hwmgr: Pointer to the hardware manager.
* @powerplay_table: Pointer to the PowerPlay Table.
- * Exception: 2 if the powerplay table is incorrect.
+ * @state_array: Pointer to the returned Tonga state array.
+ *
+ * Return: 0 on success, negative error code on failure.
*/
-static int check_powerplay_tables(
- struct pp_hwmgr *hwmgr,
- const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
- )
+static int get_tonga_state_array(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Tonga_State_Array **state_array)
{
const ATOM_Tonga_State_Array *state_arrays;
+ u16 state_array_offset;
+ size_t state_array_size;
+ size_t table_size = hwmgr->soft_pp_table_size;
- state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usStateArrayOffset));
+ PP_ASSERT_WITH_CODE((table_size >= sizeof(*powerplay_table)),
+ "Invalid PowerPlay Table!", return -1);
PP_ASSERT_WITH_CODE((ATOM_Tonga_TABLE_REVISION_TONGA <=
powerplay_table->sHeader.ucTableFormatRevision),
@@ -1127,12 +1565,34 @@ static int check_powerplay_tables(
"State table is not set!", return -1);
PP_ASSERT_WITH_CODE((0 < powerplay_table->sHeader.usStructureSize),
"Invalid PowerPlay Table!", return -1);
+
+ state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset);
+ PP_ASSERT_WITH_CODE((state_array_offset <=
+ table_size - sizeof(*state_arrays)),
+ "Invalid PowerPlay Table!", return -1);
+
+ state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) +
+ state_array_offset);
PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
"Invalid PowerPlay Table!", return -1);
+ state_array_size = struct_size(state_arrays, entries, state_arrays->ucNumEntries);
+ PP_ASSERT_WITH_CODE((state_array_size <= table_size - state_array_offset),
+ "Invalid PowerPlay Table!", return -1);
+
+ *state_array = state_arrays;
+
return 0;
}
+static int check_powerplay_tables(struct pp_hwmgr *hwmgr,
+ const ATOM_Tonga_POWERPLAYTABLE *powerplay_table)
+{
+ const ATOM_Tonga_State_Array *state_arrays;
+
+ return get_tonga_state_array(hwmgr, powerplay_table, &state_arrays);
+}
+
static int pp_tables_v1_0_initialize(struct pp_hwmgr *hwmgr)
{
int result = 0;
@@ -1236,17 +1696,16 @@ const struct pp_table_func pptable_v1_0_funcs = {
int get_number_of_powerplay_table_entries_v1_0(struct pp_hwmgr *hwmgr)
{
- ATOM_Tonga_State_Array const *state_arrays;
+ const ATOM_Tonga_State_Array *state_arrays;
const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+ int result;
PP_ASSERT_WITH_CODE((NULL != pp_table),
"Missing PowerPlay Table!", return -1);
- PP_ASSERT_WITH_CODE((pp_table->sHeader.ucTableFormatRevision >=
- ATOM_Tonga_TABLE_REVISION_TONGA),
- "Incorrect PowerPlay table revision!", return -1);
- state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
- le16_to_cpu(pp_table->usStateArrayOffset));
+ result = get_tonga_state_array(hwmgr, pp_table, &state_arrays);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Invalid PowerPlay Table State Array.", return result);
return (uint32_t)(state_arrays->ucNumEntries);
}
@@ -1287,13 +1746,15 @@ static int ppt_get_num_of_vce_state_table_entries_v1_0(struct pp_hwmgr *hwmgr)
{
const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
const ATOM_Tonga_VCE_State_Table *vce_state_table;
+ int ret;
if (pp_table == NULL)
return 0;
- vce_state_table = (void *)pp_table +
- le16_to_cpu(pp_table->usVCEStateTableOffset);
+ ret = get_tonga_vce_state_table(hwmgr, pp_table, &vce_state_table);
+ if (ret)
+ return 0;
return vce_state_table->ucNumEntries;
}
@@ -1302,18 +1763,39 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i
struct amd_vce_state *vce_state, void **clock_info, uint32_t *flag)
{
const ATOM_Tonga_VCE_State_Record *vce_state_record;
- ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record;
+ ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record = NULL;
+ ATOM_Polaris_SCLK_Dependency_Record *polaris_sclk_dep_record = NULL;
ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record;
ATOM_Tonga_MM_Dependency_Record *mm_dep_record;
const ATOM_Tonga_POWERPLAYTABLE *pptable = get_powerplay_table(hwmgr);
- const ATOM_Tonga_VCE_State_Table *vce_state_table = (ATOM_Tonga_VCE_State_Table *)(((unsigned long)pptable)
- + le16_to_cpu(pptable->usVCEStateTableOffset));
- const ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table = (ATOM_Tonga_SCLK_Dependency_Table *)(((unsigned long)pptable)
- + le16_to_cpu(pptable->usSclkDependencyTableOffset));
- const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table = (ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long)pptable)
- + le16_to_cpu(pptable->usMclkDependencyTableOffset));
- const ATOM_Tonga_MM_Dependency_Table *mm_dep_table = (ATOM_Tonga_MM_Dependency_Table *)(((unsigned long)pptable)
- + le16_to_cpu(pptable->usMMDependencyTableOffset));
+ const ATOM_Tonga_VCE_State_Table *vce_state_table;
+ const PPTable_Generic_SubTable_Header *sclk_dep_table_header;
+ const ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table;
+ const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table;
+ const ATOM_Tonga_MM_Dependency_Table *mm_dep_table;
+ int ret;
+
+ if (!pptable)
+ return -EINVAL;
+
+ ret = get_tonga_vce_state_table(hwmgr, pptable, &vce_state_table);
+ if (ret)
+ return ret;
+
+ ret = get_tonga_sclk_dependency_table(hwmgr, pptable,
+ &sclk_dep_table_header);
+ if (ret)
+ return ret;
+ sclk_dep_table = (const ATOM_Tonga_SCLK_Dependency_Table *)
+ sclk_dep_table_header;
+
+ ret = get_tonga_mclk_dependency_table(hwmgr, pptable, &mclk_dep_table);
+ if (ret)
+ return ret;
+
+ ret = get_tonga_mm_dependency_table(hwmgr, pptable, &mm_dep_table);
+ if (ret)
+ return ret;
PP_ASSERT_WITH_CODE((i < vce_state_table->ucNumEntries),
"Requested state entry ID is out of range!",
@@ -1322,10 +1804,27 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i
vce_state_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
ATOM_Tonga_VCE_State_Record,
entries, vce_state_table, i);
- sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
- ATOM_Tonga_SCLK_Dependency_Record,
- entries, sclk_dep_table,
- vce_state_record->ucSCLKIndex);
+ PP_ASSERT_WITH_CODE((vce_state_record->ucSCLKIndex <
+ sclk_dep_table->ucNumEntries),
+ "Invalid PowerPlay Table!", return -EINVAL);
+ PP_ASSERT_WITH_CODE((vce_state_record->ucVCEClockIndex <
+ mm_dep_table->ucNumEntries),
+ "Invalid PowerPlay Table!", return -EINVAL);
+ PP_ASSERT_WITH_CODE((mclk_dep_table->ucNumEntries != 0),
+ "Invalid PowerPlay Table!", return -EINVAL);
+
+ if (sclk_dep_table_header->ucRevId < 1)
+ sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Tonga_SCLK_Dependency_Record,
+ entries, sclk_dep_table,
+ vce_state_record->ucSCLKIndex);
+ else
+ polaris_sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+ ATOM_Polaris_SCLK_Dependency_Record,
+ entries,
+ (ATOM_Polaris_SCLK_Dependency_Table *)
+ sclk_dep_table_header,
+ vce_state_record->ucSCLKIndex);
mm_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
ATOM_Tonga_MM_Dependency_Record,
entries, mm_dep_table,
@@ -1334,7 +1833,10 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i
vce_state->evclk = le32_to_cpu(mm_dep_record->ulEClk);
vce_state->ecclk = le32_to_cpu(mm_dep_record->ulEClk);
- vce_state->sclk = le32_to_cpu(sclk_dep_record->ulSclk);
+ if (sclk_dep_record)
+ vce_state->sclk = le32_to_cpu(sclk_dep_record->ulSclk);
+ else
+ vce_state->sclk = le32_to_cpu(polaris_sclk_dep_record->ulSclk);
if (vce_state_record->ucMCLKIndex >= mclk_dep_table->ucNumEntries)
mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
@@ -1377,15 +1879,11 @@ int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr,
if (pp_table->sHeader.ucTableFormatRevision >=
ATOM_Tonga_TABLE_REVISION_TONGA) {
- state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
- le16_to_cpu(pp_table->usStateArrayOffset));
-
- PP_ASSERT_WITH_CODE((0 < pp_table->usStateArrayOffset),
- "Invalid PowerPlay Table State Array Offset.", return -1);
- PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
- "Invalid PowerPlay Table State Array.", return -1);
- PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries),
- "Invalid PowerPlay Table State Array Entry.", return -1);
+ result = get_tonga_state_array(hwmgr, pp_table, &state_arrays);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Invalid PowerPlay Table State Array.", return result);
+ PP_ASSERT_WITH_CODE((entry_index < state_arrays->ucNumEntries),
+ "Invalid PowerPlay Table State Array Entry.", return -1);
state_entry = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
ATOM_Tonga_State, entries,
@@ -1411,4 +1909,3 @@ int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr,
return result;
}
-
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c
index bfd8fbb0b49d..56926eec6820 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c
@@ -47,26 +47,53 @@
#define NUM_BITS_CLOCK_INFO_ARRAY_INDEX 6
+static bool pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset,
+ size_t size)
+{
+ size_t table_size = hwmgr->soft_pp_table_size;
+
+ return offset <= table_size && size <= table_size - offset;
+}
+
+static const ATOM_PPLIB_EXTENDEDHEADER *
+get_extended_header(struct pp_hwmgr *hwmgr,
+ const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table,
+ size_t min_size)
+{
+ const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3;
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header;
+ u16 offset;
+
+ if (le16_to_cpu(powerplay_table->usTableSize) <
+ sizeof(ATOM_PPLIB_POWERPLAYTABLE3) ||
+ !pp_table_has_space(hwmgr, 0, sizeof(ATOM_PPLIB_POWERPLAYTABLE3)))
+ return NULL;
+
+ powerplay_table3 = (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
+ offset = le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset);
+ if (!offset || !pp_table_has_space(hwmgr, offset,
+ sizeof(extended_header->usSize)))
+ return NULL;
+
+ extended_header = (const ATOM_PPLIB_EXTENDEDHEADER *)
+ (((unsigned long)powerplay_table) + offset);
+ if (le16_to_cpu(extended_header->usSize) < min_size ||
+ !pp_table_has_space(hwmgr, offset, min_size))
+ return NULL;
+
+ return extended_header;
+}
+
static uint16_t get_vce_table_offset(struct pp_hwmgr *hwmgr,
const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
{
uint16_t vce_table_offset = 0;
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header;
- if (le16_to_cpu(powerplay_table->usTableSize) >=
- sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
- const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
- (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
-
- if (powerplay_table3->usExtendendedHeaderOffset > 0) {
- const ATOM_PPLIB_EXTENDEDHEADER *extended_header =
- (const ATOM_PPLIB_EXTENDEDHEADER *)
- (((unsigned long)powerplay_table3) +
- le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
- if (le16_to_cpu(extended_header->usSize) >=
- SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2)
- vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset);
- }
- }
+ extended_header = get_extended_header(hwmgr, powerplay_table,
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2);
+ if (extended_header)
+ vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset);
return vce_table_offset;
}
@@ -93,7 +120,14 @@ static uint16_t get_vce_clock_info_array_size(struct pp_hwmgr *hwmgr,
if (table_offset > 0) {
const VCEClockInfoArray *p = (const VCEClockInfoArray *)
(((unsigned long) powerplay_table) + table_offset);
- table_size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo);
+ size_t size;
+
+ if (!pp_table_has_space(hwmgr, table_offset, sizeof(p->ucNumEntries)))
+ return 0;
+
+ size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo);
+ if (pp_table_has_space(hwmgr, table_offset, size))
+ table_size = size;
}
return table_size;
@@ -104,10 +138,13 @@ static uint16_t get_vce_clock_voltage_limit_table_offset(struct pp_hwmgr *hwmgr,
{
uint16_t table_offset = get_vce_clock_info_array_offset(hwmgr,
powerplay_table);
+ u16 table_size;
- if (table_offset > 0)
- return table_offset + get_vce_clock_info_array_size(hwmgr,
- powerplay_table);
+ if (table_offset > 0) {
+ table_size = get_vce_clock_info_array_size(hwmgr, powerplay_table);
+ if (table_size)
+ return table_offset + table_size;
+ }
return 0;
}
@@ -121,8 +158,15 @@ static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr,
if (table_offset > 0) {
const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *ptable =
(const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *)(((unsigned long) powerplay_table) + table_offset);
+ size_t size;
- table_size = sizeof(uint8_t) + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record);
+ if (!pp_table_has_space(hwmgr, table_offset, sizeof(ptable->numEntries)))
+ return 0;
+
+ size = sizeof(uint8_t) +
+ ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record);
+ if (pp_table_has_space(hwmgr, table_offset, size))
+ table_size = size;
}
return table_size;
}
@@ -130,9 +174,13 @@ static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr,
static uint16_t get_vce_state_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
{
uint16_t table_offset = get_vce_clock_voltage_limit_table_offset(hwmgr, powerplay_table);
+ u16 table_size;
- if (table_offset > 0)
- return table_offset + get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table);
+ if (table_offset > 0) {
+ table_size = get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table);
+ if (table_size)
+ return table_offset + table_size;
+ }
return 0;
}
@@ -143,8 +191,12 @@ static const ATOM_PPLIB_VCE_State_Table *get_vce_state_table(
{
uint16_t table_offset = get_vce_state_table_offset(hwmgr, powerplay_table);
- if (table_offset > 0)
- return (const ATOM_PPLIB_VCE_State_Table *)(((unsigned long) powerplay_table) + table_offset);
+ if (table_offset > 0) {
+ if (pp_table_has_space(hwmgr, table_offset,
+ sizeof(((ATOM_PPLIB_VCE_State_Table *)0)->numEntries)))
+ return (const ATOM_PPLIB_VCE_State_Table *)
+ (((unsigned long)powerplay_table) + table_offset);
+ }
return NULL;
}
@@ -153,21 +205,13 @@ static uint16_t get_uvd_table_offset(struct pp_hwmgr *hwmgr,
const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
{
uint16_t uvd_table_offset = 0;
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header;
+
+ extended_header = get_extended_header(hwmgr, powerplay_table,
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3);
+ if (extended_header)
+ uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset);
- if (le16_to_cpu(powerplay_table->usTableSize) >=
- sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
- const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
- (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
- if (powerplay_table3->usExtendendedHeaderOffset > 0) {
- const ATOM_PPLIB_EXTENDEDHEADER *extended_header =
- (const ATOM_PPLIB_EXTENDEDHEADER *)
- (((unsigned long)powerplay_table3) +
- le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
- if (le16_to_cpu(extended_header->usSize) >=
- SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3)
- uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset);
- }
- }
return uvd_table_offset;
}
@@ -193,8 +237,14 @@ static uint16_t get_uvd_clock_info_array_size(struct pp_hwmgr *hwmgr,
const UVDClockInfoArray *p = (const UVDClockInfoArray *)
(((unsigned long) powerplay_table)
+ table_offset);
- table_size = sizeof(UCHAR) +
- p->ucNumEntries * sizeof(UVDClockInfo);
+ size_t size;
+
+ if (!pp_table_has_space(hwmgr, table_offset, sizeof(p->ucNumEntries)))
+ return 0;
+
+ size = sizeof(UCHAR) + p->ucNumEntries * sizeof(UVDClockInfo);
+ if (pp_table_has_space(hwmgr, table_offset, size))
+ table_size = size;
}
return table_size;
@@ -206,10 +256,13 @@ static uint16_t get_uvd_clock_voltage_limit_table_offset(
{
uint16_t table_offset = get_uvd_clock_info_array_offset(hwmgr,
powerplay_table);
+ u16 table_size;
- if (table_offset > 0)
- return table_offset +
- get_uvd_clock_info_array_size(hwmgr, powerplay_table);
+ if (table_offset > 0) {
+ table_size = get_uvd_clock_info_array_size(hwmgr, powerplay_table);
+ if (table_size)
+ return table_offset + table_size;
+ }
return 0;
}
@@ -218,21 +271,12 @@ static uint16_t get_samu_table_offset(struct pp_hwmgr *hwmgr,
const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
{
uint16_t samu_table_offset = 0;
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header;
- if (le16_to_cpu(powerplay_table->usTableSize) >=
- sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
- const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
- (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
- if (powerplay_table3->usExtendendedHeaderOffset > 0) {
- const ATOM_PPLIB_EXTENDEDHEADER *extended_header =
- (const ATOM_PPLIB_EXTENDEDHEADER *)
- (((unsigned long)powerplay_table3) +
- le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
- if (le16_to_cpu(extended_header->usSize) >=
- SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4)
- samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset);
- }
- }
+ extended_header = get_extended_header(hwmgr, powerplay_table,
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4);
+ if (extended_header)
+ samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset);
return samu_table_offset;
}
@@ -254,21 +298,12 @@ static uint16_t get_acp_table_offset(struct pp_hwmgr *hwmgr,
const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
{
uint16_t acp_table_offset = 0;
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header;
- if (le16_to_cpu(powerplay_table->usTableSize) >=
- sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
- const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
- (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
- if (powerplay_table3->usExtendendedHeaderOffset > 0) {
- const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader =
- (const ATOM_PPLIB_EXTENDEDHEADER *)
- (((unsigned long)powerplay_table3) +
- le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
- if (le16_to_cpu(pExtendedHeader->usSize) >=
- SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6)
- acp_table_offset = le16_to_cpu(pExtendedHeader->usACPTableOffset);
- }
- }
+ extended_header = get_extended_header(hwmgr, powerplay_table,
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6);
+ if (extended_header)
+ acp_table_offset = le16_to_cpu(extended_header->usACPTableOffset);
return acp_table_offset;
}
@@ -290,21 +325,12 @@ static uint16_t get_cacp_tdp_table_offset(
const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
{
uint16_t cacTdpTableOffset = 0;
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header;
- if (le16_to_cpu(powerplay_table->usTableSize) >=
- sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
- const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
- (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
- if (powerplay_table3->usExtendendedHeaderOffset > 0) {
- const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader =
- (const ATOM_PPLIB_EXTENDEDHEADER *)
- (((unsigned long)powerplay_table3) +
- le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
- if (le16_to_cpu(pExtendedHeader->usSize) >=
- SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7)
- cacTdpTableOffset = le16_to_cpu(pExtendedHeader->usPowerTuneTableOffset);
- }
- }
+ extended_header = get_extended_header(hwmgr, powerplay_table,
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7);
+ if (extended_header)
+ cacTdpTableOffset = le16_to_cpu(extended_header->usPowerTuneTableOffset);
return cacTdpTableOffset;
}
@@ -341,22 +367,13 @@ static uint16_t get_sclk_vdd_gfx_table_offset(struct pp_hwmgr *hwmgr,
const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table)
{
uint16_t sclk_vdd_gfx_table_offset = 0;
+ const ATOM_PPLIB_EXTENDEDHEADER *extended_header;
- if (le16_to_cpu(powerplay_table->usTableSize) >=
- sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) {
- const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 =
- (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table;
- if (powerplay_table3->usExtendendedHeaderOffset > 0) {
- const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader =
- (const ATOM_PPLIB_EXTENDEDHEADER *)
- (((unsigned long)powerplay_table3) +
- le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset));
- if (le16_to_cpu(pExtendedHeader->usSize) >=
- SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8)
- sclk_vdd_gfx_table_offset =
- le16_to_cpu(pExtendedHeader->usSclkVddgfxTableOffset);
- }
- }
+ extended_header = get_extended_header(hwmgr, powerplay_table,
+ SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8);
+ if (extended_header)
+ sclk_vdd_gfx_table_offset =
+ le16_to_cpu(extended_header->usSclkVddgfxTableOffset);
return sclk_vdd_gfx_table_offset;
}
@@ -769,20 +786,37 @@ static ULONG size_of_entry_v2(ULONG num_dpm_levels)
}
static const ATOM_PPLIB_STATE_V2 *get_state_entry_v2(
+ struct pp_hwmgr *hwmgr,
const StateArray * pstate_arrays,
+ u16 state_array_offset,
ULONG entry_index)
{
ULONG i;
const ATOM_PPLIB_STATE_V2 *pstate;
+ size_t entry_offset;
+ size_t entry_size;
+
+ if (entry_index >= pstate_arrays->ucNumEntries)
+ return NULL;
+ entry_offset = state_array_offset + sizeof(pstate_arrays->ucNumEntries);
pstate = pstate_arrays->states;
- if (entry_index <= pstate_arrays->ucNumEntries) {
- for (i = 0; i < entry_index; i++)
- pstate = (ATOM_PPLIB_STATE_V2 *)(
- (unsigned long)pstate +
- size_of_entry_v2(pstate->ucNumDPMLevels));
+ for (i = 0; i <= entry_index; i++) {
+ if (!pp_table_has_space(hwmgr, entry_offset, sizeof(*pstate)))
+ return NULL;
+
+ entry_size = size_of_entry_v2(pstate->ucNumDPMLevels);
+ if (!pp_table_has_space(hwmgr, entry_offset, entry_size))
+ return NULL;
+
+ if (i == entry_index)
+ return pstate;
+
+ entry_offset += entry_size;
+ pstate = (ATOM_PPLIB_STATE_V2 *)((unsigned long)pstate + entry_size);
}
- return pstate;
+
+ return NULL;
}
static const unsigned char soft_dummy_pp_table[] = {
@@ -850,6 +884,8 @@ int pp_tables_get_response_times(struct pp_hwmgr *hwmgr,
PP_ASSERT_WITH_CODE(NULL != powerplay_tab,
"Missing PowerPlay Table!", return -EINVAL);
+ PP_ASSERT_WITH_CODE(pp_table_has_space(hwmgr, 0, sizeof(*powerplay_tab)),
+ "Invalid PowerPlay Table!", return -EINVAL);
*vol_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usVoltageTime);
*bb_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usBackbiasTime);
@@ -862,13 +898,21 @@ int pp_tables_get_num_of_entries(struct pp_hwmgr *hwmgr,
{
const StateArray *pstate_arrays;
const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr);
+ u16 state_array_offset;
if (powerplay_table == NULL)
return -1;
+ if (!pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table)))
+ return -1;
if (powerplay_table->sHeader.ucTableFormatRevision >= 6) {
+ state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset);
+ if (!pp_table_has_space(hwmgr, state_array_offset,
+ sizeof(pstate_arrays->ucNumEntries)))
+ return -1;
+
pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usStateArrayOffset));
+ state_array_offset);
*num_of_entries = (unsigned long)(pstate_arrays->ucNumEntries);
} else
@@ -895,49 +939,128 @@ int pp_tables_get_entry(struct pp_hwmgr *hwmgr,
const NonClockInfoArray *pnon_clock_arrays;
const ATOM_PPLIB_STATE *pstate_entry;
+ u16 state_array_offset;
+ u16 clock_info_array_offset;
+ u16 non_clock_info_array_offset;
+ size_t clock_info_offset;
+ size_t non_clock_info_offset;
+ size_t state_entry_offset;
if (powerplay_table == NULL)
return -1;
+ if (!pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table)))
+ return -1;
ps->classification.bios_index = entry_index;
if (powerplay_table->sHeader.ucTableFormatRevision >= 6) {
+ state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset);
+ if (!pp_table_has_space(hwmgr, state_array_offset,
+ sizeof(pstate_arrays->ucNumEntries)))
+ return -1;
+
pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usStateArrayOffset));
+ state_array_offset);
+
+ if (entry_index >= pstate_arrays->ucNumEntries)
+ return -1;
- if (entry_index > pstate_arrays->ucNumEntries)
+ pstate_entry_v2 = get_state_entry_v2(hwmgr, pstate_arrays,
+ state_array_offset,
+ entry_index);
+ if (!pstate_entry_v2)
+ return -1;
+
+ clock_info_array_offset =
+ le16_to_cpu(powerplay_table->usClockInfoArrayOffset);
+ if (!pp_table_has_space(hwmgr, clock_info_array_offset,
+ sizeof(*pclock_arrays)))
return -1;
- pstate_entry_v2 = get_state_entry_v2(pstate_arrays, entry_index);
pclock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usClockInfoArrayOffset));
+ clock_info_array_offset);
+ if (!pclock_arrays->ucEntrySize)
+ return -1;
+
+ non_clock_info_array_offset =
+ le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset);
+ if (!pp_table_has_space(hwmgr, non_clock_info_array_offset,
+ sizeof(*pnon_clock_arrays)))
+ return -1;
pnon_clock_arrays = (NonClockInfoArray *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset));
+ non_clock_info_array_offset);
+ if (!pnon_clock_arrays->ucEntrySize ||
+ pnon_clock_arrays->ucEntrySize < ATOM_PPLIB_NONCLOCKINFO_VER1 ||
+ (pnon_clock_arrays->ucEntrySize > ATOM_PPLIB_NONCLOCKINFO_VER1 &&
+ pnon_clock_arrays->ucEntrySize < ATOM_PPLIB_NONCLOCKINFO_VER2) ||
+ pstate_entry_v2->nonClockInfoIndex >= pnon_clock_arrays->ucNumEntries)
+ return -1;
+ non_clock_info_offset = non_clock_info_array_offset +
+ offsetof(NonClockInfoArray, nonClockInfo) +
+ pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize;
+ if (!pp_table_has_space(hwmgr, non_clock_info_offset,
+ pnon_clock_arrays->ucEntrySize))
+ return -1;
pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)(pnon_clock_arrays->nonClockInfo) +
(pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize));
result = init_non_clock_fields(hwmgr, ps, pnon_clock_arrays->ucEntrySize, pnon_clock_info);
for (i = 0; i < pstate_entry_v2->ucNumDPMLevels; i++) {
- const void *pclock_info = (const void *)(
- (unsigned long)(pclock_arrays->clockInfo) +
- (pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize));
+ const void *pclock_info;
+
+ if (pstate_entry_v2->clockInfoIndex[i] >=
+ pclock_arrays->ucNumEntries)
+ return -1;
+
+ clock_info_offset = clock_info_array_offset +
+ offsetof(ClockInfoArray, clockInfo) +
+ pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize;
+ if (!pp_table_has_space(hwmgr, clock_info_offset,
+ pclock_arrays->ucEntrySize))
+ return -1;
+
+ pclock_info = (const void *)
+ ((unsigned long)(pclock_arrays->clockInfo) +
+ (pstate_entry_v2->clockInfoIndex[i] *
+ pclock_arrays->ucEntrySize));
res = func(hwmgr, &ps->hardware, i, pclock_info);
if ((0 == result) && (0 != res))
result = res;
}
} else {
- if (entry_index > powerplay_table->ucNumStates)
+ if (entry_index >= powerplay_table->ucNumStates ||
+ !powerplay_table->ucStateEntrySize ||
+ !powerplay_table->ucNonClockSize ||
+ powerplay_table->ucNonClockSize < ATOM_PPLIB_NONCLOCKINFO_VER1 ||
+ (powerplay_table->ucNonClockSize > ATOM_PPLIB_NONCLOCKINFO_VER1 &&
+ powerplay_table->ucNonClockSize < ATOM_PPLIB_NONCLOCKINFO_VER2) ||
+ !powerplay_table->ucClockInfoSize)
+ return -1;
+
+ state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset);
+ state_entry_offset = state_array_offset +
+ entry_index * powerplay_table->ucStateEntrySize;
+ if (!pp_table_has_space(hwmgr, state_entry_offset,
+ powerplay_table->ucStateEntrySize))
return -1;
pstate_entry = (ATOM_PPLIB_STATE *)((unsigned long)powerplay_table +
- le16_to_cpu(powerplay_table->usStateArrayOffset) +
+ state_array_offset +
entry_index * powerplay_table->ucStateEntrySize);
+ non_clock_info_array_offset =
+ le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset);
+ non_clock_info_offset = non_clock_info_array_offset +
+ pstate_entry->ucNonClockStateIndex * powerplay_table->ucNonClockSize;
+ if (!pp_table_has_space(hwmgr, non_clock_info_offset,
+ powerplay_table->ucNonClockSize))
+ return -1;
+
pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)powerplay_table +
- le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset) +
+ non_clock_info_array_offset +
pstate_entry->ucNonClockStateIndex *
powerplay_table->ucNonClockSize);
@@ -946,12 +1069,23 @@ int pp_tables_get_entry(struct pp_hwmgr *hwmgr,
pnon_clock_info);
for (i = 0; i < powerplay_table->ucStateEntrySize-1; i++) {
- const void *pclock_info = (const void *)((unsigned long)powerplay_table +
- le16_to_cpu(powerplay_table->usClockInfoArrayOffset) +
+ const void *pclock_info;
+
+ clock_info_array_offset =
+ le16_to_cpu(powerplay_table->usClockInfoArrayOffset);
+ clock_info_offset = clock_info_array_offset +
+ pstate_entry->ucClockStateIndices[i] *
+ powerplay_table->ucClockInfoSize;
+ if (!pp_table_has_space(hwmgr, clock_info_offset,
+ powerplay_table->ucClockInfoSize))
+ return -1;
+
+ pclock_info = (const void *)((unsigned long)powerplay_table +
+ clock_info_array_offset +
pstate_entry->ucClockStateIndices[i] *
powerplay_table->ucClockInfoSize);
- int res = func(hwmgr, &ps->hardware, i, pclock_info);
+ res = func(hwmgr, &ps->hardware, i, pclock_info);
if ((0 == result) && (0 != res))
result = res;
@@ -1661,21 +1795,70 @@ static int get_vce_state_table_entry(struct pp_hwmgr *hwmgr,
unsigned long *flag)
{
const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr);
+ const ATOM_PPLIB_VCE_State_Table *vce_state_table;
+ const ATOM_PPLIB_VCE_State_Record *record;
+ const VCEClockInfoArray *vce_clock_info_array;
+ const VCEClockInfo *vce_clock_info;
+ const ClockInfoArray *clock_arrays;
+ u16 vce_state_table_offset;
+ u16 vce_clock_info_array_offset;
+ u16 clock_info_array_offset;
+ unsigned long clockInfoIndex;
+ size_t record_offset;
+ size_t vce_clock_info_offset;
+ size_t clock_info_offset;
+
+ if (!powerplay_table || !pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table)))
+ return -1;
- const ATOM_PPLIB_VCE_State_Table *vce_state_table = get_vce_state_table(hwmgr, powerplay_table);
+ vce_state_table_offset = get_vce_state_table_offset(hwmgr, powerplay_table);
+ vce_state_table = get_vce_state_table(hwmgr, powerplay_table);
+ if (!vce_state_table || i >= vce_state_table->numEntries)
+ return -1;
+
+ record_offset = vce_state_table_offset +
+ offsetof(ATOM_PPLIB_VCE_State_Table, entries) +
+ i * sizeof(*record);
+ if (!pp_table_has_space(hwmgr, record_offset, sizeof(*record)))
+ return -1;
+
+ record = &vce_state_table->entries[i];
+
+ vce_clock_info_array_offset =
+ get_vce_clock_info_array_offset(hwmgr, powerplay_table);
+ if (!pp_table_has_space(hwmgr, vce_clock_info_array_offset,
+ sizeof(vce_clock_info_array->ucNumEntries)))
+ return -1;
- unsigned short vce_clock_info_array_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table);
+ vce_clock_info_array = (const VCEClockInfoArray *)
+ (((unsigned long)powerplay_table) + vce_clock_info_array_offset);
+ if (record->ucVCEClockInfoIndex >= vce_clock_info_array->ucNumEntries)
+ return -1;
- const VCEClockInfoArray *vce_clock_info_array = (const VCEClockInfoArray *)(((unsigned long) powerplay_table) + vce_clock_info_array_offset);
+ vce_clock_info_offset = vce_clock_info_array_offset +
+ offsetof(VCEClockInfoArray, entries) +
+ record->ucVCEClockInfoIndex * sizeof(*vce_clock_info);
+ if (!pp_table_has_space(hwmgr, vce_clock_info_offset, sizeof(*vce_clock_info)))
+ return -1;
- const ClockInfoArray *clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usClockInfoArrayOffset));
+ vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex];
- const ATOM_PPLIB_VCE_State_Record *record = &vce_state_table->entries[i];
+ clock_info_array_offset = le16_to_cpu(powerplay_table->usClockInfoArrayOffset);
+ if (!pp_table_has_space(hwmgr, clock_info_array_offset,
+ sizeof(*clock_arrays)))
+ return -1;
- const VCEClockInfo *vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex];
+ clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) +
+ clock_info_array_offset);
+ clockInfoIndex = record->ucClockInfoIndex & 0x3F;
+ if (!clock_arrays->ucEntrySize || clockInfoIndex >= clock_arrays->ucNumEntries)
+ return -1;
- unsigned long clockInfoIndex = record->ucClockInfoIndex & 0x3F;
+ clock_info_offset = clock_info_array_offset +
+ offsetof(ClockInfoArray, clockInfo) +
+ clockInfoIndex * clock_arrays->ucEntrySize;
+ if (!pp_table_has_space(hwmgr, clock_info_offset, clock_arrays->ucEntrySize))
+ return -1;
*flag = (record->ucClockInfoIndex >> NUM_BITS_CLOCK_INFO_ARRAY_INDEX);
@@ -1699,6 +1882,10 @@ static int pp_tables_initialize(struct pp_hwmgr *hwmgr)
hwmgr->need_pp_table_upload = true;
powerplay_table = get_powerplay_table(hwmgr);
+ PP_ASSERT_WITH_CODE((powerplay_table),
+ "Missing PowerPlay Table!", return -1);
+ PP_ASSERT_WITH_CODE(pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table)),
+ "Invalid PowerPlay Table!", return -1);
result = init_powerplay_tables(hwmgr, powerplay_table);
@@ -1801,4 +1988,3 @@ const struct pp_table_func pptable_funcs = {
.pptable_get_vce_state_table_entry =
get_vce_state_table_entry,
};
-
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
index 95bf187f02a5..1f1bb274685a 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
@@ -2216,12 +2216,24 @@ static int smu7_patch_voltage_dependency_tables_with_lookup_table(
if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
voltage_id = sclk_table->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddgfx_lookup_table->count) {
+ pr_err("amdgpu: sclk[%u] vddgfx index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddgfx_lookup_table->count);
+ return -EINVAL;
+ }
sclk_table->entries[entry_id].vddgfx =
table_info->vddgfx_lookup_table->entries[voltage_id].us_vdd;
}
} else {
for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
voltage_id = sclk_table->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: sclk[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
sclk_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
@@ -2229,12 +2241,24 @@ static int smu7_patch_voltage_dependency_tables_with_lookup_table(
for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
voltage_id = mclk_table->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: mclk[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
mclk_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
for (entry_id = 0; entry_id < mm_table->count; ++entry_id) {
voltage_id = mm_table->entries[entry_id].vddcInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: mm[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
mm_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
@@ -5648,23 +5672,29 @@ static int smu7_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
}
for (i = 0; i < size; i += 3) {
- if (i + 3 > size || input[i] >= podn_dpm_table_in_backend->num_of_pl) {
- pr_info("invalid clock voltage input \n");
- return 0;
+ if (i + 3 > size) {
+ pr_info("truncated clock/voltage input\n");
+ return -EINVAL;
}
- input_level = input[i];
- input_clk = input[i+1] * 100;
- input_vol = input[i+2];
-
- if (smu7_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) {
- podn_dpm_table_in_backend->entries[input_level].clock = input_clk;
- podn_vdd_dep_in_backend->entries[input_level].clk = input_clk;
- podn_dpm_table_in_backend->entries[input_level].vddc = input_vol;
- podn_vdd_dep_in_backend->entries[input_level].vddc = input_vol;
- podn_vdd_dep_in_backend->entries[input_level].vddgfx = input_vol;
- } else {
+ if (input[i] < 0 || input[i] >= podn_dpm_table_in_backend->num_of_pl) {
+ pr_info("invalid clock/voltage level\n");
return -EINVAL;
}
+ input_clk = input[i + 1] * 100;
+ input_vol = input[i + 2];
+ if (!smu7_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol))
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 3) {
+ input_level = input[i];
+ input_clk = input[i + 1] * 100;
+ input_vol = input[i + 2];
+ podn_dpm_table_in_backend->entries[input_level].clock = input_clk;
+ podn_vdd_dep_in_backend->entries[input_level].clk = input_clk;
+ podn_dpm_table_in_backend->entries[input_level].vddc = input_vol;
+ podn_vdd_dep_in_backend->entries[input_level].vddc = input_vol;
+ podn_vdd_dep_in_backend->entries[input_level].vddgfx = input_vol;
}
return 0;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
index 4b92b52aba2b..0e237feb1629 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
@@ -685,10 +685,18 @@ static int vega10_patch_voltage_dependency_tables_with_lookup_table(
case 3: vdt = table_info->vdd_dep_on_pixclk; break;
case 4: vdt = table_info->vdd_dep_on_dispclk; break;
case 5: vdt = table_info->vdd_dep_on_phyclk; break;
+ default:
+ continue;
}
for (entry_id = 0; entry_id < vdt->count; entry_id++) {
voltage_id = vdt->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: clk_dep[%u][%u] vddc index %u out of bounds (%u)\n",
+ i, entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
vdt->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
@@ -696,23 +704,48 @@ static int vega10_patch_voltage_dependency_tables_with_lookup_table(
for (entry_id = 0; entry_id < mm_table->count; ++entry_id) {
voltage_id = mm_table->entries[entry_id].vddcInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: mm[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
mm_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
}
for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
voltage_id = mclk_table->entries[entry_id].vddInd;
+ if (voltage_id >= table_info->vddc_lookup_table->count) {
+ pr_err("amdgpu: mclk[%u] vddc index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddc_lookup_table->count);
+ return -EINVAL;
+ }
mclk_table->entries[entry_id].vddc =
table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+
voltage_id = mclk_table->entries[entry_id].vddciInd;
+ if (voltage_id >= table_info->vddci_lookup_table->count) {
+ pr_err("amdgpu: mclk[%u] vddci index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddci_lookup_table->count);
+ return -EINVAL;
+ }
mclk_table->entries[entry_id].vddci =
table_info->vddci_lookup_table->entries[voltage_id].us_vdd;
+
voltage_id = mclk_table->entries[entry_id].mvddInd;
+ if (voltage_id >= table_info->vddmem_lookup_table->count) {
+ pr_err("amdgpu: mclk[%u] vddmem index %u out of bounds (%u)\n",
+ entry_id, voltage_id,
+ table_info->vddmem_lookup_table->count);
+ return -EINVAL;
+ }
mclk_table->entries[entry_id].mvdd =
table_info->vddmem_lookup_table->entries[voltage_id].us_vdd;
}
-
return 0;
}
@@ -5215,6 +5248,11 @@ static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui
uint8_t min_active_level;
uint32_t power_profile_mode = input[size];
+ if (power_profile_mode > PP_SMC_POWER_PROFILE_CUSTOM) {
+ pr_err("Invalid power profile mode %u\n", power_profile_mode);
+ return -EINVAL;
+ }
+
if (power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) {
if (size != 0 && size != 4)
return -EINVAL;
@@ -5230,6 +5268,10 @@ static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui
return -EINVAL;
}
+ if ((input[0] & ~0xFF) || (input[1] & ~0xFF) ||
+ (input[2] & ~0xFF) || (input[3] & ~0xFF))
+ return -EINVAL;
+
data->custom_profile_mode[0] = busy_set_point = input[0];
data->custom_profile_mode[1] = FPS = input[1];
data->custom_profile_mode[2] = use_rlc_busy = input[2];
@@ -5454,11 +5496,9 @@ static int vega10_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
if (PP_OD_EDIT_SCLK_VDDC_TABLE == type) {
dpm_table = &data->dpm_table.gfx_table;
podn_vdd_dep_table = &data->odn_dpm_table.vdd_dep_on_sclk;
- data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
} else if (PP_OD_EDIT_MCLK_VDDC_TABLE == type) {
dpm_table = &data->dpm_table.mem_table;
podn_vdd_dep_table = &data->odn_dpm_table.vdd_dep_on_mclk;
- data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
} else if (PP_OD_RESTORE_DEFAULT_TABLE == type) {
memcpy(&(data->dpm_table), &(data->golden_dpm_table), sizeof(struct vega10_dpm_table));
vega10_odn_initial_default_setting(hwmgr);
@@ -5476,21 +5516,32 @@ static int vega10_odn_edit_dpm_table(struct pp_hwmgr *hwmgr,
}
for (i = 0; i < size; i += 3) {
- if (i + 3 > size || input[i] >= podn_vdd_dep_table->count) {
- pr_info("invalid clock voltage input\n");
- return 0;
+ if (i + 3 > size) {
+ pr_info("truncated clock/voltage input\n");
+ return -EINVAL;
}
- input_level = input[i];
- input_clk = input[i+1] * 100;
- input_vol = input[i+2];
-
- if (vega10_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) {
- dpm_table->dpm_levels[input_level].value = input_clk;
- podn_vdd_dep_table->entries[input_level].clk = input_clk;
- podn_vdd_dep_table->entries[input_level].vddc = input_vol;
- } else {
+ if (input[i] < 0 || input[i] >= podn_vdd_dep_table->count) {
+ pr_info("invalid clock/voltage level\n");
return -EINVAL;
}
+ input_clk = input[i + 1] * 100;
+ input_vol = input[i + 2];
+ if (!vega10_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol))
+ return -EINVAL;
+ }
+
+ if (type == PP_OD_EDIT_SCLK_VDDC_TABLE)
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+ else
+ data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+
+ for (i = 0; i < size; i += 3) {
+ input_level = input[i];
+ input_clk = input[i + 1] * 100;
+ input_vol = input[i + 2];
+ dpm_table->dpm_levels[input_level].value = input_clk;
+ podn_vdd_dep_table->entries[input_level].clk = input_clk;
+ podn_vdd_dep_table->entries[input_level].vddc = input_vol;
}
vega10_odn_update_soc_table(hwmgr, type);
return 0;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c
index 052d139584fd..62b1e068f90d 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c
@@ -63,14 +63,57 @@ static const void *get_powerplay_table(struct pp_hwmgr *hwmgr)
return table_address;
}
-static int check_powerplay_tables(
- struct pp_hwmgr *hwmgr,
- const ATOM_Vega10_POWERPLAYTABLE *powerplay_table)
+static bool vega10_pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset,
+ size_t size)
+{
+ size_t table_size = hwmgr->soft_pp_table_size;
+
+ return offset <= table_size && size <= table_size - offset;
+}
+
+static int get_vega10_subtable(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ u16 table_offset, size_t table_size, const void **table)
+{
+ PP_ASSERT_WITH_CODE((table_offset != 0),
+ "Invalid PowerPlay Table!", return -1);
+ PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *table = (const void *)(((unsigned long)powerplay_table) + table_offset);
+
+ return 0;
+}
+
+static int validate_vega10_table_entries(struct pp_hwmgr *hwmgr,
+ u16 table_offset, size_t entries_offset,
+ u8 num_entries, size_t entry_size)
+{
+ size_t table_size;
+
+ PP_ASSERT_WITH_CODE((num_entries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = entries_offset + num_entries * entry_size;
+ PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ return 0;
+}
+
+static int get_vega10_state_array(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Vega10_State_Array **state_array)
{
const ATOM_Vega10_State_Array *state_arrays;
+ u16 state_array_offset;
+ size_t state_array_size;
+ size_t table_size = hwmgr->soft_pp_table_size;
- state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usStateArrayOffset));
+ PP_ASSERT_WITH_CODE((table_size >= sizeof(*powerplay_table)),
+ "Invalid PowerPlay Table!", return -1);
PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >=
ATOM_Vega10_TABLE_REVISION_VEGA10),
@@ -79,12 +122,321 @@ static int check_powerplay_tables(
"State table is not set!", return -1);
PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0,
"Invalid PowerPlay Table!", return -1);
+
+ state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset);
+ PP_ASSERT_WITH_CODE((state_array_offset <=
+ table_size - sizeof(*state_arrays)),
+ "Invalid PowerPlay Table!", return -1);
+
+ state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) +
+ state_array_offset);
PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0,
"Invalid PowerPlay Table!", return -1);
+ state_array_size = struct_size(state_arrays, states, state_arrays->ucNumEntries);
+ PP_ASSERT_WITH_CODE((state_array_size <= table_size - state_array_offset),
+ "Invalid PowerPlay Table!", return -1);
+
+ *state_array = state_arrays;
+
return 0;
}
+static int get_vega10_gfxclk_dependency_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Vega10_GFXCLK_Dependency_Table **gfxclk_dep_table)
+{
+ const ATOM_Vega10_GFXCLK_Dependency_Table *table;
+ u16 table_offset;
+ size_t table_size;
+ size_t entry_size;
+
+ table_offset = le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset);
+ if (!table_offset)
+ return -EINVAL;
+
+ PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset,
+ sizeof(*table))),
+ "Invalid PowerPlay Table!", return -1);
+
+ table = (const ATOM_Vega10_GFXCLK_Dependency_Table *)
+ (((unsigned long)powerplay_table) + table_offset);
+ PP_ASSERT_WITH_CODE((table->ucNumEntries != 0),
+ "Invalid PowerPlay Table!", return -1);
+
+ if (table->ucRevId == 0)
+ entry_size = sizeof(ATOM_Vega10_GFXCLK_Dependency_Record);
+ else if (table->ucRevId == 1)
+ entry_size = sizeof(ATOM_Vega10_GFXCLK_Dependency_Record_V2);
+ else
+ PP_ASSERT_WITH_CODE(false,
+ "Unsupported GFXClockDependencyTable Revision!",
+ return -EINVAL);
+
+ table_size = offsetof(ATOM_Vega10_GFXCLK_Dependency_Table, entries) +
+ table->ucNumEntries * entry_size;
+ PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *gfxclk_dep_table = table;
+
+ return 0;
+}
+
+static int get_vega10_clk_dependency_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ u16 table_offset,
+ const ATOM_Vega10_SOCCLK_Dependency_Table **clk_dep_table)
+{
+ const ATOM_Vega10_SOCCLK_Dependency_Table *table;
+ int ret;
+
+ ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ ret = validate_vega10_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Vega10_SOCCLK_Dependency_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Vega10_CLK_Dependency_Record));
+ if (ret)
+ return ret;
+
+ *clk_dep_table = table;
+
+ return 0;
+}
+
+static int get_vega10_mclk_dependency_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Vega10_MCLK_Dependency_Table **mclk_dep_table)
+{
+ const ATOM_Vega10_MCLK_Dependency_Table *table;
+ u16 table_offset;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usMclkDependencyTableOffset);
+ ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ ret = validate_vega10_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Vega10_MCLK_Dependency_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Vega10_MCLK_Dependency_Record));
+ if (ret)
+ return ret;
+
+ *mclk_dep_table = table;
+
+ return 0;
+}
+
+static int get_vega10_mm_dependency_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Vega10_MM_Dependency_Table **mm_dep_table)
+{
+ const ATOM_Vega10_MM_Dependency_Table *table;
+ u16 table_offset;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usMMDependencyTableOffset);
+ ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ ret = validate_vega10_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Vega10_MM_Dependency_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Vega10_MM_Dependency_Record));
+ if (ret)
+ return ret;
+
+ *mm_dep_table = table;
+
+ return 0;
+}
+
+static int get_vega10_pcie_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const Vega10_PPTable_Generic_SubTable_Header **pcie_table)
+{
+ const ATOM_Vega10_PCIE_Table *table;
+ u16 table_offset;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usPCIETableOffset);
+ ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ if (!table->ucNumEntries) {
+ *pcie_table = (const Vega10_PPTable_Generic_SubTable_Header *)table;
+ return 0;
+ }
+
+ ret = validate_vega10_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Vega10_PCIE_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Vega10_PCIE_Record));
+ if (ret)
+ return ret;
+
+ *pcie_table = (const Vega10_PPTable_Generic_SubTable_Header *)table;
+
+ return 0;
+}
+
+static int get_vega10_hard_limit_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Vega10_Hard_Limit_Table **hard_limit_table)
+{
+ const ATOM_Vega10_Hard_Limit_Table *table;
+ u16 table_offset;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usHardLimitTableOffset);
+ ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ ret = validate_vega10_table_entries(hwmgr, table_offset,
+ offsetof(ATOM_Vega10_Hard_Limit_Table,
+ entries),
+ table->ucNumEntries,
+ sizeof(ATOM_Vega10_Hard_Limit_Record));
+ if (ret)
+ return ret;
+
+ *hard_limit_table = table;
+
+ return 0;
+}
+
+static int get_vega10_thermal_controller_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const ATOM_Vega10_Thermal_Controller **thermal_controller)
+{
+ u16 table_offset;
+
+ table_offset = le16_to_cpu(powerplay_table->usThermalControllerOffset);
+
+ return get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(**thermal_controller),
+ (const void **)thermal_controller);
+}
+
+static int get_vega10_fan_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const Vega10_PPTable_Generic_SubTable_Header **fan_table)
+{
+ const Vega10_PPTable_Generic_SubTable_Header *header;
+ u16 table_offset;
+ size_t table_size;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usFanTableOffset);
+ ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*header), (const void **)&header);
+ if (ret)
+ return ret;
+
+ if (header->ucRevId == 10)
+ table_size = sizeof(ATOM_Vega10_Fan_Table);
+ else if (header->ucRevId == 0xb)
+ table_size = sizeof(ATOM_Vega10_Fan_Table_V2);
+ else if (header->ucRevId > 0xb)
+ table_size = sizeof(ATOM_Vega10_Fan_Table_V3);
+ else
+ table_size = sizeof(*header);
+
+ PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *fan_table = header;
+
+ return 0;
+}
+
+static int get_vega10_power_tune_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ const Vega10_PPTable_Generic_SubTable_Header **power_tune_table)
+{
+ const Vega10_PPTable_Generic_SubTable_Header *header;
+ u16 table_offset;
+ size_t table_size;
+ int ret;
+
+ table_offset = le16_to_cpu(powerplay_table->usPowerTuneTableOffset);
+ ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*header), (const void **)&header);
+ if (ret)
+ return ret;
+
+ if (header->ucRevId == 5)
+ table_size = sizeof(ATOM_Vega10_PowerTune_Table);
+ else if (header->ucRevId == 6)
+ table_size = sizeof(ATOM_Vega10_PowerTune_Table_V2);
+ else
+ table_size = sizeof(ATOM_Vega10_PowerTune_Table_V3);
+
+ PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *power_tune_table = header;
+
+ return 0;
+}
+
+static int get_vega10_voltage_lookup_table(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table,
+ u16 table_offset, uint32_t max_levels,
+ const ATOM_Vega10_Voltage_Lookup_Table **lookup_table)
+{
+ const ATOM_Vega10_Voltage_Lookup_Table *table;
+ size_t table_size;
+ int ret;
+
+ ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset,
+ sizeof(*table), (const void **)&table);
+ if (ret)
+ return ret;
+
+ PP_ASSERT_WITH_CODE((table->ucNumEntries != 0 &&
+ table->ucNumEntries <= max_levels),
+ "Invalid PowerPlay Table!", return -1);
+
+ table_size = offsetof(ATOM_Vega10_Voltage_Lookup_Table, entries) +
+ table->ucNumEntries * sizeof(ATOM_Vega10_Voltage_Lookup_Record);
+ PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset,
+ table_size)),
+ "Invalid PowerPlay Table!", return -1);
+
+ *lookup_table = table;
+
+ return 0;
+}
+
+static int check_powerplay_tables(struct pp_hwmgr *hwmgr,
+ const ATOM_Vega10_POWERPLAYTABLE *powerplay_table)
+{
+ const ATOM_Vega10_State_Array *state_arrays;
+
+ return get_vega10_state_array(hwmgr, powerplay_table, &state_arrays);
+}
+
static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps)
{
set_hw_cap(
@@ -124,14 +476,16 @@ static int init_thermal_controller(
const ATOM_Vega10_Fan_Table *fan_table_v1;
const ATOM_Vega10_Fan_Table_V2 *fan_table_v2;
const ATOM_Vega10_Fan_Table_V3 *fan_table_v3;
-
- thermal_controller = (ATOM_Vega10_Thermal_Controller *)
- (((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usThermalControllerOffset));
+ int ret;
PP_ASSERT_WITH_CODE((powerplay_table->usThermalControllerOffset != 0),
"Thermal controller table not set!", return -EINVAL);
+ ret = get_vega10_thermal_controller_table(hwmgr, powerplay_table,
+ &thermal_controller);
+ if (ret)
+ return ret;
+
hwmgr->thermal_controller.ucType = thermal_controller->ucType;
hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine;
hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress;
@@ -160,9 +514,9 @@ static int init_thermal_controller(
if (!powerplay_table->usFanTableOffset)
return 0;
- header = (const Vega10_PPTable_Generic_SubTable_Header *)
- (((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usFanTableOffset));
+ ret = get_vega10_fan_table(hwmgr, powerplay_table, &header);
+ if (ret)
+ return ret;
if (header->ucRevId == 10) {
fan_table_v1 = (ATOM_Vega10_Fan_Table *)header;
@@ -307,12 +661,15 @@ static int init_over_drive_limits(
struct pp_hwmgr *hwmgr,
const ATOM_Vega10_POWERPLAYTABLE *powerplay_table)
{
- const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table =
- (const ATOM_Vega10_GFXCLK_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset));
+ const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table;
bool is_acg_enabled = false;
ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_v2;
+ int ret;
+
+ ret = get_vega10_gfxclk_dependency_table(hwmgr, powerplay_table,
+ &gfxclk_dep_table);
+ if (ret)
+ return ret;
if (gfxclk_dep_table->ucRevId == 1) {
patom_record_v2 =
@@ -344,20 +701,28 @@ static int get_mm_clock_voltage_table(
const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table)
{
uint32_t i;
+ uint32_t num_entries;
const ATOM_Vega10_MM_Dependency_Record *mm_dependency_record;
phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table;
PP_ASSERT_WITH_CODE((mm_dependency_table->ucNumEntries != 0),
"Invalid PowerPlay Table!", return -1);
- mm_table = kzalloc_flex(*mm_table, entries,
- mm_dependency_table->ucNumEntries);
+ num_entries = min_t(uint32_t, mm_dependency_table->ucNumEntries,
+ pp_entries_max(hwmgr, mm_dependency_table,
+ sizeof(*mm_dependency_table),
+ sizeof(ATOM_Vega10_MM_Dependency_Record)));
+ if (num_entries < mm_dependency_table->ucNumEntries)
+ pr_warn("amdgpu: Vega10 MM dependency table: clamping ucNumEntries %u -> %u\n",
+ mm_dependency_table->ucNumEntries, num_entries);
+
+ mm_table = kzalloc_flex(*mm_table, entries, num_entries);
if (!mm_table)
return -ENOMEM;
- mm_table->count = mm_dependency_table->ucNumEntries;
+ mm_table->count = num_entries;
- for (i = 0; i < mm_dependency_table->ucNumEntries; i++) {
+ for (i = 0; i < num_entries; i++) {
mm_dependency_record = &mm_dependency_table->entries[i];
mm_table->entries[i].vddcInd = mm_dependency_record->ucVddcInd;
mm_table->entries[i].samclock =
@@ -568,19 +933,27 @@ static int get_socclk_voltage_dependency_table(
const ATOM_Vega10_SOCCLK_Dependency_Table *clk_dep_table)
{
uint32_t i;
+ uint32_t num_entries;
phm_ppt_v1_clock_voltage_dependency_table *clk_table;
PP_ASSERT_WITH_CODE(clk_dep_table->ucNumEntries,
"Invalid PowerPlay Table!", return -1);
- clk_table = kzalloc_flex(*clk_table, entries,
- clk_dep_table->ucNumEntries);
+ num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries,
+ pp_entries_max(hwmgr, clk_dep_table,
+ sizeof(*clk_dep_table),
+ sizeof(ATOM_Vega10_CLK_Dependency_Record)));
+ if (num_entries < clk_dep_table->ucNumEntries)
+ pr_warn("amdgpu: Vega10 SOCCLK dependency table: clamping ucNumEntries %u -> %u\n",
+ clk_dep_table->ucNumEntries, num_entries);
+
+ clk_table = kzalloc_flex(*clk_table, entries, num_entries);
if (!clk_table)
return -ENOMEM;
- clk_table->count = (uint32_t)clk_dep_table->ucNumEntries;
+ clk_table->count = num_entries;
- for (i = 0; i < clk_dep_table->ucNumEntries; i++) {
+ for (i = 0; i < num_entries; i++) {
clk_table->entries[i].vddInd =
clk_dep_table->entries[i].ucVddInd;
clk_table->entries[i].clk =
@@ -598,19 +971,27 @@ static int get_mclk_voltage_dependency_table(
const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table)
{
uint32_t i;
+ uint32_t num_entries;
phm_ppt_v1_clock_voltage_dependency_table *mclk_table;
PP_ASSERT_WITH_CODE(mclk_dep_table->ucNumEntries,
"Invalid PowerPlay Table!", return -1);
- mclk_table = kzalloc_flex(*mclk_table, entries,
- mclk_dep_table->ucNumEntries);
+ num_entries = min_t(uint32_t, mclk_dep_table->ucNumEntries,
+ pp_entries_max(hwmgr, mclk_dep_table,
+ sizeof(*mclk_dep_table),
+ sizeof(ATOM_Vega10_MCLK_Dependency_Record)));
+ if (num_entries < mclk_dep_table->ucNumEntries)
+ pr_warn("amdgpu: Vega10 MCLK dependency table: clamping ucNumEntries %u -> %u\n",
+ mclk_dep_table->ucNumEntries, num_entries);
+
+ mclk_table = kzalloc_flex(*mclk_table, entries, num_entries);
if (!mclk_table)
return -ENOMEM;
- mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries;
+ mclk_table->count = num_entries;
- for (i = 0; i < mclk_dep_table->ucNumEntries; i++) {
+ for (i = 0; i < num_entries; i++) {
mclk_table->entries[i].vddInd =
mclk_dep_table->entries[i].ucVddInd;
mclk_table->entries[i].vddciInd =
@@ -633,6 +1014,7 @@ static int get_gfxclk_voltage_dependency_table(
const ATOM_Vega10_GFXCLK_Dependency_Table *clk_dep_table)
{
uint32_t i;
+ uint32_t num_entries;
struct phm_ppt_v1_clock_voltage_dependency_table
*clk_table;
ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_v2;
@@ -640,15 +1022,34 @@ static int get_gfxclk_voltage_dependency_table(
PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0),
"Invalid PowerPlay Table!", return -1);
- clk_table = kzalloc_flex(*clk_table, entries,
- clk_dep_table->ucNumEntries);
+ if (clk_dep_table->ucRevId == 0) {
+ num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries,
+ pp_entries_max(hwmgr, clk_dep_table,
+ sizeof(*clk_dep_table),
+ sizeof(ATOM_Vega10_GFXCLK_Dependency_Record)));
+ } else if (clk_dep_table->ucRevId == 1) {
+ num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries,
+ pp_entries_max(hwmgr, clk_dep_table,
+ sizeof(*clk_dep_table),
+ sizeof(ATOM_Vega10_GFXCLK_Dependency_Record_V2)));
+ } else {
+ PP_ASSERT_WITH_CODE(false,
+ "Unsupported GFXClockDependencyTable Revision!",
+ return -EINVAL);
+ }
+
+ if (num_entries < clk_dep_table->ucNumEntries)
+ pr_warn("amdgpu: Vega10 GFXCLK dependency table: clamping ucNumEntries %u -> %u\n",
+ clk_dep_table->ucNumEntries, num_entries);
+
+ clk_table = kzalloc_flex(*clk_table, entries, num_entries);
if (!clk_table)
return -ENOMEM;
- clk_table->count = clk_dep_table->ucNumEntries;
+ clk_table->count = num_entries;
if (clk_dep_table->ucRevId == 0) {
- for (i = 0; i < clk_table->count; i++) {
+ for (i = 0; i < num_entries; i++) {
clk_table->entries[i].vddInd =
clk_dep_table->entries[i].ucVddInd;
clk_table->entries[i].clk =
@@ -661,9 +1062,9 @@ static int get_gfxclk_voltage_dependency_table(
clk_table->entries[i].sclk_offset =
le16_to_cpu(clk_dep_table->entries[i].usAVFSOffset);
}
- } else if (clk_dep_table->ucRevId == 1) {
+ } else {
patom_record_v2 = (ATOM_Vega10_GFXCLK_Dependency_Record_V2 *)clk_dep_table->entries;
- for (i = 0; i < clk_table->count; i++) {
+ for (i = 0; i < num_entries; i++) {
clk_table->entries[i].vddInd =
patom_record_v2->ucVddInd;
clk_table->entries[i].clk =
@@ -677,11 +1078,6 @@ static int get_gfxclk_voltage_dependency_table(
le16_to_cpu(patom_record_v2->usAVFSOffset);
patom_record_v2++;
}
- } else {
- kfree(clk_table);
- PP_ASSERT_WITH_CODE(false,
- "Unsupported GFXClockDependencyTable Revision!",
- return -EINVAL);
}
*pp_vega10_clk_dep_table = clk_table;
@@ -696,20 +1092,28 @@ static int get_pix_clk_voltage_dependency_table(
const ATOM_Vega10_PIXCLK_Dependency_Table *clk_dep_table)
{
uint32_t i;
+ uint32_t num_entries;
struct phm_ppt_v1_clock_voltage_dependency_table
*clk_table;
PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0),
"Invalid PowerPlay Table!", return -1);
- clk_table = kzalloc_flex(*clk_table, entries,
- clk_dep_table->ucNumEntries);
+ num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries,
+ pp_entries_max(hwmgr, clk_dep_table,
+ sizeof(*clk_dep_table),
+ sizeof(ATOM_Vega10_CLK_Dependency_Record)));
+ if (num_entries < clk_dep_table->ucNumEntries)
+ pr_warn("amdgpu: Vega10 PIXCLK dependency table: clamping ucNumEntries %u -> %u\n",
+ clk_dep_table->ucNumEntries, num_entries);
+
+ clk_table = kzalloc_flex(*clk_table, entries, num_entries);
if (!clk_table)
return -ENOMEM;
- clk_table->count = clk_dep_table->ucNumEntries;
+ clk_table->count = num_entries;
- for (i = 0; i < clk_table->count; i++) {
+ for (i = 0; i < num_entries; i++) {
clk_table->entries[i].vddInd =
clk_dep_table->entries[i].ucVddInd;
clk_table->entries[i].clk =
@@ -728,6 +1132,7 @@ static int get_dcefclk_voltage_dependency_table(
const ATOM_Vega10_DCEFCLK_Dependency_Table *clk_dep_table)
{
uint32_t i;
+ uint32_t safe_entries;
uint8_t num_entries;
struct phm_ppt_v1_clock_voltage_dependency_table
*clk_table;
@@ -738,6 +1143,14 @@ static int get_dcefclk_voltage_dependency_table(
PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0),
"Invalid PowerPlay Table!", return -1);
+ safe_entries = min_t(uint32_t, clk_dep_table->ucNumEntries,
+ pp_entries_max(hwmgr, clk_dep_table,
+ sizeof(*clk_dep_table),
+ sizeof(ATOM_Vega10_CLK_Dependency_Record)));
+ if (safe_entries < clk_dep_table->ucNumEntries)
+ pr_warn("amdgpu: Vega10 DCEFCLK dependency table: clamping ucNumEntries %u -> %u\n",
+ clk_dep_table->ucNumEntries, safe_entries);
+
/*
* workaround needed to add another DPM level for pioneer cards
* as VBIOS is locked down.
@@ -747,12 +1160,12 @@ static int get_dcefclk_voltage_dependency_table(
dev_id = adev->pdev->device;
rev_id = adev->pdev->revision;
- if (dev_id == 0x6863 && rev_id == 0 &&
- clk_dep_table->entries[clk_dep_table->ucNumEntries - 1].ulClk < 90000)
- num_entries = clk_dep_table->ucNumEntries + 1 > NUM_DSPCLK_LEVELS ?
- NUM_DSPCLK_LEVELS : clk_dep_table->ucNumEntries + 1;
+ if (dev_id == 0x6863 && rev_id == 0 && safe_entries > 0 &&
+ clk_dep_table->entries[safe_entries - 1].ulClk < 90000)
+ num_entries = safe_entries + 1 > NUM_DSPCLK_LEVELS ?
+ NUM_DSPCLK_LEVELS : safe_entries + 1;
else
- num_entries = clk_dep_table->ucNumEntries;
+ num_entries = safe_entries;
clk_table = kzalloc_flex(*clk_table, entries, num_entries);
@@ -761,7 +1174,7 @@ static int get_dcefclk_voltage_dependency_table(
clk_table->count = (uint32_t)num_entries;
- for (i = 0; i < clk_dep_table->ucNumEntries; i++) {
+ for (i = 0; i < safe_entries; i++) {
clk_table->entries[i].vddInd =
clk_dep_table->entries[i].ucVddInd;
clk_table->entries[i].clk =
@@ -873,51 +1286,13 @@ static int init_powerplay_extended_tables(
int result = 0;
struct phm_ppt_v2_information *pp_table_info =
(struct phm_ppt_v2_information *)(hwmgr->pptable);
-
- const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table =
- (const ATOM_Vega10_MM_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usMMDependencyTableOffset));
- const Vega10_PPTable_Generic_SubTable_Header *power_tune_table =
- (const Vega10_PPTable_Generic_SubTable_Header *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usPowerTuneTableOffset));
- const ATOM_Vega10_SOCCLK_Dependency_Table *socclk_dep_table =
- (const ATOM_Vega10_SOCCLK_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usSocclkDependencyTableOffset));
- const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table =
- (const ATOM_Vega10_GFXCLK_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset));
- const ATOM_Vega10_DCEFCLK_Dependency_Table *dcefclk_dep_table =
- (const ATOM_Vega10_DCEFCLK_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usDcefclkDependencyTableOffset));
- const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table =
- (const ATOM_Vega10_MCLK_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
- const ATOM_Vega10_Hard_Limit_Table *hard_limits =
- (const ATOM_Vega10_Hard_Limit_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usHardLimitTableOffset));
- const Vega10_PPTable_Generic_SubTable_Header *pcie_table =
- (const Vega10_PPTable_Generic_SubTable_Header *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usPCIETableOffset));
- const ATOM_Vega10_PIXCLK_Dependency_Table *pixclk_dep_table =
- (const ATOM_Vega10_PIXCLK_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usPixclkDependencyTableOffset));
- const ATOM_Vega10_PHYCLK_Dependency_Table *phyclk_dep_table =
- (const ATOM_Vega10_PHYCLK_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usPhyClkDependencyTableOffset));
- const ATOM_Vega10_DISPCLK_Dependency_Table *dispclk_dep_table =
- (const ATOM_Vega10_DISPCLK_Dependency_Table *)
- (((unsigned long) powerplay_table) +
- le16_to_cpu(powerplay_table->usDispClkDependencyTableOffset));
+ const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table;
+ const Vega10_PPTable_Generic_SubTable_Header *power_tune_table;
+ const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table;
+ const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table;
+ const ATOM_Vega10_Hard_Limit_Table *hard_limits;
+ const Vega10_PPTable_Generic_SubTable_Header *pcie_table;
+ const ATOM_Vega10_SOCCLK_Dependency_Table *clk_dep_table;
pp_table_info->vdd_dep_on_socclk = NULL;
pp_table_info->vdd_dep_on_sclk = NULL;
@@ -929,63 +1304,114 @@ static int init_powerplay_extended_tables(
pp_table_info->vdd_dep_on_phyclk = NULL;
pp_table_info->vdd_dep_on_dispclk = NULL;
- if (powerplay_table->usMMDependencyTableOffset)
- result = get_mm_clock_voltage_table(hwmgr,
+ if (powerplay_table->usMMDependencyTableOffset) {
+ result = get_vega10_mm_dependency_table(hwmgr, powerplay_table,
+ &mm_dependency_table);
+ if (!result)
+ result = get_mm_clock_voltage_table(hwmgr,
&pp_table_info->mm_dep_table,
mm_dependency_table);
+ }
- if (!result && powerplay_table->usPowerTuneTableOffset)
- result = get_tdp_table(hwmgr,
+ if (!result && powerplay_table->usPowerTuneTableOffset) {
+ result = get_vega10_power_tune_table(hwmgr, powerplay_table,
+ &power_tune_table);
+ if (!result)
+ result = get_tdp_table(hwmgr,
&pp_table_info->tdp_table,
power_tune_table);
+ }
- if (!result && powerplay_table->usSocclkDependencyTableOffset)
- result = get_socclk_voltage_dependency_table(hwmgr,
+ if (!result && powerplay_table->usSocclkDependencyTableOffset) {
+ result = get_vega10_clk_dependency_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usSocclkDependencyTableOffset),
+ &clk_dep_table);
+ if (!result)
+ result = get_socclk_voltage_dependency_table(hwmgr,
&pp_table_info->vdd_dep_on_socclk,
- socclk_dep_table);
+ (const ATOM_Vega10_SOCCLK_Dependency_Table *)
+ clk_dep_table);
+ }
- if (!result && powerplay_table->usGfxclkDependencyTableOffset)
- result = get_gfxclk_voltage_dependency_table(hwmgr,
- &pp_table_info->vdd_dep_on_sclk,
- gfxclk_dep_table);
+ if (!result && powerplay_table->usGfxclkDependencyTableOffset) {
+ result = get_vega10_gfxclk_dependency_table(hwmgr,
+ powerplay_table, &gfxclk_dep_table);
+ if (!result)
+ result = get_gfxclk_voltage_dependency_table(hwmgr,
+ &pp_table_info->vdd_dep_on_sclk,
+ gfxclk_dep_table);
+ }
- if (!result && powerplay_table->usPixclkDependencyTableOffset)
- result = get_pix_clk_voltage_dependency_table(hwmgr,
+ if (!result && powerplay_table->usPixclkDependencyTableOffset) {
+ result = get_vega10_clk_dependency_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usPixclkDependencyTableOffset),
+ &clk_dep_table);
+ if (!result)
+ result = get_pix_clk_voltage_dependency_table(hwmgr,
&pp_table_info->vdd_dep_on_pixclk,
(const ATOM_Vega10_PIXCLK_Dependency_Table *)
- pixclk_dep_table);
+ clk_dep_table);
+ }
- if (!result && powerplay_table->usPhyClkDependencyTableOffset)
- result = get_pix_clk_voltage_dependency_table(hwmgr,
+ if (!result && powerplay_table->usPhyClkDependencyTableOffset) {
+ result = get_vega10_clk_dependency_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usPhyClkDependencyTableOffset),
+ &clk_dep_table);
+ if (!result)
+ result = get_pix_clk_voltage_dependency_table(hwmgr,
&pp_table_info->vdd_dep_on_phyclk,
(const ATOM_Vega10_PIXCLK_Dependency_Table *)
- phyclk_dep_table);
+ clk_dep_table);
+ }
- if (!result && powerplay_table->usDispClkDependencyTableOffset)
- result = get_pix_clk_voltage_dependency_table(hwmgr,
+ if (!result && powerplay_table->usDispClkDependencyTableOffset) {
+ result = get_vega10_clk_dependency_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usDispClkDependencyTableOffset),
+ &clk_dep_table);
+ if (!result)
+ result = get_pix_clk_voltage_dependency_table(hwmgr,
&pp_table_info->vdd_dep_on_dispclk,
(const ATOM_Vega10_PIXCLK_Dependency_Table *)
- dispclk_dep_table);
+ clk_dep_table);
+ }
- if (!result && powerplay_table->usDcefclkDependencyTableOffset)
- result = get_dcefclk_voltage_dependency_table(hwmgr,
+ if (!result && powerplay_table->usDcefclkDependencyTableOffset) {
+ result = get_vega10_clk_dependency_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usDcefclkDependencyTableOffset),
+ &clk_dep_table);
+ if (!result)
+ result = get_dcefclk_voltage_dependency_table(hwmgr,
&pp_table_info->vdd_dep_on_dcefclk,
- dcefclk_dep_table);
+ (const ATOM_Vega10_DCEFCLK_Dependency_Table *)
+ clk_dep_table);
+ }
- if (!result && powerplay_table->usMclkDependencyTableOffset)
- result = get_mclk_voltage_dependency_table(hwmgr,
+ if (!result && powerplay_table->usMclkDependencyTableOffset) {
+ result = get_vega10_mclk_dependency_table(hwmgr, powerplay_table,
+ &mclk_dep_table);
+ if (!result)
+ result = get_mclk_voltage_dependency_table(hwmgr,
&pp_table_info->vdd_dep_on_mclk,
mclk_dep_table);
+ }
- if (!result && powerplay_table->usPCIETableOffset)
- result = get_pcie_table(hwmgr,
+ if (!result && powerplay_table->usPCIETableOffset) {
+ result = get_vega10_pcie_table(hwmgr, powerplay_table,
+ &pcie_table);
+ if (!result)
+ result = get_pcie_table(hwmgr,
&pp_table_info->pcie_table,
pcie_table);
+ }
- if (!result && powerplay_table->usHardLimitTableOffset)
- result = get_hard_limits(hwmgr,
+ if (!result && powerplay_table->usHardLimitTableOffset) {
+ result = get_vega10_hard_limit_table(hwmgr, powerplay_table,
+ &hard_limits);
+ if (!result)
+ result = get_hard_limits(hwmgr,
&pp_table_info->max_clock_voltage_on_dc,
hard_limits);
+ }
hwmgr->dyn_state.max_clock_voltage_on_dc.sclk =
pp_table_info->max_clock_voltage_on_dc.sclk;
@@ -1034,18 +1460,28 @@ static int get_vddc_lookup_table(
uint32_t max_levels)
{
uint32_t i;
+ uint32_t num_entries;
phm_ppt_v1_voltage_lookup_table *table;
PP_ASSERT_WITH_CODE((vddc_lookup_pp_tables->ucNumEntries != 0),
"Invalid SOC_VDDD Lookup Table!", return 1);
- table = kzalloc_flex(*table, entries, max_levels);
+ num_entries = min_t(uint32_t, vddc_lookup_pp_tables->ucNumEntries,
+ min_t(uint32_t, max_levels,
+ pp_entries_max(hwmgr, vddc_lookup_pp_tables,
+ sizeof(*vddc_lookup_pp_tables),
+ sizeof(ATOM_Vega10_Voltage_Lookup_Record))));
+ if (num_entries < vddc_lookup_pp_tables->ucNumEntries)
+ pr_warn("amdgpu: Vega10 VddcLookup table: clamping ucNumEntries %u -> %u\n",
+ vddc_lookup_pp_tables->ucNumEntries, num_entries);
+
+ table = kzalloc_flex(*table, entries, num_entries);
if (!table)
return -ENOMEM;
- table->count = vddc_lookup_pp_tables->ucNumEntries;
+ table->count = num_entries;
- for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++)
+ for (i = 0; i < num_entries; i++)
table->entries[i].us_vdd =
le16_to_cpu(vddc_lookup_pp_tables->entries[i].usVdd);
@@ -1113,30 +1549,39 @@ static int init_dpm_2_parameters(
}
if (powerplay_table->usVddcLookupTableOffset) {
- const ATOM_Vega10_Voltage_Lookup_Table *vddc_table =
- (ATOM_Vega10_Voltage_Lookup_Table *)
- (((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usVddcLookupTableOffset));
- result = get_vddc_lookup_table(hwmgr,
- &pp_table_info->vddc_lookup_table, vddc_table, 8);
+ const ATOM_Vega10_Voltage_Lookup_Table *vddc_table;
+
+ result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usVddcLookupTableOffset),
+ 8, &vddc_table);
+ if (!result)
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_info->vddc_lookup_table,
+ vddc_table, 8);
}
- if (powerplay_table->usVddmemLookupTableOffset) {
- const ATOM_Vega10_Voltage_Lookup_Table *vdd_mem_table =
- (ATOM_Vega10_Voltage_Lookup_Table *)
- (((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usVddmemLookupTableOffset));
- result = get_vddc_lookup_table(hwmgr,
- &pp_table_info->vddmem_lookup_table, vdd_mem_table, 4);
+ if (!result && powerplay_table->usVddmemLookupTableOffset) {
+ const ATOM_Vega10_Voltage_Lookup_Table *vdd_mem_table;
+
+ result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usVddmemLookupTableOffset),
+ 4, &vdd_mem_table);
+ if (!result)
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_info->vddmem_lookup_table,
+ vdd_mem_table, 4);
}
- if (powerplay_table->usVddciLookupTableOffset) {
- const ATOM_Vega10_Voltage_Lookup_Table *vddci_table =
- (ATOM_Vega10_Voltage_Lookup_Table *)
- (((unsigned long)powerplay_table) +
- le16_to_cpu(powerplay_table->usVddciLookupTableOffset));
- result = get_vddc_lookup_table(hwmgr,
- &pp_table_info->vddci_lookup_table, vddci_table, 4);
+ if (!result && powerplay_table->usVddciLookupTableOffset) {
+ const ATOM_Vega10_Voltage_Lookup_Table *vddci_table;
+
+ result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table,
+ le16_to_cpu(powerplay_table->usVddciLookupTableOffset),
+ 4, &vddci_table);
+ if (!result)
+ result = get_vddc_lookup_table(hwmgr,
+ &pp_table_info->vddci_lookup_table,
+ vddci_table, 4);
}
return result;
@@ -1247,15 +1692,14 @@ int vega10_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr)
{
const ATOM_Vega10_State_Array *state_arrays;
const ATOM_Vega10_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+ int result;
PP_ASSERT_WITH_CODE((pp_table != NULL),
"Missing PowerPlay Table!", return -1);
- PP_ASSERT_WITH_CODE((pp_table->sHeader.format_revision >=
- ATOM_Vega10_TABLE_REVISION_VEGA10),
- "Incorrect PowerPlay table revision!", return -1);
- state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)pp_table) +
- le16_to_cpu(pp_table->usStateArrayOffset));
+ result = get_vega10_state_array(hwmgr, pp_table, &state_arrays);
+ PP_ASSERT_WITH_CODE((result == 0),
+ "Invalid PowerPlay Table State Array.", return result);
return (uint32_t)(state_arrays->ucNumEntries);
}
@@ -1306,17 +1750,11 @@ int vega10_get_powerplay_table_entry(struct pp_hwmgr *hwmgr,
if (pp_table->sHeader.format_revision >=
ATOM_Vega10_TABLE_REVISION_VEGA10) {
- state_arrays = (ATOM_Vega10_State_Array *)
- (((unsigned long)pp_table) +
- le16_to_cpu(pp_table->usStateArrayOffset));
-
- PP_ASSERT_WITH_CODE(pp_table->usStateArrayOffset > 0,
- "Invalid PowerPlay Table State Array Offset.",
- return -1);
- PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0,
+ result = get_vega10_state_array(hwmgr, pp_table, &state_arrays);
+ PP_ASSERT_WITH_CODE((result == 0),
"Invalid PowerPlay Table State Array.",
- return -1);
- PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries),
+ return result);
+ PP_ASSERT_WITH_CODE((entry_index < state_arrays->ucNumEntries),
"Invalid PowerPlay Table State Array Entry.",
return -1);
@@ -1358,4 +1796,3 @@ int vega10_baco_set_cap(struct pp_hwmgr *hwmgr)
PHM_PlatformCaps_BACO);
return result;
}
-
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c
index 55e13f376039..dcb9c749eba3 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c
@@ -64,6 +64,13 @@ static int check_powerplay_tables(
struct pp_hwmgr *hwmgr,
const ATOM_Vega12_POWERPLAYTABLE *powerplay_table)
{
+ size_t smc_pptable_size =
+ offsetofend(ATOM_Vega12_POWERPLAYTABLE, smcPPTable);
+ size_t table_size = hwmgr->soft_pp_table_size;
+
+ PP_ASSERT_WITH_CODE((table_size >= smc_pptable_size),
+ "Invalid PowerPlay Table!", return -1);
+
PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >=
ATOM_VEGA12_TABLE_REVISION_VEGA12),
"Unsupported PPTable format!", return -1);
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c
index 36cb7aa80d07..a0c884c2341d 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c
@@ -66,6 +66,13 @@ static int check_powerplay_tables(
struct pp_hwmgr *hwmgr,
const ATOM_Vega20_POWERPLAYTABLE *powerplay_table)
{
+ size_t smc_pptable_size =
+ offsetofend(ATOM_Vega20_POWERPLAYTABLE, smcPPTable);
+ size_t table_size = hwmgr->soft_pp_table_size;
+
+ PP_ASSERT_WITH_CODE((table_size >= smc_pptable_size),
+ "Invalid PowerPlay Table!", return -1);
+
PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >=
ATOM_VEGA20_TABLE_REVISION_VEGA20),
"Unsupported PPTable format!", return -1);
diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h
index ca71efaa1656..7ebc1344023f 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h
+++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h
@@ -829,4 +829,21 @@ int smu8_init_function_pointers(struct pp_hwmgr *hwmgr);
int vega12_hwmgr_init(struct pp_hwmgr *hwmgr);
int vega20_hwmgr_init(struct pp_hwmgr *hwmgr);
+static inline uint32_t pp_entries_max(const struct pp_hwmgr *hwmgr,
+ const void *sub_table,
+ size_t hdr_size, size_t rec_size)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)hwmgr->adev;
+ const char *bios_end = (const char *)adev->bios + adev->bios_size;
+ const char *pp_end = (const char *)hwmgr->soft_pp_table
+ + hwmgr->soft_pp_table_size;
+ const char *entries = (const char *)sub_table + hdr_size;
+
+ if (pp_end > bios_end)
+ return 0;
+ if (!rec_size || entries >= pp_end)
+ return 0;
+ return (uint32_t)((pp_end - entries) / rec_size);
+}
+
#endif /* _HWMGR_H_ */
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index 208a2fba6d40..541cf0a985eb 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -591,17 +591,13 @@ static int smu_get_power_num_states(void *handle,
return 0;
}
-bool is_support_sw_smu(struct amdgpu_device *adev)
+void amdgpu_smu_early_init(struct amdgpu_device *adev)
{
/* vega20 is 11.0.2, but it's supported via the powerplay code */
- if (adev->asic_type == CHIP_VEGA20)
- return false;
-
- if ((amdgpu_ip_version(adev, MP1_HWIP, 0) >= IP_VERSION(11, 0, 0)) &&
- amdgpu_device_ip_is_valid(adev, AMD_IP_BLOCK_TYPE_SMC))
- return true;
-
- return false;
+ adev->is_sw_smu = adev->asic_type != CHIP_VEGA20 &&
+ (amdgpu_ip_version(adev, MP1_HWIP, 0) >=
+ IP_VERSION(11, 0, 0) &&
+ amdgpu_device_ip_is_valid(adev, AMD_IP_BLOCK_TYPE_SMC));
}
bool is_support_cclk_dpm(struct amdgpu_device *adev)
@@ -667,25 +663,28 @@ static int smu_sys_set_pp_table(void *handle,
{
struct smu_context *smu = handle;
struct smu_table_context *smu_table = &smu->smu_table;
- ATOM_COMMON_TABLE_HEADER *header = (ATOM_COMMON_TABLE_HEADER *)buf;
+ ATOM_COMMON_TABLE_HEADER *header;
+ void *hardcode_pptable;
int ret = 0;
if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
return -EOPNOTSUPP;
+ if (!buf || size < sizeof(*header))
+ return -EINVAL;
+
+ header = (ATOM_COMMON_TABLE_HEADER *)buf;
if (header->usStructureSize != size) {
dev_err(smu->adev->dev, "pp table size not matched !\n");
return -EIO;
}
- if (!smu_table->hardcode_pptable || smu_table->power_play_table_size < size) {
- kfree(smu_table->hardcode_pptable);
- smu_table->hardcode_pptable = kzalloc(size, GFP_KERNEL);
- if (!smu_table->hardcode_pptable)
- return -ENOMEM;
- }
+ hardcode_pptable = kmemdup(buf, size, GFP_KERNEL);
+ if (!hardcode_pptable)
+ return -ENOMEM;
- memcpy(smu_table->hardcode_pptable, buf, size);
+ kfree(smu_table->hardcode_pptable);
+ smu_table->hardcode_pptable = hardcode_pptable;
smu_table->power_play_table = smu_table->hardcode_pptable;
smu_table->power_play_table_size = size;
@@ -2772,7 +2771,6 @@ const struct amd_ip_funcs smu_ip_funcs = {
.suspend = smu_suspend,
.resume = smu_resume,
.is_idle = NULL,
- .check_soft_reset = NULL,
.wait_for_idle = NULL,
.soft_reset = NULL,
.set_clockgating_state = smu_set_clockgating_state,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index d76e0b005308..378781c05bea 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -849,8 +849,6 @@ struct pptable_funcs {
*/
int (*set_default_dpm_table)(struct smu_context *smu);
- int (*set_power_state)(struct smu_context *smu);
-
/**
* @populate_umd_state_clk: Populate the UMD power state table with
* defaults.
@@ -904,16 +902,6 @@ struct pptable_funcs {
pp_clock_levels_with_latency
*clocks);
/**
- * @get_clock_by_type_with_voltage: Get the speed and voltage of a clock
- * domain.
- */
- int (*get_clock_by_type_with_voltage)(struct smu_context *smu,
- enum amd_pp_clock_type type,
- struct
- pp_clock_levels_with_voltage
- *clocks);
-
- /**
* @get_power_profile_mode: Print all power profile modes to
* buffer. Star current mode.
*/
@@ -1355,11 +1343,6 @@ struct pptable_funcs {
int (*register_irq_handler)(struct smu_context *smu);
/**
- * @set_azalia_d3_pme: Wake the audio decode engine from d3 sleep.
- */
- int (*set_azalia_d3_pme)(struct smu_context *smu);
-
- /**
* @get_max_sustainable_clocks_by_dc: Get a copy of the max sustainable
* clock speeds table.
*
@@ -1376,18 +1359,6 @@ struct pptable_funcs {
int (*get_bamaco_support)(struct smu_context *smu);
/**
- * @baco_get_state: Get the current BACO state.
- *
- * Return: Current BACO state.
- */
- enum smu_baco_state (*baco_get_state)(struct smu_context *smu);
-
- /**
- * @baco_set_state: Enter/exit BACO.
- */
- int (*baco_set_state)(struct smu_context *smu, enum smu_baco_state state);
-
- /**
* @baco_enter: Enter BACO.
*/
int (*baco_enter)(struct smu_context *smu);
@@ -1952,7 +1923,13 @@ int smu_link_reset(struct smu_context *smu);
extern const struct amd_ip_funcs smu_ip_funcs;
-bool is_support_sw_smu(struct amdgpu_device *adev);
+void amdgpu_smu_early_init(struct amdgpu_device *adev);
+
+static inline bool is_support_sw_smu(struct amdgpu_device *adev)
+{
+ return adev->is_sw_smu;
+}
+
bool is_support_cclk_dpm(struct amdgpu_device *adev);
int smu_write_watermarks_table(struct smu_context *smu);
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h
index dd94e8a9e218..c0accee9a9c8 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h
@@ -199,8 +199,6 @@ int smu_v11_0_gfx_off_control(struct smu_context *smu, bool enable);
int smu_v11_0_register_irq_handler(struct smu_context *smu);
-int smu_v11_0_set_azalia_d3_pme(struct smu_context *smu);
-
int smu_v11_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu,
struct pp_smu_nv_clock_table *max_clocks);
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
index 89bbda0670ef..7f21f867d73c 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
@@ -180,8 +180,6 @@ int smu_v13_0_gfx_off_control(struct smu_context *smu, bool enable);
int smu_v13_0_register_irq_handler(struct smu_context *smu);
-int smu_v13_0_set_azalia_d3_pme(struct smu_context *smu);
-
int smu_v13_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu,
struct pp_smu_nv_clock_table *max_clocks);
@@ -255,10 +253,6 @@ void smu_v13_0_init_msg_ctl(struct smu_context *smu,
int smu_v13_0_mode1_reset(struct smu_context *smu);
-int smu_v13_0_get_pptable_from_firmware(struct smu_context *smu,
- void **table,
- uint32_t *size,
- uint32_t pptable_id);
int smu_v13_0_update_pcie_parameters(struct smu_context *smu,
uint8_t pcie_gen_cap,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h
index 4eb40ff8aff2..dc8e13a7c879 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h
@@ -203,10 +203,6 @@ int smu_v14_0_set_gfx_power_up_by_imu(struct smu_context *smu);
int smu_v14_0_set_default_dpm_tables(struct smu_context *smu);
-int smu_v14_0_get_pptable_from_firmware(struct smu_context *smu,
- void **table,
- uint32_t *size,
- uint32_t pptable_id);
int smu_v14_0_od_edit_dpm_table(struct smu_context *smu,
enum PP_OD_DPM_TABLE_COMMAND type,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
index e6fd8be2cc4a..13723d45a7de 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
@@ -211,10 +211,6 @@ int smu_v15_0_deep_sleep_control(struct smu_context *smu,
int smu_v15_0_set_gfx_power_up_by_imu(struct smu_context *smu);
-int smu_v15_0_get_pptable_from_firmware(struct smu_context *smu,
- void **table,
- uint32_t *size,
- uint32_t pptable_id);
int smu_v15_0_od_edit_dpm_table(struct smu_context *smu,
enum PP_OD_DPM_TABLE_COMMAND type,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c
index 54d3dba7d354..db5db2c9c8e8 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c
@@ -1466,9 +1466,10 @@ static int arcturus_set_power_profile_mode(struct smu_context *smu,
return -ENOMEM;
}
if (custom_params && custom_params_max_idx) {
- if (custom_params_max_idx != ARCTURUS_CUSTOM_PARAMS_COUNT)
- return -EINVAL;
- if (custom_params[0] >= ARCTURUS_CUSTOM_PARAMS_CLOCK_COUNT)
+ if (!smu_cmn_custom_params_count_valid(custom_params_max_idx,
+ ARCTURUS_CUSTOM_PARAMS_COUNT) ||
+ !smu_cmn_custom_params_clock_valid(custom_params[0],
+ ARCTURUS_CUSTOM_PARAMS_CLOCK_COUNT))
return -EINVAL;
idx = custom_params[0] * ARCTURUS_CUSTOM_PARAMS_COUNT;
smu->custom_profile_params[idx] = 1;
@@ -1932,7 +1933,6 @@ static const struct pptable_funcs arcturus_ppt_funcs = {
.set_xgmi_pstate = smu_v11_0_set_xgmi_pstate,
.gfx_off_control = smu_v11_0_gfx_off_control,
.register_irq_handler = smu_v11_0_register_irq_handler,
- .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme,
.get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc,
.get_bamaco_support = smu_v11_0_get_bamaco_support,
.baco_enter = smu_v11_0_baco_enter,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
index cd0457e13f54..8feea44f3ca0 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
@@ -1843,9 +1843,10 @@ static int navi10_set_power_profile_mode(struct smu_context *smu,
return -ENOMEM;
}
if (custom_params && custom_params_max_idx) {
- if (custom_params_max_idx != NAVI10_CUSTOM_PARAMS_COUNT)
- return -EINVAL;
- if (custom_params[0] >= NAVI10_CUSTOM_PARAMS_CLOCKS_COUNT)
+ if (!smu_cmn_custom_params_count_valid(custom_params_max_idx,
+ NAVI10_CUSTOM_PARAMS_COUNT) ||
+ !smu_cmn_custom_params_clock_valid(custom_params[0],
+ NAVI10_CUSTOM_PARAMS_CLOCKS_COUNT))
return -EINVAL;
idx = custom_params[0] * NAVI10_CUSTOM_PARAMS_COUNT;
smu->custom_profile_params[idx] = 1;
@@ -3337,7 +3338,6 @@ static const struct pptable_funcs navi10_ppt_funcs = {
.set_xgmi_pstate = smu_v11_0_set_xgmi_pstate,
.gfx_off_control = smu_v11_0_gfx_off_control,
.register_irq_handler = smu_v11_0_register_irq_handler,
- .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme,
.get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc,
.get_bamaco_support = smu_v11_0_get_bamaco_support,
.baco_enter = navi10_baco_enter,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
index f799e489b481..c0de73b85353 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
@@ -1755,9 +1755,10 @@ static int sienna_cichlid_set_power_profile_mode(struct smu_context *smu,
return -ENOMEM;
}
if (custom_params && custom_params_max_idx) {
- if (custom_params_max_idx != SIENNA_CICHLID_CUSTOM_PARAMS_COUNT)
- return -EINVAL;
- if (custom_params[0] >= SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT)
+ if (!smu_cmn_custom_params_count_valid(custom_params_max_idx,
+ SIENNA_CICHLID_CUSTOM_PARAMS_COUNT) ||
+ !smu_cmn_custom_params_clock_valid(custom_params[0],
+ SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT))
return -EINVAL;
idx = custom_params[0] * SIENNA_CICHLID_CUSTOM_PARAMS_COUNT;
smu->custom_profile_params[idx] = 1;
@@ -3144,7 +3145,6 @@ static const struct pptable_funcs sienna_cichlid_ppt_funcs = {
.set_xgmi_pstate = smu_v11_0_set_xgmi_pstate,
.gfx_off_control = smu_v11_0_gfx_off_control,
.register_irq_handler = smu_v11_0_register_irq_handler,
- .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme,
.get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc,
.get_bamaco_support = smu_v11_0_get_bamaco_support,
.baco_enter = sienna_cichlid_baco_enter,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
index d68ceee16d8f..a889d846e9c5 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
@@ -192,81 +192,22 @@ int smu_v11_0_check_fw_status(struct smu_context *smu)
return -EIO;
}
-static int smu_v11_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size)
-{
- struct amdgpu_device *adev = smu->adev;
- uint32_t ppt_offset_bytes;
- const struct smc_firmware_header_v2_0 *v2;
-
- v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data;
-
- ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes);
- *size = le32_to_cpu(v2->ppt_size_bytes);
- *table = (uint8_t *)v2 + ppt_offset_bytes;
-
- return 0;
-}
-
-static int smu_v11_0_set_pptable_v2_1(struct smu_context *smu, void **table,
- uint32_t *size, uint32_t pptable_id)
-{
- struct amdgpu_device *adev = smu->adev;
- const struct smc_firmware_header_v2_1 *v2_1;
- struct smc_soft_pptable_entry *entries;
- uint32_t pptable_count = 0;
- int i = 0;
-
- v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data;
- entries = (struct smc_soft_pptable_entry *)
- ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset));
- pptable_count = le32_to_cpu(v2_1->pptable_count);
- for (i = 0; i < pptable_count; i++) {
- if (le32_to_cpu(entries[i].id) == pptable_id) {
- *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes));
- *size = le32_to_cpu(entries[i].ppt_size_bytes);
- break;
- }
- }
-
- if (i == pptable_count)
- return -EINVAL;
-
- return 0;
-}
-
int smu_v11_0_setup_pptable(struct smu_context *smu)
{
struct amdgpu_device *adev = smu->adev;
- const struct smc_firmware_header_v1_0 *hdr;
- int ret, index;
- uint32_t size = 0;
uint16_t atom_table_size;
uint8_t frev, crev;
+ uint32_t size = 0;
+ int ret, index;
void *table;
- uint16_t version_major, version_minor;
-
- if (!amdgpu_sriov_vf(adev)) {
- hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
- version_major = le16_to_cpu(hdr->header.header_version_major);
- version_minor = le16_to_cpu(hdr->header.header_version_minor);
- if (version_major == 2 && smu->smu_table.boot_values.pp_table_id > 0) {
- dev_info(adev->dev, "use driver provided pptable %d\n", smu->smu_table.boot_values.pp_table_id);
- switch (version_minor) {
- case 0:
- ret = smu_v11_0_set_pptable_v2_0(smu, &table, &size);
- break;
- case 1:
- ret = smu_v11_0_set_pptable_v2_1(smu, &table, &size,
- smu->smu_table.boot_values.pp_table_id);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- if (ret)
- return ret;
- goto out;
- }
+
+ if (!amdgpu_sriov_vf(adev) &&
+ smu->smu_table.boot_values.pp_table_id > 0) {
+ ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size,
+ smu->smu_table.boot_values.pp_table_id);
+ if (ret)
+ return ret;
+ goto out;
}
dev_info(adev->dev, "use vbios provided pptable\n");
@@ -1476,11 +1417,6 @@ int smu_v11_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu,
return 0;
}
-int smu_v11_0_set_azalia_d3_pme(struct smu_context *smu)
-{
- return smu_cmn_send_smc_msg(smu, SMU_MSG_BacoAudioD3PME, NULL);
-}
-
int smu_v11_0_baco_set_armd3_sequence(struct smu_context *smu,
enum smu_baco_seq baco_seq)
{
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
index 75335da224c7..2b011610e3c7 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
@@ -1444,7 +1444,6 @@ static int renoir_get_enabled_mask(struct smu_context *smu,
}
static const struct pptable_funcs renoir_ppt_funcs = {
- .set_power_state = NULL,
.emit_clk_levels = renoir_emit_clk_levels,
.get_current_power_state = renoir_get_current_power_state,
.dpm_set_vcn_enable = renoir_dpm_set_vcn_enable,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
index 9d8b1227388f..cd7bf36673cb 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
@@ -2004,7 +2004,6 @@ static const struct pptable_funcs aldebaran_ppt_funcs = {
.disable_thermal_alert = smu_v13_0_disable_thermal_alert,
.set_xgmi_pstate = smu_v13_0_set_xgmi_pstate,
.register_irq_handler = smu_v13_0_register_irq_handler,
- .set_azalia_d3_pme = smu_v13_0_set_azalia_d3_pme,
.get_max_sustainable_clocks_by_dc = smu_v13_0_get_max_sustainable_clocks_by_dc,
.get_bamaco_support = aldebaran_get_bamaco_support,
.get_dpm_ultimate_freq = aldebaran_get_dpm_ultimate_freq,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
index be9a7a32de99..4f10bce36756 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
@@ -218,7 +218,7 @@ int smu_v13_0_init_pptable_microcode(struct smu_context *smu)
if (!pptable_id)
return 0;
- ret = smu_v13_0_get_pptable_from_firmware(smu, &table, &size, pptable_id);
+ ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id);
if (ret)
return ret;
@@ -258,48 +258,6 @@ int smu_v13_0_check_fw_status(struct smu_context *smu)
return -EIO;
}
-static int smu_v13_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size)
-{
- struct amdgpu_device *adev = smu->adev;
- uint32_t ppt_offset_bytes;
- const struct smc_firmware_header_v2_0 *v2;
-
- v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data;
-
- ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes);
- *size = le32_to_cpu(v2->ppt_size_bytes);
- *table = (uint8_t *)v2 + ppt_offset_bytes;
-
- return 0;
-}
-
-static int smu_v13_0_set_pptable_v2_1(struct smu_context *smu, void **table,
- uint32_t *size, uint32_t pptable_id)
-{
- struct amdgpu_device *adev = smu->adev;
- const struct smc_firmware_header_v2_1 *v2_1;
- struct smc_soft_pptable_entry *entries;
- uint32_t pptable_count = 0;
- int i = 0;
-
- v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data;
- entries = (struct smc_soft_pptable_entry *)
- ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset));
- pptable_count = le32_to_cpu(v2_1->pptable_count);
- for (i = 0; i < pptable_count; i++) {
- if (le32_to_cpu(entries[i].id) == pptable_id) {
- *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes));
- *size = le32_to_cpu(entries[i].ppt_size_bytes);
- break;
- }
- }
-
- if (i == pptable_count)
- return -EINVAL;
-
- return 0;
-}
-
static int smu_v13_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size)
{
struct amdgpu_device *adev = smu->adev;
@@ -322,45 +280,6 @@ static int smu_v13_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl
return 0;
}
-int smu_v13_0_get_pptable_from_firmware(struct smu_context *smu,
- void **table,
- uint32_t *size,
- uint32_t pptable_id)
-{
- const struct smc_firmware_header_v1_0 *hdr;
- struct amdgpu_device *adev = smu->adev;
- uint16_t version_major, version_minor;
- int ret;
-
- hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
- if (!hdr)
- return -EINVAL;
-
- dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id);
-
- version_major = le16_to_cpu(hdr->header.header_version_major);
- version_minor = le16_to_cpu(hdr->header.header_version_minor);
- if (version_major != 2) {
- dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n",
- version_major, version_minor);
- return -EINVAL;
- }
-
- switch (version_minor) {
- case 0:
- ret = smu_v13_0_set_pptable_v2_0(smu, table, size);
- break;
- case 1:
- ret = smu_v13_0_set_pptable_v2_1(smu, table, size, pptable_id);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
int smu_v13_0_setup_pptable(struct smu_context *smu)
{
struct amdgpu_device *adev = smu->adev;
@@ -380,7 +299,7 @@ int smu_v13_0_setup_pptable(struct smu_context *smu)
if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1))
ret = smu_v13_0_get_pptable_from_vbios(smu, &table, &size);
else
- ret = smu_v13_0_get_pptable_from_firmware(smu, &table, &size, pptable_id);
+ ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id);
if (ret)
return ret;
@@ -1401,15 +1320,6 @@ int smu_v13_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu,
return 0;
}
-int smu_v13_0_set_azalia_d3_pme(struct smu_context *smu)
-{
- int ret = 0;
-
- ret = smu_cmn_send_smc_msg(smu, SMU_MSG_BacoAudioD3PME, NULL);
-
- return ret;
-}
-
static int smu_v13_0_wait_for_reset_complete(struct smu_context *smu,
uint64_t event_arg)
{
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
index acbd7046d8a5..4ce1429cf57b 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@ -2619,9 +2619,10 @@ static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu,
return -ENOMEM;
}
if (custom_params && custom_params_max_idx) {
- if (custom_params_max_idx != SMU_13_0_0_CUSTOM_PARAMS_COUNT)
- return -EINVAL;
- if (custom_params[0] >= SMU_13_0_0_CUSTOM_PARAMS_CLOCK_COUNT)
+ if (!smu_cmn_custom_params_count_valid(custom_params_max_idx,
+ SMU_13_0_0_CUSTOM_PARAMS_COUNT) ||
+ !smu_cmn_custom_params_clock_valid(custom_params[0],
+ SMU_13_0_0_CUSTOM_PARAMS_CLOCK_COUNT))
return -EINVAL;
idx = custom_params[0] * SMU_13_0_0_CUSTOM_PARAMS_COUNT;
smu->custom_profile_params[idx] = 1;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
index fe929bd89058..dea27fcb2b20 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
@@ -1042,7 +1042,7 @@ static int smu_v13_0_12_get_badpage_count(struct amdgpu_device *adev, uint32_t *
/* eeprom is not ready */
if (ret != -EBUSY)
return ret;
- mdelay(10);
+ usleep_range(10000, 15000);
now = (uint64_t)ktime_to_ms(ktime_get());
} while (now < end);
@@ -1137,16 +1137,10 @@ static const struct ras_eeprom_smu_funcs smu_v13_0_12_eeprom_smu_funcs = {
static void smu_v13_0_12_ras_smu_feature_flags(struct amdgpu_device *adev, uint64_t *flags)
{
- struct smu_context *smu = adev->powerplay.pp_handle;
-
if (!flags)
return;
*flags = 0ULL;
-
- if (smu_v13_0_6_cap_supported(smu, SMU_CAP(RAS_EEPROM)))
- *flags |= RAS_SMU_FEATURE_BIT__RAS_EEPROM;
-
}
const struct ras_smu_drv smu_v13_0_12_ras_smu_drv = {
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
index b12388134489..334c92a28994 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
@@ -44,8 +44,6 @@
#include "amdgpu_xgmi.h"
#include <linux/pci.h>
#include "amdgpu_ras.h"
-#include "amdgpu_mca.h"
-#include "amdgpu_aca.h"
#include "smu_cmn.h"
#include "mp/mp_13_0_6_offset.h"
#include "mp/mp_13_0_6_sh_mask.h"
@@ -99,25 +97,6 @@ static const struct smu_feature_bits smu_v13_0_6_dpm_features = {
#define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK 0xE0
#define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT 0x5
#define LINK_SPEED_MAX 4
-#define MCA_BANK_IPID(_ip, _hwid, _type) \
- [AMDGPU_MCA_IP_##_ip] = { .hwid = _hwid, .mcatype = _type, }
-
-struct mca_bank_ipid {
- enum amdgpu_mca_ip ip;
- uint16_t hwid;
- uint16_t mcatype;
-};
-
-struct mca_ras_info {
- enum amdgpu_ras_block blkid;
- enum amdgpu_mca_ip ip;
- int *err_code_array;
- int err_code_count;
- int (*get_err_count)(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count);
- bool (*bank_is_valid)(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry);
-};
#define P2S_TABLE_ID_A 0x50325341
#define P2S_TABLE_ID_X 0x50325358
@@ -1943,17 +1922,6 @@ static int smu_v13_0_6_notify_unload(struct smu_context *smu)
return 0;
}
-static int smu_v13_0_6_mca_set_debug_mode(struct smu_context *smu, bool enable)
-{
- /* NOTE: this ClearMcaOnRead message is only supported for smu version 85.72.0 or higher */
- if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(MCA_DEBUG_MODE)))
- return 0;
-
- return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_ClearMcaOnRead,
- enable ? 0 : ClearMcaOnRead_UE_FLAG_MASK | ClearMcaOnRead_CE_POLL_MASK,
- NULL);
-}
-
static int smu_v13_0_6_system_features_control(struct smu_context *smu,
bool enable)
{
@@ -3299,622 +3267,6 @@ static int smu_v13_0_6_post_init(struct smu_context *smu)
return 0;
}
-static int mca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable)
-{
- struct smu_context *smu = adev->powerplay.pp_handle;
-
- return smu_v13_0_6_mca_set_debug_mode(smu, enable);
-}
-
-static int smu_v13_0_6_get_valid_mca_count(struct smu_context *smu, enum amdgpu_mca_error_type type, uint32_t *count)
-{
- uint32_t msg;
- int ret;
-
- if (!count)
- return -EINVAL;
-
- switch (type) {
- case AMDGPU_MCA_ERROR_TYPE_UE:
- msg = SMU_MSG_QueryValidMcaCount;
- break;
- case AMDGPU_MCA_ERROR_TYPE_CE:
- msg = SMU_MSG_QueryValidMcaCeCount;
- break;
- default:
- return -EINVAL;
- }
-
- ret = smu_cmn_send_smc_msg(smu, msg, count);
- if (ret) {
- *count = 0;
- return ret;
- }
-
- return 0;
-}
-
-static int __smu_v13_0_6_mca_dump_bank(struct smu_context *smu, enum amdgpu_mca_error_type type,
- int idx, int offset, uint32_t *val)
-{
- uint32_t msg, param;
-
- switch (type) {
- case AMDGPU_MCA_ERROR_TYPE_UE:
- msg = SMU_MSG_McaBankDumpDW;
- break;
- case AMDGPU_MCA_ERROR_TYPE_CE:
- msg = SMU_MSG_McaBankCeDumpDW;
- break;
- default:
- return -EINVAL;
- }
-
- param = ((idx & 0xffff) << 16) | (offset & 0xfffc);
-
- return smu_cmn_send_smc_msg_with_param(smu, msg, param, val);
-}
-
-static int smu_v13_0_6_mca_dump_bank(struct smu_context *smu, enum amdgpu_mca_error_type type,
- int idx, int offset, uint32_t *val, int count)
-{
- int ret, i;
-
- if (!val)
- return -EINVAL;
-
- for (i = 0; i < count; i++) {
- ret = __smu_v13_0_6_mca_dump_bank(smu, type, idx, offset + (i << 2), &val[i]);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static const struct mca_bank_ipid smu_v13_0_6_mca_ipid_table[AMDGPU_MCA_IP_COUNT] = {
- MCA_BANK_IPID(UMC, 0x96, 0x0),
- MCA_BANK_IPID(SMU, 0x01, 0x1),
- MCA_BANK_IPID(MP5, 0x01, 0x2),
- MCA_BANK_IPID(PCS_XGMI, 0x50, 0x0),
-};
-
-static void mca_bank_entry_info_decode(struct mca_bank_entry *entry, struct mca_bank_info *info)
-{
- u64 ipid = entry->regs[MCA_REG_IDX_IPID];
- u32 instidhi, instid;
-
- /* NOTE: All MCA IPID register share the same format,
- * so the driver can share the MCMP1 register header file.
- * */
-
- info->hwid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, HardwareID);
- info->mcatype = REG_GET_FIELD(ipid, MCMP1_IPIDT0, McaType);
-
- /*
- * Unfied DieID Format: SAASS. A:AID, S:Socket.
- * Unfied DieID[4] = InstanceId[0]
- * Unfied DieID[0:3] = InstanceIdHi[0:3]
- */
- instidhi = REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdHi);
- instid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdLo);
- info->aid = ((instidhi >> 2) & 0x03);
- info->socket_id = ((instid & 0x1) << 2) | (instidhi & 0x03);
-}
-
-static int mca_bank_read_reg(struct amdgpu_device *adev, enum amdgpu_mca_error_type type,
- int idx, int reg_idx, uint64_t *val)
-{
- struct smu_context *smu = adev->powerplay.pp_handle;
- uint32_t data[2] = {0, 0};
- int ret;
-
- if (!val || reg_idx >= MCA_REG_IDX_COUNT)
- return -EINVAL;
-
- ret = smu_v13_0_6_mca_dump_bank(smu, type, idx, reg_idx * 8, data, ARRAY_SIZE(data));
- if (ret)
- return ret;
-
- *val = (uint64_t)data[1] << 32 | data[0];
-
- dev_dbg(adev->dev, "mca read bank reg: type:%s, index: %d, reg_idx: %d, val: 0x%016llx\n",
- type == AMDGPU_MCA_ERROR_TYPE_UE ? "UE" : "CE", idx, reg_idx, *val);
-
- return 0;
-}
-
-static int mca_get_mca_entry(struct amdgpu_device *adev, enum amdgpu_mca_error_type type,
- int idx, struct mca_bank_entry *entry)
-{
- int i, ret;
-
- /* NOTE: populated all mca register by default */
- for (i = 0; i < ARRAY_SIZE(entry->regs); i++) {
- ret = mca_bank_read_reg(adev, type, idx, i, &entry->regs[i]);
- if (ret)
- return ret;
- }
-
- entry->idx = idx;
- entry->type = type;
-
- mca_bank_entry_info_decode(entry, &entry->info);
-
- return 0;
-}
-
-static int mca_decode_ipid_to_hwip(uint64_t val)
-{
- const struct mca_bank_ipid *ipid;
- uint16_t hwid, mcatype;
- int i;
-
- hwid = REG_GET_FIELD(val, MCMP1_IPIDT0, HardwareID);
- mcatype = REG_GET_FIELD(val, MCMP1_IPIDT0, McaType);
-
- for (i = 0; i < ARRAY_SIZE(smu_v13_0_6_mca_ipid_table); i++) {
- ipid = &smu_v13_0_6_mca_ipid_table[i];
-
- if (!ipid->hwid)
- continue;
-
- if (ipid->hwid == hwid && ipid->mcatype == mcatype)
- return i;
- }
-
- return AMDGPU_MCA_IP_UNKNOW;
-}
-
-static int mca_umc_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count)
-{
- uint64_t status0;
- uint32_t ext_error_code;
- uint32_t odecc_err_cnt;
-
- status0 = entry->regs[MCA_REG_IDX_STATUS];
- ext_error_code = MCA_REG__STATUS__ERRORCODEEXT(status0);
- odecc_err_cnt = MCA_REG__MISC0__ERRCNT(entry->regs[MCA_REG_IDX_MISC0]);
-
- if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) {
- *count = 0;
- return 0;
- }
-
- if (umc_v12_0_is_deferred_error(adev, status0) ||
- umc_v12_0_is_uncorrectable_error(adev, status0) ||
- umc_v12_0_is_correctable_error(adev, status0))
- *count = (ext_error_code == 0) ? odecc_err_cnt : 1;
-
- amdgpu_umc_update_ecc_status(adev,
- entry->regs[MCA_REG_IDX_STATUS],
- entry->regs[MCA_REG_IDX_IPID],
- entry->regs[MCA_REG_IDX_ADDR]);
-
- return 0;
-}
-
-static int mca_pcs_xgmi_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry,
- uint32_t *count)
-{
- u32 ext_error_code;
- u32 err_cnt;
-
- ext_error_code = MCA_REG__STATUS__ERRORCODEEXT(entry->regs[MCA_REG_IDX_STATUS]);
- err_cnt = MCA_REG__MISC0__ERRCNT(entry->regs[MCA_REG_IDX_MISC0]);
-
- if (type == AMDGPU_MCA_ERROR_TYPE_UE &&
- (ext_error_code == 0 || ext_error_code == 9))
- *count = err_cnt;
- else if (type == AMDGPU_MCA_ERROR_TYPE_CE && ext_error_code == 6)
- *count = err_cnt;
-
- return 0;
-}
-
-static bool mca_smu_check_error_code(struct amdgpu_device *adev, const struct mca_ras_info *mca_ras,
- uint32_t errcode)
-{
- int i;
-
- if (!mca_ras->err_code_count || !mca_ras->err_code_array)
- return true;
-
- for (i = 0; i < mca_ras->err_code_count; i++) {
- if (errcode == mca_ras->err_code_array[i])
- return true;
- }
-
- return false;
-}
-
-static int mca_gfx_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count)
-{
- uint64_t status0, misc0;
-
- status0 = entry->regs[MCA_REG_IDX_STATUS];
- if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) {
- *count = 0;
- return 0;
- }
-
- if (type == AMDGPU_MCA_ERROR_TYPE_UE &&
- REG_GET_FIELD(status0, MCMP1_STATUST0, UC) == 1 &&
- REG_GET_FIELD(status0, MCMP1_STATUST0, PCC) == 1) {
- *count = 1;
- return 0;
- } else {
- misc0 = entry->regs[MCA_REG_IDX_MISC0];
- *count = REG_GET_FIELD(misc0, MCMP1_MISC0T0, ErrCnt);
- }
-
- return 0;
-}
-
-static int mca_smu_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count)
-{
- uint64_t status0, misc0;
-
- status0 = entry->regs[MCA_REG_IDX_STATUS];
- if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) {
- *count = 0;
- return 0;
- }
-
- if (type == AMDGPU_MCA_ERROR_TYPE_UE &&
- REG_GET_FIELD(status0, MCMP1_STATUST0, UC) == 1 &&
- REG_GET_FIELD(status0, MCMP1_STATUST0, PCC) == 1) {
- if (count)
- *count = 1;
- return 0;
- }
-
- misc0 = entry->regs[MCA_REG_IDX_MISC0];
- *count = REG_GET_FIELD(misc0, MCMP1_MISC0T0, ErrCnt);
-
- return 0;
-}
-
-static bool mca_gfx_smu_bank_is_valid(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry)
-{
- uint32_t instlo;
-
- instlo = REG_GET_FIELD(entry->regs[MCA_REG_IDX_IPID], MCMP1_IPIDT0, InstanceIdLo);
- instlo &= GENMASK(31, 1);
- switch (instlo) {
- case 0x36430400: /* SMNAID XCD 0 */
- case 0x38430400: /* SMNAID XCD 1 */
- case 0x40430400: /* SMNXCD XCD 0, NOTE: FIXME: fix this error later */
- return true;
- default:
- return false;
- }
-
- return false;
-};
-
-static bool mca_smu_bank_is_valid(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry)
-{
- struct smu_context *smu = adev->powerplay.pp_handle;
- uint32_t errcode, instlo;
-
- instlo = REG_GET_FIELD(entry->regs[MCA_REG_IDX_IPID], MCMP1_IPIDT0, InstanceIdLo);
- instlo &= GENMASK(31, 1);
- if (instlo != 0x03b30400)
- return false;
-
- if (smu_v13_0_6_cap_supported(smu, SMU_CAP(ACA_SYND))) {
- errcode = MCA_REG__SYND__ERRORINFORMATION(entry->regs[MCA_REG_IDX_SYND]);
- errcode &= 0xff;
- } else {
- errcode = REG_GET_FIELD(entry->regs[MCA_REG_IDX_STATUS], MCMP1_STATUST0, ErrorCode);
- }
-
- return mca_smu_check_error_code(adev, mca_ras, errcode);
-}
-
-static int sdma_err_codes[] = { CODE_SDMA0, CODE_SDMA1, CODE_SDMA2, CODE_SDMA3 };
-static int mmhub_err_codes[] = {
- CODE_DAGB0, CODE_DAGB0 + 1, CODE_DAGB0 + 2, CODE_DAGB0 + 3, CODE_DAGB0 + 4, /* DAGB0-4 */
- CODE_EA0, CODE_EA0 + 1, CODE_EA0 + 2, CODE_EA0 + 3, CODE_EA0 + 4, /* MMEA0-4*/
- CODE_VML2, CODE_VML2_WALKER, CODE_MMCANE,
-};
-
-static int vcn_err_codes[] = {
- CODE_VIDD, CODE_VIDV,
-};
-static int jpeg_err_codes[] = {
- CODE_JPEG0S, CODE_JPEG0D, CODE_JPEG1S, CODE_JPEG1D,
- CODE_JPEG2S, CODE_JPEG2D, CODE_JPEG3S, CODE_JPEG3D,
- CODE_JPEG4S, CODE_JPEG4D, CODE_JPEG5S, CODE_JPEG5D,
- CODE_JPEG6S, CODE_JPEG6D, CODE_JPEG7S, CODE_JPEG7D,
-};
-
-static const struct mca_ras_info mca_ras_table[] = {
- {
- .blkid = AMDGPU_RAS_BLOCK__UMC,
- .ip = AMDGPU_MCA_IP_UMC,
- .get_err_count = mca_umc_mca_get_err_count,
- }, {
- .blkid = AMDGPU_RAS_BLOCK__GFX,
- .ip = AMDGPU_MCA_IP_SMU,
- .get_err_count = mca_gfx_mca_get_err_count,
- .bank_is_valid = mca_gfx_smu_bank_is_valid,
- }, {
- .blkid = AMDGPU_RAS_BLOCK__SDMA,
- .ip = AMDGPU_MCA_IP_SMU,
- .err_code_array = sdma_err_codes,
- .err_code_count = ARRAY_SIZE(sdma_err_codes),
- .get_err_count = mca_smu_mca_get_err_count,
- .bank_is_valid = mca_smu_bank_is_valid,
- }, {
- .blkid = AMDGPU_RAS_BLOCK__MMHUB,
- .ip = AMDGPU_MCA_IP_SMU,
- .err_code_array = mmhub_err_codes,
- .err_code_count = ARRAY_SIZE(mmhub_err_codes),
- .get_err_count = mca_smu_mca_get_err_count,
- .bank_is_valid = mca_smu_bank_is_valid,
- }, {
- .blkid = AMDGPU_RAS_BLOCK__XGMI_WAFL,
- .ip = AMDGPU_MCA_IP_PCS_XGMI,
- .get_err_count = mca_pcs_xgmi_mca_get_err_count,
- }, {
- .blkid = AMDGPU_RAS_BLOCK__VCN,
- .ip = AMDGPU_MCA_IP_SMU,
- .err_code_array = vcn_err_codes,
- .err_code_count = ARRAY_SIZE(vcn_err_codes),
- .get_err_count = mca_smu_mca_get_err_count,
- .bank_is_valid = mca_smu_bank_is_valid,
- }, {
- .blkid = AMDGPU_RAS_BLOCK__JPEG,
- .ip = AMDGPU_MCA_IP_SMU,
- .err_code_array = jpeg_err_codes,
- .err_code_count = ARRAY_SIZE(jpeg_err_codes),
- .get_err_count = mca_smu_mca_get_err_count,
- .bank_is_valid = mca_smu_bank_is_valid,
- },
-};
-
-static const struct mca_ras_info *mca_get_mca_ras_info(struct amdgpu_device *adev, enum amdgpu_ras_block blkid)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mca_ras_table); i++) {
- if (mca_ras_table[i].blkid == blkid)
- return &mca_ras_table[i];
- }
-
- return NULL;
-}
-
-static int mca_get_valid_mca_count(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, uint32_t *count)
-{
- struct smu_context *smu = adev->powerplay.pp_handle;
- int ret;
-
- switch (type) {
- case AMDGPU_MCA_ERROR_TYPE_UE:
- case AMDGPU_MCA_ERROR_TYPE_CE:
- ret = smu_v13_0_6_get_valid_mca_count(smu, type, count);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static bool mca_bank_is_valid(struct amdgpu_device *adev, const struct mca_ras_info *mca_ras,
- enum amdgpu_mca_error_type type, struct mca_bank_entry *entry)
-{
- if (mca_decode_ipid_to_hwip(entry->regs[MCA_REG_IDX_IPID]) != mca_ras->ip)
- return false;
-
- if (mca_ras->bank_is_valid)
- return mca_ras->bank_is_valid(mca_ras, adev, type, entry);
-
- return true;
-}
-
-static int mca_smu_parse_mca_error_count(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type,
- struct mca_bank_entry *entry, uint32_t *count)
-{
- const struct mca_ras_info *mca_ras;
-
- if (!entry || !count)
- return -EINVAL;
-
- mca_ras = mca_get_mca_ras_info(adev, blk);
- if (!mca_ras)
- return -EOPNOTSUPP;
-
- if (!mca_bank_is_valid(adev, mca_ras, type, entry)) {
- *count = 0;
- return 0;
- }
-
- return mca_ras->get_err_count(mca_ras, adev, type, entry, count);
-}
-
-static int mca_smu_get_mca_entry(struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, int idx, struct mca_bank_entry *entry)
-{
- return mca_get_mca_entry(adev, type, idx, entry);
-}
-
-static int mca_smu_get_valid_mca_count(struct amdgpu_device *adev,
- enum amdgpu_mca_error_type type, uint32_t *count)
-{
- return mca_get_valid_mca_count(adev, type, count);
-}
-
-static const struct amdgpu_mca_smu_funcs smu_v13_0_6_mca_smu_funcs = {
- .max_ue_count = 12,
- .max_ce_count = 12,
- .mca_set_debug_mode = mca_smu_set_debug_mode,
- .mca_parse_mca_error_count = mca_smu_parse_mca_error_count,
- .mca_get_mca_entry = mca_smu_get_mca_entry,
- .mca_get_valid_mca_count = mca_smu_get_valid_mca_count,
-};
-
-static int aca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable)
-{
- struct smu_context *smu = adev->powerplay.pp_handle;
-
- return smu_v13_0_6_mca_set_debug_mode(smu, enable);
-}
-
-static int smu_v13_0_6_get_valid_aca_count(struct smu_context *smu, enum aca_smu_type type, u32 *count)
-{
- uint32_t msg;
- int ret;
-
- if (!count)
- return -EINVAL;
-
- switch (type) {
- case ACA_SMU_TYPE_UE:
- msg = SMU_MSG_QueryValidMcaCount;
- break;
- case ACA_SMU_TYPE_CE:
- msg = SMU_MSG_QueryValidMcaCeCount;
- break;
- default:
- return -EINVAL;
- }
-
- ret = smu_cmn_send_smc_msg(smu, msg, count);
- if (ret) {
- *count = 0;
- return ret;
- }
-
- return 0;
-}
-
-static int aca_smu_get_valid_aca_count(struct amdgpu_device *adev,
- enum aca_smu_type type, u32 *count)
-{
- struct smu_context *smu = adev->powerplay.pp_handle;
- int ret;
-
- switch (type) {
- case ACA_SMU_TYPE_UE:
- case ACA_SMU_TYPE_CE:
- ret = smu_v13_0_6_get_valid_aca_count(smu, type, count);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
-static int __smu_v13_0_6_aca_bank_dump(struct smu_context *smu, enum aca_smu_type type,
- int idx, int offset, u32 *val)
-{
- uint32_t msg, param;
-
- switch (type) {
- case ACA_SMU_TYPE_UE:
- msg = SMU_MSG_McaBankDumpDW;
- break;
- case ACA_SMU_TYPE_CE:
- msg = SMU_MSG_McaBankCeDumpDW;
- break;
- default:
- return -EINVAL;
- }
-
- param = ((idx & 0xffff) << 16) | (offset & 0xfffc);
-
- return smu_cmn_send_smc_msg_with_param(smu, msg, param, (uint32_t *)val);
-}
-
-static int smu_v13_0_6_aca_bank_dump(struct smu_context *smu, enum aca_smu_type type,
- int idx, int offset, u32 *val, int count)
-{
- int ret, i;
-
- if (!val)
- return -EINVAL;
-
- for (i = 0; i < count; i++) {
- ret = __smu_v13_0_6_aca_bank_dump(smu, type, idx, offset + (i << 2), &val[i]);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int aca_bank_read_reg(struct amdgpu_device *adev, enum aca_smu_type type,
- int idx, int reg_idx, u64 *val)
-{
- struct smu_context *smu = adev->powerplay.pp_handle;
- u32 data[2] = {0, 0};
- int ret;
-
- if (!val || reg_idx >= ACA_REG_IDX_COUNT)
- return -EINVAL;
-
- ret = smu_v13_0_6_aca_bank_dump(smu, type, idx, reg_idx * 8, data, ARRAY_SIZE(data));
- if (ret)
- return ret;
-
- *val = (u64)data[1] << 32 | data[0];
-
- dev_dbg(adev->dev, "mca read bank reg: type:%s, index: %d, reg_idx: %d, val: 0x%016llx\n",
- type == ACA_SMU_TYPE_UE ? "UE" : "CE", idx, reg_idx, *val);
-
- return 0;
-}
-
-static int aca_smu_get_valid_aca_bank(struct amdgpu_device *adev,
- enum aca_smu_type type, int idx, struct aca_bank *bank)
-{
- int i, ret, count;
-
- count = min_t(int, 16, ARRAY_SIZE(bank->regs));
- for (i = 0; i < count; i++) {
- ret = aca_bank_read_reg(adev, type, idx, i, &bank->regs[i]);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int aca_smu_parse_error_code(struct amdgpu_device *adev, struct aca_bank *bank)
-{
- struct smu_context *smu = adev->powerplay.pp_handle;
- int error_code;
-
- if (smu_v13_0_6_cap_supported(smu, SMU_CAP(ACA_SYND)))
- error_code = ACA_REG__SYND__ERRORINFORMATION(bank->regs[ACA_REG_IDX_SYND]);
- else
- error_code = ACA_REG__STATUS__ERRORCODE(bank->regs[ACA_REG_IDX_STATUS]);
-
- return error_code & 0xff;
-}
-
-static const struct aca_smu_funcs smu_v13_0_6_aca_smu_funcs = {
- .max_ue_bank_count = 12,
- .max_ce_bank_count = 12,
- .set_debug_mode = aca_smu_set_debug_mode,
- .get_valid_aca_count = aca_smu_get_valid_aca_count,
- .get_valid_aca_bank = aca_smu_get_valid_aca_bank,
- .parse_error_code = aca_smu_parse_error_code,
-};
-
static void smu_v13_0_6_set_temp_funcs(struct smu_context *smu)
{
smu->smu_temp.temp_funcs = (amdgpu_ip_version(smu->adev, MP1_HWIP, 0)
@@ -3929,9 +3281,6 @@ static int smu_v13_0_6_get_ras_smu_drv(struct smu_context *smu, const struct ras
if (amdgpu_sriov_vf(smu->adev))
return -EOPNOTSUPP;
- if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_HROM_EN_BIT))
- smu_v13_0_6_cap_set(smu, SMU_CAP(RAS_EEPROM));
-
switch (amdgpu_ip_version(smu->adev, MP1_HWIP, 0)) {
case IP_VERSION(13, 0, 12):
*ras_smu_drv = &smu_v13_0_12_ras_smu_drv;
@@ -4020,7 +3369,5 @@ void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu)
smu->smc_fw_caps |= SMU_FW_CAP_RAS_PRI;
smu_v13_0_init_msg_ctl(smu, message_map);
smu_v13_0_6_set_temp_funcs(smu);
- amdgpu_mca_smu_init_funcs(smu->adev, &smu_v13_0_6_mca_smu_funcs);
- amdgpu_aca_set_smu_funcs(smu->adev, &smu_v13_0_6_aca_smu_funcs);
}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
index 42c9ceeb4f7d..5f23f2e7f401 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
@@ -2574,9 +2574,10 @@ static int smu_v13_0_7_set_power_profile_mode(struct smu_context *smu,
return -ENOMEM;
}
if (custom_params && custom_params_max_idx) {
- if (custom_params_max_idx != SMU_13_0_7_CUSTOM_PARAMS_COUNT)
- return -EINVAL;
- if (custom_params[0] >= SMU_13_0_7_CUSTOM_PARAMS_CLOCK_COUNT)
+ if (!smu_cmn_custom_params_count_valid(custom_params_max_idx,
+ SMU_13_0_7_CUSTOM_PARAMS_COUNT) ||
+ !smu_cmn_custom_params_clock_valid(custom_params[0],
+ SMU_13_0_7_CUSTOM_PARAMS_CLOCK_COUNT))
return -EINVAL;
idx = custom_params[0] * SMU_13_0_7_CUSTOM_PARAMS_COUNT;
smu->custom_profile_params[idx] = 1;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
index d0a8df1aa6b6..2a0c7cde938d 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
@@ -194,7 +194,7 @@ int smu_v14_0_init_pptable_microcode(struct smu_context *smu)
if (!pptable_id)
return 0;
- ret = smu_v14_0_get_pptable_from_firmware(smu, &table, &size, pptable_id);
+ ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id);
if (ret)
return ret;
@@ -229,48 +229,6 @@ int smu_v14_0_check_fw_status(struct smu_context *smu)
return -EIO;
}
-static int smu_v14_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size)
-{
- struct amdgpu_device *adev = smu->adev;
- uint32_t ppt_offset_bytes;
- const struct smc_firmware_header_v2_0 *v2;
-
- v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data;
-
- ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes);
- *size = le32_to_cpu(v2->ppt_size_bytes);
- *table = (uint8_t *)v2 + ppt_offset_bytes;
-
- return 0;
-}
-
-static int smu_v14_0_set_pptable_v2_1(struct smu_context *smu, void **table,
- uint32_t *size, uint32_t pptable_id)
-{
- struct amdgpu_device *adev = smu->adev;
- const struct smc_firmware_header_v2_1 *v2_1;
- struct smc_soft_pptable_entry *entries;
- uint32_t pptable_count = 0;
- int i = 0;
-
- v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data;
- entries = (struct smc_soft_pptable_entry *)
- ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset));
- pptable_count = le32_to_cpu(v2_1->pptable_count);
- for (i = 0; i < pptable_count; i++) {
- if (le32_to_cpu(entries[i].id) == pptable_id) {
- *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes));
- *size = le32_to_cpu(entries[i].ppt_size_bytes);
- break;
- }
- }
-
- if (i == pptable_count)
- return -EINVAL;
-
- return 0;
-}
-
static int smu_v14_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size)
{
struct amdgpu_device *adev = smu->adev;
@@ -293,45 +251,6 @@ static int smu_v14_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl
return 0;
}
-int smu_v14_0_get_pptable_from_firmware(struct smu_context *smu,
- void **table,
- uint32_t *size,
- uint32_t pptable_id)
-{
- const struct smc_firmware_header_v1_0 *hdr;
- struct amdgpu_device *adev = smu->adev;
- uint16_t version_major, version_minor;
- int ret;
-
- hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
- if (!hdr)
- return -EINVAL;
-
- dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id);
-
- version_major = le16_to_cpu(hdr->header.header_version_major);
- version_minor = le16_to_cpu(hdr->header.header_version_minor);
- if (version_major != 2) {
- dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n",
- version_major, version_minor);
- return -EINVAL;
- }
-
- switch (version_minor) {
- case 0:
- ret = smu_v14_0_set_pptable_v2_0(smu, table, size);
- break;
- case 1:
- ret = smu_v14_0_set_pptable_v2_1(smu, table, size, pptable_id);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
int smu_v14_0_setup_pptable(struct smu_context *smu)
{
struct amdgpu_device *adev = smu->adev;
@@ -351,7 +270,7 @@ int smu_v14_0_setup_pptable(struct smu_context *smu)
if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1))
ret = smu_v14_0_get_pptable_from_vbios(smu, &table, &size);
else
- ret = smu_v14_0_get_pptable_from_firmware(smu, &table, &size, pptable_id);
+ ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
index fdc1456b885c..d6cf643205ab 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
@@ -1828,9 +1828,10 @@ static int smu_v14_0_2_set_power_profile_mode(struct smu_context *smu,
return -ENOMEM;
}
if (custom_params && custom_params_max_idx) {
- if (custom_params_max_idx != SMU_14_0_2_CUSTOM_PARAMS_COUNT)
- return -EINVAL;
- if (custom_params[0] >= SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT)
+ if (!smu_cmn_custom_params_count_valid(custom_params_max_idx,
+ SMU_14_0_2_CUSTOM_PARAMS_COUNT) ||
+ !smu_cmn_custom_params_clock_valid(custom_params[0],
+ SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT))
return -EINVAL;
idx = custom_params[0] * SMU_14_0_2_CUSTOM_PARAMS_COUNT;
smu->custom_profile_params[idx] = 1;
@@ -2889,8 +2890,6 @@ static const struct pptable_funcs smu_v14_0_2_ppt_funcs = {
.deep_sleep_control = smu_v14_0_deep_sleep_control,
.gfx_ulv_control = smu_v14_0_gfx_ulv_control,
.get_bamaco_support = smu_v14_0_get_bamaco_support,
- .baco_get_state = smu_v14_0_baco_get_state,
- .baco_set_state = smu_v14_0_baco_set_state,
.baco_enter = smu_v14_0_2_baco_enter,
.baco_exit = smu_v14_0_2_baco_exit,
.mode1_reset_is_support = smu_v14_0_2_is_mode1_reset_supported,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c
index a1318409e4b5..f3fb6ed4bc95 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c
@@ -174,7 +174,7 @@ int smu_v15_0_init_pptable_microcode(struct smu_context *smu)
if (!pptable_id)
return 0;
- ret = smu_v15_0_get_pptable_from_firmware(smu, &table, &size, pptable_id);
+ ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id);
if (ret)
return ret;
@@ -207,48 +207,6 @@ int smu_v15_0_check_fw_status(struct smu_context *smu)
return -EIO;
}
-static int smu_v15_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size)
-{
- struct amdgpu_device *adev = smu->adev;
- uint32_t ppt_offset_bytes;
- const struct smc_firmware_header_v2_0 *v2;
-
- v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data;
-
- ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes);
- *size = le32_to_cpu(v2->ppt_size_bytes);
- *table = (uint8_t *)v2 + ppt_offset_bytes;
-
- return 0;
-}
-
-static int smu_v15_0_set_pptable_v2_1(struct smu_context *smu, void **table,
- uint32_t *size, uint32_t pptable_id)
-{
- struct amdgpu_device *adev = smu->adev;
- const struct smc_firmware_header_v2_1 *v2_1;
- struct smc_soft_pptable_entry *entries;
- uint32_t pptable_count = 0;
- int i = 0;
-
- v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data;
- entries = (struct smc_soft_pptable_entry *)
- ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset));
- pptable_count = le32_to_cpu(v2_1->pptable_count);
- for (i = 0; i < pptable_count; i++) {
- if (le32_to_cpu(entries[i].id) == pptable_id) {
- *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes));
- *size = le32_to_cpu(entries[i].ppt_size_bytes);
- break;
- }
- }
-
- if (i == pptable_count)
- return -EINVAL;
-
- return 0;
-}
-
static int smu_v15_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size)
{
struct amdgpu_device *adev = smu->adev;
@@ -271,45 +229,6 @@ static int smu_v15_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl
return 0;
}
-int smu_v15_0_get_pptable_from_firmware(struct smu_context *smu,
- void **table,
- uint32_t *size,
- uint32_t pptable_id)
-{
- const struct smc_firmware_header_v1_0 *hdr;
- struct amdgpu_device *adev = smu->adev;
- uint16_t version_major, version_minor;
- int ret;
-
- hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
- if (!hdr)
- return -EINVAL;
-
- dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id);
-
- version_major = le16_to_cpu(hdr->header.header_version_major);
- version_minor = le16_to_cpu(hdr->header.header_version_minor);
- if (version_major != 2) {
- dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n",
- version_major, version_minor);
- return -EINVAL;
- }
-
- switch (version_minor) {
- case 0:
- ret = smu_v15_0_set_pptable_v2_0(smu, table, size);
- break;
- case 1:
- ret = smu_v15_0_set_pptable_v2_1(smu, table, size, pptable_id);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-}
-
int smu_v15_0_setup_pptable(struct smu_context *smu)
{
struct amdgpu_device *adev = smu->adev;
@@ -329,7 +248,7 @@ int smu_v15_0_setup_pptable(struct smu_context *smu)
if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1))
ret = smu_v15_0_get_pptable_from_vbios(smu, &table, &size);
else
- ret = smu_v15_0_get_pptable_from_firmware(smu, &table, &size, pptable_id);
+ ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
index d365f06ac1ac..2bd3ea17e789 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
@@ -1544,3 +1544,143 @@ int smu_cmn_dpm_pcie_width_idx(int width)
return ret;
}
+
+static int smu_cmn_get_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size)
+{
+ const struct smc_firmware_header_v2_0 *v2;
+ struct amdgpu_device *adev = smu->adev;
+ size_t fw_size = adev->pm.fw->size;
+ uint32_t ppt_offset_bytes;
+ uint32_t ppt_size_bytes;
+
+ if (fw_size < sizeof(*v2)) {
+ dev_err(adev->dev,
+ "SMC firmware too small for v2.0 header: %zu < %zu\n",
+ fw_size, sizeof(*v2));
+ return -EINVAL;
+ }
+
+ v2 = (const struct smc_firmware_header_v2_0 *)adev->pm.fw->data;
+
+ ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes);
+ ppt_size_bytes = le32_to_cpu(v2->ppt_size_bytes);
+
+ if (ppt_offset_bytes > fw_size ||
+ ppt_size_bytes > fw_size - ppt_offset_bytes) {
+ dev_err(adev->dev,
+ "pptable v2.0 exceeds firmware binary: offset %u + size %u > %zu\n",
+ ppt_offset_bytes, ppt_size_bytes, fw_size);
+ return -EINVAL;
+ }
+
+ *size = ppt_size_bytes;
+ *table = (uint8_t *)v2 + ppt_offset_bytes;
+
+ return 0;
+}
+
+static int smu_cmn_get_pptable_v2_1(struct smu_context *smu, void **table,
+ uint32_t *size, uint32_t pptable_id)
+{
+ const struct smc_firmware_header_v2_1 *v2_1;
+ struct amdgpu_device *adev = smu->adev;
+ struct smc_soft_pptable_entry *entries;
+ size_t fw_size = adev->pm.fw->size;
+ uint32_t pptable_entry_offset;
+ uint32_t ppt_offset_bytes;
+ uint32_t ppt_size_bytes;
+ uint32_t pptable_count;
+ int i;
+
+ if (fw_size < sizeof(*v2_1)) {
+ dev_err(adev->dev,
+ "SMC firmware too small for v2.1 header: %zu < %zu\n",
+ fw_size, sizeof(*v2_1));
+ return -EINVAL;
+ }
+
+ v2_1 = (const struct smc_firmware_header_v2_1 *)adev->pm.fw->data;
+
+ pptable_entry_offset = le32_to_cpu(v2_1->pptable_entry_offset);
+ pptable_count = le32_to_cpu(v2_1->pptable_count);
+
+ if (pptable_entry_offset > fw_size ||
+ pptable_count > (fw_size - pptable_entry_offset) / sizeof(*entries)) {
+ dev_err(adev->dev,
+ "pptable v2.1 entry array exceeds firmware binary: offset %u, count %u\n",
+ pptable_entry_offset, pptable_count);
+ return -EINVAL;
+ }
+
+ entries = (struct smc_soft_pptable_entry *)
+ ((uint8_t *)v2_1 + pptable_entry_offset);
+
+ for (i = 0; i < pptable_count; i++) {
+ if (le32_to_cpu(entries[i].id) != pptable_id)
+ continue;
+
+ ppt_offset_bytes = le32_to_cpu(entries[i].ppt_offset_bytes);
+ ppt_size_bytes = le32_to_cpu(entries[i].ppt_size_bytes);
+
+ if (ppt_offset_bytes > fw_size ||
+ ppt_size_bytes > fw_size - ppt_offset_bytes) {
+ dev_err(adev->dev,
+ "pptable entry %d exceeds firmware binary: offset %u + size %u > %zu\n",
+ i, ppt_offset_bytes, ppt_size_bytes, fw_size);
+ return -EINVAL;
+ }
+
+ *table = (uint8_t *)v2_1 + ppt_offset_bytes;
+ *size = ppt_size_bytes;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * smu_cmn_get_pptable_from_firmware - locate the soft pptable embedded in the
+ * SMC firmware binary.
+ * @smu: SMU context
+ * @table: on success, set to the start of the pptable within the firmware
+ * blob
+ * @size: on success, set to the pptable size in bytes
+ * @pptable_id: the entry ID to search for (used only for v2.1 binaries)
+ *
+ * Reads the firmware header version and dispatches to the appropriate v2.x
+ * parser. Only major version 2 is supported; minor version selects between
+ * the single-entry (v2.0) and multi-entry directory (v2.1) layouts.
+ *
+ * Return: 0 on success, -EINVAL for an unsupported version or if the
+ * requested pptable cannot be found or exceeds the binary bounds.
+ */
+int smu_cmn_get_pptable_from_firmware(struct smu_context *smu, void **table,
+ uint32_t *size, uint32_t pptable_id)
+{
+ const struct smc_firmware_header_v1_0 *hdr;
+ struct amdgpu_device *adev = smu->adev;
+ uint16_t version_major, version_minor;
+
+ hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
+ if (!hdr)
+ return -EINVAL;
+
+ dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id);
+
+ version_major = le16_to_cpu(hdr->header.header_version_major);
+ version_minor = le16_to_cpu(hdr->header.header_version_minor);
+ if (version_major != 2) {
+ dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n",
+ version_major, version_minor);
+ return -EINVAL;
+ }
+
+ switch (version_minor) {
+ case 0:
+ return smu_cmn_get_pptable_v2_0(smu, table, size);
+ case 1:
+ return smu_cmn_get_pptable_v2_1(smu, table, size, pptable_id);
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
index 0e119965ce13..ae6742f5298f 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
@@ -113,6 +113,16 @@ static inline int pcie_gen_to_speed(uint32_t gen)
return ((gen == 0) ? link_speed[0] : link_speed[gen - 1]);
}
+static inline bool smu_cmn_custom_params_count_valid(u32 max_idx, u32 params_count)
+{
+ return max_idx == params_count;
+}
+
+static inline bool smu_cmn_custom_params_clock_valid(long clock_idx, long clock_count)
+{
+ return clock_idx >= 0 && clock_idx < clock_count;
+}
+
int smu_cmn_send_smc_msg_with_param(struct smu_context *smu,
enum smu_message_type msg,
uint32_t param,
@@ -239,6 +249,9 @@ int smu_cmn_dpm_pcie_gen_idx(int gen);
int smu_cmn_dpm_pcie_width_idx(int width);
int smu_cmn_check_fw_version(struct smu_context *smu);
+int smu_cmn_get_pptable_from_firmware(struct smu_context *smu, void **table,
+ uint32_t *size, uint32_t pptable_id);
+
/*SMU gpu metrics */
/* Attribute ID mapping */
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c
index 658bf3fdb66b..bfbfdffbfbe6 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c
@@ -30,9 +30,6 @@
#include "amdgpu_ras_mgr.h"
#include "amdgpu_virt_ras_cmd.h"
-/* inject address is 52 bits */
-#define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52)
-
#define AMDGPU_RAS_TYPE_RASCORE 0x1
#define AMDGPU_RAS_TYPE_AMDGPU 0x2
#define AMDGPU_RAS_TYPE_VF 0x3
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c
index 3ed3ff42b7e1..9c6d0024210d 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c
@@ -67,7 +67,7 @@ static int ras_eeprom_i2c_config(struct ras_core_context *ras_core)
struct ras_eeprom_control *control = &ras_core->ras_eeprom;
u8 i2c_addr;
- if (amdgpu_atomfirmware_ras_rom_addr(adev, &i2c_addr)) {
+ if (adev->bios && amdgpu_atomfirmware_ras_rom_addr(adev, &i2c_addr)) {
/* The address given by VBIOS is an 8-bit, wire-format
* address, i.e. the most significant byte.
*
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c
index a22d1aebbeb9..b62bbb5ea292 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c
@@ -95,11 +95,35 @@ static int amdgpu_ras_mgr_init_aca_config(struct amdgpu_device *adev,
return 0;
}
+static uint64_t amdgpu_ras_mgr_reserved_vram_size(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ uint64_t reserved_pages_in_bytes = 0;
+
+ if (!con || (adev->flags & AMD_IS_APU))
+ return 0;
+
+ switch (amdgpu_ip_version(adev, MP0_HWIP, 0)) {
+ case IP_VERSION(13, 0, 6):
+ case IP_VERSION(13, 0, 12):
+ reserved_pages_in_bytes = RAS_RESERVED_VRAM_SIZE_DEFAULT;
+ break;
+ case IP_VERSION(13, 0, 14):
+ reserved_pages_in_bytes = (RAS_RESERVED_VRAM_SIZE_DEFAULT << 1);
+ break;
+ default:
+ break;
+ }
+ return reserved_pages_in_bytes;
+}
+
static int amdgpu_ras_mgr_init_eeprom_config(struct amdgpu_device *adev,
struct ras_core_config *config)
{
struct ras_eeprom_config *eeprom_cfg = &config->eeprom_cfg;
+ uint64_t ras_reserved_vram_size;
+ ras_reserved_vram_size = amdgpu_ras_mgr_reserved_vram_size(adev);
eeprom_cfg->eeprom_sys_fn = &amdgpu_ras_eeprom_i2c_sys_func;
eeprom_cfg->eeprom_i2c_adapter = adev->pm.ras_eeprom_i2c_bus;
if (eeprom_cfg->eeprom_i2c_adapter) {
@@ -133,7 +157,7 @@ static int amdgpu_ras_mgr_init_eeprom_config(struct amdgpu_device *adev,
div64_u64(adev->gmc.mc_vram_size, TYPICAL_ECC_BAD_PAGE_RATE);
else if (amdgpu_bad_page_threshold == WARN_NONSTOP_OVER_THRESHOLD)
eeprom_cfg->eeprom_record_threshold_count =
- COUNT_BAD_PAGE_THRESHOLD(RAS_RESERVED_VRAM_SIZE_DEFAULT);
+ COUNT_BAD_PAGE_THRESHOLD(ras_reserved_vram_size);
else
eeprom_cfg->eeprom_record_threshold_count = amdgpu_bad_page_threshold;
@@ -142,6 +166,21 @@ static int amdgpu_ras_mgr_init_eeprom_config(struct amdgpu_device *adev,
return 0;
}
+static bool amdgpu_ras_mgr_eeprom_is_supported(struct amdgpu_device *adev)
+{
+ if (amdgpu_sriov_vf(adev))
+ return false;
+
+ switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {
+ case IP_VERSION(13, 0, 6):
+ case IP_VERSION(13, 0, 12):
+ case IP_VERSION(13, 0, 14):
+ return (adev->gmc.is_app_apu) ? false : true;
+ default:
+ return false;
+ }
+}
+
static int amdgpu_ras_mgr_init_mp1_config(struct amdgpu_device *adev,
struct ras_core_config *config)
{
@@ -266,7 +305,8 @@ static struct ras_core_context *amdgpu_ras_mgr_create_ras_core(struct amdgpu_dev
init_config.aca_ip_version = IP_VERSION(1, 0, 0);
init_config.sys_fn = &amdgpu_ras_sys_fn;
- init_config.ras_eeprom_supported = true;
+ init_config.ras_eeprom_supported =
+ amdgpu_ras_mgr_eeprom_is_supported(adev);
init_config.poison_supported =
amdgpu_ras_is_poison_mode_supported(adev);
@@ -425,6 +465,28 @@ static int amdgpu_ras_mgr_hw_fini(struct amdgpu_ip_block *ip_block)
return 0;
}
+int amdgpu_ras_mgr_resume_after_reset(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ struct amdgpu_ip_block *ip_block;
+
+ if (!con || !con->uniras_enabled)
+ return 0;
+
+ if (!ras_mgr || !ras_mgr->ras_core)
+ return -EINVAL;
+
+ if (ras_mgr->ras_is_ready)
+ return 0;
+
+ ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_RAS);
+ if (!ip_block)
+ return -EINVAL;
+
+ return amdgpu_ras_mgr_hw_init(ip_block);
+}
+
struct amdgpu_ras_mgr *amdgpu_ras_mgr_get_context(struct amdgpu_device *adev)
{
if (!adev || !adev->psp.ras_context.ras)
@@ -733,3 +795,13 @@ int amdgpu_ras_mgr_lookup_bad_pages_in_a_row(struct amdgpu_device *adev,
return ras_core_convert_soc_pa_to_cur_nps_pages(ras_mgr->ras_core,
addr, nps_page_addr, max_page_count);
}
+
+int amdgpu_ras_mgr_set_debug_mode(struct amdgpu_device *adev, bool enable)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!ras_mgr || !ras_mgr->ras_core || !ras_mgr->ras_is_ready)
+ return false;
+
+ return ras_core_set_debug_mode(ras_mgr->ras_core, enable);
+}
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h
index 4f44a917d48b..a20bb8fdce87 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h
@@ -82,6 +82,8 @@ int amdgpu_ras_mgr_handle_ras_cmd(struct amdgpu_device *adev,
void *output, uint32_t out_size);
int amdgpu_ras_mgr_pre_reset(struct amdgpu_device *adev);
int amdgpu_ras_mgr_post_reset(struct amdgpu_device *adev);
+int amdgpu_ras_mgr_resume_after_reset(struct amdgpu_device *adev);
int amdgpu_ras_mgr_lookup_bad_pages_in_a_row(struct amdgpu_device *adev,
uint64_t addr, uint64_t *nps_page_addr, uint32_t max_page_count);
+int amdgpu_ras_mgr_set_debug_mode(struct amdgpu_device *adev, bool enable);
#endif
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c
index 2098f24d4940..3c4575a5d902 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c
@@ -24,6 +24,7 @@
#include "amdgpu_smu.h"
#include "amdgpu_reset.h"
#include "amdgpu_ras_mp1_v13_0.h"
+#include "smu13_driver_if_v13_0_6.h"
#define RAS_MP1_MSG_QueryValidMcaCeCount 0x3A
#define RAS_MP1_MSG_McaBankCeDumpDW 0x3B
@@ -131,10 +132,23 @@ static int mp1_v13_0_get_ras_enabled_mask(struct ras_core_context *ras_core,
return ret;
}
+static int mp1_v13_0_set_debug_mode(struct ras_core_context *ras_core, bool enable)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ int ret;
+ u32 smu_msg = SMU_MSG_ClearMcaOnRead;
+
+ ret = amdgpu_smu_ras_send_msg(adev, smu_msg,
+ enable ? 0 : ClearMcaOnRead_UE_FLAG_MASK |
+ ClearMcaOnRead_CE_POLL_MASK, NULL);
+ return ret;
+}
+
const struct ras_mp1_sys_func amdgpu_ras_mp1_sys_func_v13_0 = {
.mp1_get_valid_bank_count = mp1_v13_0_get_valid_bank_count,
.mp1_dump_valid_bank = mp1_v13_0_dump_valid_bank,
.mp1_send_eeprom_msg = mp1_v13_0_eeprom_send_msg,
.mp1_get_ras_enabled_mask = mp1_v13_0_get_ras_enabled_mask,
+ .mp1_set_debug_mode = mp1_v13_0_set_debug_mode,
};
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c
index 7d728e523604..e4444798bc73 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c
@@ -267,6 +267,24 @@ static int amdgpu_ras_sys_put_gpu_mem(struct ras_core_context *ras_core,
return 0;
}
+static int amdgpu_ras_sys_check_address_sanity(struct ras_core_context *ras_core,
+ uint64_t addr)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+
+ if ((addr >= adev->gmc.mc_vram_size &&
+ adev->gmc.mc_vram_size) ||
+ (addr >= RAS_UMC_INJECT_ADDR_LIMIT))
+ return -EINVAL;
+
+ if (addr >= adev->gmc.real_vram_size) {
+ RAS_DEV_WARN(ras_core->dev, "Recorded address out of range: 0x%llx!\n", addr);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
const struct ras_sys_func amdgpu_ras_sys_fn = {
.ras_notifier = amdgpu_ras_sys_event_notifier,
.get_utc_second_timestamp = amdgpu_ras_sys_get_utc_second_timestamp,
@@ -277,4 +295,5 @@ const struct ras_sys_func amdgpu_ras_sys_fn = {
.detect_ras_interrupt = amdgpu_ras_sys_detect_ras_interrupt,
.get_gpu_mem = amdgpu_ras_sys_get_gpu_mem,
.put_gpu_mem = amdgpu_ras_sys_put_gpu_mem,
+ .check_address_sanity = amdgpu_ras_sys_check_address_sanity,
};
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h
index f34dda7ce87b..2775c7bf41b7 100644
--- a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h
@@ -30,6 +30,9 @@
#include <linux/mempool.h>
#include "amdgpu.h"
+/* inject address is 52 bits */
+#define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52)
+
#define RAS_DEV_ERR(device, fmt, ...) \
do { \
if (device) \
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras.h b/drivers/gpu/drm/amd/ras/rascore/ras.h
index c059fcebaf00..878dfdfcb18a 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras.h
+++ b/drivers/gpu/drm/amd/ras/rascore/ras.h
@@ -167,6 +167,7 @@ struct ras_mp1_sys_func {
enum ras_fw_eeprom_cmd index, uint32_t param, uint32_t *read_arg);
int (*mp1_get_ras_enabled_mask)(struct ras_core_context *ras_core,
uint64_t *enabled_mask);
+ int (*mp1_set_debug_mode)(struct ras_core_context *ras_core, bool enable);
};
struct ras_eeprom_sys_func {
@@ -231,6 +232,7 @@ struct ras_sys_func {
enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem);
int (*put_gpu_mem)(struct ras_core_context *ras_core,
enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem);
+ int (*check_address_sanity)(struct ras_core_context *ras_core, uint64_t addr);
};
struct ras_ecc_count {
@@ -398,4 +400,7 @@ int ras_core_get_device_system_info(struct ras_core_context *ras_core,
struct device_system_info *dev_info);
int ras_core_convert_soc_pa_to_cur_nps_pages(struct ras_core_context *ras_core,
uint64_t soc_pa, uint64_t *page_pfn, uint32_t max_pages);
+int ras_core_check_address_sanity(struct ras_core_context *ras_core, uint64_t addr);
+
+int ras_core_set_debug_mode(struct ras_core_context *ras_core, bool enable);
#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c
index 210fbd8851a6..840610538c1f 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c
@@ -213,7 +213,7 @@ static int aca_parse_umc_bank(struct ras_core_context *ras_core,
struct aca_bank_reg *bank = (struct aca_bank_reg *)data;
struct aca_bank_ecc *ecc = (struct aca_bank_ecc *)buf;
struct aca_ecc_info bank_info;
- uint32_t ext_error_code;
+ uint32_t ext_error_code, misc0_errcnt;
uint64_t status0;
status0 = bank->regs[ACA_REG_IDX__STATUS];
@@ -228,15 +228,14 @@ static int aca_parse_umc_bank(struct ras_core_context *ras_core,
ecc->bank_info.addr = bank->regs[ACA_REG_IDX__ADDR];
ext_error_code = ACA_REG_STATUS_ERRORCODEEXT(status0);
+ misc0_errcnt = ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]);
if (aca_check_umc_de(ras_core, status0))
- ecc->de_count = 1;
+ ecc->de_count = misc0_errcnt ? misc0_errcnt : 1;
else if (aca_check_umc_ue(ras_core, status0))
- ecc->ue_count = ext_error_code ?
- 1 : ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]);
+ ecc->ue_count = ext_error_code ? 1 : misc0_errcnt;
else if (aca_check_umc_ce(ras_core, status0))
- ecc->ce_count = ext_error_code ?
- 1 : ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]);
+ ecc->ce_count = ext_error_code ? 1 : misc0_errcnt;
return 0;
}
@@ -266,7 +265,7 @@ static int aca_parse_bank_default(struct ras_core_context *ras_core,
ecc->bank_info.addr = bank->regs[ACA_REG_IDX__ADDR];
if (aca_check_bank_is_de(ras_core, status)) {
- ecc->de_count = 1;
+ ecc->de_count = 0;
} else {
if (bank->ecc_type == RAS_ERR_TYPE__UE)
ecc->ue_count = 1;
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_core.c b/drivers/gpu/drm/amd/ras/rascore/ras_core.c
index 62d124a3eeac..c63a358b7e57 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_core.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_core.c
@@ -151,6 +151,11 @@ bool ras_core_gpu_is_rma(struct ras_core_context *ras_core)
return ras_core->is_rma;
}
+int ras_core_set_debug_mode(struct ras_core_context *ras_core, bool enable)
+{
+ return ras_mp1_set_debug_mode(ras_core, enable);
+}
+
static int ras_core_seqno_fifo_write(struct ras_core_context *ras_core,
enum ras_seqno_fifo fifo_type, uint64_t seqno)
{
@@ -676,3 +681,13 @@ int ras_core_convert_soc_pa_to_cur_nps_pages(struct ras_core_context *ras_core,
return count;
}
+
+int ras_core_check_address_sanity(struct ras_core_context *ras_core,
+ uint64_t addr)
+{
+ if (ras_core && ras_core->sys_fn &&
+ ras_core->sys_fn->check_address_sanity)
+ return ras_core->sys_fn->check_address_sanity(ras_core, addr);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c
index 3a0ea036c9be..62d1a319c08c 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c
@@ -746,6 +746,9 @@ static int ras_eeprom_update_header(struct ras_eeprom_control *control)
int res;
bad_page_count = ras_umc_get_badpage_count(ras_core);
+ ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM,
+ &bad_page_count);
+
/* Modify the header if it exceeds.
*/
if (threshold_config != 0 &&
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c
index f5fa80db91fb..59e195652e42 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c
@@ -72,7 +72,7 @@ int ras_fw_get_badpage_count(struct ras_core_context *ras_core,
if (ret != -EBUSY)
return ret;
- mdelay(10);
+ usleep_range(10000, 15000);
now = (uint64_t)ktime_to_ms(ktime_get());
} while (now < end);
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c
index f3321df85021..26af09f3574a 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c
@@ -59,9 +59,20 @@ int ras_mp1_dump_bank(struct ras_core_context *ras_core,
return mp1->ip_func->dump_valid_bank(ras_core, type, idx, reg_idx, val);
}
+int ras_mp1_set_debug_mode(struct ras_core_context *ras_core, bool enable)
+{
+ struct ras_mp1 *mp1 = &ras_core->ras_mp1;
+
+ if (!mp1->ip_func || !mp1->ip_func->set_debug_mode)
+ return -EOPNOTSUPP;
+
+ return mp1->ip_func->set_debug_mode(ras_core, enable);
+}
+
int ras_mp1_hw_init(struct ras_core_context *ras_core)
{
struct ras_mp1 *mp1 = &ras_core->ras_mp1;
+ int ret = 0;
mp1->mp1_ip_version = ras_core->config->mp1_ip_version;
mp1->sys_func = ras_core->config->mp1_cfg.mp1_sys_fn;
@@ -71,8 +82,14 @@ int ras_mp1_hw_init(struct ras_core_context *ras_core)
}
mp1->ip_func = ras_mp1_get_ip_funcs(ras_core, mp1->mp1_ip_version);
+ if (!mp1->ip_func)
+ return -EINVAL;
+
+ ret = ras_mp1_set_debug_mode(ras_core, false);
+ if (ret)
+ return -EINVAL;
- return mp1->ip_func ? RAS_CORE_OK : -EINVAL;
+ return ret;
}
int ras_mp1_hw_fini(struct ras_core_context *ras_core)
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h
index de1d08286f41..5bc7c1b7fdab 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h
@@ -31,6 +31,7 @@ struct ras_mp1_ip_func {
enum ras_err_type type, u32 *count);
int (*dump_valid_bank)(struct ras_core_context *ras_core,
enum ras_err_type type, u32 idx, u32 reg_idx, u64 *val);
+ int (*set_debug_mode)(struct ras_core_context *ras_core, bool enable);
};
struct ras_mp1 {
@@ -47,4 +48,6 @@ int ras_mp1_get_bank_count(struct ras_core_context *ras_core,
int ras_mp1_dump_bank(struct ras_core_context *ras_core,
u32 ecc_type, u32 idx, u32 reg_idx, u64 *val);
+
+int ras_mp1_set_debug_mode(struct ras_core_context *ras_core, bool enable);
#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c
index 310d39fc816b..1fcfc1995ad3 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c
@@ -99,7 +99,20 @@ static int mp1_v13_0_dump_bank(struct ras_core_context *ras_core,
return sys_func->mp1_dump_valid_bank(ras_core, msg, idx, reg_idx, val);
}
+static int mp1_v13_0_set_debug_mode(struct ras_core_context *ras_core, bool enable)
+{
+ struct ras_mp1 *mp1 = &ras_core->ras_mp1;
+ const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
+
+ if (!sys_func || !sys_func->mp1_set_debug_mode)
+ return -RAS_CORE_NOT_SUPPORTED;
+
+ return sys_func->mp1_set_debug_mode(ras_core, enable);
+}
+
+
const struct ras_mp1_ip_func mp1_ras_func_v13_0 = {
.get_valid_bank_count = mp1_v13_0_get_bank_count,
.dump_valid_bank = mp1_v13_0_dump_bank,
+ .set_debug_mode = mp1_v13_0_set_debug_mode,
};
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_process.c b/drivers/gpu/drm/amd/ras/rascore/ras_process.c
index 3267dcdb169c..c001074c8c56 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_process.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_process.c
@@ -248,9 +248,10 @@ int ras_process_init(struct ras_core_context *ras_core)
ras_proc->ras_process_thread = kthread_run(ras_process_thread,
(void *)ras_core, "ras_process_thread");
- if (!ras_proc->ras_process_thread) {
+ if (IS_ERR(ras_proc->ras_process_thread)) {
RAS_DEV_ERR(ras_core->dev, "Failed to create ras_process_thread.\n");
- ret = -ENOMEM;
+ ret = PTR_ERR(ras_proc->ras_process_thread);
+ ras_proc->ras_process_thread = NULL;
goto err;
}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c
index f32ee2fecf53..e366fb97293e 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c
@@ -406,7 +406,7 @@ static int ras_umc_update_eeprom_ram_data(struct ras_core_context *ras_core,
struct ras_umc *ras_umc = &ras_core->ras_umc;
struct eeprom_store_record *data = &ras_umc->umc_err_data.ram_data;
uint64_t page_pfn[16];
- int count = 0, j;
+ int count = 0, i, j;
if (!data->space_left &&
ras_umc_realloc_err_data_space(ras_core, data, 256)) {
@@ -418,10 +418,23 @@ static int ras_umc_update_eeprom_ram_data(struct ras_core_context *ras_core,
bps, bps->cur_nps, page_pfn, ARRAY_SIZE(page_pfn));
if (count > 0) {
for (j = 0; j < count; j++) {
+ if (ras_core_check_address_sanity(ras_core,
+ page_pfn[j] << AMDGPU_GPU_PAGE_SHIFT)) {
+
+ for (i = 0; i < data->count; i++)
+ if (page_pfn[j] == data->bps[i].cur_nps_retired_row_pfn)
+ break;
+ data->bps[data->count].cur_nps_retired_row_pfn = U64_MAX;
+ data->count++;
+ data->space_left--;
+ continue;
+ }
+
bps->cur_nps_retired_row_pfn = page_pfn[j];
memcpy(&data->bps[data->count], bps, sizeof(*data->bps));
data->count++;
data->space_left--;
+ data->bad_page_num++;
}
} else {
RAS_DEV_ERR(ras_core->dev, "Failed to convert record to nps pages!");
@@ -431,6 +444,14 @@ static int ras_umc_update_eeprom_ram_data(struct ras_core_context *ras_core,
return 0;
}
+static void ras_umc_update_bad_pages(struct ras_core_context *ras_core)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct eeprom_store_record *data = &ras_umc->umc_err_data.ram_data;
+
+ data->bad_page_num_old = data->bad_page_num;
+}
+
/* it deal with vram only. */
static int ras_umc_add_bad_pages(struct ras_core_context *ras_core,
struct eeprom_umc_record *bps,
@@ -506,6 +527,7 @@ int ras_umc_load_bad_pages(struct ras_core_context *ras_core)
} else {
ras_core->ras_umc.umc_err_data.last_retired_pfn = UMC_INV_MEM_PFN;
ret = ras_umc_add_bad_pages(ras_core, bps, ras_num_recs, true);
+ ras_umc_update_bad_pages(ras_core);
}
kfree(bps);
@@ -521,7 +543,8 @@ static int ras_umc_save_bad_pages(struct ras_core_context *ras_core)
{
struct ras_umc *ras_umc = &ras_core->ras_umc;
struct eeprom_store_record *data = &ras_umc->umc_err_data.rom_data;
- uint32_t eeprom_record_num;
+ struct eeprom_store_record *ram_data = &ras_umc->umc_err_data.ram_data;
+ uint32_t eeprom_record_num, logical_count = 0;
int save_count;
int ret = 0;
@@ -534,6 +557,7 @@ static int ras_umc_save_bad_pages(struct ras_core_context *ras_core)
eeprom_record_num = ras_eeprom_get_record_count(ras_core);
mutex_lock(&ras_umc->umc_lock);
save_count = data->count - eeprom_record_num;
+ logical_count = ram_data->bad_page_num - ram_data->bad_page_num_old;
/* only new entries are saved */
if (save_count > 0) {
if (ras_fw_eeprom_supported(ras_core))
@@ -547,8 +571,8 @@ static int ras_umc_save_bad_pages(struct ras_core_context *ras_core)
ret = -EIO;
goto exit;
}
-
- RAS_DEV_INFO(ras_core->dev, "Saved %d pages to EEPROM table.\n", save_count);
+ ras_umc_update_bad_pages(ras_core);
+ RAS_DEV_INFO(ras_core->dev, "Saved %d pages to EEPROM table.\n", logical_count);
}
exit:
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h
index 237525b46b9b..ee7100f25f51 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h
@@ -119,6 +119,12 @@ struct eeprom_store_record {
int count;
/* the space can place new entries */
int space_left;
+ /* logical bad page number */
+ int bad_page_num;
+ /* the bad page number is ras_num_recs or
+ * ras_num_recs * retire_unit
+ */
+ int bad_page_num_old;
};
struct ras_umc_err_data {
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h
index 8a35ad856165..650b5f1f22f7 100644
--- a/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h
@@ -290,6 +290,8 @@
/* R13 bit shift should be considered, double the number */
#define UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL (UMC_V12_0_NA_MAP_PA_NUM * 2)
+/* UMC register per channel offset */
+#define UMC_V12_0_PER_CHANNEL_OFFSET 0x400
/* C2, C3, C4, R13, four MCA bits are looped in page retirement */
#define UMC_V12_0_RETIRE_LOOP_BITS 4
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
index 83f1ae31cbdb..9755e717ca8b 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.c
+++ b/drivers/gpu/drm/radeon/r600_dpm.c
@@ -932,7 +932,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
dep_table);
if (ret) {
- kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
+ r600_free_extended_power_table(rdev);
return ret;
}
}
@@ -943,8 +943,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
dep_table);
if (ret) {
- kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
- kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries);
+ r600_free_extended_power_table(rdev);
return ret;
}
}
@@ -955,9 +954,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk,
dep_table);
if (ret) {
- kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries);
- kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries);
- kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries);
+ r600_free_extended_power_table(rdev);
return ret;
}
}
@@ -1296,17 +1293,29 @@ void r600_free_extended_power_table(struct radeon_device *rdev)
struct radeon_dpm_dynamic_state *dyn_state = &rdev->pm.dpm.dyn_state;
kfree(dyn_state->vddc_dependency_on_sclk.entries);
+ dyn_state->vddc_dependency_on_sclk.entries = NULL;
kfree(dyn_state->vddci_dependency_on_mclk.entries);
+ dyn_state->vddci_dependency_on_mclk.entries = NULL;
kfree(dyn_state->vddc_dependency_on_mclk.entries);
+ dyn_state->vddc_dependency_on_mclk.entries = NULL;
kfree(dyn_state->mvdd_dependency_on_mclk.entries);
+ dyn_state->mvdd_dependency_on_mclk.entries = NULL;
kfree(dyn_state->cac_leakage_table.entries);
+ dyn_state->cac_leakage_table.entries = NULL;
kfree(dyn_state->phase_shedding_limits_table.entries);
+ dyn_state->phase_shedding_limits_table.entries = NULL;
kfree(dyn_state->ppm_table);
+ dyn_state->ppm_table = NULL;
kfree(dyn_state->cac_tdp_table);
+ dyn_state->cac_tdp_table = NULL;
kfree(dyn_state->vce_clock_voltage_dependency_table.entries);
+ dyn_state->vce_clock_voltage_dependency_table.entries = NULL;
kfree(dyn_state->uvd_clock_voltage_dependency_table.entries);
+ dyn_state->uvd_clock_voltage_dependency_table.entries = NULL;
kfree(dyn_state->samu_clock_voltage_dependency_table.entries);
+ dyn_state->samu_clock_voltage_dependency_table.entries = NULL;
kfree(dyn_state->acp_clock_voltage_dependency_table.entries);
+ dyn_state->acp_clock_voltage_dependency_table.entries = NULL;
}
enum radeon_pcie_gen r600_get_pcie_gen_support(struct radeon_device *rdev,
diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
index 9f3090db2f16..b32c72a662b6 100644
--- a/include/uapi/drm/amdgpu_drm.h
+++ b/include/uapi/drm/amdgpu_drm.h
@@ -58,6 +58,7 @@ extern "C" {
#define DRM_AMDGPU_USERQ_SIGNAL 0x17
#define DRM_AMDGPU_USERQ_WAIT 0x18
#define DRM_AMDGPU_GEM_LIST_HANDLES 0x19
+#define DRM_AMDGPU_PROC_OPTIONS 0x1A
#define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create)
#define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap)
@@ -79,6 +80,7 @@ extern "C" {
#define DRM_IOCTL_AMDGPU_USERQ_SIGNAL DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_USERQ_SIGNAL, struct drm_amdgpu_userq_signal)
#define DRM_IOCTL_AMDGPU_USERQ_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_USERQ_WAIT, struct drm_amdgpu_userq_wait)
#define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles)
+#define DRM_IOCTL_AMDGPU_PROC_OPTIONS DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options)
/**
* DOC: memory domains
@@ -1673,6 +1675,25 @@ struct drm_amdgpu_info_uq_metadata {
#define AMDGPU_FAMILY_GC_11_5_4 154 /* GC 11.5.4 */
#define AMDGPU_FAMILY_GC_12_0_0 152 /* GC 12.0.0 */
+/*
+ * Definition of user options
+ *
+ * option: AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY
+ * 0: Disable sigbus delay - SIGBUS will be raised immediately
+ * 0xFFFFFFFF: SIGBUS will not be raised
+ * other: Set the sigbus delay in milliseconds
+ */
+#define AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY 0
+
+#define AMDGPU_PROC_OPTIONS_KFD_SIGBUS_DELAY_DISABLED 0xFFFFFFFFu
+
+struct drm_amdgpu_proc_options {
+ __u32 op;
+ struct {
+ __u32 value;
+ } kfd_sigbus_delay;
+};
+
#if defined(__cplusplus)
}
#endif