* [PATCH 00/74] DC Patches November 16, 2026
@ 2026-11-11 1:28 Wayne Lin
2026-11-11 1:28 ` [PATCH 01/74] drm/amd/display: Fix elapsed time indentation Wayne Lin
` (74 more replies)
0 siblings, 75 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Wayne Lin
This DC patchset brings improvements in multiple areas. In summary, we highlight:
- Build DCN401 virtual commits and seamless plane updates in DC2, and
dispatch the hardware span of a commit through dc2_funcs.
- Add the leaf block-sequence entries a virtual commit needs, and read
the accelerated-mode bit through a query builder.
- Resolve watermark policy and the DPP, DTBCLK and DCN401 DENTIST
targets when the sequence is built.
- Fix DSC teardown gating in the post-unlock sequence.
- Move HDMI FRL status polling outside dc_lock and stop it on fini.
- Relax stutter constraints for DRR streams, including ramless idle,
track static-screen idle, and enable the relaxation on DCN42. Raise
the DCN42 DDR5 stutter latency.
- Let DC_PSR_MASK enable PSR without a DMUB PSR cap.
- Enable HDCP on DCN6 and the DF C-state vote on DCN42B.
- Enable SST PCON FRL DSC passthrough and program the correct DP HPO
APG instance.
- Exit IPS before checking flip pending, and keep the HPD IRQ reset
path from taking dc_lock.
- Skip UPSP programming when the registers are absent, and skip
visual confirm color programming when firmware owns it.
- Align DCN42B with DCN42 and extend the IOMMU mismatch workaround to
DCN42B. Restore the missing DCN6 vtotal cap and vstartup assignment.
- Update the DMUB interface to 0.1.83.0 with an extended SoC bounding
box, RAMLESS_LOBF commands, and a seamless-boot eDP request.
- Initialize the DMUB LSDMA ring, accept a 1932-byte PSP footer, and
update the default VCO frequency.
- Fix the FFE link-rate change path and the p-state enable sequence.
Commit the validated target when a minimal transition cannot be
built.
- Fix KUnit tiled EDID size
- fix hubbub read state logging
- skip_lcdvdd_off for eDP link lost retrain
- Don't shrink zero-area recout in full viewport visual confirm
- panel specific wa not fall back to tmds
- Use expanded DMUB-provided SoC BB and IP caps
- Only force P-State on first plane if TTU is below WM
- Program the kept-plane scaler in DC2
- Add and remove two MPO planes in DC2
- Add DC2 state facts and split the builders
- Program DC2 commit stereo once
- Maximum SOP states is 8
- Use soc_bb hostvm fraction instead of hardcoded value
- Increase state if dcfclk deep sleep needed exceeds dcfclk
- Flip dml2_core_utils_get_segment_horizontal_contiguous() Inputs
- Update DCN6 Calcs
- Add effective_number_of_dpp parameter
- Read DRAM channel width from SMU init table
- Update DCN42B stutter latency to match DCN42
- Expect VRAM|GTT for the DMUB framebuffer
- Report QoS latency bounds and request
- upsp fixes
- Limit VRAM aperture retrieval to DCN6
- Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant
- Seed boot clocks to avoid lowering pre-OS display clocks
- Account for split pipe overlap in alt channel copy size
- Get OPP heads and DPP pipes by stream in dc_resource
- Program ODM 2:1 and 3:1 on one stream in DC2
- Enable Replay timing sync by default
- Various cleanups.
---
Alex Hung (2):
drm/amd/display: Fix elapsed time indentation
drm/amd/display: Fix KUnit tiled EDID size
Allen Pan (1):
drm/amd/display: Update DCN42B stutter latency to match DCN42
Alvin Lee (4):
drm/amd/display: Update indicate pstate enable sequence
drm/amd/display: Update DP HPO to use correct APG instance
drm/amd/display: Updating missing assignments for DCN6
drm/amd/display: Don't shrink zero-area recout in full viewport visual
confirm
Aric Cyr (1):
drm/amd/display: Ensure exit IPS before checking flip status
Ashwin Santhosh (2):
drm/amd/display: Skip visual confirm color programming when FW owns it
drm/amd/display: Account for split pipe overlap in alt channel copy
size
Austin Zheng (5):
drm/amd/display: Fix Stage Optimizer Parameter Naming
drm/amd/display: Read DCN6 SOP constraints from the idle row
drm/amd/display: Use soc_bb hostvm fraction instead of hardcoded value
drm/amd/display: Flip
dml2_core_utils_get_segment_horizontal_contiguous() Inputs
drm/amd/display: Add effective_number_of_dpp parameter
Charlene Liu (6):
drm/amd/display: Extend the IOMMU mismatch workaround to DCN42B
drm/amd/display: update dcn42b based on dcn42
drm/amd/display: enable DF C-state vote on DCN42B
drm/amd/display: Increase the DCN42 DDR5 stutter latency
drm/amd/display: Keep panel power across link-loss retrain
drm/amd/display: Keep patched HDMI panels on FRL across detect
Dillon Varone (3):
drm/amd/display: Relax stutter constraints for DRR streams
drm/amd/display: cleanup tiling and address formats
drm/amd/display: Enable DRR stutter constraint relaxation on DCN42
Eric Bernstein (1):
drm/amd/display: Skip UPSP programming when UPSP regs are absent
Fangzhi Zuo (4):
drm/amd/display: Correct the forced YCbCr420 comment
drm/amd/display: Move HDMI FRL Polling Outside dc_lock
drm/amd/display: Stop HDMI FRL status polling on fini
drm/amd/display: Enable SST PCON FRL DSC passthrough
Gabriel Abarca (2):
drm/amd/display: Increase state if dcfclk deep sleep needed exceeds
dcfclk
drm/amd/display: Update DCN6 Calcs
George Zhang (1):
drm/amd/display: Don't let HPD IRQ handler take dc_lock in reset
Ilya Bakoulin (1):
drm/amd/display: Fix FFE test automation link rate change
Ivan Lipski (4):
drm/amd/display: Initialize DMUB LSDMA ring buffer
drm/amd/display: Enable HDCP on DCN6
drm/amd/display: Use expanded DMUB-provided SoC BB and IP caps
drm/amd/display: Limit VRAM aperture retrieval to DCN6
James Lin (1):
drm/amd/display: commit validated target when minimal transition
cannot be built
Jing Zhou (1):
drm/amd/display: Seed boot clocks to avoid lowering pre-OS display
clocks
Karen Chen (1):
drm/amd/display: fix hubbub read state logging
Nevenko Stupar (1):
drm/amd/display: Update default VCO frequency
Ovidiu Bunea (1):
drm/amd/display: Only force P-State on first plane if TTU is below WM
Piotr Maziarz (3):
drm/amd/display: Resolve clk_mgr DPP DTO targets when the sequence is
built
drm/amd/display: Resolve clk_mgr DTBCLK DTO targets when the sequence
is built
drm/amd/display: Resolve the DCN401 DENTIST divider when the sequence
is built
Rafal Ostrowski (4):
drm/amd/display: Remove duplicate block sequence leftovers
drm/amd/display: HWSS Refactor - Watermarks
drm/amd/display: Fix DSC teardown gating in post-unlock BLS sequence
drm/amd/display: Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant
Ray Wu (2):
drm/amd/display: Allow DC_PSR_MASK to enable PSR without DMUB PSR cap
drm/amd/display: Enable Replay timing sync by default
Sreeja Golui (2):
drm/amd/display: Include Ramless Idle in stutter relaxation
drm/amd/display: detect static screen state and propagate
Sridevi Arvindekar (1):
drm/amd/display: Bypass chroma upsampling without the DPP cap
Taimur Hassan (4):
drm/amd/display: [FW Promotion] Release 0.1.83.0
drm/amd/display: Promote DC to 3.2.406
drm/amd/display: [FW Promotion] Release 0.1.84.0
drm/amd/display: Promote DC to 3.2.407
Tony Cheng (13):
drm/amd/display: Dispatch commit_state_hw through dc2_funcs
drm/amd/display: Add leaf BLS entries for a virtual commit
drm/amd/display: Build the dcn401 virtual commit natively
drm/amd/display: Build the dcn401 seamless update in DC2
drm/amd/display: Read accelerated mode through a query builder
drm/amd/display: Build the kept-plane surface update in DC2
drm/amd/display: Skip first-enable steps on a kept plane
drm/amd/display: Program the kept-plane scaler in DC2
drm/amd/display: Add and remove two MPO planes in DC2
drm/amd/display: Add DC2 state facts and split the builders
drm/amd/display: Program DC2 commit stereo once
drm/amd/display: Get OPP heads and DPP pipes by stream in dc_resource
drm/amd/display: Program ODM 2:1 and 3:1 on one stream in DC2
Wayne Lin (1):
drm/amd/display: Expect VRAM|GTT for the DMUB framebuffer
Wenjing Liu (2):
drm/amd/display: Read DRAM channel width from SMU init table
drm/amd/display: Report QoS latency bounds and request
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 17 +-
.../display/amdgpu_dm/amdgpu_dm_connector.c | 16 +-
.../amd/display/amdgpu_dm/amdgpu_dm_dmub.c | 15 +-
.../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 3 +-
.../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 9 +-
.../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 10 +-
.../drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c | 3 +-
.../tests/amdgpu_dm_connector_test.c | 9 +-
.../amdgpu_dm/tests/amdgpu_dm_dmub_test.c | 45 +-
.../amdgpu_dm/tests/amdgpu_dm_hdcp_test.c | 27 +
.../amdgpu_dm/tests/amdgpu_dm_irq_test.c | 6 +
.../amdgpu_dm/tests/amdgpu_dm_psr_test.c | 35 +-
drivers/gpu/drm/amd/display/dc/Makefile | 3 +
.../amd/display/dc/bls_builder/bls_builder.h | 6 +-
.../dc/bls_builder/bls_builder_internal.h | 2 +
.../bls_builder/bls_builder_private_types.h | 137 +
.../dc/bls_builder/dcn401_builder_internal.h | 110 +
.../dc/bls_builder/dcn401_init_builder.c | 36 +
.../bls_builder/dcn401_non_seamless_builder.c | 501 ++++
.../dc/bls_builder/dcn401_seamless_builder.c | 2235 +++++++++++++++++
.../display/dc/bls_executor/bls_executor.c | 1076 ++++++++
.../gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c | 83 +
.../dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 170 +-
.../dc/clk_mgr/dcn401/dcn401_clk_mgr.h | 15 +-
.../display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c | 31 +-
.../dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c | 25 +-
.../dc/clk_mgr/dcn42b/dcn42b_clk_mgr.h | 1 +
.../drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h | 3 +-
.../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 157 +-
.../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h | 14 +-
drivers/gpu/drm/amd/display/dc/core/dc.c | 115 +-
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 317 ++-
.../gpu/drm/amd/display/dc/core/dc_resource.c | 48 +-
.../amd/display/dc/core/dc_resource_helper.c | 7 +-
.../gpu/drm/amd/display/dc/core2/dc_core2.c | 341 ++-
.../gpu/drm/amd/display/dc/core2/dc_core2.h | 1 +
.../drm/amd/display/dc/core2/dc_core2_init.c | 2 +-
.../display/dc/core2/dc_core2_legacy_init.c | 2 +-
drivers/gpu/drm/amd/display/dc/dc.h | 2 +-
.../amd/display/dc/dc_api_dispatch/api_shim.c | 25 +
.../amd/display/dc/dc_api_dispatch/api_shim.h | 9 +-
drivers/gpu/drm/amd/display/dc/dc_dsc.h | 1 +
.../gpu/drm/amd/display/dc/dc_hdmi_types.h | 1 +
drivers/gpu/drm/amd/display/dc/dc_hw_types.h | 16 +-
.../gpu/drm/amd/display/dc/dc_spl_translate.c | 8 +-
drivers/gpu/drm/amd/display/dc/dc_stream.h | 3 +
.../dml21/inc/bounding_boxes/dcn42_soc_bb.h | 4 +-
.../dml21/inc/bounding_boxes/dcn42b_soc_bb.h | 4 +-
.../dml21/inc/dml_top_display_cfg_types.h | 1 +
.../dc/dml2_0/dml21/inc/dml_top_types.h | 2 +
.../src/dml2_core/dml2_core_dcn4_calcs.c | 2 +
.../dml2_core/dml2_core_dcn5_calcs_dchub.c | 75 +-
.../dml2_core/dml2_core_dcn6_calcs_dchub.c | 30 +-
.../dml2_core_dcn6_funcs_mode_programming.c | 34 +-
.../dml2_core_dcn6_funcs_mode_support.c | 19 +-
.../src/dml2_core/dml2_core_shared_types.h | 3 +
.../dml21/src/dml2_core/dml2_core_utils.c | 4 +
.../dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c | 20 +-
.../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h | 4 +-
.../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h | 8 +-
.../dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c | 19 +-
.../src/inc/dml2_internal_shared_types.h | 4 +-
.../dml21_wrapper/dml21_translation_helper.c | 17 +-
.../dc/dml2_wrapper/dml2_mall_phantom.c | 1 -
.../display/dc/dml2_wrapper/dml2_wrapper.h | 1 +
.../drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c | 2 +-
.../amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c | 4 +
.../display/dc/hubbub/dcn10/dcn10_hubbub.c | 3 +-
.../display/dc/hubbub/dcn10/dcn10_hubbub.h | 1 +
.../display/dc/hubbub/dcn20/dcn20_hubbub.c | 8 +-
.../display/dc/hubbub/dcn201/dcn201_hubbub.c | 3 +-
.../display/dc/hubbub/dcn21/dcn21_hubbub.c | 3 +-
.../display/dc/hubbub/dcn21/dcn21_hubbub.h | 1 +
.../display/dc/hubbub/dcn30/dcn30_hubbub.c | 5 +-
.../display/dc/hubbub/dcn30/dcn30_hubbub.h | 1 +
.../display/dc/hubbub/dcn31/dcn31_hubbub.c | 5 +-
.../display/dc/hubbub/dcn32/dcn32_hubbub.c | 23 +-
.../display/dc/hubbub/dcn35/dcn35_hubbub.c | 7 +-
.../display/dc/hubbub/dcn35/dcn35_hubbub.h | 1 +
.../display/dc/hubbub/dcn401/dcn401_hubbub.c | 12 +-
.../display/dc/hubbub/dcn401/dcn401_hubbub.h | 3 +-
.../display/dc/hubbub/dcn42/dcn42_hubbub.c | 20 +-
.../display/dc/hubbub/dcn60/dcn60_hubbub.c | 3 +-
.../amd/display/dc/hubp/dcn50/dcn50_hubp.c | 52 +-
.../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 61 +-
.../amd/display/dc/hwss/dcn10/dcn10_hwseq.h | 9 +-
.../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 48 +-
.../amd/display/dc/hwss/dcn20/dcn20_hwseq.h | 9 +-
.../amd/display/dc/hwss/dcn302/dcn302_hwseq.c | 15 +-
.../amd/display/dc/hwss/dcn302/dcn302_hwseq.h | 6 +-
.../amd/display/dc/hwss/dcn303/dcn303_hwseq.c | 11 +-
.../amd/display/dc/hwss/dcn303/dcn303_hwseq.h | 6 +-
.../amd/display/dc/hwss/dcn31/dcn31_hwseq.c | 8 +-
.../amd/display/dc/hwss/dcn31/dcn31_hwseq.h | 3 +-
.../amd/display/dc/hwss/dcn314/dcn314_hwseq.c | 15 +-
.../amd/display/dc/hwss/dcn314/dcn314_hwseq.h | 3 +-
.../amd/display/dc/hwss/dcn32/dcn32_hwseq.c | 7 +-
.../amd/display/dc/hwss/dcn32/dcn32_hwseq.h | 3 +-
.../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 180 +-
.../amd/display/dc/hwss/dcn401/dcn401_hwseq.h | 2 +
.../amd/display/dc/hwss/dcn401/dcn401_init.c | 2 +
.../amd/display/dc/hwss/dcn60/dcn60_hwseq.c | 9 +
.../amd/display/dc/hwss/dcn60/dcn60_hwseq.h | 1 +
.../amd/display/dc/hwss/dcn60/dcn60_init.c | 2 +-
.../drm/amd/display/dc/hwss/hw_sequencer.h | 422 +++-
.../display/dc/hwss/hw_sequencer_private.h | 14 +-
.../amd/display/dc/inc/dc_core_interface.h | 12 +
.../amd/display/dc/inc/hw/clk_mgr_internal.h | 38 +
.../gpu/drm/amd/display/dc/inc/hw/dchubbub.h | 22 +-
drivers/gpu/drm/amd/display/dc/inc/resource.h | 21 +-
.../display/dc/inc/soc_and_ip_translator.h | 4 +-
.../drm/amd/display/dc/link/link_detection.c | 26 +
.../dc/link/protocols/link_dp_irq_handler.c | 8 +-
.../display/dc/link/protocols/link_hdmi_frl.c | 30 +-
.../dc/resource/dcn42/dcn42_resource.c | 2 +
.../dc/resource/dcn42b/dcn42b_resource.c | 5 +-
.../dc/resource/dcn60/dcn60_resource.c | 30 +-
.../dcn401/dcn401_soc_and_ip_translator.c | 4 +-
.../dcn42/dcn42_soc_and_ip_translator.c | 4 +-
.../dcn42b/dcn42b_soc_and_ip_translator.c | 4 +-
.../dcn60/dcn60_soc_and_ip_translator.c | 96 +-
.../amd/display/dc/state_facts/state_facts.c | 294 +++
.../amd/display/dc/state_facts/state_facts.h | 132 +
.../gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 213 +-
.../amd/display/modules/power/power_replay.c | 2 +-
125 files changed, 6985 insertions(+), 905 deletions(-)
create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h
create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
create mode 100644 drivers/gpu/drm/amd/display/dc/state_facts/state_facts.c
create mode 100644 drivers/gpu/drm/amd/display/dc/state_facts/state_facts.h
--
2.43.0
^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH 01/74] drm/amd/display: Fix elapsed time indentation
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 02/74] drm/amd/display: Correct the forced YCbCr420 comment Wayne Lin
` (73 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
From: Alex Hung <alex.hung@amd.com>
[WHAT]
Fix incorrect three-tab indentation in hdmi_frl_perform_link_training.
No functional change.
Signed-off-by: Alex Hung <alex.hung@amd.com>
---
.../gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
index 960f18fe275b..097df185a618 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
@@ -514,8 +514,7 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
do {
flt_poll_curr = dm_get_timestamp(ddc_service->ctx);
- elapsed = dm_get_elapse_time_in_ns(ddc_service->ctx,
- flt_poll_curr, flt_poll_start);
+ elapsed = dm_get_elapse_time_in_ns(ddc_service->ctx, flt_poll_curr, flt_poll_start);
if (elapsed < (num_polls * wait_time_ns))
continue;
num_polls++;
@@ -622,8 +621,7 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
do {
flt_poll_curr = dm_get_timestamp(ddc_service->ctx);
- elapsed = dm_get_elapse_time_in_ns(ddc_service->ctx,
- flt_poll_curr, flt_poll_start);
+ elapsed = dm_get_elapse_time_in_ns(ddc_service->ctx, flt_poll_curr, flt_poll_start);
if (elapsed < (num_polls * wait_time_ns))
continue;
num_polls++;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 02/74] drm/amd/display: Correct the forced YCbCr420 comment
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
2026-11-11 1:28 ` [PATCH 01/74] drm/amd/display: Fix elapsed time indentation Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 03/74] drm/amd/display: Remove duplicate block sequence leftovers Wayne Lin
` (72 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
From: Fangzhi Zuo <jerry.zuo@amd.com>
[Why]
The comment said an explicit YCbCr420 force is honoured on modes that
only advertise RGB or 4:4:4. The check below does not do that. A forced
YCbCr420 is admitted only when the mode is 4:2:0-only or 4:2:0-also.
HDMI 2.0/CTA-861 do not permit 4:2:0 on any other VIC (HF1-31, HF1-32,
HF1-51).
[How]
Correct the YCbCr420 sentences in the comment so they match that
check. Encoding selection is unchanged. The RGB-force sentence is
left as it was.
Cc: Ivan Lipski <ivan.lipski@amd.com>
Signed-off-by: Fangzhi Zuo <jerry.zuo@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/amdgpu_dm/amdgpu_dm_connector.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index e8f050feb6e3..430b2667bcb5 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -2426,14 +2426,14 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector,
* - A 420-only mode collapses the mask to YCbCr420 alone.
* - The debugfs force_yuv_pixel_format override pins the encoding to a
* single dc_pixel_encoding when set (PIXEL_ENCODING_UNDEFINED means
- * "no override"). An explicit YCbCr420 force is honoured even on
- * modes the sink only lists as RGB/4:4:4 capable
- * (drm_mode_is_420_also() clear), as required for HDMI compliance
- * testing; dc_validate_stream() still rejects anything the link
- * genuinely cannot carry. The YCbCr422/YCbCr444 forces stay gated on
- * the sink's advertised caps. An RGB force pins to RGB alone and is
- * always honoured, since RGB is the mandatory baseline every sink
- * (DP or HDMI) supports.
+ * "no override"). A YCbCr420 force is gated on the mode actually
+ * advertising 4:2:0 (drm_mode_is_420_only()/drm_mode_is_420_also());
+ * HDMI 2.0/CTA-861 only permit 4:2:0 on those VICs, so forcing it on
+ * an RGB/4:4:4-only mode would violate the "Y420 Not Allowed"
+ * compliance requirement (HF1-31/HF1-32). The YCbCr422/YCbCr444
+ * forces stay gated on the sink's advertised caps. An RGB force pins
+ * to RGB alone and is always honoured, since RGB is the mandatory
+ * baseline every sink (DP or HDMI) supports.
*/
want_420 = (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) ||
(drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 03/74] drm/amd/display: Remove duplicate block sequence leftovers
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
2026-11-11 1:28 ` [PATCH 01/74] drm/amd/display: Fix elapsed time indentation Wayne Lin
2026-11-11 1:28 ` [PATCH 02/74] drm/amd/display: Correct the forced YCbCr420 comment Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 04/74] drm/amd/display: Skip UPSP programming when UPSP regs are absent Wayne Lin
` (71 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Rafal Ostrowski, Alvin Lee
From: Rafal Ostrowski <rafal.ostrowski@amd.com>
[Why]
hw_sequencer.h carried parameter structs, union members, executor
declarations that no builder, adder or dispatcher case uses.
Each one duplicates work that existing opcodes already do.
These are also breaking BLS rules as contain dc,
dc_state or pipe_ctx pointers.
Below list contains removed structures and information where
it is already supported by other HWSS operations:
- program_tg_params, hwss_program_tg:
TG_PROGRAM_GLOBAL_SYNC, TG_WAIT_FOR_STATE, TG_SET_VTG_PARAMS,
TG_SETUP_VERTICAL_INTERRUPT2
- setup_vupdate_interrupt_params:
TG_SETUP_VERTICAL_INTERRUPT2
- update_dchubp_dpp_params:
update_dchubp_dpp_sequence steps
- set_disp_pattern_generator_params:
OPP_SET_DISP_PATTERN_GENERATOR
- mpc_update_mpcc_params, hwss_mpc_update_mpcc:
MPC_UPDATE_BLENDING, MPC_REMOVE_MPCC, MPC_INSERT_PLANE,
MPC_UPDATE_VISUAL_CONFIRM
- hubp_program_mcache_id_and_split_coordinate_params:
HUBP_PROGRAM_MCACHE_ID
- hwss_blank_pixel_data:
blank_pixel_data_sequence steps
- PROGRAM_MALL_PIPE_CONFIG:
HUBP_UPDATE_MALL_SEL and HUBP_PREPARE_SUBVP_BUFFERING from
program_mall_pipe_config_sequence
[How]
Remove the unused structs and union members, the hwss_mpc_update_mpcc
executor, the hwss_blank_pixel_data and hwss_program_tg declarations and
the PROGRAM_MALL_PIPE_CONFIG enumerator.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Rafal Ostrowski <rafal.ostrowski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 10 ----
.../drm/amd/display/dc/hwss/hw_sequencer.h | 51 -------------------
2 files changed, 61 deletions(-)
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 694c425e22b4..512e128d52b3 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
@@ -4191,16 +4191,6 @@ void hwss_dpp_set_cursor_matrix(union block_sequence_params *params)
dpp->funcs->set_cursor_matrix(dpp, color_space, *cursor_csc_color_matrix);
}
-void hwss_mpc_update_mpcc(union block_sequence_params *params)
-{
- struct dc *dc = params->mpc_update_mpcc_params.dc;
- struct pipe_ctx *pipe_ctx = params->mpc_update_mpcc_params.pipe_ctx;
- struct dce_hwseq *hws = dc->hwseq;
-
- if (hws->funcs.update_mpcc)
- hws->funcs.update_mpcc(dc, pipe_ctx);
-}
-
void hwss_mpc_update_blending(union block_sequence_params *params)
{
struct mpc *mpc = params->mpc_update_blending_params.mpc;
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 6580d13d9041..9f97503a02ff 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -456,12 +456,6 @@ struct dsc_enable_with_opp_params {
int opp_inst;
};
-struct program_tg_params {
- struct dc *dc;
- struct pipe_ctx *pipe_ctx;
- struct dc_state *context;
-};
-
struct tg_program_global_sync_params {
struct timing_generator *tg;
int vready_offset;
@@ -504,11 +498,6 @@ struct pipe_control_lock_params {
struct tg_3dlut_wa_unlock_params tg_3dlut_wa_unlock_params;
};
-struct setup_vupdate_interrupt_params {
- struct dc *dc;
- struct pipe_ctx *pipe_ctx;
-};
-
struct tg_setup_vertical_interrupt2_params {
struct timing_generator *tg;
int start_line;
@@ -531,12 +520,6 @@ struct program_det_segments_params {
unsigned int det_size;
};
-struct update_dchubp_dpp_params {
- struct dc *dc;
- struct pipe_ctx *pipe_ctx;
- struct dc_state *context;
-};
-
struct opp_set_dyn_expansion_params {
struct output_pixel_processor *opp;
enum dc_color_space color_space;
@@ -583,18 +566,6 @@ struct set_abm_immediate_disable_params {
struct pipe_ctx *pipe_ctx;
};
-struct set_disp_pattern_generator_params {
- struct dc *dc;
- struct pipe_ctx *pipe_ctx;
- enum controller_dp_test_pattern test_pattern;
- enum controller_dp_color_space color_space;
- enum dc_color_depth color_depth;
- const struct tg_color *solid_color;
- int width;
- int height;
- int offset;
-};
-
struct mpc_update_blending_params {
struct mpc *mpc;
struct mpcc_blnd_cfg blnd_cfg;
@@ -935,11 +906,6 @@ struct dpp_set_cursor_matrix_params {
struct dc_csc_transform *cursor_csc_color_matrix;
};
-struct mpc_update_mpcc_params {
- struct dc *dc;
- struct pipe_ctx *pipe_ctx;
-};
-
struct dpp_set_scaler_params {
struct dpp *dpp;
const struct scaler_data *scl_data;
@@ -951,11 +917,6 @@ struct hubp_mem_program_viewport_params {
const struct rect *viewport_c;
};
-struct hubp_program_mcache_id_and_split_coordinate_params {
- struct hubp *hubp;
- struct mcache_regs_struct *mcache_regs;
-};
-
struct cursor_lock_params {
struct dc *dc;
struct pipe_ctx *pipe_ctx;
@@ -1237,7 +1198,6 @@ union block_sequence_params {
struct dsc_disconnect_params dsc_disconnect_params;
struct dsc_read_state_params dsc_read_state_params;
struct dsc_enable_with_opp_params dsc_enable_with_opp_params;
- struct program_tg_params program_tg_params;
struct tg_program_global_sync_params tg_program_global_sync_params;
struct tg_wait_for_state_params tg_wait_for_state_params;
struct tg_set_vtg_params_params tg_set_vtg_params_params;
@@ -1245,10 +1205,8 @@ union block_sequence_params {
struct dpp_set_hdr_multiplier_params dpp_set_hdr_multiplier_params;
struct tg_set_gsl_params tg_set_gsl_params;
struct tg_set_gsl_source_select_params tg_set_gsl_source_select_params;
- struct setup_vupdate_interrupt_params setup_vupdate_interrupt_params;
struct program_det_size_params program_det_size_params;
struct program_det_segments_params program_det_segments_params;
- struct update_dchubp_dpp_params update_dchubp_dpp_params;
struct opp_set_dyn_expansion_params opp_set_dyn_expansion_params;
struct opp_program_fmt_params opp_program_fmt_params;
struct opp_program_bit_depth_reduction_params opp_program_bit_depth_reduction_params;
@@ -1256,7 +1214,6 @@ union block_sequence_params {
struct set_abm_pipe_params set_abm_pipe_params;
struct set_abm_level_params set_abm_level_params;
struct set_abm_immediate_disable_params set_abm_immediate_disable_params;
- struct set_disp_pattern_generator_params set_disp_pattern_generator_params;
struct mpc_remove_mpcc_params mpc_remove_mpcc_params;
struct dc_set_optimized_required_params dc_set_optimized_required_params;
struct hubp_disconnect_params hubp_disconnect_params;
@@ -1318,7 +1275,6 @@ union block_sequence_params {
struct hubp_setup_interdependent2_params hubp_setup_interdependent2_params;
struct hubp_setup_interdependent_params hubp_setup_interdependent_params;
struct dpp_set_cursor_matrix_params dpp_set_cursor_matrix_params;
- struct mpc_update_mpcc_params mpc_update_mpcc_params;
struct mpc_update_blending_params mpc_update_blending_params;
struct mpc_assert_idle_mpcc_params mpc_assert_idle_mpcc_params;
struct mpc_insert_plane_params mpc_insert_plane_params;
@@ -1467,7 +1423,6 @@ enum block_sequence_func {
HUBP_WAIT_FLIP_PENDING,
TG_WAIT_DOUBLE_BUFFER_PENDING,
UPDATE_FORCE_PSTATE,
- PROGRAM_MALL_PIPE_CONFIG,
HUBBUB_APPLY_DEDCN21_147_WA,
MPC_SET_DWB_MUX,
MPC_DISABLE_DWB_MUX,
@@ -2112,8 +2067,6 @@ void hwss_opp_pipe_clock_control(union block_sequence_params *params);
void hwss_opp_program_left_edge_extra_pixel(union block_sequence_params *params);
-void hwss_blank_pixel_data(union block_sequence_params *params);
-
void hwss_dccg_set_dto_dscclk(union block_sequence_params *params);
void hwss_dsc_set_config(union block_sequence_params *params);
@@ -2164,8 +2117,6 @@ void hwss_prepare_bandwidth(struct dc *dc, union block_sequence_params *params);
void hwss_link_set_dpms_on(union block_sequence_params *params);
-void hwss_program_tg(union block_sequence_params *params);
-
void hwss_tg_program_global_sync(union block_sequence_params *params);
void hwss_tg_wait_for_state(union block_sequence_params *params);
@@ -2327,8 +2278,6 @@ void hwss_hubp_setup_interdependent(union block_sequence_params *params);
void hwss_dpp_set_cursor_matrix(union block_sequence_params *params);
-void hwss_mpc_update_mpcc(union block_sequence_params *params);
-
void hwss_mpc_update_blending(union block_sequence_params *params);
void hwss_mpc_assert_idle_mpcc(union block_sequence_params *params);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 04/74] drm/amd/display: Skip UPSP programming when UPSP regs are absent
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (2 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 03/74] drm/amd/display: Remove duplicate block sequence leftovers Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 05/74] drm/amd/display: Dispatch commit_state_hw through dc2_funcs Wayne Lin
` (70 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Eric Bernstein,
Nicholas Kazlauskas, Eric Bernstein
From: Eric Bernstein <Eric.Bernstein@amd.com>
why:
DPP60 is shared by ASICs without a UPSP block. On those, UPSP register
offsets are 0 but masks may be non-zero, causing writes to address 0.
how:
Gate UPSP register access in DPP60 on the CNVC_UPSP_MODE register offset
instead of the field mask.
Cc: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Signed-off-by: Eric Bernstein <eric.bernstein@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c | 2 +-
drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c
index 2ba8218e2ac2..dfdbc539ac42 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c
@@ -321,7 +321,7 @@ void dcn6_dpp_reset(struct dpp *dpp_base)
struct dcn60_dpp *dpp = TO_DCN60_DPP(dpp_base);
if (dpp->scl_data.upsp) {
- if (dpp->tf_mask->UPSP_MODE) {
+ if (REG(CNVC_UPSP_MODE)) {
// Reset dpp registers used for upsp, lls
REG_WRITE(CNVC_UPSP_MODE, 0);
REG_WRITE(PRE_CSC_MODE, 0);
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c
index d1a81e5616b3..b75eb2772799 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c
@@ -288,6 +288,10 @@ void dpp60_dscl_program_upsp(struct dpp *dpp_base,
{
struct dcn60_dpp *dpp = TO_DCN60_DPP(dpp_base);
+ /* UPSP block is not present on all ASICs */
+ if (!REG(CNVC_UPSP_MODE))
+ return;
+
REG_SET_8(CNVC_UPSP_MODE, 0,
UPSP_MODE, dscl_prog_data->upsp_mode,
UPSP_V_NUM_TAPS, dscl_prog_data->upsp_v_num_taps,
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 05/74] drm/amd/display: Dispatch commit_state_hw through dc2_funcs
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (3 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 04/74] drm/amd/display: Skip UPSP programming when UPSP regs are absent Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 06/74] drm/amd/display: Add leaf BLS entries for a virtual commit Wayne Lin
` (69 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
dc_commit_state_no_check is one function, so Core2 has no place to
run the hardware span of a commit. Calling that span from Core2
would put a DC1 helper on the Core2 path, which a native commit is
not allowed to do. A Core2 display therefore cannot be lit, and a
native builder has nowhere to attach.
Split the hardware span out as legacy_commit_state_hw and dispatch
it through commit_state_hw on dc2_funcs. The shim calls the Core2
function only when DC2_SELECTION_CORE2 is set. Every other
selection keeps the prologue, the legacy span and the epilogue,
including the early return when the hardware span fails.
core2_commit_state_hw returns DC_NOT_SUPPORTED and does not fall
back to DC1. The builders a later native commit needs are created
once in core2_construct and stored on struct dc2. Hardware init
reads that table instead of calling the factory again. A DCN with
no builder factory keeps NULL slots.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/core/dc.c | 46 ++++++++++++-------
.../gpu/drm/amd/display/dc/core2/dc_core2.c | 17 ++++++-
.../gpu/drm/amd/display/dc/core2/dc_core2.h | 1 +
.../drm/amd/display/dc/core2/dc_core2_init.c | 2 +-
.../display/dc/core2/dc_core2_legacy_init.c | 2 +-
.../amd/display/dc/dc_api_dispatch/api_shim.c | 7 +++
.../amd/display/dc/dc_api_dispatch/api_shim.h | 5 +-
.../amd/display/dc/inc/dc_core_interface.h | 5 ++
8 files changed, 65 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 8344d821d2e8..6b90c88b6cc3 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -2348,31 +2348,17 @@ static void determine_pipe_unlock_order(struct dc *dc, struct dc_state *context)
}
}
-/**
- * dc_commit_state_no_check - Apply context to the hardware
- *
- * @dc: DC object with the current status to be updated
- * @context: New state that will become the current status at the end of this function
- *
- * Applies given context to the hardware and copy it into current context.
- * It's up to the user to release the src context afterwards.
- *
- * Return: an enum dc_status result code for the operation
- */
-static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *context)
+/* Hardware span of a commit. The API shim picks Core2 or this body. */
+enum dc_status legacy_commit_state_hw(struct dc *dc, struct dc_state *context)
{
struct dc_bios *dcb = dc->ctx->dc_bios;
enum dc_status result = DC_ERROR_UNEXPECTED;
struct pipe_ctx *pipe;
unsigned int i, k, l;
struct dc_stream_state *dc_streams[MAX_STREAMS] = {0};
- struct dc_state *old_state;
bool subvp_prev_use = false;
bool dmub_locked = false;
- dc_z10_restore(dc);
- dc_allow_idle_optimizations(dc, false);
-
for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
@@ -2564,6 +2550,33 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
if (dc->hwss.update_dsc_pg)
dc->hwss.update_dsc_pg(dc, context, true);
+ return result;
+}
+
+/**
+ * dc_commit_state_no_check - Apply context to the hardware
+ *
+ * @dc: DC object with the current status to be updated
+ * @context: New state that will become the current status at the end of this function
+ *
+ * Applies given context to the hardware and copy it into current context.
+ * It's up to the user to release the src context afterwards.
+ *
+ * Return: an enum dc_status result code for the operation
+ */
+static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *context)
+{
+ enum dc_status result;
+ unsigned int i;
+ struct dc_state *old_state;
+
+ dc_z10_restore(dc);
+ dc_allow_idle_optimizations(dc, false);
+
+ result = api_shim_commit_state_hw(dc, context);
+ if (result != DC_OK)
+ return result;
+
if (dc->ctx->dce_version >= DCE_VERSION_MAX)
TRACE_DCN_CLOCK_STATE(&context->bw_ctx.bw.dcn.clk);
else
@@ -6771,6 +6784,7 @@ void legacy_hardware_release(struct dc *dc)
static const struct dc2_funcs dc2_legacy_funcs_table = {
.hardware_init = legacy_hardware_init,
.hardware_release = legacy_hardware_release,
+ .commit_state_hw = legacy_commit_state_hw,
};
const struct dc2_funcs *dc2_legacy_funcs(void)
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
index f496e4d66575..8ef078e01dd4 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
@@ -7,6 +7,7 @@
#include "dc.h"
#include "core2/dc_core2.h"
#include "core2/dc_core2_init.h"
+#include "dc_api_dispatch/api_shim.h"
static void core2_hardware_release(struct dc *dc)
{
@@ -16,6 +17,7 @@ static void core2_hardware_release(struct dc *dc)
static const struct dc2_funcs core2_funcs_table = {
.hardware_init = core2_hardware_init,
.hardware_release = core2_hardware_release,
+ .commit_state_hw = core2_commit_state_hw,
};
const struct dc2_funcs *core2_funcs(void)
@@ -25,10 +27,23 @@ const struct dc2_funcs *core2_funcs(void)
void core2_construct(struct dc *dc)
{
- (void)dc;
+ struct dc2 *dc2 = TO_DC2(dc);
+ unsigned int name;
+
+ for (name = 0; name < BLS_BUILDER_NAME_COUNT; name++)
+ dc2->builders[name] = bls_builder_create(dc, name);
}
void core2_destruct(struct dc *dc)
{
(void)dc;
}
+
+/* Never falls back to legacy_commit_state_hw: DC2 must not call DC1. */
+enum dc_status core2_commit_state_hw(struct dc *dc, struct dc_state *context)
+{
+ (void)dc;
+ (void)context;
+
+ return DC_NOT_SUPPORTED;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.h b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.h
index a761e85a9973..7537863dcda4 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.h
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.h
@@ -15,5 +15,6 @@ struct dc_core2 {
/* Core2-local; selection lives in the API shim, not here. */
const struct dc2_funcs *core2_funcs(void);
+enum dc_status core2_commit_state_hw(struct dc *dc, struct dc_state *context);
void core2_construct(struct dc *dc);
void core2_destruct(struct dc *dc);
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2_init.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2_init.c
index 69a376351df1..319aa3e219d2 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2_init.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2_init.c
@@ -42,7 +42,7 @@ static void core2_run_native_hardware_init(struct dc *dc)
const struct bls_builder *builder;
enum bls_builder_result result;
- builder = bls_builder_create(dc, DCN_DECL_INITIALIZATION);
+ builder = TO_DC2(dc)->builders[DCN_DECL_INITIALIZATION];
if (!builder || builder->kind != BLS_BUILDER_DECLARATIVE ||
!builder->build.declarative)
return;
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2_legacy_init.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2_legacy_init.c
index 285dfc9e6e69..760cb25e9cbe 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2_legacy_init.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2_legacy_init.c
@@ -199,7 +199,7 @@ bool core2_run_legacy_hardware_init(struct dc *dc,
info = &dc->current_state->hw_init_info;
memset(info, 0, sizeof(*info));
info->next_stage = LEGACY_INIT_STAGE_CLOCKS;
- builder = bls_builder_create(dc, DCN_DECL_INITIALIZATION_LEGACY);
+ builder = TO_DC2(dc)->builders[DCN_DECL_INITIALIZATION_LEGACY];
if (!builder || builder->kind != BLS_BUILDER_DECLARATIVE ||
!builder->build.declarative)
return false;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.c b/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.c
index 3dcfde8308c5..eca9b2de92fa 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.c
@@ -30,3 +30,10 @@ void dc_hardware_release(struct dc *dc)
else
legacy_hardware_release(dc);
}
+
+enum dc_status api_shim_commit_state_hw(struct dc *dc, struct dc_state *context)
+{
+ if (TO_DC2(dc)->selection == DC2_SELECTION_CORE2)
+ return core2_funcs()->commit_state_hw(dc, context);
+ return legacy_commit_state_hw(dc, context);
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.h b/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.h
index b972ae06663d..6d9ad4192296 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.h
@@ -7,7 +7,7 @@
#include "dc.h"
#include "inc/dc_core_interface.h"
-#include "bls_builder/dmub_abi.h"
+#include "bls_builder/bls_builder.h"
struct dc2 {
/* First member: TO_DC2 is a typed upcast, not container_of. */
@@ -16,8 +16,11 @@ struct dc2 {
/* Resolved by hardware init; later builds take it as dmub_abi. */
struct dmub_abi dmub_abi;
bool dmub_abi_resolved;
+ /* Set once by core2_construct; NULL where this DCN has no builder. */
+ const struct bls_builder *builders[BLS_BUILDER_NAME_COUNT];
};
#define TO_DC2(ptr) ((struct dc2 *)(ptr))
void api_shim_construct(struct dc *dc, const struct dc_init_data *init_params);
+enum dc_status api_shim_commit_state_hw(struct dc *dc, struct dc_state *context);
diff --git a/drivers/gpu/drm/amd/display/dc/inc/dc_core_interface.h b/drivers/gpu/drm/amd/display/dc/inc/dc_core_interface.h
index 35a6218c1285..383d14af91f1 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/dc_core_interface.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/dc_core_interface.h
@@ -5,7 +5,10 @@
#pragma once
+#include "core_status.h"
+
struct dc;
+struct dc_state;
/*
* Construction-time Core implementation selection. Zero is LEGACY so existing
@@ -24,8 +27,10 @@ enum dc2_hw_ownership {
struct dc2_funcs {
void (*hardware_init)(struct dc *dc);
void (*hardware_release)(struct dc *dc);
+ enum dc_status (*commit_state_hw)(struct dc *dc, struct dc_state *context);
};
void legacy_hardware_init(struct dc *dc);
void legacy_hardware_release(struct dc *dc);
+enum dc_status legacy_commit_state_hw(struct dc *dc, struct dc_state *context);
const struct dc2_funcs *dc2_legacy_funcs(void);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 06/74] drm/amd/display: Add leaf BLS entries for a virtual commit
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (4 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 05/74] drm/amd/display: Dispatch commit_state_hw through dc2_funcs Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 07/74] drm/amd/display: Build the dcn401 virtual commit natively Wayne Lin
` (68 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
The virtual enable and stream-off that DC1 performs sit inside HWSS
and link helpers: dcn401_enable_stream, link_set_dpms_on,
link_set_dpms_off, dcn401_prepare_bandwidth and
dcn20_wait_for_blank_complete. The DC2 executor has no leaf entry
for those calls, so a builder cannot emit them without calling DC1.
Add one block_sequence entry per call, with its params next to the
enum in hw_sequencer.h, and a DC2 case that makes that single call.
Entries the virtual path already shares with other builders keep
their enum values and params. TG_LOCK calls lock or unlock
directly; preflight rejects the DMUB-inbox1 and triple-buffer
variants because they are not leaf calls. HUBP_PG_CONTROL and
DC_IP_REQUEST_CNTL stay hwseq calls because the hwseq owns those
registers. Preflight checks the block, the function and any pointer
payload before the sequence runs.
Match DC1 when a call fails. enable_crtc, disable_crtc and
program_pix_clk stop the sequence. A blank-wait timeout and a
failed cs_power_down follow continue_on_error, because DC1 only
logs them. A clock raise whose program_watermarks or
program_arbiter reports a change sets dc->optimized_required.
DC1 never emits the new enum values, and hwss_execute_sequence is
unchanged, so the legacy commit is unaffected. The builder that
orders these entries follows in a later patch.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../display/dc/bls_executor/bls_executor.c | 584 ++++++++++++++++++
.../drm/amd/display/dc/hwss/hw_sequencer.h | 192 ++++++
2 files changed, 776 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
index 7600954c3478..5111c65fd961 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
@@ -13,8 +13,12 @@
#include "abm.h"
#include "audio.h"
#include "dchubbub.h"
+#include "hubp.h"
+#include "dpp.h"
+#include "mpc.h"
#include "dio.h"
#include "link_encoder.h"
+#include "vpg.h"
#include "panel_cntl.h"
#include "link_service.h"
#include "dc_bios_types.h"
@@ -31,6 +35,15 @@ static noinline_for_stack bool dc2_bls_step_supported(const struct dc2 *dc2,
struct clk_mgr *clk_mgr;
struct dccg *dccg;
struct dc_context *ctx;
+ struct hubbub *hubbub;
+ struct timing_generator *tg;
+ struct output_pixel_processor *opp;
+ struct clock_source *cs;
+ struct link_encoder *link_enc;
+ struct dce_hwseq *hws;
+ struct hubp *hubp;
+ struct dpp *dpp;
+ struct mpc *mpc;
switch (step->func) {
case LEGACY_CLK_MGR_INIT_CLOCKS:
@@ -283,6 +296,197 @@ static noinline_for_stack bool dc2_bls_step_supported(const struct dc2 *dc2,
return hubbub && hubbub->funcs &&
hubbub->funcs->force_pstate_change_control;
}
+ case CLK_MGR_UPDATE_CLOCKS_FOR_STATE:
+ clk_mgr = step->params.clk_mgr_update_clocks_for_state_params
+ .clk_mgr;
+ return clk_mgr && clk_mgr->funcs && clk_mgr->funcs->update_clocks &&
+ step->params.clk_mgr_update_clocks_for_state_params
+ .context;
+ case HUBBUB_PROGRAM_WATERMARKS:
+ hubbub = step->params.hubbub_program_watermarks_params.hubbub;
+ return hubbub && hubbub->funcs &&
+ hubbub->funcs->program_watermarks &&
+ step->params.hubbub_program_watermarks_params
+ .watermarks;
+ case HUBBUB_PROGRAM_ARBITER:
+ hubbub = step->params.hubbub_program_arbiter_params.hubbub;
+ return hubbub && hubbub->funcs &&
+ hubbub->funcs->program_arbiter &&
+ step->params.hubbub_program_arbiter_params.arb_regs;
+ case HUBBUB_PROGRAM_COMPBUF_SEGMENTS:
+ hubbub = step->params.hubbub_program_compbuf_segments_params
+ .hubbub;
+ return hubbub && hubbub->funcs &&
+ hubbub->funcs->program_compbuf_segments;
+ case HUBP_PROGRAM_DET_SEGMENTS:
+ hubbub = step->params.program_det_segments_params.hubbub;
+ return hubbub && hubbub->funcs &&
+ hubbub->funcs->program_det_segments;
+ case TG_LOCK: {
+ const struct tg_lock_params *p = &step->params.tg_lock_params;
+
+ /* DMUB inbox1 and triple-buffer locks are not leaf calls. */
+ return p->tg && p->tg->funcs && !p->use_dmub_inbox1 &&
+ !p->triplebuffer_flips &&
+ (p->lock ? p->tg->funcs->lock : p->tg->funcs->unlock);
+ }
+ case TG_ENABLE_CRTC:
+ tg = step->params.tg_enable_crtc_params.tg;
+ return tg && tg->funcs && tg->funcs->enable_crtc;
+ case TG_DISABLE_CRTC:
+ tg = step->params.tg_disable_crtc_params.tg;
+ return tg && tg->funcs && tg->funcs->disable_crtc;
+ case TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR:
+ tg = step->params.tg_wait_odm_doublebuffer_pending_clear_params.tg;
+ return tg && tg->funcs &&
+ tg->funcs->wait_odm_doublebuffer_pending_clear;
+ case TG_PROGRAM_STEREO:
+ tg = step->params.tg_program_stereo_params.tg;
+ return tg && tg->funcs && tg->funcs->program_stereo &&
+ step->params.tg_program_stereo_params.timing;
+ case OPP_PROGRAM_STEREO:
+ opp = step->params.opp_program_stereo_params.opp;
+ return opp && opp->funcs && opp->funcs->opp_program_stereo &&
+ step->params.opp_program_stereo_params.timing;
+ case TG_WAIT_FOR_STATE:
+ tg = step->params.tg_wait_for_state_params.tg;
+ return tg && tg->funcs && tg->funcs->wait_for_state;
+ case TG_SETUP_VERTICAL_INTERRUPT2:
+ tg = step->params.tg_setup_vertical_interrupt2_params.tg;
+ return tg && tg->funcs && tg->funcs->setup_vertical_interrupt2;
+ case OPTC_SET_ODM_BYPASS:
+ tg = step->params.set_odm_bypass_params.tg;
+ return tg && tg->funcs && tg->funcs->set_odm_bypass &&
+ step->params.set_odm_bypass_params.timing;
+ case TG_ENABLE_OPTC_CLOCK:
+ tg = step->params.tg_enable_optc_clock_params.tg;
+ return tg && tg->funcs && tg->funcs->enable_optc_clock;
+ case TG_PROGRAM_TIMING:
+ tg = step->params.tg_program_timing_params.tg;
+ return tg && tg->funcs && tg->funcs->program_timing &&
+ step->params.tg_program_timing_params.timing;
+ case TG_SET_H_TIMING_DIV_MANUAL_MODE:
+ tg = step->params.tg_set_h_timing_div_manual_mode_params.tg;
+ return tg && tg->funcs &&
+ tg->funcs->set_h_timing_div_manual_mode;
+ case TG_SET_DRR:
+ tg = step->params.tg_set_drr_params.tg;
+ return tg && tg->funcs && tg->funcs->set_drr;
+ case TG_SET_STATIC_SCREEN_CONTROL:
+ tg = step->params.tg_set_static_screen_control_params.tg;
+ return tg && tg->funcs && tg->funcs->set_static_screen_control;
+ case TG_SET_OUT_MUX:
+ tg = step->params.tg_set_out_mux_params.tg;
+ return tg && tg->funcs && tg->funcs->set_out_mux;
+ case TG_SET_EARLY_CONTROL:
+ tg = step->params.tg_set_early_control_params.tg;
+ return tg && tg->funcs && tg->funcs->set_early_control;
+ case OPP_PROGRAM_FMT:
+ opp = step->params.opp_program_fmt_params.opp;
+ return opp && opp->funcs && opp->funcs->opp_program_fmt &&
+ step->params.opp_program_fmt_params.fmt_bit_depth &&
+ step->params.opp_program_fmt_params.clamping;
+ case OPP_SET_DYN_EXPANSION:
+ opp = step->params.opp_set_dyn_expansion_params.opp;
+ return opp && opp->funcs && opp->funcs->opp_set_dyn_expansion;
+ case OPP_PIPE_CLOCK_CONTROL:
+ opp = step->params.opp_pipe_clock_control_params.opp;
+ return opp && opp->funcs && opp->funcs->opp_pipe_clock_control;
+ case OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL:
+ opp = step->params.opp_program_left_edge_extra_pixel_params.opp;
+ return opp && opp->funcs &&
+ opp->funcs->opp_program_left_edge_extra_pixel;
+ case OPP_SET_DISP_PATTERN_GENERATOR:
+ opp = step->params.opp_set_disp_pattern_generator_params.opp;
+ return opp && opp->funcs &&
+ opp->funcs->opp_set_disp_pattern_generator;
+ case OPP_WAIT_FOR_BLANK_COMPLETE:
+ opp = step->params.opp_wait_for_blank_complete_params.opp;
+ return opp && opp->funcs && opp->funcs->dpg_is_pending &&
+ opp->funcs->dpg_is_blanked &&
+ step->params.opp_wait_for_blank_complete_params
+ .polling_interval_us;
+ case DCCG_SET_PIXEL_RATE_DIV:
+ dccg = step->params.dccg_set_pixel_rate_div_params.dccg;
+ return dccg && dccg->funcs && dccg->funcs->set_pixel_rate_div;
+ case DCCG_DISABLE_SYMCLK_SE:
+ dccg = step->params.dccg_disable_symclk_se_params.dccg;
+ return dccg && dccg->funcs && dccg->funcs->disable_symclk_se;
+ case DCCG_ENABLE_SYMCLK_SE:
+ dccg = step->params.dccg_enable_symclk_se_params.dccg;
+ return dccg && dccg->funcs && dccg->funcs->enable_symclk_se;
+ case DCCG_SET_DTBCLK_P_SRC:
+ dccg = step->params.dccg_set_dtbclk_p_src_params.dccg;
+ return dccg && dccg->funcs && dccg->funcs->set_dtbclk_p_src;
+ case CLK_SRC_PROGRAM_PIX_CLK: {
+ const struct clk_src_program_pix_clk_params *p =
+ &step->params.clk_src_program_pix_clk_params;
+
+ return p->clock_source && p->clock_source->funcs &&
+ p->clock_source->funcs->program_pix_clk &&
+ p->pix_clk_params && p->pll_settings;
+ }
+ case CLK_SRC_POWER_DOWN:
+ cs = step->params.clk_src_power_down_params.clock_source;
+ return cs && cs->funcs && cs->funcs->cs_power_down;
+ case DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE:
+ link_enc = step->params.link_enc_connect_dig_be_to_fe_params
+ .link_enc;
+ return link_enc && link_enc->funcs &&
+ link_enc->funcs->connect_dig_be_to_fe;
+ case DIO_LINK_ENC_ENABLE_DP_OUTPUT:
+ link_enc = step->params.link_enc_enable_dp_output_params
+ .link_enc;
+ return link_enc && link_enc->funcs &&
+ link_enc->funcs->enable_dp_output &&
+ step->params.link_enc_enable_dp_output_params
+ .link_settings;
+ case DIO_LINK_ENC_DISABLE_OUTPUT:
+ link_enc = step->params.link_enc_disable_output_params.link_enc;
+ return link_enc && link_enc->funcs &&
+ link_enc->funcs->disable_output;
+ case BIOS_SET_SCRATCH_CRITICAL_STATE: {
+ struct dc_bios *dcb =
+ step->params.bios_set_scratch_critical_state_params.dcb;
+
+ return dcb && dcb->funcs && dcb->funcs->set_scratch_critical_state;
+ }
+ case HUBP_PG_CONTROL:
+ hws = step->params.hubp_pg_control_params.hws;
+ return hws && hws->funcs.hubp_pg_control;
+ case DC_IP_REQUEST_CNTL:
+ hws = step->params.dc_ip_request_cntl_params.hws;
+ return hws && hws->funcs.dc_ip_request_cntl;
+ case HUBP_CLK_CNTL:
+ hubp = step->params.hubp_clk_cntl_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->hubp_clk_cntl;
+ case HUBP_SET_BLANK:
+ hubp = step->params.hubp_set_blank_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->set_blank;
+ case HUBP_UPDATE_MALL_SEL:
+ hubp = step->params.hubp_update_mall_sel_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->hubp_update_mall_sel;
+ case HUBP_SET_FLIP_CONTROL_GSL:
+ hubp = step->params.set_flip_control_gsl_params.hubp;
+ return hubp && hubp->funcs &&
+ hubp->funcs->hubp_set_flip_control_surface_gsl;
+ case HUBP_RESET:
+ hubp = step->params.hubp_reset_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->hubp_reset;
+ case DPP_RESET:
+ dpp = step->params.dpp_reset_params.dpp;
+ return dpp && dpp->funcs && dpp->funcs->dpp_reset;
+ case MPC_ASSERT_IDLE_MPCC:
+ mpc = step->params.mpc_assert_idle_mpcc_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->wait_for_idle;
+ case DPP_DPPCLK_CONTROL:
+ dpp = step->params.dpp_dppclk_control_params.dpp;
+ return dpp && dpp->funcs && dpp->funcs->dpp_dppclk_control;
+ case MPC_REMOVE_MPCC:
+ mpc = step->params.mpc_remove_mpcc_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->remove_mpcc &&
+ step->params.mpc_remove_mpcc_params.mpc_tree_params &&
+ step->params.mpc_remove_mpcc_params.mpcc_to_remove;
default:
return false;
}
@@ -346,6 +550,21 @@ static noinline_for_stack enum dc2_bls_exec_result dc2_bls_exec_dmub_gpint(
return DC2_BLS_EXEC_OK;
}
+static noinline_for_stack bool dc2_bls_wait_for_blank_complete(
+ const struct opp_wait_for_blank_complete_params *w)
+{
+ unsigned int waited_us;
+
+ for (waited_us = 0; w->opp->funcs->dpg_is_pending(w->opp);
+ waited_us += w->polling_interval_us) {
+ if (waited_us >= w->timeout_us)
+ return false;
+ udelay(w->polling_interval_us);
+ }
+
+ return w->opp->funcs->dpg_is_blanked(w->opp);
+}
+
enum dc2_bls_exec_result dc2_bls_execute(
struct dc2 *dc2,
const struct block_sequence *steps,
@@ -354,6 +573,13 @@ enum dc2_bls_exec_result dc2_bls_execute(
enum dc2_bls_exec_result result = DC2_BLS_EXEC_OK;
const struct block_sequence *entry;
enum dc2_bls_exec_result sent;
+ struct timing_generator *tg;
+ struct output_pixel_processor *opp;
+ struct clock_source *cs;
+ struct link_encoder *link_enc;
+ struct hubp *hubp;
+ struct dpp *dpp;
+ struct mpc *mpc;
unsigned int i;
if (!steps || num_steps > MAX_HWSS_BLOCK_SEQUENCE_SIZE)
@@ -693,6 +919,364 @@ enum dc2_bls_exec_result dc2_bls_execute(
p->num_chans, p->words_per_umc);
break;
}
+ case CLK_MGR_UPDATE_CLOCKS_FOR_STATE: {
+ const struct clk_mgr_update_clocks_for_state_params *p =
+ &entry->params
+ .clk_mgr_update_clocks_for_state_params;
+ bool *p_state = &p->context->bw_ctx.bw.dcn.clk
+ .p_state_change_support;
+ bool saved = *p_state;
+
+ if (p->disallow_p_state_change)
+ *p_state = false;
+ p->clk_mgr->funcs->update_clocks(p->clk_mgr, p->context,
+ p->safe_to_lower);
+ *p_state = saved;
+ break;
+ }
+ case HUBBUB_PROGRAM_WATERMARKS: {
+ const struct hubbub_program_watermarks_params *p =
+ &entry->params.hubbub_program_watermarks_params;
+ bool changed = p->hubbub->funcs->program_watermarks(
+ p->hubbub, p->watermarks, p->refclk_mhz,
+ p->safe_to_lower);
+
+ if (p->dc && !p->safe_to_lower)
+ p->dc->optimized_required |= changed;
+ break;
+ }
+ case HUBBUB_PROGRAM_ARBITER: {
+ const struct hubbub_program_arbiter_params *p =
+ &entry->params.hubbub_program_arbiter_params;
+ bool changed = p->hubbub->funcs->program_arbiter(p->hubbub,
+ p->arb_regs, p->safe_to_lower);
+
+ if (p->dc && !p->safe_to_lower)
+ p->dc->optimized_required |= changed;
+ break;
+ }
+ case HUBBUB_PROGRAM_COMPBUF_SEGMENTS: {
+ const struct hubbub_program_compbuf_segments_params *p =
+ &entry->params
+ .hubbub_program_compbuf_segments_params;
+
+ p->hubbub->funcs->program_compbuf_segments(p->hubbub,
+ p->compbuf_size, p->safe_to_lower);
+ break;
+ }
+ case HUBP_PROGRAM_DET_SEGMENTS: {
+ const struct program_det_segments_params *p =
+ &entry->params.program_det_segments_params;
+
+ p->hubbub->funcs->program_det_segments(p->hubbub,
+ p->hubp_inst, p->det_size);
+ break;
+ }
+ case TG_LOCK: {
+ const struct tg_lock_params *p =
+ &entry->params.tg_lock_params;
+
+ if (p->lock)
+ p->tg->funcs->lock(p->tg);
+ else
+ p->tg->funcs->unlock(p->tg);
+ break;
+ }
+ case TG_ENABLE_CRTC:
+ tg = entry->params.tg_enable_crtc_params.tg;
+ if (!tg->funcs->enable_crtc(tg))
+ return DC2_BLS_EXEC_ERROR;
+ break;
+ case TG_DISABLE_CRTC:
+ tg = entry->params.tg_disable_crtc_params.tg;
+ if (!tg->funcs->disable_crtc(tg))
+ return DC2_BLS_EXEC_ERROR;
+ break;
+ case TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR:
+ tg = entry->params
+ .tg_wait_odm_doublebuffer_pending_clear_params.tg;
+ tg->funcs->wait_odm_doublebuffer_pending_clear(tg);
+ break;
+ case TG_PROGRAM_STEREO: {
+ const struct tg_program_stereo_params *p =
+ &entry->params.tg_program_stereo_params;
+ struct crtc_stereo_flags flags = p->flags;
+
+ p->tg->funcs->program_stereo(p->tg, p->timing, &flags);
+ break;
+ }
+ case OPP_PROGRAM_STEREO: {
+ const struct opp_program_stereo_params *p =
+ &entry->params.opp_program_stereo_params;
+
+ p->opp->funcs->opp_program_stereo(p->opp, p->enable,
+ p->timing);
+ break;
+ }
+ case TG_WAIT_FOR_STATE:
+ tg = entry->params.tg_wait_for_state_params.tg;
+ tg->funcs->wait_for_state(tg,
+ entry->params.tg_wait_for_state_params.state);
+ break;
+ case TG_SETUP_VERTICAL_INTERRUPT2:
+ tg = entry->params.tg_setup_vertical_interrupt2_params.tg;
+ tg->funcs->setup_vertical_interrupt2(tg, entry->params
+ .tg_setup_vertical_interrupt2_params.start_line);
+ break;
+ case OPTC_SET_ODM_BYPASS:
+ tg = entry->params.set_odm_bypass_params.tg;
+ tg->funcs->set_odm_bypass(tg,
+ entry->params.set_odm_bypass_params.timing);
+ break;
+ case TG_ENABLE_OPTC_CLOCK:
+ tg = entry->params.tg_enable_optc_clock_params.tg;
+ tg->funcs->enable_optc_clock(tg,
+ entry->params.tg_enable_optc_clock_params.enable);
+ break;
+ case TG_PROGRAM_TIMING: {
+ const struct tg_program_timing_params *p =
+ &entry->params.tg_program_timing_params;
+
+ p->tg->funcs->program_timing(p->tg, p->timing,
+ p->vready_offset, p->vstartup_start,
+ p->vupdate_offset, p->vupdate_width,
+ p->pstate_keepout, p->signal, p->use_vbios);
+ break;
+ }
+ case TG_SET_H_TIMING_DIV_MANUAL_MODE:
+ tg = entry->params.tg_set_h_timing_div_manual_mode_params.tg;
+ tg->funcs->set_h_timing_div_manual_mode(tg, entry->params
+ .tg_set_h_timing_div_manual_mode_params
+ .manual_mode);
+ break;
+ case TG_SET_DRR:
+ tg = entry->params.tg_set_drr_params.tg;
+ tg->funcs->set_drr(tg, &entry->params.tg_set_drr_params.drr);
+ break;
+ case TG_SET_STATIC_SCREEN_CONTROL: {
+ const struct tg_set_static_screen_control_params *p =
+ &entry->params
+ .tg_set_static_screen_control_params;
+
+ p->tg->funcs->set_static_screen_control(p->tg,
+ p->event_triggers, p->num_frames);
+ break;
+ }
+ case TG_SET_OUT_MUX:
+ tg = entry->params.tg_set_out_mux_params.tg;
+ tg->funcs->set_out_mux(tg,
+ entry->params.tg_set_out_mux_params.dest);
+ break;
+ case TG_SET_EARLY_CONTROL:
+ tg = entry->params.tg_set_early_control_params.tg;
+ tg->funcs->set_early_control(tg, entry->params
+ .tg_set_early_control_params.early_cntl);
+ break;
+ case OPP_PROGRAM_FMT:
+ opp = entry->params.opp_program_fmt_params.opp;
+ opp->funcs->opp_program_fmt(opp,
+ entry->params.opp_program_fmt_params
+ .fmt_bit_depth,
+ entry->params.opp_program_fmt_params.clamping);
+ break;
+ case OPP_SET_DYN_EXPANSION: {
+ const struct opp_set_dyn_expansion_params *p =
+ &entry->params.opp_set_dyn_expansion_params;
+
+ p->opp->funcs->opp_set_dyn_expansion(p->opp,
+ p->color_space, p->color_depth, p->signal);
+ break;
+ }
+ case OPP_PIPE_CLOCK_CONTROL:
+ opp = entry->params.opp_pipe_clock_control_params.opp;
+ opp->funcs->opp_pipe_clock_control(opp, entry->params
+ .opp_pipe_clock_control_params.enable);
+ break;
+ case OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL: {
+ const struct opp_program_left_edge_extra_pixel_params *p =
+ &entry->params
+ .opp_program_left_edge_extra_pixel_params;
+
+ p->opp->funcs->opp_program_left_edge_extra_pixel(p->opp,
+ p->pixel_encoding, p->is_otg_master);
+ break;
+ }
+ case OPP_SET_DISP_PATTERN_GENERATOR: {
+ const struct opp_set_disp_pattern_generator_params *p =
+ &entry->params
+ .opp_set_disp_pattern_generator_params;
+
+ p->opp->funcs->opp_set_disp_pattern_generator(p->opp,
+ p->test_pattern, p->color_space,
+ p->color_depth,
+ p->use_solid_color ? &p->solid_color : NULL,
+ p->width, p->height, p->offset,
+ p->disable_dyn_exp_for_test_pattern);
+ break;
+ }
+ case OPP_WAIT_FOR_BLANK_COMPLETE:
+ if (dc2_bls_wait_for_blank_complete(&entry->params
+ .opp_wait_for_blank_complete_params))
+ break;
+ if (!entry->params.opp_wait_for_blank_complete_params
+ .continue_on_error)
+ return DC2_BLS_EXEC_ERROR;
+ result = DC2_BLS_EXEC_CONTINUED_ON_ERROR;
+ break;
+ case DCCG_SET_PIXEL_RATE_DIV: {
+ const struct dccg_set_pixel_rate_div_params *p =
+ &entry->params.dccg_set_pixel_rate_div_params;
+
+ p->dccg->funcs->set_pixel_rate_div(p->dccg, p->otg_inst,
+ p->k1, p->k2);
+ break;
+ }
+ case DCCG_DISABLE_SYMCLK_SE: {
+ const struct dccg_disable_symclk_se_params *p =
+ &entry->params.dccg_disable_symclk_se_params;
+
+ p->dccg->funcs->disable_symclk_se(p->dccg,
+ p->stream_enc_inst, p->link_enc_inst);
+ break;
+ }
+ case DCCG_ENABLE_SYMCLK_SE: {
+ const struct dccg_enable_symclk_se_params *p =
+ &entry->params.dccg_enable_symclk_se_params;
+
+ p->dccg->funcs->enable_symclk_se(p->dccg,
+ p->stream_enc_inst, p->link_enc_inst);
+ break;
+ }
+ case DCCG_SET_DTBCLK_P_SRC: {
+ const struct dccg_set_dtbclk_p_src_params *p =
+ &entry->params.dccg_set_dtbclk_p_src_params;
+
+ p->dccg->funcs->set_dtbclk_p_src(p->dccg, p->src,
+ p->otg_inst);
+ break;
+ }
+ case CLK_SRC_PROGRAM_PIX_CLK: {
+ const struct clk_src_program_pix_clk_params *p =
+ &entry->params.clk_src_program_pix_clk_params;
+
+ if (!p->clock_source->funcs->program_pix_clk(
+ p->clock_source, p->pix_clk_params,
+ p->encoding, p->pll_settings))
+ return DC2_BLS_EXEC_ERROR;
+ break;
+ }
+ case CLK_SRC_POWER_DOWN:
+ cs = entry->params.clk_src_power_down_params.clock_source;
+ if (cs->funcs->cs_power_down(cs))
+ break;
+ if (!entry->params.clk_src_power_down_params
+ .continue_on_error)
+ return DC2_BLS_EXEC_ERROR;
+ result = DC2_BLS_EXEC_CONTINUED_ON_ERROR;
+ break;
+ case DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE: {
+ const struct link_enc_connect_dig_be_to_fe_params *p =
+ &entry->params
+ .link_enc_connect_dig_be_to_fe_params;
+
+ p->link_enc->funcs->connect_dig_be_to_fe(p->link_enc,
+ p->engine, p->connect);
+ break;
+ }
+ case DIO_LINK_ENC_ENABLE_DP_OUTPUT: {
+ const struct link_enc_enable_dp_output_params *p =
+ &entry->params.link_enc_enable_dp_output_params;
+
+ p->link_enc->funcs->enable_dp_output(p->link_enc,
+ p->link_settings, p->clock_source);
+ break;
+ }
+ case DIO_LINK_ENC_DISABLE_OUTPUT:
+ link_enc = entry->params.link_enc_disable_output_params
+ .link_enc;
+ link_enc->funcs->disable_output(link_enc,
+ entry->params.link_enc_disable_output_params
+ .signal);
+ break;
+ case BIOS_SET_SCRATCH_CRITICAL_STATE: {
+ const struct bios_set_scratch_critical_state_params *p =
+ &entry->params
+ .bios_set_scratch_critical_state_params;
+
+ p->dcb->funcs->set_scratch_critical_state(p->dcb, p->state);
+ break;
+ }
+ case HUBP_PG_CONTROL: {
+ const struct hubp_pg_control_params *p =
+ &entry->params.hubp_pg_control_params;
+
+ p->hws->funcs.hubp_pg_control(p->hws, p->hubp_inst,
+ p->power_on);
+ break;
+ }
+ case DC_IP_REQUEST_CNTL: {
+ const struct dc_ip_request_cntl_params *p =
+ &entry->params.dc_ip_request_cntl_params;
+
+ p->hws->funcs.dc_ip_request_cntl(p->hws, p->enable);
+ break;
+ }
+ case HUBP_CLK_CNTL:
+ hubp = entry->params.hubp_clk_cntl_params.hubp;
+ hubp->funcs->hubp_clk_cntl(hubp,
+ entry->params.hubp_clk_cntl_params.enable);
+ hubp->power_gated =
+ !entry->params.hubp_clk_cntl_params.enable;
+ break;
+ case HUBP_SET_BLANK:
+ hubp = entry->params.hubp_set_blank_params.hubp;
+ hubp->funcs->set_blank(hubp,
+ entry->params.hubp_set_blank_params.blank);
+ break;
+ case HUBP_UPDATE_MALL_SEL: {
+ const struct hubp_update_mall_sel_params *p =
+ &entry->params.hubp_update_mall_sel_params;
+
+ p->hubp->funcs->hubp_update_mall_sel(p->hubp, p->mall_sel,
+ p->cache_cursor);
+ break;
+ }
+ case HUBP_SET_FLIP_CONTROL_GSL: {
+ const struct set_flip_control_gsl_params *p =
+ &entry->params.set_flip_control_gsl_params;
+
+ p->hubp->funcs->hubp_set_flip_control_surface_gsl(p->hubp,
+ p->flip_immediate);
+ break;
+ }
+ case HUBP_RESET:
+ hubp = entry->params.hubp_reset_params.hubp;
+ hubp->funcs->hubp_reset(hubp);
+ break;
+ case DPP_RESET:
+ dpp = entry->params.dpp_reset_params.dpp;
+ dpp->funcs->dpp_reset(dpp);
+ break;
+ case MPC_ASSERT_IDLE_MPCC:
+ mpc = entry->params.mpc_assert_idle_mpcc_params.mpc;
+ mpc->funcs->wait_for_idle(mpc,
+ entry->params.mpc_assert_idle_mpcc_params.mpcc_id);
+ break;
+ case DPP_DPPCLK_CONTROL:
+ dpp = entry->params.dpp_dppclk_control_params.dpp;
+ dpp->funcs->dpp_dppclk_control(dpp,
+ entry->params.dpp_dppclk_control_params
+ .dppclk_div,
+ entry->params.dpp_dppclk_control_params.enable);
+ break;
+ case MPC_REMOVE_MPCC: {
+ const struct mpc_remove_mpcc_params *p =
+ &entry->params.mpc_remove_mpcc_params;
+
+ p->mpc->funcs->remove_mpcc(p->mpc, p->mpc_tree_params,
+ p->mpcc_to_remove);
+ break;
+ }
default:
ASSERT(false);
return DC2_BLS_EXEC_ERROR;
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 9f97503a02ff..3042785cb8fc 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -29,6 +29,7 @@
#include "inc/clock_source.h"
#include "inc/hw/timing_generator.h"
#include "inc/hw/opp.h"
+#include "inc/hw/dccg.h"
#include "inc/hw/link_encoder.h"
#include "inc/core_status.h"
#include "inc/hw/hw_shared.h"
@@ -285,6 +286,143 @@ struct bios_is_accelerated_mode_params {
bool *out_accelerated_mode;
};
+struct clk_mgr_update_clocks_for_state_params {
+ struct clk_mgr *clk_mgr;
+ struct dc_state *context;
+ bool safe_to_lower;
+ /* Clears context's p_state_change_support for this call only. */
+ bool disallow_p_state_change;
+};
+
+struct tg_enable_optc_clock_params {
+ struct timing_generator *tg;
+ bool enable;
+};
+
+struct tg_program_timing_params {
+ struct timing_generator *tg;
+ const struct dc_crtc_timing *timing;
+ int vready_offset;
+ int vstartup_start;
+ int vupdate_offset;
+ int vupdate_width;
+ int pstate_keepout;
+ enum signal_type signal;
+ bool use_vbios;
+};
+
+struct tg_set_h_timing_div_manual_mode_params {
+ struct timing_generator *tg;
+ bool manual_mode;
+};
+
+struct tg_set_drr_params {
+ struct timing_generator *tg;
+ struct drr_params drr;
+};
+
+struct tg_set_static_screen_control_params {
+ struct timing_generator *tg;
+ uint32_t event_triggers;
+ uint32_t num_frames;
+};
+
+struct tg_set_out_mux_params {
+ struct timing_generator *tg;
+ enum otg_out_mux_dest dest;
+};
+
+struct tg_set_early_control_params {
+ struct timing_generator *tg;
+ uint32_t early_cntl;
+};
+
+struct tg_disable_crtc_params {
+ struct timing_generator *tg;
+};
+
+struct tg_wait_odm_doublebuffer_pending_clear_params {
+ struct timing_generator *tg;
+};
+
+struct tg_program_stereo_params {
+ struct timing_generator *tg;
+ const struct dc_crtc_timing *timing;
+ struct crtc_stereo_flags flags;
+};
+
+struct opp_program_stereo_params {
+ struct output_pixel_processor *opp;
+ bool enable;
+ const struct dc_crtc_timing *timing;
+};
+
+struct opp_wait_for_blank_complete_params {
+ struct output_pixel_processor *opp;
+ unsigned int polling_interval_us;
+ unsigned int timeout_us;
+ bool continue_on_error;
+};
+
+struct dccg_set_pixel_rate_div_params {
+ struct dccg *dccg;
+ uint32_t otg_inst;
+ enum pixel_rate_div k1;
+ enum pixel_rate_div k2;
+};
+
+struct dccg_disable_symclk_se_params {
+ struct dccg *dccg;
+ uint32_t stream_enc_inst;
+ uint32_t link_enc_inst;
+};
+
+struct dccg_enable_symclk_se_params {
+ struct dccg *dccg;
+ uint32_t stream_enc_inst;
+ uint32_t link_enc_inst;
+};
+
+struct dccg_set_dtbclk_p_src_params {
+ struct dccg *dccg;
+ enum streamclk_source src;
+ uint32_t otg_inst;
+};
+
+struct clk_src_program_pix_clk_params {
+ struct clock_source *clock_source;
+ struct pixel_clk_params *pix_clk_params;
+ enum dp_link_encoding encoding;
+ struct pll_settings *pll_settings;
+};
+
+struct clk_src_power_down_params {
+ struct clock_source *clock_source;
+ bool continue_on_error;
+};
+
+struct link_enc_connect_dig_be_to_fe_params {
+ struct link_encoder *link_enc;
+ enum engine_id engine;
+ bool connect;
+};
+
+struct link_enc_enable_dp_output_params {
+ struct link_encoder *link_enc;
+ const struct dc_link_settings *link_settings;
+ enum clock_source_id clock_source;
+};
+
+struct link_enc_disable_output_params {
+ struct link_encoder *link_enc;
+ enum signal_type signal;
+};
+
+struct bios_set_scratch_critical_state_params {
+ struct dc_bios *dcb;
+ bool state;
+};
+
struct lsdma_send_pio_copy_params {
struct dc_dmub_srv *dc_dmub_srv;
uint64_t src_addr;
@@ -1350,6 +1488,37 @@ union block_sequence_params {
clk_mgr_initialize_min_clocks_params;
struct clk_mgr_notify_wm_ranges_params clk_mgr_notify_wm_ranges_params;
struct bios_is_accelerated_mode_params bios_is_accelerated_mode_params;
+ struct clk_mgr_update_clocks_for_state_params
+ clk_mgr_update_clocks_for_state_params;
+ struct tg_enable_optc_clock_params tg_enable_optc_clock_params;
+ struct tg_program_timing_params tg_program_timing_params;
+ struct tg_set_h_timing_div_manual_mode_params
+ tg_set_h_timing_div_manual_mode_params;
+ struct tg_set_drr_params tg_set_drr_params;
+ struct tg_set_static_screen_control_params
+ tg_set_static_screen_control_params;
+ struct tg_set_out_mux_params tg_set_out_mux_params;
+ struct tg_set_early_control_params tg_set_early_control_params;
+ struct tg_disable_crtc_params tg_disable_crtc_params;
+ struct tg_wait_odm_doublebuffer_pending_clear_params
+ tg_wait_odm_doublebuffer_pending_clear_params;
+ struct tg_program_stereo_params tg_program_stereo_params;
+ struct opp_program_stereo_params opp_program_stereo_params;
+ struct opp_wait_for_blank_complete_params
+ opp_wait_for_blank_complete_params;
+ struct dccg_set_pixel_rate_div_params dccg_set_pixel_rate_div_params;
+ struct dccg_disable_symclk_se_params dccg_disable_symclk_se_params;
+ struct dccg_enable_symclk_se_params dccg_enable_symclk_se_params;
+ struct dccg_set_dtbclk_p_src_params dccg_set_dtbclk_p_src_params;
+ struct clk_src_program_pix_clk_params clk_src_program_pix_clk_params;
+ struct clk_src_power_down_params clk_src_power_down_params;
+ struct link_enc_connect_dig_be_to_fe_params
+ link_enc_connect_dig_be_to_fe_params;
+ struct link_enc_enable_dp_output_params
+ link_enc_enable_dp_output_params;
+ struct link_enc_disable_output_params link_enc_disable_output_params;
+ struct bios_set_scratch_critical_state_params
+ bios_set_scratch_critical_state_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;
@@ -1559,6 +1728,29 @@ enum block_sequence_func {
HUBBUB_SET_REQUEST_LIMIT,
BIOS_IS_ACCELERATED_MODE,
DMUB_FAMS2_UPDATE_CONFIG,
+ CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
+ TG_ENABLE_OPTC_CLOCK,
+ TG_PROGRAM_TIMING,
+ TG_SET_H_TIMING_DIV_MANUAL_MODE,
+ TG_SET_DRR,
+ TG_SET_STATIC_SCREEN_CONTROL,
+ TG_SET_OUT_MUX,
+ TG_SET_EARLY_CONTROL,
+ TG_DISABLE_CRTC,
+ TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR,
+ TG_PROGRAM_STEREO,
+ OPP_PROGRAM_STEREO,
+ OPP_WAIT_FOR_BLANK_COMPLETE,
+ DCCG_SET_PIXEL_RATE_DIV,
+ DCCG_DISABLE_SYMCLK_SE,
+ DCCG_ENABLE_SYMCLK_SE,
+ DCCG_SET_DTBCLK_P_SRC,
+ CLK_SRC_PROGRAM_PIX_CLK,
+ CLK_SRC_POWER_DOWN,
+ DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
+ DIO_LINK_ENC_ENABLE_DP_OUTPUT,
+ DIO_LINK_ENC_DISABLE_OUTPUT,
+ BIOS_SET_SCRATCH_CRITICAL_STATE,
/* This must be the last value in this enum, add new ones above */
HWSS_BLOCK_SEQUENCE_FUNC_COUNT
};
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 07/74] drm/amd/display: Build the dcn401 virtual commit natively
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (5 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 06/74] drm/amd/display: Add leaf BLS entries for a virtual commit Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 08/74] drm/amd/display: Move HDMI FRL Polling Outside dc_lock Wayne Lin
` (67 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
core2_commit_state_hw returns DC_NOT_SUPPORTED for every state, so
selecting Core2 cannot light a display. Start the native commit
with the smallest transition: one virtual stream.
Register a declarative non-seamless commit builder for dcn401. Its
support check covers no stream to no stream, no stream to one
stream with no plane, and one stream with one plane back to no
stream. A stream is never in both states. The stream must be
virtual, one pipe, RGB and sRGB, with no audio, DSC, ODM, MPO,
stereo, DRR or seamless boot. FAMS2, visual confirm and the DSC
power gate must be off. On stream-off the plane's MPCC must be the
only one in the OPP tree. link_set_dpms_on and link_set_dpms_off
program nothing for a virtual signal, so a virtual stream is
rejected here, and only a commit with no stream in either state is
built. Anything else returns DC_NOT_SUPPORTED.
Emit the leaf calls legacy_commit_state_hw makes on dcn401, in
that order. Leave out dcn10_setup_stereo's generic GPIO mux: it
routes no signal for a virtual stream. Skip dig_connect_to_otg for
virtual, as DC1 does. The head runs through the ODM double-buffer
and blank waits. The tail is only the deferred bandwidth lower,
plus a p-state release when no OTG master released it, and it
runs after the head returns so that double buffer can complete at
vupdate.
The steps are static arrays named for where they land, not for
stream-on or stream-off. skip_if carries "stream mode unchanged"
and "new state has a stream", so those conditions are data instead
of branches. Parameters come from the validated state. The support
check requires an OTG master for each stream, and both sequences
fit in DC2_BLS_MAX_STEPS, so building the list has no error path.
A failed check logs which condition failed with DC_LOG_DEBUG.
If the VBIOS scratch still says VBIOS mode, return
DC_NOT_SUPPORTED. This builder has no accelerated-mode entries,
and dcn401_init_hw leaves VBIOS mode only when
debug.disable_boot_optimizations is set. On success, write the
software state DC1 writes. A failed build returns
DC_NOT_SUPPORTED, a failed execution returns DC_ERROR_UNEXPECTED,
and neither writes that state or falls back to DC1.
Do not call set_wait_for_update_needed_for_pipe. It is an hwss
helper that reads the OTG, and this commit stays on leaf calls.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/Makefile | 1 +
.../amd/display/dc/bls_builder/bls_builder.h | 1 +
.../dc/bls_builder/bls_builder_internal.h | 1 +
.../bls_builder/bls_builder_private_types.h | 72 +
.../dc/bls_builder/dcn401_commit_builder.c | 1256 +++++++++++++++++
.../dc/bls_builder/dcn401_init_builder.c | 2 +
.../gpu/drm/amd/display/dc/core2/dc_core2.c | 164 ++-
7 files changed, 1494 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile
index 84c55a253ebc..ec3ef6ed6ed4 100644
--- a/drivers/gpu/drm/amd/display/dc/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/Makefile
@@ -86,6 +86,7 @@ FILES += core2/dc_core2.o
FILES += core2/dc_core2_init.o
FILES += core2/dc_core2_legacy_init.o
FILES += bls_builder/bls_builder.o
+FILES += bls_builder/dcn401_commit_builder.o
FILES += bls_builder/dcn401_init_builder.o
FILES += bls_builder/dcn401_init_builder_legacy.o
FILES += bls_executor/bls_executor.o
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
index 422ee25cf348..485e877df5d2 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
@@ -24,6 +24,7 @@ enum bls_builder_result {
enum bls_builder_name {
DCN_DECL_INITIALIZATION,
DCN_DECL_INITIALIZATION_LEGACY,
+ DCN_DECL_NON_SEAMLESS_COMMIT,
BLS_BUILDER_NAME_COUNT,
};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_internal.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_internal.h
index 216879c93f9e..a641941a2f2f 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_internal.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_internal.h
@@ -9,3 +9,4 @@
const struct bls_builder *dcn401_bls_builder_create(
enum bls_builder_name name);
+const struct bls_builder *dcn401_non_seamless_commit_builder(void);
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
new file mode 100644
index 000000000000..025b2e3b62ad
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#pragma once
+
+#include "hw_sequencer.h"
+#include "dccg.h"
+
+/*
+ * Step tables for the declarative builders. A change here changes what
+ * a builder can express; a change to a builder's .c alone does not.
+ */
+
+/*
+ * One dcn401 commit step is one BLS entry. The union holds the
+ * arguments that differ between the times DC1 makes that call; the
+ * rest come from the state. skip_if names when the step is left out.
+ */
+struct dcn401_commit_step_meta {
+ union {
+ struct {
+ bool safe_to_lower;
+ } clk_mgr_update_clocks_for_state;
+ struct {
+ bool safe_to_lower;
+ } hubbub_program_watermarks;
+ struct {
+ bool safe_to_lower;
+ } hubbub_program_arbiter;
+ struct {
+ bool safe_to_lower;
+ } hubbub_program_compbuf_segments;
+ struct {
+ bool state;
+ } bios_set_scratch_critical_state;
+ struct {
+ bool lock;
+ } tg_lock;
+ struct {
+ bool enable;
+ } tg_enable_optc_clock;
+ struct {
+ bool from_adjust;
+ bool with_mid;
+ } tg_set_drr;
+ struct {
+ enum streamclk_source src;
+ } dccg_set_dtbclk_p_src;
+ struct {
+ enum pixel_rate_div k1;
+ } dccg_set_pixel_rate_div;
+ struct {
+ bool connect;
+ } link_enc_connect_dig_be_to_fe;
+ struct {
+ bool enable;
+ } dc_ip_request_cntl;
+ };
+ struct {
+ /* The OTG master's stream has mode_changed clear. */
+ bool stream_mode_unchanged;
+ /* The new state has a stream. */
+ bool new_state_has_stream;
+ } skip_if;
+};
+
+struct dcn401_commit_step {
+ enum block_sequence_func func;
+ struct dcn401_commit_step_meta meta;
+};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
new file mode 100644
index 000000000000..340d1aec2f4c
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
@@ -0,0 +1,1256 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include "dm_services.h"
+#include "core_types.h"
+#include "link_encoder.h"
+#include "stream_encoder.h"
+#include "clk_mgr.h"
+#include "dccg.h"
+#include "dc_bios_types.h"
+#include "dc_dmub_srv.h"
+#include "bls_builder/bls_builder_internal.h"
+#include "bls_builder/dcn401_init_builder.h"
+#include "bls_builder/bls_builder_private_types.h"
+
+#undef DC_LOGGER
+#define DC_LOGGER \
+ dc->ctx->logger
+
+/*
+ * DCN_DECL_NON_SEAMLESS_COMMIT for one virtual stream without planes.
+ * The new state has no stream, or one stream with no plane; the current
+ * state has no stream, or one stream with one plane. A plane is added
+ * later by commit_planes, not by this commit.
+ *
+ * The sequence follows legacy_commit_state_hw on dcn401: the same leaf
+ * calls in the same order, including the calls DC1 repeats. head_sequence
+ * holds everything through the ODM double-buffer and blank waits.
+ * tail_sequence is the deferred bandwidth lower, and it runs only after
+ * that head has finished.
+ */
+
+#define DCN401_DPG_POLL_INTERVAL_US 100
+#define DCN401_DPG_POLL_TIMEOUT_US 100000
+#define DCN401_STATIC_SCREEN_NUM_FRAMES 2
+
+/*
+ * Each helper takes the same arguments. CHECK_CONDITION logs the reason
+ * and returns. current_state or new_state is NULL when the helper looks
+ * at only one of them.
+ */
+
+static void dcn401_commit_check_failed(const struct dc *dc, const char *msg)
+{
+ if (dc && dc->ctx && dc->ctx->logger)
+ DC_LOG_DEBUG("%s\n", msg);
+}
+
+#define CHECK_CONDITION(name, msg) \
+ do { \
+ if (!check_##name(dc, current_state, new_state)) { \
+ dcn401_commit_check_failed(dc, msg); \
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE; \
+ } \
+ } while (0)
+
+#define CHECK_RESOURCE(name, msg) \
+ do { \
+ if (!check_##name(dc, current_state, new_state)) { \
+ dcn401_commit_check_failed(dc, msg); \
+ return BLS_BUILD_ERROR_RESOURCE_MAPPING; \
+ } \
+ } while (0)
+
+static const struct dc_state *dcn401_checked_state(
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ return current_state ? current_state : new_state;
+}
+
+static bool check_dc_resources(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return dc->res_pool && dc->res_pool->hubbub && dc->res_pool->dccg &&
+ dc->res_pool->mpc && dc->hwseq && dc->clk_mgr &&
+ dc->ctx && dc->ctx->dc_bios;
+}
+
+static bool check_fams2_debug(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return !dc->debug.fams2_config.bits.enable;
+}
+
+static bool check_cursor_offload(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return !dc->ctx->dmub_srv || !dc->ctx->dmub_srv->cursor_offload_enabled;
+}
+
+static bool check_dc_mode_softmax(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return !dc->clk_mgr->dc_mode_softmax_enabled;
+}
+
+static bool check_visual_confirm(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return dc->debug.visual_confirm == VISUAL_CONFIRM_DISABLE &&
+ !dc->debug.force_dpg_on_flip &&
+ !dc->debug.enable_hblank_borrow &&
+ !dc->debug.enable_otg_frame_sync_pwa;
+}
+
+/* With the DSC power gate disabled, update_dsc_pg only reads. */
+static bool check_dsc_power_gate(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return dc->debug.disable_dsc_power_gate;
+}
+
+static bool check_stream_config_update(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return !dc->ctx->cp_psp.funcs.update_stream_config;
+}
+
+static bool check_one_stream(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_state *state = dcn401_checked_state(current_state,
+ new_state);
+
+ (void)dc;
+ return state->stream_count <= 1 && !state->phantom_stream_count;
+}
+
+static bool check_fams2_state(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_state *state = dcn401_checked_state(current_state,
+ new_state);
+
+ (void)dc;
+ return !state->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable;
+}
+
+static bool check_stream_link(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_state *state = dcn401_checked_state(current_state,
+ new_state);
+ const struct dc_stream_state *stream = state->streams[0];
+
+ (void)dc;
+ return stream && stream->link;
+}
+
+/* link_set_dpms_on and _off program nothing for a virtual signal. */
+static bool check_virtual_signal(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_state *state = dcn401_checked_state(current_state,
+ new_state);
+
+ (void)dc;
+ (void)state;
+ return false;
+}
+
+static bool check_link_encoder(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_link *link = dcn401_checked_state(current_state,
+ new_state)->streams[0]->link;
+
+ (void)dc;
+ return link->ep_type == DISPLAY_ENDPOINT_PHY && link->link_enc;
+}
+
+static bool check_timing_flags(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_crtc_timing *timing = &dcn401_checked_state(
+ current_state, new_state)->streams[0]->timing;
+
+ (void)dc;
+ return !timing->flags.DSC && !timing->flags.INTERLACE &&
+ timing->timing_3d_format == TIMING_3D_FORMAT_NONE;
+}
+
+/* The DPG black and the pixel rate divider are fixed for RGB. */
+static bool check_rgb_srgb(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_stream_state *stream = dcn401_checked_state(
+ current_state, new_state)->streams[0];
+
+ (void)dc;
+ return stream->timing.pixel_encoding == PIXEL_ENCODING_RGB &&
+ stream->output_color_space == COLOR_SPACE_SRGB;
+}
+
+static bool check_no_drr(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_stream_state *stream = dcn401_checked_state(
+ current_state, new_state)->streams[0];
+
+ (void)dc;
+ return !stream->ignore_msa_timing_param &&
+ !stream->adjust.v_total_min &&
+ !stream->adjust.v_total_max;
+}
+
+static bool check_no_boot_dpms_writeback(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_stream_state *stream = dcn401_checked_state(
+ current_state, new_state)->streams[0];
+
+ (void)dc;
+ return !stream->apply_seamless_boot_optimization && !stream->dpms_off &&
+ !stream->num_wb_info;
+}
+
+static bool check_abm(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_stream_state *stream = dcn401_checked_state(
+ current_state, new_state)->streams[0];
+
+ (void)dc;
+ return !stream->abm_level ||
+ stream->abm_level == ABM_LEVEL_IMMEDIATE_DISABLE;
+}
+
+static bool check_plane_count(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_state *state = dcn401_checked_state(current_state,
+ new_state);
+ /*
+ * The current state carries the removed plane. The new state has
+ * none.
+ */
+ bool has_plane = current_state != NULL;
+
+ (void)dc;
+ return state->stream_status[0].plane_count == (has_plane ? 1 : 0);
+}
+
+/* The pipe for this state's one stream, or NULL when it is not exactly one. */
+static const struct pipe_ctx *dcn401_state_otg_master(
+ const struct dc_state *state, bool *not_one_pipe)
+{
+ const struct pipe_ctx *otg_master = NULL;
+ unsigned int i;
+
+ *not_one_pipe = false;
+ for (i = 0; i < MAX_PIPES; i++) {
+ const struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
+
+ if (!pipe->stream)
+ continue;
+ if (pipe->stream != state->streams[0] || otg_master) {
+ *not_one_pipe = true;
+ return NULL;
+ }
+ otg_master = pipe;
+ }
+ return otg_master;
+}
+
+static const struct pipe_ctx *dcn401_checked_otg_master(
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ bool not_one_pipe = false;
+
+ return dcn401_state_otg_master(dcn401_checked_state(current_state,
+ new_state), ¬_one_pipe);
+}
+
+static bool check_one_otg_pipe(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ bool not_one_pipe = false;
+
+ (void)dc;
+ dcn401_state_otg_master(dcn401_checked_state(current_state, new_state),
+ ¬_one_pipe);
+ return !not_one_pipe;
+}
+
+static bool check_otg_master_present(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ return dcn401_checked_otg_master(current_state, new_state) != NULL;
+}
+
+static bool check_odm_combine(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+
+ (void)dc;
+ return pipe && !pipe->next_odm_pipe && !pipe->prev_odm_pipe;
+}
+
+static bool check_mpc_blend(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+
+ (void)dc;
+ return pipe && !pipe->top_pipe && !pipe->bottom_pipe;
+}
+
+static bool check_pipe_plane(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+ /*
+ * The current state carries the removed plane. The new state has
+ * none.
+ */
+ bool has_plane = current_state != NULL;
+
+ (void)dc;
+ return pipe && !!pipe->plane_state == has_plane;
+}
+
+static bool check_otg_master_blocks(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+
+ (void)dc;
+ return pipe && pipe->stream_res.tg && pipe->stream_res.opp &&
+ pipe->stream_res.stream_enc && pipe->clock_source;
+}
+
+static bool check_plane_blocks(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+
+ (void)dc;
+ if (!current_state)
+ return true;
+ return pipe && pipe->plane_res.hubp && pipe->plane_res.dpp &&
+ pipe->plane_res.mpcc_inst < MAX_MPCC;
+}
+
+static bool check_mpcc_alone(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+ const struct mpcc *mpcc;
+
+ if (!current_state)
+ return true;
+ if (!pipe || !dc->res_pool || !dc->res_pool->mpc)
+ return false;
+ mpcc = &dc->res_pool->mpc->mpcc_array[pipe->plane_res.mpcc_inst];
+ /* The removed MPCC must be the only one in the OPP's tree. */
+ return pipe->stream_res.opp->mpc_tree_params.opp_list == mpcc &&
+ !mpcc->mpcc_bot &&
+ mpcc->dpp_id == pipe->plane_res.dpp->inst;
+}
+
+static bool check_pipe_audio(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+
+ (void)dc;
+ return pipe && !pipe->stream_res.audio;
+}
+
+static bool check_pipe_dsc(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+
+ (void)dc;
+ return pipe && !pipe->stream_res.dsc;
+}
+
+static bool check_pipe_gsl(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+
+ (void)dc;
+ return pipe && !pipe->stream_res.gsl_group;
+}
+
+static bool check_link_enc_assignment(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
+ new_state);
+
+ if (!dc->config.unify_link_enc_assignment)
+ return true;
+ return pipe && pipe->link_res.dio_link_enc ==
+ pipe->stream->link->link_enc;
+}
+
+static enum bls_builder_result dcn401_check_commit_state_support(
+ const struct dc *dc, const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_state *state = dcn401_checked_state(current_state,
+ new_state);
+
+ if (!state)
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
+ CHECK_CONDITION(one_stream, "more than one stream");
+ CHECK_CONDITION(fams2_state, "fams2 is enabled");
+ if (!state->stream_count)
+ return BLS_BUILD_SUCCESS;
+
+ CHECK_RESOURCE(stream_link, "stream has no link");
+ CHECK_CONDITION(virtual_signal, "signal is not virtual");
+ CHECK_RESOURCE(link_encoder, "link encoder is not DIO");
+ CHECK_CONDITION(timing_flags, "timing is DSC, interlace, or stereo");
+ CHECK_CONDITION(rgb_srgb, "stream is not RGB sRGB");
+ CHECK_CONDITION(no_drr, "DRR is set");
+ CHECK_CONDITION(no_boot_dpms_writeback,
+ "seamless boot, dpms, or writeback is set");
+ CHECK_CONDITION(abm, "ABM level is set");
+ CHECK_CONDITION(plane_count, "plane count does not match the state");
+ CHECK_CONDITION(one_otg_pipe, "stream is not on one pipe");
+ CHECK_RESOURCE(otg_master_present, "stream has no otg master");
+ CHECK_CONDITION(odm_combine, "odm is set");
+ CHECK_CONDITION(mpc_blend, "top or bottom pipe is set");
+ CHECK_CONDITION(pipe_plane, "pipe plane does not match the state");
+ CHECK_RESOURCE(otg_master_blocks, "otg master is missing a block");
+ CHECK_RESOURCE(plane_blocks, "plane is missing hubp or dpp");
+ CHECK_CONDITION(mpcc_alone, "mpcc is not alone in the opp tree");
+ CHECK_CONDITION(pipe_audio, "audio is set");
+ CHECK_CONDITION(pipe_dsc, "dsc is set");
+ CHECK_CONDITION(pipe_gsl, "gsl is set");
+ CHECK_CONDITION(link_enc_assignment, "link encoder assignment differs");
+ return BLS_BUILD_SUCCESS;
+}
+
+static bool check_stream_not_kept(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ /* A stream is either removed or added, never kept. */
+ return !current_state->stream_count || !new_state->stream_count;
+}
+
+static bool check_link_not_active(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ if (!new_state->stream_count)
+ return true;
+ return !new_state->streams[0]->link->link_status.link_active;
+}
+
+static enum bls_builder_result dcn401_check_non_seamless_commit_support(
+ const struct dc *dc, const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ enum bls_builder_result result;
+
+ if (!dc || !current_state || !new_state)
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
+
+ CHECK_RESOURCE(dc_resources, "dc resources are missing");
+ CHECK_CONDITION(fams2_debug, "FAMS2 debug is enabled");
+ CHECK_CONDITION(cursor_offload, "cursor offload is enabled");
+ CHECK_CONDITION(dc_mode_softmax, "dc mode softmax is enabled");
+ CHECK_CONDITION(visual_confirm, "visual confirm, dpg, hblank borrow, "
+ "or frame sync is set");
+ CHECK_CONDITION(dsc_power_gate, "DSC power gate is enabled");
+ CHECK_CONDITION(stream_config_update, "stream config update is set");
+
+ result = dcn401_check_commit_state_support(dc, current_state, NULL);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+ result = dcn401_check_commit_state_support(dc, NULL, new_state);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+
+ CHECK_CONDITION(stream_not_kept, "stream is kept across the commit");
+ CHECK_CONDITION(link_not_active, "link is already active");
+ return BLS_BUILD_SUCCESS;
+}
+
+static struct pipe_ctx *get_otg_master_from_state_by_stream(
+ struct dc_state *state, const struct dc_stream_state *stream)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_PIPES; i++) {
+ if (state->res_ctx.pipe_ctx[i].stream == stream)
+ return &state->res_ctx.pipe_ctx[i];
+ }
+ return NULL;
+}
+
+/*
+ * Net effect: dcn401_prepare_bandwidth without FAMS2, SubVP or DC-mode
+ * softmax.
+ */
+static const struct dcn401_commit_step bls_head_per_state_new[] = {
+ { .func = CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
+ .meta.clk_mgr_update_clocks_for_state.safe_to_lower = false },
+ { .func = HUBBUB_PROGRAM_WATERMARKS,
+ .meta.hubbub_program_watermarks.safe_to_lower = false },
+ { .func = HUBBUB_PROGRAM_ARBITER,
+ .meta.hubbub_program_arbiter.safe_to_lower = false },
+ { .func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS,
+ .meta.hubbub_program_compbuf_segments.safe_to_lower = false },
+};
+
+/*
+ * Net effect: disable the stream of the current state.
+ * - disable_dangling_plane: disconnect the MPCC under the OTG lock, then
+ * dcn20_disable_plane.
+ * - link_set_dpms_off for the virtual signal: dcn401_disable_stream, then
+ * disable_link, which disables the output as DisplayPort.
+ * - dcn401_reset_back_end_for_pipe after link_set_dpms_off, then the
+ * clock source power-down from dcn401_reset_hw_ctx_wrap.
+ */
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_current[] = {
+ { .func = TG_LOCK, .meta.tg_lock.lock = true },
+ { .func = OPP_SET_DISP_PATTERN_GENERATOR },
+ { .func = HUBP_PROGRAM_DET_SEGMENTS },
+ { .func = MPC_REMOVE_MPCC },
+ { .func = TG_LOCK, .meta.tg_lock.lock = false },
+ { .func = MPC_ASSERT_IDLE_MPCC },
+ { .func = HUBP_SET_BLANK },
+ { .func = HUBP_UPDATE_MALL_SEL },
+ { .func = HUBP_SET_FLIP_CONTROL_GSL },
+ { .func = HUBP_CLK_CNTL },
+ { .func = DPP_DPPCLK_CONTROL },
+ /*
+ * dcn10_plane_atomic_power_down sets IP_REQUEST_EN only when it
+ * reads 0. Every writer clears it again, so it always reads 0 here.
+ */
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true },
+ { .func = HUBP_PG_CONTROL },
+ { .func = HUBP_RESET },
+ { .func = DPP_RESET },
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false },
+ { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
+
+ { .func = DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
+ .meta.link_enc_connect_dig_be_to_fe.connect = false },
+ { .func = DCCG_DISABLE_SYMCLK_SE },
+ { .func = DIO_LINK_ENC_DISABLE_OUTPUT },
+
+ { .func = TG_DISABLE_CRTC },
+ { .func = TG_ENABLE_OPTC_CLOCK,
+ .meta.tg_enable_optc_clock.enable = false },
+ { .func = OPTC_SET_ODM_BYPASS },
+ { .func = TG_SET_DRR },
+ /* link_set_dpms_off disabled the output already. */
+ { .func = DCCG_SET_DTBCLK_P_SRC,
+ .meta.dccg_set_dtbclk_p_src.src = REFCLK },
+ { .func = CLK_SRC_POWER_DOWN },
+};
+
+/*
+ * Net effect: enable the stream of the new state. Between the BIOS
+ * critical-state calls:
+ * - dcn401_enable_stream_timing plus the dce110_apply_single_controller
+ * calls around it, up to the DIG connect.
+ * - link_set_dpms_on for the virtual signal: enable_link_virtual, then
+ * dcn401_enable_stream. The virtual stream encoder has no enable, map,
+ * input-mode or FIFO funcs, and update_info_frame programs nothing.
+ * DC1 skips dig_connect_to_otg for the virtual signal.
+ */
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_new[] = {
+ { .func = BIOS_SET_SCRATCH_CRITICAL_STATE,
+ .meta.bios_set_scratch_critical_state.state = true },
+
+ { .func = OPP_PROGRAM_FMT },
+ { .func = OPP_SET_DYN_EXPANSION },
+ { .func = DCCG_SET_PIXEL_RATE_DIV,
+ .meta.dccg_set_pixel_rate_div.k1 = PIXEL_RATE_DIV_BY_4 },
+ { .func = DCCG_SET_DTBCLK_P_SRC,
+ .meta.dccg_set_dtbclk_p_src.src = DPREFCLK },
+ { .func = TG_ENABLE_OPTC_CLOCK,
+ .meta.tg_enable_optc_clock.enable = true },
+ { .func = CLK_SRC_PROGRAM_PIX_CLK },
+ { .func = TG_PROGRAM_TIMING },
+ { .func = TG_SET_H_TIMING_DIV_MANUAL_MODE },
+ { .func = OPP_PIPE_CLOCK_CONTROL },
+ { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
+ { .func = OPP_PIPE_CLOCK_CONTROL },
+ { .func = OPP_SET_DISP_PATTERN_GENERATOR },
+ { .func = TG_ENABLE_CRTC },
+ { .func = OPP_WAIT_FOR_BLANK_COMPLETE },
+ { .func = TG_SET_DRR, .meta.tg_set_drr.from_adjust = true,
+ .meta.tg_set_drr.with_mid = true },
+ { .func = TG_SET_STATIC_SCREEN_CONTROL },
+ { .func = TG_SETUP_VERTICAL_INTERRUPT2 },
+ { .func = TG_SET_DRR, .meta.tg_set_drr.from_adjust = true },
+ { .func = TG_SET_STATIC_SCREEN_CONTROL },
+
+ { .func = TG_SET_OUT_MUX },
+ { .func = DIO_LINK_ENC_ENABLE_DP_OUTPUT },
+ { .func = DCCG_ENABLE_SYMCLK_SE },
+ /* dcn401_enable_stream divides by one unless the signal is HDMI or DVI. */
+ { .func = DCCG_SET_PIXEL_RATE_DIV,
+ .meta.dccg_set_pixel_rate_div.k1 = PIXEL_RATE_DIV_BY_1 },
+ { .func = DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
+ .meta.link_enc_connect_dig_be_to_fe.connect = true },
+ { .func = TG_SET_EARLY_CONTROL },
+
+ { .func = BIOS_SET_SCRATCH_CRITICAL_STATE,
+ .meta.bios_set_scratch_critical_state.state = false },
+};
+
+/*
+ * Net effect: finish the head for the new OTG master. program_front_end
+ * for a master with no plane, then stereo, then the waits that complete
+ * the head's double buffering. legacy_commit_state_hw sets up stereo for
+ * a mode-changed stream, then dc_enable_stereo sets it up again for every
+ * stream; dcn10_setup_stereo with no 3D format is the two STEREO calls.
+ * The deferred tail starts only after these waits.
+ */
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_new_front_end[] = {
+ { .func = TG_LOCK, .meta.tg_lock.lock = true },
+ { .func = OPTC_SET_ODM_BYPASS },
+ { .func = OPP_PIPE_CLOCK_CONTROL },
+ { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
+ { .func = OPP_SET_DYN_EXPANSION },
+ { .func = OPP_PROGRAM_FMT },
+ { .func = OPP_SET_DISP_PATTERN_GENERATOR },
+ { .func = TG_LOCK, .meta.tg_lock.lock = false },
+ { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
+
+ { .func = OPP_PROGRAM_STEREO,
+ .meta.skip_if.stream_mode_unchanged = true },
+ { .func = TG_PROGRAM_STEREO,
+ .meta.skip_if.stream_mode_unchanged = true },
+ { .func = OPP_PROGRAM_STEREO },
+ { .func = TG_PROGRAM_STEREO },
+
+ { .func = TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR },
+ { .func = OPP_WAIT_FOR_BLANK_COMPLETE },
+};
+
+/*
+ * Net effect: release the forced p-state when no OTG master did, then
+ * dcn401_optimize_bandwidth without FAMS2, SubVP or DC-mode softmax.
+ */
+static const struct dcn401_commit_step bls_tail_per_state_new[] = {
+ { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
+ .meta.skip_if.new_state_has_stream = true },
+ { .func = HUBBUB_PROGRAM_WATERMARKS,
+ .meta.hubbub_program_watermarks.safe_to_lower = true },
+ { .func = HUBBUB_PROGRAM_ARBITER,
+ .meta.hubbub_program_arbiter.safe_to_lower = true },
+ { .func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS,
+ .meta.hubbub_program_compbuf_segments.safe_to_lower = true },
+ { .func = CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
+ .meta.clk_mgr_update_clocks_for_state.safe_to_lower = true },
+};
+
+/*
+ * Bounds on what build adds. The support check allows one stream per
+ * state, and never one in both.
+ */
+static_assert(ARRAY_SIZE(bls_head_per_state_new) +
+ ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_current) <=
+ DC2_BLS_MAX_STEPS,
+ "dcn401 commit stream-off head exceeds DC2_BLS_MAX_STEPS");
+static_assert(ARRAY_SIZE(bls_head_per_state_new) +
+ ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_new) +
+ ARRAY_SIZE(
+ bls_head_per_stream_per_otg_master_pipe_new_front_end) <=
+ DC2_BLS_MAX_STEPS,
+ "dcn401 commit enable head exceeds DC2_BLS_MAX_STEPS");
+static_assert(ARRAY_SIZE(bls_tail_per_state_new) <= DC2_BLS_MAX_STEPS,
+ "dcn401 commit tail sequence exceeds DC2_BLS_MAX_STEPS");
+
+/* pipe is NULL only for the bandwidth and p-state steps. */
+struct dcn401_commit_step_args {
+ struct dc *dc;
+ struct dc_state *new_state;
+ struct pipe_ctx *pipe;
+};
+
+/* enable_stream_timing_calc: manual mode unless every span is even. */
+static bool dcn401_h_timing_div_manual_mode(
+ const struct dc_crtc_timing *timing)
+{
+ unsigned int h_blank_start = timing->h_total - timing->h_front_porch;
+ unsigned int h_blank_end = h_blank_start - timing->h_addressable;
+
+ return (timing->h_total % 2) || (h_blank_start % 2) ||
+ (h_blank_end % 2) || (timing->h_sync_width % 2);
+}
+
+/* dcn10_get_vupdate_offset_from_vsync for progressive timing. */
+static int dcn401_vupdate_start_line(const struct pipe_ctx *pipe)
+{
+ const struct dc_crtc_timing *timing = &pipe->stream->timing;
+ int front_porch = timing->v_front_porch ? timing->v_front_porch : 1;
+ int vesa_sync_start = timing->v_addressable + timing->v_border_bottom +
+ front_porch;
+ int asic_blank_end = timing->v_total - vesa_sync_start -
+ timing->v_border_top;
+ int start_line = asic_blank_end -
+ (int)pipe->pipe_dlg_param.vstartup_start + 1;
+
+ return start_line < 0 ? 0 : start_line;
+}
+
+/* dcn401_enable_stream_calc's early control for this lane count. */
+static uint32_t dcn401_early_cntl(const struct pipe_ctx *pipe)
+{
+ const struct dc_crtc_timing *timing = &pipe->stream->timing;
+ unsigned int lane_count =
+ pipe->stream->link->cur_link_settings.lane_count;
+ uint32_t early_cntl = 0;
+
+ if (lane_count)
+ early_cntl = (timing->h_addressable + timing->h_border_left +
+ timing->h_border_right) % lane_count;
+ return early_cntl ? early_cntl : lane_count;
+}
+
+/* dcn20_blank_pixel_data(blank): sRGB black over the whole OTG. */
+static void dcn401_opp_blank_params(const struct dc *dc,
+ const struct pipe_ctx *pipe,
+ struct opp_set_disp_pattern_generator_params *p)
+{
+ const struct dc_crtc_timing *timing = &pipe->stream->timing;
+
+ p->opp = pipe->stream_res.opp;
+ p->test_pattern = CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR;
+ p->color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED;
+ p->color_depth = timing->display_color_depth;
+ p->use_solid_color = true;
+ p->width = timing->h_addressable + timing->h_border_left +
+ timing->h_border_right;
+ p->height = timing->v_addressable + timing->v_border_top +
+ timing->v_border_bottom;
+ p->disable_dyn_exp_for_test_pattern =
+ dc->debug.disable_dynamic_expansion_for_test_pattern;
+}
+
+static void dcn401_program_timing_params(const struct pipe_ctx *pipe,
+ struct tg_program_timing_params *p)
+{
+ const union dml2_global_sync_programming *sync = &pipe->global_sync;
+
+ p->tg = pipe->stream_res.tg;
+ p->timing = &pipe->stream->timing;
+ p->vready_offset = sync->dcn4x.vready_offset_pixels;
+ p->vstartup_start = sync->dcn4x.vstartup_lines;
+ p->vupdate_offset = sync->dcn4x.vupdate_offset_pixels;
+ p->vupdate_width = sync->dcn4x.vupdate_vupdate_width_pixels;
+ p->pstate_keepout = sync->dcn4x.pstate_keepout_start_lines;
+ p->signal = pipe->stream->signal;
+ p->use_vbios = true;
+}
+
+static void dcn401_drr_params(const struct pipe_ctx *pipe, bool with_mid,
+ struct drr_params *drr)
+{
+ const struct dc_crtc_timing_adjust *adjust = &pipe->stream->adjust;
+
+ drr->vertical_total_min = adjust->v_total_min;
+ drr->vertical_total_max = adjust->v_total_max;
+ if (with_mid) {
+ drr->vertical_total_mid = adjust->v_total_mid;
+ drr->vertical_total_mid_frame_num =
+ adjust->v_total_mid_frame_num;
+ }
+}
+
+static void dcn401_update_clocks_params(const struct dc *dc,
+ struct dc_state *context, bool safe_to_lower,
+ struct clk_mgr_update_clocks_for_state_params *p)
+{
+ p->clk_mgr = dc->clk_mgr;
+ p->context = context;
+ p->safe_to_lower = safe_to_lower;
+ p->disallow_p_state_change = !safe_to_lower &&
+ context->bw_ctx.bw.dcn.clk.p_state_change_support;
+}
+
+/* Writes one entry; build sized every sequence before adding to it. */
+static void dcn401_add_step(struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *args,
+ const struct dcn401_commit_step *step)
+{
+ struct block_sequence *entry =
+ &sequence->steps[(*sequence->num_steps)++];
+ union block_sequence_params *p = &entry->params;
+ struct dc *dc = args->dc;
+ struct dcn_bw_output *bw = &args->new_state->bw_ctx.bw.dcn;
+ struct pipe_ctx *pipe = args->pipe;
+ const struct dcn401_commit_step_meta *m = &step->meta;
+
+ memset(entry, 0, sizeof(*entry));
+ entry->func = step->func;
+ switch (step->func) {
+ case CLK_MGR_UPDATE_CLOCKS_FOR_STATE:
+ dcn401_update_clocks_params(dc, args->new_state,
+ m->clk_mgr_update_clocks_for_state
+ .safe_to_lower,
+ &p->clk_mgr_update_clocks_for_state_params);
+ break;
+ case HUBBUB_PROGRAM_WATERMARKS:
+ p->hubbub_program_watermarks_params.dc = dc;
+ p->hubbub_program_watermarks_params.hubbub =
+ dc->res_pool->hubbub;
+ p->hubbub_program_watermarks_params.watermarks =
+ &bw->watermarks;
+ p->hubbub_program_watermarks_params.refclk_mhz =
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz /
+ 1000;
+ p->hubbub_program_watermarks_params.safe_to_lower =
+ m->hubbub_program_watermarks.safe_to_lower;
+ break;
+ case HUBBUB_PROGRAM_ARBITER:
+ p->hubbub_program_arbiter_params.dc = dc;
+ p->hubbub_program_arbiter_params.hubbub = dc->res_pool->hubbub;
+ p->hubbub_program_arbiter_params.arb_regs = &bw->arb_regs;
+ p->hubbub_program_arbiter_params.safe_to_lower =
+ m->hubbub_program_arbiter.safe_to_lower;
+ break;
+ case HUBBUB_PROGRAM_COMPBUF_SEGMENTS:
+ p->hubbub_program_compbuf_segments_params.hubbub =
+ dc->res_pool->hubbub;
+ p->hubbub_program_compbuf_segments_params.compbuf_size =
+ bw->arb_regs.compbuf_size;
+ p->hubbub_program_compbuf_segments_params.safe_to_lower =
+ m->hubbub_program_compbuf_segments
+ .safe_to_lower;
+ break;
+ case HUBBUB_FORCE_PSTATE_CHANGE_CONTROL:
+ p->hubbub_force_pstate_change_control_params.hubbub =
+ dc->res_pool->hubbub;
+ break;
+ case BIOS_SET_SCRATCH_CRITICAL_STATE:
+ p->bios_set_scratch_critical_state_params.dcb =
+ dc->ctx->dc_bios;
+ p->bios_set_scratch_critical_state_params.state =
+ m->bios_set_scratch_critical_state.state;
+ break;
+ case TG_LOCK:
+ p->tg_lock_params.dc = dc;
+ p->tg_lock_params.tg = pipe->stream_res.tg;
+ p->tg_lock_params.lock = m->tg_lock.lock;
+ break;
+ case TG_ENABLE_CRTC:
+ p->tg_enable_crtc_params.tg = pipe->stream_res.tg;
+ break;
+ case TG_DISABLE_CRTC:
+ p->tg_disable_crtc_params.tg = pipe->stream_res.tg;
+ break;
+ case TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR:
+ p->tg_wait_odm_doublebuffer_pending_clear_params.tg =
+ pipe->stream_res.tg;
+ break;
+ case TG_ENABLE_OPTC_CLOCK:
+ p->tg_enable_optc_clock_params.tg = pipe->stream_res.tg;
+ p->tg_enable_optc_clock_params.enable =
+ m->tg_enable_optc_clock.enable;
+ break;
+ case TG_SET_DRR:
+ p->tg_set_drr_params.tg = pipe->stream_res.tg;
+ if (m->tg_set_drr.from_adjust)
+ dcn401_drr_params(pipe, m->tg_set_drr.with_mid,
+ &p->tg_set_drr_params.drr);
+ break;
+ case TG_SET_STATIC_SCREEN_CONTROL:
+ /* DRR is off, so no static-screen event triggers the OTG. */
+ p->tg_set_static_screen_control_params.tg = pipe->stream_res.tg;
+ p->tg_set_static_screen_control_params.num_frames =
+ DCN401_STATIC_SCREEN_NUM_FRAMES;
+ break;
+ case TG_PROGRAM_TIMING:
+ dcn401_program_timing_params(pipe,
+ &p->tg_program_timing_params);
+ break;
+ case TG_SET_H_TIMING_DIV_MANUAL_MODE:
+ p->tg_set_h_timing_div_manual_mode_params.tg =
+ pipe->stream_res.tg;
+ p->tg_set_h_timing_div_manual_mode_params.manual_mode =
+ dcn401_h_timing_div_manual_mode(
+ &pipe->stream->timing);
+ break;
+ case TG_SETUP_VERTICAL_INTERRUPT2:
+ p->tg_setup_vertical_interrupt2_params.tg = pipe->stream_res.tg;
+ p->tg_setup_vertical_interrupt2_params.start_line =
+ dcn401_vupdate_start_line(pipe);
+ break;
+ case TG_SET_OUT_MUX:
+ p->tg_set_out_mux_params.tg = pipe->stream_res.tg;
+ p->tg_set_out_mux_params.dest = OUT_MUX_DIO;
+ break;
+ case TG_SET_EARLY_CONTROL:
+ p->tg_set_early_control_params.tg = pipe->stream_res.tg;
+ p->tg_set_early_control_params.early_cntl =
+ dcn401_early_cntl(pipe);
+ break;
+ case TG_PROGRAM_STEREO:
+ p->tg_program_stereo_params.tg = pipe->stream_res.tg;
+ p->tg_program_stereo_params.timing = &pipe->stream->timing;
+ break;
+ case OPTC_SET_ODM_BYPASS:
+ p->set_odm_bypass_params.tg = pipe->stream_res.tg;
+ p->set_odm_bypass_params.timing = &pipe->stream->timing;
+ break;
+ case OPP_PIPE_CLOCK_CONTROL:
+ p->opp_pipe_clock_control_params.opp = pipe->stream_res.opp;
+ p->opp_pipe_clock_control_params.enable = true;
+ break;
+ case OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL:
+ p->opp_program_left_edge_extra_pixel_params.opp =
+ pipe->stream_res.opp;
+ p->opp_program_left_edge_extra_pixel_params.pixel_encoding =
+ pipe->stream->timing.pixel_encoding;
+ p->opp_program_left_edge_extra_pixel_params.is_otg_master =
+ true;
+ break;
+ case OPP_PROGRAM_FMT:
+ p->opp_program_fmt_params.opp = pipe->stream_res.opp;
+ p->opp_program_fmt_params.fmt_bit_depth =
+ &pipe->stream->bit_depth_params;
+ p->opp_program_fmt_params.clamping = &pipe->stream->clamping;
+ break;
+ case OPP_SET_DYN_EXPANSION:
+ p->opp_set_dyn_expansion_params.opp = pipe->stream_res.opp;
+ p->opp_set_dyn_expansion_params.color_space =
+ COLOR_SPACE_YCBCR601;
+ p->opp_set_dyn_expansion_params.color_depth =
+ pipe->stream->timing.display_color_depth;
+ p->opp_set_dyn_expansion_params.signal = pipe->stream->signal;
+ break;
+ case OPP_SET_DISP_PATTERN_GENERATOR:
+ dcn401_opp_blank_params(dc, pipe,
+ &p->opp_set_disp_pattern_generator_params);
+ break;
+ case OPP_WAIT_FOR_BLANK_COMPLETE:
+ p->opp_wait_for_blank_complete_params.opp =
+ pipe->stream_res.opp;
+ p->opp_wait_for_blank_complete_params.polling_interval_us =
+ DCN401_DPG_POLL_INTERVAL_US;
+ p->opp_wait_for_blank_complete_params.timeout_us =
+ DCN401_DPG_POLL_TIMEOUT_US;
+ /* DC1 ignores the result of dcn20_wait_for_blank_complete. */
+ p->opp_wait_for_blank_complete_params.continue_on_error = true;
+ break;
+ case OPP_PROGRAM_STEREO:
+ p->opp_program_stereo_params.opp = pipe->stream_res.opp;
+ p->opp_program_stereo_params.timing = &pipe->stream->timing;
+ break;
+ case DCCG_SET_DTBCLK_P_SRC:
+ p->dccg_set_dtbclk_p_src_params.dccg = dc->res_pool->dccg;
+ p->dccg_set_dtbclk_p_src_params.src =
+ m->dccg_set_dtbclk_p_src.src;
+ p->dccg_set_dtbclk_p_src_params.otg_inst =
+ pipe->stream_res.tg->inst;
+ break;
+ case DCCG_SET_PIXEL_RATE_DIV:
+ p->dccg_set_pixel_rate_div_params.dccg = dc->res_pool->dccg;
+ p->dccg_set_pixel_rate_div_params.otg_inst =
+ pipe->stream_res.tg->inst;
+ p->dccg_set_pixel_rate_div_params.k1 =
+ m->dccg_set_pixel_rate_div.k1;
+ p->dccg_set_pixel_rate_div_params.k2 = PIXEL_RATE_DIV_NA;
+ break;
+ case CLK_SRC_PROGRAM_PIX_CLK:
+ p->clk_src_program_pix_clk_params.clock_source =
+ pipe->clock_source;
+ p->clk_src_program_pix_clk_params.pix_clk_params =
+ &pipe->stream_res.pix_clk_params;
+ /* DC1 gives virtual a dummy 8b/10b DP link setting. */
+ p->clk_src_program_pix_clk_params.encoding = DP_8b_10b_ENCODING;
+ p->clk_src_program_pix_clk_params.pll_settings =
+ &pipe->pll_settings;
+ break;
+ case CLK_SRC_POWER_DOWN:
+ p->clk_src_power_down_params.clock_source = pipe->clock_source;
+ /* dcn401_reset_hw_ctx_wrap ignores a failed power-down. */
+ p->clk_src_power_down_params.continue_on_error = true;
+ break;
+ case DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE:
+ p->link_enc_connect_dig_be_to_fe_params.link_enc =
+ pipe->stream->link->link_enc;
+ p->link_enc_connect_dig_be_to_fe_params.engine =
+ pipe->stream_res.stream_enc->id;
+ p->link_enc_connect_dig_be_to_fe_params.connect =
+ m->link_enc_connect_dig_be_to_fe.connect;
+ break;
+ case DIO_LINK_ENC_ENABLE_DP_OUTPUT:
+ p->link_enc_enable_dp_output_params.link_enc =
+ pipe->stream->link->link_enc;
+ p->link_enc_enable_dp_output_params.link_settings =
+ &pipe->link_config.dp_link_settings;
+ p->link_enc_enable_dp_output_params.clock_source =
+ pipe->clock_source->id;
+ break;
+ case DIO_LINK_ENC_DISABLE_OUTPUT:
+ p->link_enc_disable_output_params.link_enc =
+ pipe->stream->link->link_enc;
+ p->link_enc_disable_output_params.signal =
+ SIGNAL_TYPE_DISPLAY_PORT;
+ break;
+ case DCCG_ENABLE_SYMCLK_SE:
+ p->dccg_enable_symclk_se_params.dccg = dc->res_pool->dccg;
+ p->dccg_enable_symclk_se_params.stream_enc_inst =
+ pipe->stream_res.stream_enc->stream_enc_inst;
+ p->dccg_enable_symclk_se_params.link_enc_inst =
+ pipe->stream->link->link_enc->transmitter -
+ TRANSMITTER_UNIPHY_A;
+ break;
+ case DCCG_DISABLE_SYMCLK_SE:
+ p->dccg_disable_symclk_se_params.dccg = dc->res_pool->dccg;
+ p->dccg_disable_symclk_se_params.stream_enc_inst =
+ pipe->stream_res.stream_enc->stream_enc_inst;
+ p->dccg_disable_symclk_se_params.link_enc_inst =
+ pipe->stream->link->link_enc->transmitter -
+ TRANSMITTER_UNIPHY_A;
+ break;
+ case HUBP_PROGRAM_DET_SEGMENTS:
+ p->program_det_segments_params.hubbub = dc->res_pool->hubbub;
+ p->program_det_segments_params.hubp_inst =
+ pipe->plane_res.hubp->inst;
+ break;
+ case MPC_REMOVE_MPCC:
+ p->mpc_remove_mpcc_params.mpc = dc->res_pool->mpc;
+ p->mpc_remove_mpcc_params.mpc_tree_params =
+ &pipe->stream_res.opp->mpc_tree_params;
+ p->mpc_remove_mpcc_params.mpcc_to_remove =
+ &dc->res_pool->mpc->mpcc_array[
+ pipe->plane_res.mpcc_inst];
+ break;
+ case MPC_ASSERT_IDLE_MPCC:
+ p->mpc_assert_idle_mpcc_params.mpc = dc->res_pool->mpc;
+ p->mpc_assert_idle_mpcc_params.mpcc_id =
+ pipe->plane_res.mpcc_inst;
+ break;
+ case HUBP_SET_BLANK:
+ p->hubp_set_blank_params.hubp = pipe->plane_res.hubp;
+ p->hubp_set_blank_params.blank = true;
+ break;
+ case HUBP_UPDATE_MALL_SEL:
+ p->hubp_update_mall_sel_params.hubp = pipe->plane_res.hubp;
+ break;
+ case HUBP_SET_FLIP_CONTROL_GSL:
+ p->set_flip_control_gsl_params.hubp = pipe->plane_res.hubp;
+ break;
+ case HUBP_CLK_CNTL:
+ p->hubp_clk_cntl_params.hubp = pipe->plane_res.hubp;
+ break;
+ case DPP_DPPCLK_CONTROL:
+ p->dpp_dppclk_control_params.dpp = pipe->plane_res.dpp;
+ break;
+ case DC_IP_REQUEST_CNTL:
+ p->dc_ip_request_cntl_params.hws = dc->hwseq;
+ p->dc_ip_request_cntl_params.enable =
+ m->dc_ip_request_cntl.enable;
+ break;
+ case HUBP_PG_CONTROL:
+ p->hubp_pg_control_params.hws = dc->hwseq;
+ p->hubp_pg_control_params.hubp_inst =
+ pipe->plane_res.hubp->inst;
+ p->hubp_pg_control_params.power_on = false;
+ break;
+ case HUBP_RESET:
+ p->hubp_reset_params.hubp = pipe->plane_res.hubp;
+ break;
+ case DPP_RESET:
+ p->dpp_reset_params.dpp = pipe->plane_res.dpp;
+ break;
+ default:
+ /* The step tables use only the funcs above. */
+ ASSERT(false);
+ break;
+ }
+}
+
+static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
+ const struct dcn401_commit_step *step)
+{
+ if (step->meta.skip_if.stream_mode_unchanged &&
+ !args->pipe->stream->mode_changed)
+ return true;
+ if (step->meta.skip_if.new_state_has_stream &&
+ args->new_state->stream_count)
+ return true;
+ return false;
+}
+
+static void dcn401_add_steps(struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *args,
+ const struct dcn401_commit_step *steps, unsigned int count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ if (!dcn401_skip_step(args, &steps[i]))
+ dcn401_add_step(sequence, args, &steps[i]);
+ }
+}
+
+/* The support check found an OTG master for each stream of state. */
+static void dcn401_add_steps_per_stream_per_otg_master_pipe(
+ struct block_sequence_state *sequence, struct dc *dc,
+ struct dc_state *new_state, struct dc_state *state,
+ const struct dcn401_commit_step *steps, unsigned int count)
+{
+ struct dcn401_commit_step_args args = {0};
+ unsigned int i;
+
+ args.dc = dc;
+ args.new_state = new_state;
+ for (i = 0; i < state->stream_count; i++) {
+ args.pipe = get_otg_master_from_state_by_stream(state,
+ state->streams[i]);
+ dcn401_add_steps(sequence, &args, steps, count);
+ }
+}
+
+#define DCN401_ADD_STEPS(sequence, args, steps) \
+ dcn401_add_steps(sequence, args, steps, ARRAY_SIZE(steps))
+#define DCN401_ADD_STEPS_PER_STREAM(sequence, dc, new_state, state, steps) \
+ dcn401_add_steps_per_stream_per_otg_master_pipe(sequence, dc, \
+ new_state, state, steps, ARRAY_SIZE(steps))
+
+static enum bls_builder_result dcn401_build_non_seamless_commit(
+ struct bls_builder_declarative_params *params)
+{
+ struct dcn401_commit_step_args state_args = {0};
+ struct block_sequence_state head;
+ struct block_sequence_state tail;
+ struct dc_state *current_state;
+ struct dc_state *new_state;
+ enum bls_builder_result result;
+ struct dc *dc;
+
+ if (!params)
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
+ result = dcn401_check_non_seamless_commit_support(params->dc,
+ params->current_state, params->new_state);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+
+ /* The entries point into the states. The build writes neither. */
+ dc = params->dc;
+ current_state = (struct dc_state *)params->current_state;
+ new_state = (struct dc_state *)params->new_state;
+ state_args.dc = dc;
+ state_args.new_state = new_state;
+
+ params->head_num_steps = 0;
+ params->tail_num_steps = 0;
+ head.steps = params->head_sequence;
+ head.num_steps = ¶ms->head_num_steps;
+ tail.steps = params->tail_sequence;
+ tail.num_steps = ¶ms->tail_num_steps;
+
+ DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new);
+ DCN401_ADD_STEPS_PER_STREAM(&head, dc, new_state, current_state,
+ bls_head_per_stream_per_otg_master_pipe_current);
+ DCN401_ADD_STEPS_PER_STREAM(&head, dc, new_state, new_state,
+ bls_head_per_stream_per_otg_master_pipe_new);
+ DCN401_ADD_STEPS_PER_STREAM(&head, dc, new_state, new_state,
+ bls_head_per_stream_per_otg_master_pipe_new_front_end);
+ DCN401_ADD_STEPS(&tail, &state_args, bls_tail_per_state_new);
+
+ return BLS_BUILD_SUCCESS;
+}
+
+static bool dcn401_non_seamless_commit_check_capability(
+ const struct bls_builder_check_capability_params *params)
+{
+ return params && dcn401_check_non_seamless_commit_support(params->dc,
+ params->current_state, params->new_state) ==
+ BLS_BUILD_SUCCESS;
+}
+
+const struct bls_builder *dcn401_non_seamless_commit_builder(void)
+{
+ static const struct bls_builder builder = {
+ .name = DCN_DECL_NON_SEAMLESS_COMMIT,
+ .kind = BLS_BUILDER_DECLARATIVE,
+ .check_capability = dcn401_non_seamless_commit_check_capability,
+ .build.declarative = dcn401_build_non_seamless_commit,
+ };
+
+ return &builder;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
index 589a950e078b..4c0043c84907 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
@@ -407,6 +407,8 @@ const struct bls_builder *dcn401_bls_builder_create(
return &initialization_builder;
case DCN_DECL_INITIALIZATION_LEGACY:
return &legacy_initialization_builder;
+ case DCN_DECL_NON_SEAMLESS_COMMIT:
+ return dcn401_non_seamless_commit_builder();
default:
return NULL;
}
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
index 8ef078e01dd4..43bb5f461ef2 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
@@ -5,6 +5,12 @@
#include "dm_services.h"
#include "dc.h"
+#include "core_types.h"
+#include "resource.h"
+#include "dc_bios_types.h"
+#include "bls_builder/bls_builder.h"
+#include "bls_executor/bls_executor.h"
+#include "workaround_manager/workaround_manager.h"
#include "core2/dc_core2.h"
#include "core2/dc_core2_init.h"
#include "dc_api_dispatch/api_shim.h"
@@ -39,11 +45,163 @@ void core2_destruct(struct dc *dc)
(void)dc;
}
+/*
+ * The VBIOS scratch read legacy_commit_state_hw makes
+ * before it enables accelerated mode. The commit has no accelerated-mode
+ * entries, so it runs only after init has left VBIOS mode. Remove when a
+ * BLC exposes the accelerated-mode bit.
+ */
+static bool core2_is_accelerated_mode(struct dc *dc)
+{
+ struct dc_bios *dcb = dc->ctx->dc_bios;
+
+ return dcb && dcb->funcs && dcb->funcs->is_accelerated_mode &&
+ dcb->funcs->is_accelerated_mode(dcb);
+}
+
+static struct pipe_ctx *core2_otg_master(struct dc_state *state)
+{
+ unsigned int i;
+
+ if (!state->stream_count)
+ return NULL;
+ for (i = 0; i < MAX_PIPES; i++) {
+ if (state->res_ctx.pipe_ctx[i].stream == state->streams[0])
+ return &state->res_ctx.pipe_ctx[i];
+ }
+ return NULL;
+}
+
+static bool core2_run_commit_sequence(struct dc *dc,
+ const struct bls_builder *builder,
+ const struct workaround_manager *wam,
+ struct block_sequence *steps, unsigned int *num_steps)
+{
+ struct workaround_context wa_ctx = {0};
+ struct block_sequence_state sequence;
+ enum dc2_bls_exec_result result;
+
+ sequence.steps = steps;
+ sequence.num_steps = num_steps;
+ wa_ctx.current_state = dc->current_state;
+ wa_ctx.builder_name = builder->name;
+ wa_ctx.dcn_version = dc->ctx->dce_version;
+ wa_ctx.bug_wa = &dc->work_arounds;
+ wa_ctx.debug = &dc->debug;
+ if (workaround_manager_apply(wam, &sequence, &wa_ctx) != WAM_OK ||
+ *num_steps > DC2_BLS_MAX_STEPS)
+ return false;
+
+ result = dc2_bls_execute(TO_DC2(dc), steps, *num_steps);
+ return result == DC2_BLS_EXEC_OK ||
+ result == DC2_BLS_EXEC_CONTINUED_ON_ERROR;
+}
+
+/*
+ * dcn401_prepare_bandwidth's optimized_required writes. The Executor ORs
+ * in the watermark and arbiter results.
+ */
+static void core2_commit_prepare_done(struct dc *dc, struct dc_state *context)
+{
+ if (context->bw_ctx.bw.dcn.clk.p_state_change_support ||
+ context->bw_ctx.bw.dcn.arb_regs.compbuf_size !=
+ dc->current_state->bw_ctx.bw.dcn.arb_regs.compbuf_size)
+ dc->optimized_required = true;
+}
+
+/* The software state DC1 writes while it enables the stream. */
+static void core2_commit_enable_done(struct pipe_ctx *pipe)
+{
+ struct dc_stream_state *stream = pipe->stream;
+ struct dc_link *link = stream->link;
+
+ link->phy_state.symclk_state = SYMCLK_ON_TX_ON;
+ link->link_state_valid = true;
+ link->link_status.link_active = true;
+ link->psr_settings.psr_feature_enabled = false;
+ link->replay_settings.replay_feature_enabled = false;
+ stream->timing_adjust_pending = false;
+ pipe->plane_res.scl_data.lb_params.alpha_en = false;
+}
+
+/* The software state DC1 writes while it disables the stream. */
+static void core2_commit_disable_done(struct dc *dc, struct pipe_ctx *pipe)
+{
+ struct dc_link *link = pipe->stream->link;
+
+ pipe->plane_res.hubp->power_gated = true;
+ link->link_status.link_active = false;
+ link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
+ pipe->stream->timing_adjust_pending = true;
+ dc->optimized_required = true;
+}
+
/* Never falls back to legacy_commit_state_hw: DC2 must not call DC1. */
enum dc_status core2_commit_state_hw(struct dc *dc, struct dc_state *context)
{
- (void)dc;
- (void)context;
+ const struct bls_builder *builder =
+ TO_DC2(dc)->builders[DCN_DECL_NON_SEAMLESS_COMMIT];
+ const struct workaround_manager *wam = dc2_workaround_manager_get();
+ struct bls_builder_check_capability_params check = {
+ .dc = dc,
+ .current_state = dc->current_state,
+ .new_state = context,
+ };
+ struct bls_builder_declarative_params *params;
+ struct pipe_ctx *old_pipe;
+ struct pipe_ctx *new_pipe;
+ enum dc_status status = DC_ERROR_UNEXPECTED;
+
+ if (!builder || !builder->check_capability ||
+ builder->kind != BLS_BUILDER_DECLARATIVE ||
+ !builder->build.declarative || !wam ||
+ !builder->check_capability(&check) ||
+ !core2_is_accelerated_mode(dc))
+ return DC_NOT_SUPPORTED;
+
+ old_pipe = core2_otg_master(dc->current_state);
+ new_pipe = core2_otg_master(context);
+
+ /*
+ * link_set_dpms_on builds the info frame into the
+ * pipe before it programs the packets. Remove when the builder
+ * computes it.
+ */
+ if (new_pipe)
+ resource_build_info_frame(new_pipe);
+
+ /* 128 block-sequence entries; keep them off the kernel stack. */
+ params = kvzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return DC_ERROR_UNEXPECTED;
+ params->dc = dc;
+ params->current_state = dc->current_state;
+ params->new_state = context;
+
+ if (builder->build.declarative(params) != BLS_BUILD_SUCCESS) {
+ status = DC_NOT_SUPPORTED;
+ goto out;
+ }
+ if (!core2_run_commit_sequence(dc, builder, wam,
+ params->head_sequence, ¶ms->head_num_steps))
+ goto out;
+ /*
+ * The head ends with the ODM double-buffer and blank waits when it
+ * programs an OTG. The tail is deferred until that head returns, so
+ * it runs after double buffering completes.
+ */
+ if (!core2_run_commit_sequence(dc, builder, wam,
+ params->tail_sequence, ¶ms->tail_num_steps))
+ goto out;
+
+ core2_commit_prepare_done(dc, context);
+ if (old_pipe)
+ core2_commit_disable_done(dc, old_pipe);
+ if (new_pipe)
+ core2_commit_enable_done(new_pipe);
+ status = DC_OK;
- return DC_NOT_SUPPORTED;
+out:
+ kvfree(params);
+ return status;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 08/74] drm/amd/display: Move HDMI FRL Polling Outside dc_lock
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (6 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 07/74] drm/amd/display: Build the dcn401 virtual commit natively Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 09/74] drm/amd/display: Extend the IOMMU mismatch workaround to DCN42B Wayne Lin
` (66 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
From: Fangzhi Zuo <jerry.zuo@amd.com>
[Why]
amdgpu_dm_commit_streams() started or cancelled the self-rearming HDMI FRL
poll under dm->dc_lock. cancel_delayed_work_sync() can wait for a worker
that needs the same lock, creating a lock dependency that could deadlock.
[How]
Release dm->dc_lock after dc_commit_streams() and the idle-optimization
update, then scan the committed streams and start or stop FRL polling
outside the lock.
Cc: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Fangzhi Zuo <jerry.zuo@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
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 33c73ca9cff1..7fffbb609225 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -4749,6 +4749,11 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_commit *state,
dc_exit_ips_for_hw_access(dm->dc);
WARN_ON(!dc_commit_streams(dm->dc, ¶ms));
+ /* Allow idle optimization when vblank count is 0 for display off */
+ if (dm->active_vblank_irq_count == 0 && amdgpu_dm_is_headless(dm->adev))
+ dc_allow_idle_optimizations(dm->dc, true);
+ mutex_unlock(&dm->dc_lock);
+
bool frl_stream_found = false;
for (i = 0; i < params.stream_count; i++) {
@@ -4759,6 +4764,7 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_commit *state,
break;
}
}
+
if (frl_stream_found) {
if (queue_delayed_work(dm->hdmi_frl_status_polling_wq,
&dm->hdmi_frl_status_polling_work,
@@ -4768,10 +4774,6 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_commit *state,
if (cancel_delayed_work_sync(&dm->hdmi_frl_status_polling_work))
drm_dbg_kms(dev, "200ms frl status polling stops ...\n");
}
- /* Allow idle optimization when vblank count is 0 for display off */
- if ((dm->active_vblank_irq_count == 0) && amdgpu_dm_is_headless(dm->adev))
- dc_allow_idle_optimizations(dm->dc, true);
- mutex_unlock(&dm->dc_lock);
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 09/74] drm/amd/display: Extend the IOMMU mismatch workaround to DCN42B
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (7 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 08/74] drm/amd/display: Move HDMI FRL Polling Outside dc_lock Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 10/74] drm/amd/display: Stop HDMI FRL status polling on fini Wayne Lin
` (65 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Charlene Liu, Gabe Teeger
From: Charlene Liu <Charlene.Liu@amd.com>
Set iommu_mismatch_temp_wka to 0x7 in the DCN42B debug defaults.
Cc: Gabe Teeger <gabe.teeger@amd.com>
Signed-off-by: Charlene Liu <Charlene.Liu@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c | 1 +
1 file changed, 1 insertion(+)
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 cff4607f3658..784cce09b89b 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
@@ -875,6 +875,7 @@ static const struct dc_debug_options debug_defaults_drv = {
.min_deep_sleep_dcfclk_khz = 8000,
.replay_skip_crtc_disabled = true,
.psr_skip_crtc_disable = true,
+ .iommu_mismatch_temp_wka = 0x7,
};
static const struct dc_check_config config_defaults = {
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 10/74] drm/amd/display: Stop HDMI FRL status polling on fini
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (8 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 09/74] drm/amd/display: Extend the IOMMU mismatch workaround to DCN42B Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-12 8:51 ` Eren Özen
2026-11-11 1:28 ` [PATCH 11/74] drm/amd/display: Relax stutter constraints for DRR streams Wayne Lin
` (64 subsequent siblings)
74 siblings, 1 reply; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
From: Fangzhi Zuo <jerry.zuo@amd.com>
[Why]
The HDMI FRL status polling work re-queues itself and its workqueue was not
stopped during amdgpu_dm_fini(). During unbind or unload, the worker could
access freed DC state, and the workqueue was leaked. This fixes the FRL
polling teardown introduced by the HDMI FRL support change.
[How]
Cancel the delayed polling work synchronously and destroy the workqueue in
amdgpu_dm_fini() before the remaining display state is torn down.
Cc: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Fangzhi Zuo <jerry.zuo@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 7 +++++++
1 file changed, 7 insertions(+)
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 7fffbb609225..a7c2b0094944 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -885,6 +885,13 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
adev->dm.vblank_control_workqueue = NULL;
}
+ /* The poll re-queues itself, so stop it before the wq and dc go away. */
+ if (adev->dm.hdmi_frl_status_polling_wq) {
+ cancel_delayed_work_sync(&adev->dm.hdmi_frl_status_polling_work);
+ destroy_workqueue(adev->dm.hdmi_frl_status_polling_wq);
+ adev->dm.hdmi_frl_status_polling_wq = NULL;
+ }
+
if (adev->dm.idle_workqueue) {
if (adev->dm.idle_workqueue->running) {
adev->dm.idle_workqueue->enable = false;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 11/74] drm/amd/display: Relax stutter constraints for DRR streams
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (9 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 10/74] drm/amd/display: Stop HDMI FRL status polling on fini Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 12/74] drm/amd/display: Include Ramless Idle in stutter relaxation Wayne Lin
` (63 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Dillon Varone
From: Dillon Varone <Dillon.Varone@amd.com>
Why:
DRR can extend blanking beyond nominal vblank, but the existing stutter
eligibility checks can reject streams using the nominal blanking duration.
How:
Add a PMO option to relax the blanking factor for fixed-DRR streams,
and apply the relaxed threshold when checking Z8 and regular stutter
latency requirements.
Signed-off-by: Dillon Varone <Dillon.Varone@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dc/dml2_0/dml21/inc/dml_top_types.h | 1 +
.../dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c | 19 ++++++++++++++++---
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h
index 75a24a85775e..fd7ccefc7fb7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h
@@ -83,6 +83,7 @@ struct dml2_pmo_options {
bool force_optional_uclk_pstate_support;
bool force_optional_mcache_support;
bool force_optional_ppt_temp_read_admissibility;
+ bool relax_stutter_constraint_for_drr;
struct dml2_pmo_pstate_strategy *override_strategy_lists[DML2_MAX_PLANES];
unsigned int num_override_strategies_per_list[DML2_MAX_PLANES];
};
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
index de078ccd91a2..3584dbe4c9fb 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
@@ -8,7 +8,7 @@
#include "dml2_pmo_dcn4_fams2.h"
static const double MIN_VACTIVE_MARGIN_PCT = 0.25; // We need more than non-zero margin because DET buffer granularity can alter vactive latency hiding
-static const double MIN_BLANK_STUTTER_FACTOR = 3.0;
+static const double MIN_BLANK_STUTTER_FACTOR = 3.0f;
static const struct dml2_pmo_pstate_strategy base_strategy_list_1_display[] = {
// VActive Preferred
@@ -2357,6 +2357,16 @@ bool pmo_dcn4_fams2_optimize_for_pstate_support(struct dml2_pmo_optimize_for_pst
return success;
}
+static bool should_relax_stutter_constraint_for_stream(const struct dml2_pmo_init_for_stutter_in_out *in_out,
+ unsigned int stream_index)
+{
+ // relax the stutter constraint when:
+ // 1. Stream has DRR fixed enabled (assumed blank is extended sufficiently beyond nominal)
+ // 2. The option to relax stutter constraint is enabled in PMO options.
+ return in_out->base_display_config->display_config.stream_descriptors[stream_index].timing.drr_config.drr_active_fixed &&
+ in_out->instance->options->relax_stutter_constraint_for_drr;
+}
+
bool pmo_dcn4_fams2_init_for_stutter(struct dml2_pmo_init_for_stutter_in_out *in_out)
{
bool success = true;
@@ -2365,6 +2375,7 @@ bool pmo_dcn4_fams2_init_for_stutter(struct dml2_pmo_init_for_stutter_in_out *in
bool z8_stutter_optimization_too_expensive = false;
bool stutter_optimization_too_expensive = false;
double line_time_us, vblank_nom_time_us;
+ double stutter_factor;
unsigned int i;
@@ -2382,15 +2393,17 @@ bool pmo_dcn4_fams2_init_for_stutter(struct dml2_pmo_init_for_stutter_in_out *in
}
for (i = 0; i < in_out->base_display_config->display_config.num_streams; i++) {
+ stutter_factor = should_relax_stutter_constraint_for_stream(in_out, i) ? 1.0f : MIN_BLANK_STUTTER_FACTOR;
+
line_time_us = (double)in_out->base_display_config->display_config.stream_descriptors[i].timing.h_total / (in_out->base_display_config->display_config.stream_descriptors[i].timing.pixel_clock_khz * 1000) * 1000000;
vblank_nom_time_us = line_time_us * in_out->base_display_config->display_config.stream_descriptors[i].timing.vblank_nom;
- if (vblank_nom_time_us < pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us * MIN_BLANK_STUTTER_FACTOR) {
+ if (vblank_nom_time_us < pmo->soc_bb->power_management_parameters.z8_stutter_exit_latency_us * stutter_factor) {
z8_stutter_optimization_too_expensive = true;
break;
}
- if (vblank_nom_time_us < pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us * MIN_BLANK_STUTTER_FACTOR) {
+ if (vblank_nom_time_us < pmo->soc_bb->power_management_parameters.stutter_enter_plus_exit_latency_us * stutter_factor) {
stutter_optimization_too_expensive = true;
break;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 12/74] drm/amd/display: Include Ramless Idle in stutter relaxation
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (10 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 11/74] drm/amd/display: Relax stutter constraints for DRR streams Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 13/74] drm/amd/display: Initialize DMUB LSDMA ring buffer Wayne Lin
` (62 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Sreeja Golui, Dillon Varone
From: Sreeja Golui <Sreeja.Golui@amd.com>
Why:
Ramless Idle static-screen streams can also use extended blanking and
should receive the relaxed stutter constraint.
How:
Add a timing flag for Ramless Idle static-screen mode and treat it
like fixed DRR when deciding whether to relax the stutter factor.
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Sreeja Golui <Sreeja.Golui@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h | 1 +
.../dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h
index 27ffe7ed11c7..816443c9427b 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h
@@ -289,6 +289,7 @@ struct dml2_timing_cfg {
bool disallowed;
bool drr_active_variable;
bool drr_active_fixed;
+ bool drr_active_idle_static_screen;
} drr_config;
unsigned long vblank_nom;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
index 3584dbe4c9fb..62ac29dfe9be 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
@@ -2361,9 +2361,10 @@ static bool should_relax_stutter_constraint_for_stream(const struct dml2_pmo_ini
unsigned int stream_index)
{
// relax the stutter constraint when:
- // 1. Stream has DRR fixed enabled (assumed blank is extended sufficiently beyond nominal)
+ // 1. Stream has fixed DRR or Ramless Idle static screen enabled (extended blank)
// 2. The option to relax stutter constraint is enabled in PMO options.
- return in_out->base_display_config->display_config.stream_descriptors[stream_index].timing.drr_config.drr_active_fixed &&
+ return (in_out->base_display_config->display_config.stream_descriptors[stream_index].timing.drr_config.drr_active_fixed ||
+ in_out->base_display_config->display_config.stream_descriptors[stream_index].timing.drr_config.drr_active_idle_static_screen) &&
in_out->instance->options->relax_stutter_constraint_for_drr;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 13/74] drm/amd/display: Initialize DMUB LSDMA ring buffer
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (11 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 12/74] drm/amd/display: Include Ramless Idle in stutter relaxation Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 14/74] drm/amd/display: Fix FFE test automation link rate change Wayne Lin
` (61 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Alvin Lee
From: Ivan Lipski <ivan.lipski@amd.com>
[Why&How]
DMUB uses LSDMA for alt-ch aperture copies.
In dm_dmub_hw_init(), query DMUB feature caps first, then set
lsdma_rb_fb from DMUB_WINDOW_LSDMA_BUFFER and call dmub_lsdma_init().
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/amdgpu_dm/amdgpu_dm_dmub.c | 6 ++++
.../amdgpu_dm/tests/amdgpu_dm_dmub_test.c | 31 +++++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
index 0ea8f9a582cc..d655ae8bbb52 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
@@ -382,11 +382,17 @@ int dm_dmub_hw_init(struct amdgpu_device *adev)
return -ENOMEM;
}
+ /* Load DMUB caps */
+ dc_dmub_srv_query_caps_cmd(adev->dm.dc->ctx->dmub_srv);
+
/* Cursor offload has to be enabled; prime it on every DMUB init,
* including post-suspend and GPU-reset.
*/
dc_dmub_srv_cursor_offload_init(adev->dm.dc);
+ dmub_srv->lsdma_rb_fb = fb_info->fb[DMUB_WINDOW_LSDMA_BUFFER];
+ dmub_lsdma_init(adev->dm.dc->ctx->dmub_srv);
+
drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n",
adev->dm.dmcub_fw_version);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
index 2e273dea17ef..b48c96ec4250 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
@@ -70,6 +70,16 @@ static void dm_test_dmub_init_reg_offsets(struct dmub_srv *dmub,
{
}
+static void dm_test_dmub_set_inbox1_wptr(struct dmub_srv *dmub, uint32_t wptr_offset)
+{
+}
+
+/* The fake firmware consumes inbox1 commands instantly: rptr tracks wptr. */
+static uint32_t dm_test_dmub_get_inbox1_ptr(struct dmub_srv *dmub)
+{
+ return dmub->inbox1.rb.wrpt;
+}
+
static bool dm_test_dmcu_init(struct dmcu *dmcu)
{
return true;
@@ -99,6 +109,9 @@ static struct dmub_srv *dm_test_alloc_dmub_srv(struct kunit *test)
dmub_srv->hw_funcs.is_hw_init = dm_test_dmub_hw_initialized;
dmub_srv->hw_funcs.get_fw_status = dm_test_dmub_fw_ready;
dmub_srv->hw_funcs.init_reg_offsets = dm_test_dmub_init_reg_offsets;
+ dmub_srv->hw_funcs.set_inbox1_wptr = dm_test_dmub_set_inbox1_wptr;
+ dmub_srv->hw_funcs.get_inbox1_rptr = dm_test_dmub_get_inbox1_ptr;
+ dmub_srv->hw_funcs.get_inbox1_wptr = dm_test_dmub_get_inbox1_ptr;
return dmub_srv;
}
@@ -650,6 +663,23 @@ static void dm_test_dmub_hw_init_success_fake_dmub(struct kunit *test)
KUNIT_EXPECT_NOT_NULL(test, adev->dm.dc->ctx->dmub_srv);
}
+/**
+ * dm_test_dmub_hw_init_lsdma_buffer - Test hw init assigns the LSDMA ring buffer
+ * @test: The KUnit test context
+ *
+ * dm_dmub_hw_init() should point the DMUB LSDMA ring buffer at the
+ * DMUB_WINDOW_LSDMA_BUFFER framebuffer window.
+ */
+static void dm_test_dmub_hw_init_lsdma_buffer(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_test_alloc_adev_with_dmub(test);
+ struct dmub_fb *lsdma_fb = &adev->dm.dmub_fb_info->fb[DMUB_WINDOW_LSDMA_BUFFER];
+
+ KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0);
+ KUNIT_EXPECT_PTR_EQ(test, adev->dm.dmub_srv->lsdma_rb_fb.cpu_addr, lsdma_fb->cpu_addr);
+ KUNIT_EXPECT_EQ(test, adev->dm.dmub_srv->lsdma_rb_fb.size, lsdma_fb->size);
+}
+
/**
* dm_test_dmub_hw_init_no_hw_support - Test hw init returns 0 when HW is unsupported
* @test: The KUnit test context
@@ -1917,6 +1947,7 @@ static struct kunit_case amdgpu_dm_dmub_tests[] = {
KUNIT_CASE(dm_test_dmub_hw_init_no_fb_info),
KUNIT_CASE(dm_test_dmub_hw_init_no_firmware),
KUNIT_CASE(dm_test_dmub_hw_init_success_fake_dmub),
+ KUNIT_CASE(dm_test_dmub_hw_init_lsdma_buffer),
KUNIT_CASE(dm_test_dmub_hw_init_no_hw_support),
KUNIT_CASE(dm_test_dmub_hw_init_bss_data),
KUNIT_CASE(dm_test_dmub_hw_init_hw_init_fails),
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 14/74] drm/amd/display: Fix FFE test automation link rate change
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (12 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 13/74] drm/amd/display: Initialize DMUB LSDMA ring buffer Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 15/74] drm/amd/display: Update indicate pstate enable sequence Wayne Lin
` (60 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Ilya Bakoulin, Wenjing Liu
From: Ilya Bakoulin <Ilya.Bakoulin@amd.com>
[Why/How]
Sink requesting a new link rate is not handled properly on hotplug when
FLT_NO_TIMEOUT is set. Check the existing config on the sink to see if a
link rate update is needed.
Cc: Wenjing Liu <wenjing.liu@amd.com>
Signed-off-by: Ilya Bakoulin <Ilya.Bakoulin@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h | 1 +
.../display/dc/link/protocols/link_hdmi_frl.c | 17 ++++++++++++++---
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
index 9e0fe41f28a5..446db6dab312 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
@@ -276,6 +276,7 @@ struct dc_hdmi_frl_flags {
bool force_frl_max;
bool apply_vsdb_rcc_wa;
bool no_timeout;
+ enum hdmi_frl_link_rate no_timeout_frl_link_rate;
};
struct dc_hdmi_frl_link_training_overrides {
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
index 097df185a618..c31be4900e8d 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
@@ -461,7 +461,8 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
sizeof(offset), &test_req.byte, sizeof(test_req.byte));
FRL_INFO("FRL TEST REQ: FLT_no_timeout = %d \n", test_req.fields.FLT_NO_TIMEOUT);
- if (test_req.fields.FLT_NO_TIMEOUT && ddc_service->link->frl_flags.no_timeout) {
+ if (test_req.fields.FLT_NO_TIMEOUT && ddc_service->link->frl_flags.no_timeout &&
+ ddc_service->link->frl_flags.no_timeout_frl_link_rate == link_settings->frl_link_rate) {
bool de_emphasis_only = false;
bool pre_shoot_only = false;
bool no_ffe = false;
@@ -538,6 +539,7 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
if (test_req.fields.FLT_NO_TIMEOUT) {
ddc_service->link->frl_flags.no_timeout = true;
+ ddc_service->link->frl_flags.no_timeout_frl_link_rate = link_settings->frl_link_rate;
/*Specify FRL rate*/
write_buffer[0] = HDMI_SCDC_CONFIG_1;
@@ -834,6 +836,15 @@ enum link_result hdmi_frl_perform_link_training_with_fallback(
msleep(200);
+ /* If we're in a NO_TIMEOUT state and link rate changes, cycle the link on/off
+ * so that the new link rate is properly applied */
+ if (link->frl_flags.no_timeout) {
+ if (link->frl_flags.no_timeout_frl_link_rate != link->frl_link_settings.frl_link_rate) {
+ link->hpo_frl_link_enc->funcs->disable_link_encoder(link->hpo_frl_link_enc);
+ link->link_enc->funcs->disable_output(link->link_enc, SIGNAL_TYPE_HDMI_FRL);
+ }
+ }
+
link->ctx->dc->hwss.setup_hdmi_frl_link(link, 0,
frl_phy_clock_source_id);
@@ -1023,7 +1034,7 @@ void hdmi_frl_set_preferred_link_settings(struct dc *dc,
link_stream->link->frl_link_settings = *link_setting;
link_stream->link->frl_verified_link_cap = *link_setting;
- while (link_stat != LINK_RESULT_SUCCESS) {
+ while (link_stat != LINK_RESULT_SUCCESS && link_stat != LINK_RESULT_NO_TIMEOUT) {
link_set_dpms_off(pipe);
/* For DCN3.0, can also have 4:1 combine mode.
* TODO: Add function get_odm_combine_mode that has different
@@ -1101,7 +1112,7 @@ void hdmi_frl_set_preferred_link_settings(struct dc *dc,
link_stat = hdmi_frl_perform_link_training_with_retries(link_stream->link);
/* Enable FRL packet transmission */
- if (link_stat == LINK_RESULT_SUCCESS) {
+ if (link_stat == LINK_RESULT_SUCCESS || link_stat == LINK_RESULT_NO_TIMEOUT) {
link_stream->link->hpo_frl_link_enc->funcs->enable_output(
link_stream->link->hpo_frl_link_enc);
if (link_stream->link->frl_flags.apply_vsdb_rcc_wa)
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 15/74] drm/amd/display: Update indicate pstate enable sequence
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (13 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 14/74] drm/amd/display: Fix FFE test automation link rate change Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 16/74] drm/amd/display: HWSS Refactor - Watermarks Wayne Lin
` (59 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Alvin Lee, Wenjing Liu
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="y", Size: 4951 bytes --]
From: Alvin Lee <Alvin.Lee2@amd.com>
[Description]
- The enable sequence for indicate pstate status must assign the
non UCLK / FCLK params (alt-ch, wait_resp, etc.) correctly.
- This fixes a bug where UCLK is being enabled (but alt-ch is being disabled),
but clk_mgr still sends the pstate message with alt-ch enabled.
- Only UCLK and UCLK will be sent with enabled status on the first call
because this is required for the UTM QoS update. The other status bits
must be correct from the start
Cc: Wenjing Liu <wenjing.liu@amd.com>
Signed-off-by: Alvin Lee <Alvin.Lee2@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 35 +++++++------------
1 file changed, 13 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
index 98d146bc25c0..4242a4b5c902 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
@@ -1370,6 +1370,7 @@ static void dcn60_make_bandwidth_clocks_update_action(
* @block_sequence: output array of block sequence steps
* @action: action struct populated by dcn60_make_bandwidth_clocks_update_action
* @clk_mgr_base: current clock manager state (read-only, used for original values)
+ * @context: dc state with active plane information
* @new_clocks: requested clock values for message parameters
*
* Return: number of steps written to block_sequence
@@ -1378,9 +1379,11 @@ static unsigned int dcn60_build_bandwidth_clocks_block_sequence_with_action(
struct dcn60_clk_mgr_block_sequence *block_sequence,
struct dcn60_bandwidth_clocks_update_action *action,
struct clk_mgr *clk_mgr_base,
+ struct dc_state *context,
struct dc_clocks *new_clocks)
{
unsigned int num_steps = 0;
+ bool no_active_planes = clk_mgr_helper_get_active_plane_cnt(clk_mgr_base->ctx->dc, context) == 0;
/* CLK_MGR60_INDICATE_PSTATE_STATUS — enable */
if ((action->uclk_pstate.enable && action->uclk_pstate.send_message)
@@ -1394,14 +1397,11 @@ static unsigned int dcn60_build_bandwidth_clocks_block_sequence_with_action(
action->fclk_pstate.enable
|| clk_mgr_base->clks.fclk_p_state_change_support;
block_sequence[num_steps].params.indicate_pstate_status_params.drr_enable =
- action->fams.enable
- || clk_mgr_base->clks.fw_based_mclk_switching;
- block_sequence[num_steps].params.indicate_pstate_status_params.wait_resp =
- action->fams.enable
- || clk_mgr_base->clks.fw_based_mclk_switching;
+ new_clocks->fw_based_mclk_switching;
block_sequence[num_steps].params.indicate_pstate_status_params.alt_ch_enable =
- action->alt_ch.enable
- || clk_mgr_base->clks.alt_ch_pstate_switch;
+ new_clocks->alt_ch_pstate_switch;
+ block_sequence[num_steps].params.indicate_pstate_status_params.wait_resp =
+ new_clocks->fw_based_mclk_switching;
block_sequence[num_steps].func = CLK_MGR60_INDICATE_PSTATE_STATUS;
num_steps++;
}
@@ -1443,24 +1443,15 @@ static unsigned int dcn60_build_bandwidth_clocks_block_sequence_with_action(
|| (action->fams.disable && action->fams.send_message)
|| (action->alt_ch.disable && action->alt_ch.send_message)) {
block_sequence[num_steps].params.indicate_pstate_status_params.allow_uclk =
- !action->uclk_pstate.disable
- && (action->uclk_pstate.enable
- || clk_mgr_base->clks.p_state_change_support);
+ new_clocks->p_state_change_support || no_active_planes;
block_sequence[num_steps].params.indicate_pstate_status_params.allow_fclk =
- !action->fclk_pstate.disable
- && (action->fclk_pstate.enable
- || clk_mgr_base->clks.fclk_p_state_change_support);
+ new_clocks->fclk_p_state_change_support || no_active_planes;
block_sequence[num_steps].params.indicate_pstate_status_params.drr_enable =
- !action->fams.disable
- && (action->fams.enable
- || clk_mgr_base->clks.fw_based_mclk_switching);
+ new_clocks->fw_based_mclk_switching;
block_sequence[num_steps].params.indicate_pstate_status_params.alt_ch_enable =
- !action->alt_ch.disable
- && (action->alt_ch.enable
- || clk_mgr_base->clks.alt_ch_pstate_switch);
+ new_clocks->alt_ch_pstate_switch;
block_sequence[num_steps].params.indicate_pstate_status_params.wait_resp =
- action->fams.enable
- || clk_mgr_base->clks.fw_based_mclk_switching;
+ new_clocks->fw_based_mclk_switching;
block_sequence[num_steps].func = CLK_MGR60_INDICATE_PSTATE_STATUS;
num_steps++;
}
@@ -1552,7 +1543,7 @@ static unsigned int dcn60_build_update_bandwidth_clocks_sequence(
num_steps = dcn60_build_bandwidth_clocks_block_sequence_with_action(
clk_mgr60->block_sequence, &action,
- clk_mgr_base, new_clocks);
+ clk_mgr_base, context, new_clocks);
dcn60_update_bandwidth_clocks_state_with_action(clk_mgr_base, &action, new_clocks);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 16/74] drm/amd/display: HWSS Refactor - Watermarks
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (14 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 15/74] drm/amd/display: Update indicate pstate enable sequence Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 17/74] drm/amd/display: Resolve clk_mgr DPP DTO targets when the sequence is built Wayne Lin
` (58 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Rafal Ostrowski, Alvin Lee
From: Rafal Ostrowski <rafal.ostrowski@amd.com>
[Why]
BLS prepare_bandwidth (DPMS on) did not match the legacy path:
- DC_SET_OPTIMIZED_REQUIRED, HUBBUB_PROGRAM_WATERMARKS and
HUBBUB_PROGRAM_ARBITER steps kept a dc pointer and read dc->debug and
clk_mgr state at execution, so a step depended on state outside the
sequence.
- prepare_bandwidth_sequence set dc->optimized_required while building,
so the flag changed before the sequence ran instead of at its position
in it.
[How]
Modified opcodes (flat params, no dc pointer):
- DC_SET_OPTIMIZED_REQUIRED: the executor returns the value and the
dispatcher assigns dc->optimized_required.
- HUBBUB_PROGRAM_WATERMARKS, HUBBUB_PROGRAM_ARBITER: debug policy,
p-state transition policy and arbiter values are resolved at build
time. Executors return pending work and the dispatcher ORs it into
dc->optimized_required when !safe_to_lower.
Interface:
- hubbub_funcs.program_watermarks takes struct hubbub_watermark_policy;
program_arbiter takes pstate_stall_threshold and
allow_sdpif_rate_limit_when_cstate_req. All hubbub implementations are
updated without functional change (on DCN42 pstate_stall_threshold is
unused because firmware manages the P-state stall threshold).
- hwss_prepare_hubbub_program_{watermarks,arbiter}() fill the step
parameters. Legacy DCN10/20/401 prepare/optimize_bandwidth use them too,
so both paths resolve the policy in one place.
- dcn401_prepare_bandwidth_sequence adds DC_SET_OPTIMIZED_REQUIRED steps
instead of writing dc->optimized_required at build time.
The DCN401 DC2 commit builder fills the watermark and arbiter params with
the same helpers. The DC2 executor passes the policy and flat arbiter
arguments and ORs optimized_required onto dc2.
Notes:
- The watermarks step keeps a pointer to the context watermarks (a copy
would add ~160 B per step); they must not change between build and
execution.
- Builders validate their arguments, ASSERT, and add no step on invalid
input.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Rafal Ostrowski <rafal.ostrowski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dc/bls_builder/dcn401_commit_builder.c | 23 ++-
.../display/dc/bls_executor/bls_executor.c | 17 ++-
drivers/gpu/drm/amd/display/dc/core/dc.c | 2 +-
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 138 ++++++++++++------
.../display/dc/hubbub/dcn10/dcn10_hubbub.c | 3 +-
.../display/dc/hubbub/dcn10/dcn10_hubbub.h | 1 +
.../display/dc/hubbub/dcn20/dcn20_hubbub.c | 6 +-
.../display/dc/hubbub/dcn201/dcn201_hubbub.c | 3 +-
.../display/dc/hubbub/dcn21/dcn21_hubbub.c | 3 +-
.../display/dc/hubbub/dcn21/dcn21_hubbub.h | 1 +
.../display/dc/hubbub/dcn30/dcn30_hubbub.c | 5 +-
.../display/dc/hubbub/dcn30/dcn30_hubbub.h | 1 +
.../display/dc/hubbub/dcn31/dcn31_hubbub.c | 5 +-
.../display/dc/hubbub/dcn32/dcn32_hubbub.c | 23 ++-
.../display/dc/hubbub/dcn35/dcn35_hubbub.c | 7 +-
.../display/dc/hubbub/dcn35/dcn35_hubbub.h | 1 +
.../display/dc/hubbub/dcn401/dcn401_hubbub.c | 12 +-
.../display/dc/hubbub/dcn401/dcn401_hubbub.h | 3 +-
.../display/dc/hubbub/dcn42/dcn42_hubbub.c | 20 +--
.../display/dc/hubbub/dcn60/dcn60_hubbub.c | 3 +-
.../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 14 +-
.../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 17 ++-
.../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 43 ++++--
.../drm/amd/display/dc/hwss/hw_sequencer.h | 36 +++--
.../gpu/drm/amd/display/dc/inc/hw/dchubbub.h | 22 ++-
25 files changed, 265 insertions(+), 144 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
index 340d1aec2f4c..5fe4a1ec7166 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
@@ -871,23 +871,18 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
&p->clk_mgr_update_clocks_for_state_params);
break;
case HUBBUB_PROGRAM_WATERMARKS:
- p->hubbub_program_watermarks_params.dc = dc;
- p->hubbub_program_watermarks_params.hubbub =
- dc->res_pool->hubbub;
- p->hubbub_program_watermarks_params.watermarks =
- &bw->watermarks;
- p->hubbub_program_watermarks_params.refclk_mhz =
+ p->hubbub_program_watermarks_params =
+ hwss_prepare_hubbub_program_watermarks(dc,
+ dc->res_pool->hubbub, &bw->watermarks,
dc->res_pool->ref_clocks.dchub_ref_clock_inKhz /
- 1000;
- p->hubbub_program_watermarks_params.safe_to_lower =
- m->hubbub_program_watermarks.safe_to_lower;
+ 1000,
+ m->hubbub_program_watermarks.safe_to_lower);
break;
case HUBBUB_PROGRAM_ARBITER:
- p->hubbub_program_arbiter_params.dc = dc;
- p->hubbub_program_arbiter_params.hubbub = dc->res_pool->hubbub;
- p->hubbub_program_arbiter_params.arb_regs = &bw->arb_regs;
- p->hubbub_program_arbiter_params.safe_to_lower =
- m->hubbub_program_arbiter.safe_to_lower;
+ p->hubbub_program_arbiter_params =
+ hwss_prepare_hubbub_program_arbiter(
+ dc->res_pool->hubbub, &bw->arb_regs,
+ m->hubbub_program_arbiter.safe_to_lower);
break;
case HUBBUB_PROGRAM_COMPBUF_SEGMENTS:
p->hubbub_program_compbuf_segments_params.hubbub =
diff --git a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
index 5111c65fd961..3a80c9261710 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
@@ -311,8 +311,7 @@ static noinline_for_stack bool dc2_bls_step_supported(const struct dc2 *dc2,
case HUBBUB_PROGRAM_ARBITER:
hubbub = step->params.hubbub_program_arbiter_params.hubbub;
return hubbub && hubbub->funcs &&
- hubbub->funcs->program_arbiter &&
- step->params.hubbub_program_arbiter_params.arb_regs;
+ hubbub->funcs->program_arbiter;
case HUBBUB_PROGRAM_COMPBUF_SEGMENTS:
hubbub = step->params.hubbub_program_compbuf_segments_params
.hubbub;
@@ -939,20 +938,22 @@ enum dc2_bls_exec_result dc2_bls_execute(
&entry->params.hubbub_program_watermarks_params;
bool changed = p->hubbub->funcs->program_watermarks(
p->hubbub, p->watermarks, p->refclk_mhz,
- p->safe_to_lower);
+ p->policy, p->safe_to_lower);
- if (p->dc && !p->safe_to_lower)
- p->dc->optimized_required |= changed;
+ if (dc2 && !p->safe_to_lower)
+ dc2->dc.optimized_required |= changed;
break;
}
case HUBBUB_PROGRAM_ARBITER: {
const struct hubbub_program_arbiter_params *p =
&entry->params.hubbub_program_arbiter_params;
bool changed = p->hubbub->funcs->program_arbiter(p->hubbub,
- p->arb_regs, p->safe_to_lower);
+ p->pstate_stall_threshold,
+ p->allow_sdpif_rate_limit_when_cstate_req,
+ p->safe_to_lower);
- if (p->dc && !p->safe_to_lower)
- p->dc->optimized_required |= changed;
+ if (dc2 && !p->safe_to_lower)
+ dc2->dc.optimized_required |= changed;
break;
}
case HUBBUB_PROGRAM_COMPBUF_SEGMENTS: {
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 6b90c88b6cc3..9d871a821c8d 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -4412,7 +4412,7 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc,
if (dpms_pipe_ctx->stream_res.audio && !dc->debug.az_endpoint_mute_only)
hwss_add_disable_audio_stream(&seq_state, dpms_pipe_ctx);
- hwss_add_dc_set_optimized_required(&seq_state, dc, true);
+ hwss_add_dc_set_optimized_required(&seq_state, true);
} else {
if (get_seamless_boot_stream_count(context) == 0 && dc->hwss.prepare_bandwidth_sequence)
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 512e128d52b3..2384cbf16ee1 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
@@ -1913,7 +1913,7 @@ void hwss_execute_sequence(struct dc *dc,
hwss_mpc_remove_mpcc(params);
break;
case DC_SET_OPTIMIZED_REQUIRED:
- hwss_dc_set_optimized_required(params);
+ dc->optimized_required = hwss_dc_set_optimized_required(params);
break;
case HUBP_DISCONNECT:
hwss_hubp_disconnect(params);
@@ -2217,10 +2217,10 @@ void hwss_execute_sequence(struct dc *dc,
hwss_clk_mgr_update_clocks(params);
break;
case HUBBUB_PROGRAM_WATERMARKS:
- hwss_hubbub_program_watermarks(params);
+ dc->optimized_required |= hwss_hubbub_program_watermarks(params);
break;
case HUBBUB_PROGRAM_ARBITER:
- hwss_hubbub_program_arbiter(params);
+ dc->optimized_required |= hwss_hubbub_program_arbiter(params);
break;
case HUBBUB_PROGRAM_COMPBUF_SEGMENTS:
hwss_hubbub_program_compbuf_segments(params);
@@ -3593,12 +3593,12 @@ void hwss_mpc_remove_mpcc(union block_sequence_params *params)
mpc->funcs->remove_mpcc(mpc, mpc_tree_params, mpcc_to_remove);
}
-void hwss_dc_set_optimized_required(union block_sequence_params *params)
+bool hwss_dc_set_optimized_required(union block_sequence_params *params)
{
- struct dc *dc = params->dc_set_optimized_required_params.dc;
- bool optimized_required = params->dc_set_optimized_required_params.optimized_required;
+ if (!params)
+ return false;
- dc->optimized_required = optimized_required;
+ return params->dc_set_optimized_required_params.optimized_required;
}
void hwss_hubp_disconnect(union block_sequence_params *params)
@@ -4655,35 +4655,41 @@ void hwss_clk_mgr_update_clocks(union block_sequence_params *params)
/*
* Hubbub executor functions
*/
-void hwss_hubbub_program_watermarks(union block_sequence_params *params)
+bool 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;
+ struct hubbub_program_watermarks_params *wm;
+ bool pending;
- if (hubbub && hubbub->funcs && hubbub->funcs->program_watermarks) {
- bool wm_changed = hubbub->funcs->program_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower);
+ if (!params)
+ return false;
- if (dc && !safe_to_lower)
- dc->optimized_required |= wm_changed;
- }
+ wm = ¶ms->hubbub_program_watermarks_params;
+ if (!wm->hubbub || !wm->hubbub->funcs || !wm->hubbub->funcs->program_watermarks ||
+ !wm->watermarks)
+ return false;
+
+ pending = wm->hubbub->funcs->program_watermarks(wm->hubbub, wm->watermarks,
+ wm->refclk_mhz, wm->policy, wm->safe_to_lower);
+
+ return !wm->safe_to_lower && pending;
}
-void hwss_hubbub_program_arbiter(union block_sequence_params *params)
+bool 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;
+ struct hubbub_program_arbiter_params *arb;
+ bool pending;
- if (hubbub && hubbub->funcs && hubbub->funcs->program_arbiter) {
- bool arb_changed = hubbub->funcs->program_arbiter(hubbub, arb_regs, safe_to_lower);
+ if (!params)
+ return false;
- if (dc && !safe_to_lower)
- dc->optimized_required |= arb_changed;
- }
+ arb = ¶ms->hubbub_program_arbiter_params;
+ if (!arb->hubbub || !arb->hubbub->funcs || !arb->hubbub->funcs->program_arbiter)
+ return false;
+
+ pending = arb->hubbub->funcs->program_arbiter(arb->hubbub, arb->pstate_stall_threshold,
+ arb->allow_sdpif_rate_limit_when_cstate_req, arb->safe_to_lower);
+
+ return !arb->safe_to_lower && pending;
}
void hwss_hubbub_program_compbuf_segments(union block_sequence_params *params)
@@ -4837,11 +4843,15 @@ void hwss_add_dsc_disconnect(struct block_sequence_state *seq_state,
}
void hwss_add_dc_set_optimized_required(struct block_sequence_state *seq_state,
- struct dc *dc, bool optimized_required)
+ bool optimized_required)
{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps) {
+ ASSERT(false);
+ return;
+ }
+
if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
seq_state->steps[*seq_state->num_steps].func = DC_SET_OPTIMIZED_REQUIRED;
- seq_state->steps[*seq_state->num_steps].params.dc_set_optimized_required_params.dc = dc;
seq_state->steps[*seq_state->num_steps].params.dc_set_optimized_required_params.optimized_required = optimized_required;
(*seq_state->num_steps)++;
}
@@ -6421,20 +6431,45 @@ void hwss_add_setup_periodic_interrupt(struct block_sequence_state *seq_state,
/*
* Hubbub helper functions
*/
+struct hubbub_program_watermarks_params hwss_prepare_hubbub_program_watermarks(
+ const struct dc *dc,
+ struct hubbub *hubbub,
+ union dcn_watermark_set *watermarks,
+ unsigned int refclk_mhz,
+ bool safe_to_lower)
+{
+ struct hubbub_program_watermarks_params prepared = {};
+
+ prepared.hubbub = hubbub;
+ prepared.watermarks = watermarks;
+ prepared.refclk_mhz = refclk_mhz;
+ prepared.policy.urgent_watermark_override = dc->debug.urgent_watermark_override;
+ prepared.policy.disable_stutter = dc->debug.disable_stutter;
+ prepared.policy.disable_stutter_for_program = dc->debug.disable_stutter_for_wm_program;
+ prepared.policy.force_usr_allow = dc->debug.force_usr_allow;
+ prepared.safe_to_lower = safe_to_lower;
+
+ return prepared;
+}
+
void hwss_add_hubbub_program_watermarks(struct block_sequence_state *seq_state,
- struct dc *dc,
+ const struct dc *dc,
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
bool safe_to_lower)
{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps || !dc || !watermarks ||
+ !hubbub || !hubbub->funcs || !hubbub->funcs->program_watermarks) {
+ ASSERT(false);
+ return;
+ }
+
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->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params =
+ hwss_prepare_hubbub_program_watermarks(dc, hubbub, watermarks,
+ refclk_mhz, safe_to_lower);
(*seq_state->num_steps)++;
}
}
@@ -6451,18 +6486,37 @@ void hwss_add_dp_trace_source_sequence(struct block_sequence_state *seq_state,
}
}
+struct hubbub_program_arbiter_params hwss_prepare_hubbub_program_arbiter(
+ struct hubbub *hubbub,
+ const struct dml2_display_arb_regs *arb_regs,
+ bool safe_to_lower)
+{
+ struct hubbub_program_arbiter_params prepared = {};
+
+ prepared.hubbub = hubbub;
+ prepared.pstate_stall_threshold = arb_regs->pstate_stall_threshold;
+ prepared.allow_sdpif_rate_limit_when_cstate_req =
+ arb_regs->allow_sdpif_rate_limit_when_cstate_req != 0;
+ prepared.safe_to_lower = safe_to_lower;
+
+ return prepared;
+}
+
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,
+ const struct dml2_display_arb_regs *arb_regs,
bool safe_to_lower)
{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps || !arb_regs ||
+ !hubbub || !hubbub->funcs || !hubbub->funcs->program_arbiter) {
+ ASSERT(false);
+ return;
+ }
+
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->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params =
+ hwss_prepare_hubbub_program_arbiter(hubbub, arb_regs, safe_to_lower);
(*seq_state->num_steps)++;
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.c
index 4e33648f74bb..faad1c6f4509 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.c
@@ -584,6 +584,7 @@ bool hubbub1_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
struct dcn10_hubbub *hubbub1 = TO_DCN10_HUBBUB(hubbub);
@@ -606,7 +607,7 @@ bool hubbub1_program_watermarks(
REG_UPDATE(DCHUBBUB_ARB_DF_REQ_OUTSTAND,
DCHUBBUB_ARB_MIN_REQ_OUTSTAND, 68);
- hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
#if 0
REG_UPDATE_2(DCHUBBUB_ARB_WATERMARK_CHANGE_CNTL,
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h
index 53fac324748d..581e2c1578b5 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn10/dcn10_hubbub.h
@@ -606,6 +606,7 @@ bool hubbub1_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower);
void hubbub1_allow_self_refresh_control(struct hubbub *hubbub, bool allow);
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c
index e5b55fe502ca..46e43a51ab22 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c
@@ -592,6 +592,7 @@ static bool hubbub2_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
@@ -611,8 +612,7 @@ static bool hubbub2_program_watermarks(
* here we are going to LOWER watermarks to go to dummy p-state only, but this has
* to be done prepare_bandwidth, not optimize
*/
- if (hubbub1->base.ctx->dc->clk_mgr->clks.prev_p_state_change_support == true &&
- hubbub1->base.ctx->dc->clk_mgr->clks.p_state_change_support == false)
+ if (policy.force_safe_to_lower)
safe_to_lower = true;
if (hubbub1_program_pstate_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower))
@@ -622,7 +622,7 @@ static bool hubbub2_program_watermarks(
DCHUBBUB_ARB_SAT_LEVEL, 60 * refclk_mhz);
REG_UPDATE(DCHUBBUB_ARB_DF_REQ_OUTSTAND, DCHUBBUB_ARB_MIN_REQ_OUTSTAND, 180);
- hubbub->funcs->allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ hubbub->funcs->allow_self_refresh_control(hubbub, !policy.disable_stutter);
return wm_pending;
}
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn201/dcn201_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn201/dcn201_hubbub.c
index e6b527cd2047..d626c1de92a7 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn201/dcn201_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn201/dcn201_hubbub.c
@@ -54,6 +54,7 @@ static bool hubbub201_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
@@ -70,7 +71,7 @@ static bool hubbub201_program_watermarks(
REG_UPDATE(DCHUBBUB_ARB_DF_REQ_OUTSTAND,
DCHUBBUB_ARB_MIN_REQ_OUTSTAND, 68);
- hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
return wm_pending;
}
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.c
index 881e91e9385d..3a92682c9cda 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.c
@@ -575,6 +575,7 @@ bool hubbub21_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
@@ -610,7 +611,7 @@ bool hubbub21_program_watermarks(
REG_UPDATE(DCHUBBUB_ARB_HOSTVM_CNTL,
DCHUBBUB_ARB_MAX_QOS_COMMIT_THRESHOLD, 0xF);
- hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
return wm_pending;
}
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.h
index ab2ce0313529..626c864f9535 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn21/dcn21_hubbub.h
@@ -129,6 +129,7 @@ bool hubbub21_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower);
bool hubbub21_program_urgent_watermarks(
struct hubbub *hubbub,
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c
index 582d647da812..71f45e07c10e 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c
@@ -97,6 +97,7 @@ bool hubbub3_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
@@ -129,8 +130,8 @@ bool hubbub3_program_watermarks(
REG_UPDATE(DCHUBBUB_ARB_DF_REQ_OUTSTAND,
DCHUBBUB_ARB_MIN_REQ_OUTSTAND, 0x1FF);
- if (safe_to_lower || hubbub->ctx->dc->debug.disable_stutter)
- hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ if (safe_to_lower || policy.disable_stutter)
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
return wm_pending;
}
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h
index 9e14de3ccaee..321024242412 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h
@@ -126,6 +126,7 @@ bool hubbub3_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower);
void hubbub3_force_pstate_change_control(struct hubbub *hubbub,
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 786843362061..2af656c2c529 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
@@ -719,6 +719,7 @@ static bool hubbub31_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
bool wm_pending = false;
@@ -750,8 +751,8 @@ static bool hubbub31_program_watermarks(
REG_UPDATE(DCHUBBUB_ARB_DF_REQ_OUTSTAND,
DCHUBBUB_ARB_MIN_REQ_OUTSTAND, 0x1FF);*/
- if (safe_to_lower || hubbub->ctx->dc->debug.disable_stutter)
- hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ if (safe_to_lower || policy.disable_stutter)
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
return wm_pending;
}
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c
index dd9bc1f08b56..ee01d52e6944 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c
@@ -761,14 +761,16 @@ static bool hubbub32_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
- struct dc *dc = hubbub->ctx->dc;
+ const uint32_t hw_internal_rev = hubbub->ctx->asic_id.hw_internal_rev;
+ const bool sdp_control_wa = policy.disable_stutter_for_program &&
+ (ASICREV_IS_GC_11_0_0(hw_internal_rev) ||
+ ASICREV_IS_GC_11_0_3(hw_internal_rev));
bool wm_pending = false;
- if (!safe_to_lower && dc->debug.disable_stutter_for_wm_program &&
- (ASICREV_IS_GC_11_0_0(dc->ctx->asic_id.hw_internal_rev) ||
- ASICREV_IS_GC_11_0_3(dc->ctx->asic_id.hw_internal_rev))) {
+ if (!safe_to_lower && sdp_control_wa) {
/* before raising watermarks, SDP control give to DF, stutter must be disabled */
wm_pending = true;
hubbub32_set_sdp_control(hubbub, false);
@@ -807,18 +809,15 @@ static bool hubbub32_program_watermarks(
if (safe_to_lower) {
/* after lowering watermarks, stutter setting is restored, SDP control given to DC */
- hubbub1_allow_self_refresh_control(hubbub, !dc->debug.disable_stutter);
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
- if (dc->debug.disable_stutter_for_wm_program &&
- (ASICREV_IS_GC_11_0_0(dc->ctx->asic_id.hw_internal_rev) ||
- ASICREV_IS_GC_11_0_3(dc->ctx->asic_id.hw_internal_rev))) {
+ if (sdp_control_wa)
hubbub32_set_sdp_control(hubbub, true);
- }
- } else if (dc->debug.disable_stutter) {
- hubbub1_allow_self_refresh_control(hubbub, !dc->debug.disable_stutter);
+ } else if (policy.disable_stutter) {
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
}
- hubbub32_force_usr_retraining_allow(hubbub, dc->debug.force_usr_allow);
+ hubbub32_force_usr_retraining_allow(hubbub, policy.force_usr_allow);
return wm_pending;
}
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 ad333cd3241b..771e209a0b3f 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
@@ -301,6 +301,7 @@ bool hubbub35_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
bool wm_pending = false;
@@ -328,10 +329,10 @@ bool hubbub35_program_watermarks(
DCHUBBUB_ARB_MIN_REQ_OUTSTAND_COMMIT_THRESHOLD, 0xA);/*hw delta*/
REG_UPDATE(DCHUBBUB_ARB_HOSTVM_CNTL, DCHUBBUB_ARB_MAX_QOS_COMMIT_THRESHOLD, 0xF);
- if (safe_to_lower || hubbub->ctx->dc->debug.disable_stutter)
- hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ if (safe_to_lower || policy.disable_stutter)
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
- hubbub32_force_usr_retraining_allow(hubbub, hubbub->ctx->dc->debug.force_usr_allow);
+ hubbub32_force_usr_retraining_allow(hubbub, policy.force_usr_allow);
return wm_pending;
}
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h
index ddc614c98234..831b9888c831 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h
@@ -365,6 +365,7 @@ bool hubbub35_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower);
void hubbub35_init_watermarks(struct hubbub *hubbub);
void dcn35_program_compbuf_size(struct hubbub *hubbub,
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c
index 957e8f5a6475..c18457c779de 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c
@@ -455,6 +455,7 @@ static bool hubbub401_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
bool wm_pending = false;
@@ -490,9 +491,9 @@ static bool hubbub401_program_watermarks(
DCHUBBUB_ARB_MIN_REQ_OUTSTAND, 0x1FF);
*/
- hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
- hubbub32_force_usr_retraining_allow(hubbub, hubbub->ctx->dc->debug.force_usr_allow);
+ hubbub32_force_usr_retraining_allow(hubbub, policy.force_usr_allow);
return wm_pending;
}
@@ -1194,19 +1195,20 @@ void dcn401_wait_for_det_update(struct hubbub *hubbub, int hubp_inst)
}
}
-bool dcn401_program_arbiter(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs, bool safe_to_lower)
+bool dcn401_program_arbiter(struct hubbub *hubbub, uint32_t pstate_stall_threshold,
+ bool allow_sdpif_rate_limit_when_cstate_req, bool safe_to_lower)
{
struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
bool wm_pending = false;
uint32_t temp;
- bool allow_sdpif_rate_limit_when_cstate_req = arb_regs->allow_sdpif_rate_limit_when_cstate_req != 0;
/* request backpressure and outstanding return threshold (unused)*/
//REG_UPDATE(DCHUBBUB_TIMEOUT_DETECTION_CTRL1, DCHUBBUB_TIMEOUT_REQ_STALL_THRESHOLD, arb_regs->req_stall_threshold);
/* P-State stall threshold */
- REG_UPDATE(DCHUBBUB_TIMEOUT_DETECTION_CTRL2, DCHUBBUB_TIMEOUT_PSTATE_STALL_THRESHOLD, arb_regs->pstate_stall_threshold);
+ REG_UPDATE(DCHUBBUB_TIMEOUT_DETECTION_CTRL2,
+ DCHUBBUB_TIMEOUT_PSTATE_STALL_THRESHOLD, pstate_stall_threshold);
if (safe_to_lower || allow_sdpif_rate_limit_when_cstate_req > hubbub2->allow_sdpif_rate_limit_when_cstate_req) {
hubbub2->allow_sdpif_rate_limit_when_cstate_req = allow_sdpif_rate_limit_when_cstate_req;
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h
index 346bead81504..2694b9769bb4 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.h
@@ -198,7 +198,8 @@ bool hubbub401_get_dcc_compression_cap(
bool dcn401_program_arbiter(
struct hubbub *hubbub,
- struct dml2_display_arb_regs *arb_regs,
+ uint32_t pstate_stall_threshold,
+ bool allow_sdpif_rate_limit_when_cstate_req,
bool safe_to_lower);
void hubbub401_construct(struct dcn20_hubbub *hubbub2,
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c
index 244f3f2824ed..b8ebe44c69c6 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn42/dcn42_hubbub.c
@@ -27,10 +27,10 @@
static bool hubbub42_program_urgent_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
+ uint32_t urgent_floor,
bool safe_to_lower)
{
struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
- const unsigned int urgent_floor = hubbub->ctx->dc->debug.urgent_watermark_override;
const unsigned int urgent_a = max(urgent_floor,
watermarks->dcn4x.a.urgent);
const unsigned int urgent_b = max(urgent_floor,
@@ -445,17 +445,19 @@ static bool hubbub42_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
bool wm_pending = false;
struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
- if (!safe_to_lower && hubbub->ctx->dc->debug.disable_stutter_for_wm_program) {
+ if (!safe_to_lower && policy.disable_stutter_for_program) {
/* before raising watermarks, stutter must be disabled */
wm_pending = true;
hubbub42_allow_self_refresh_control(hubbub, false);
}
- if (hubbub42_program_urgent_watermarks(hubbub, watermarks, safe_to_lower))
+ if (hubbub42_program_urgent_watermarks(hubbub, watermarks,
+ policy.urgent_watermark_override, safe_to_lower))
wm_pending = true;
if (hubbub42_program_stutter_watermarks(hubbub, watermarks, safe_to_lower))
@@ -477,9 +479,9 @@ static bool hubbub42_program_watermarks(
DCHUBBUB_ARB_MIN_REQ_OUTSTAND_COMMIT_THRESHOLD, 0xA);/*hw delta*/
REG_UPDATE(DCHUBBUB_ARB_HOSTVM_CNTL, DCHUBBUB_ARB_MAX_QOS_COMMIT_THRESHOLD, 0xF);
- if (safe_to_lower || hubbub->ctx->dc->debug.disable_stutter)
- hubbub42_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
- hubbub32_force_usr_retraining_allow(hubbub, hubbub->ctx->dc->debug.force_usr_allow);
+ if (safe_to_lower || policy.disable_stutter)
+ hubbub42_allow_self_refresh_control(hubbub, !policy.disable_stutter);
+ hubbub32_force_usr_retraining_allow(hubbub, policy.force_usr_allow);
return wm_pending;
}
@@ -494,14 +496,14 @@ static void hubbub42_set_request_limit(struct hubbub *hubbub, int memory_channel
REG_UPDATE(SDPIF_REQUEST_RATE_LIMIT, SDPIF_REQUEST_RATE_LIMIT, request_limit);
}
-static bool dcn42_program_arbiter(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs,
- bool safe_to_lower)
+static bool dcn42_program_arbiter(struct hubbub *hubbub, uint32_t pstate_stall_threshold,
+ bool allow_sdpif_rate_limit_when_cstate_req, bool safe_to_lower)
{
struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
bool wm_pending = false;
uint32_t temp;
- bool allow_sdpif_rate_limit_when_cstate_req = arb_regs->allow_sdpif_rate_limit_when_cstate_req != 0;
+ (void)pstate_stall_threshold;
/* request backpressure and outstanding return threshold (unused)*/
//REG_UPDATE(DCHUBBUB_TIMEOUT_DETECTION_CTRL1, DCHUBBUB_TIMEOUT_REQ_STALL_THRESHOLD, arb_regs->req_stall_threshold);
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c
index 07282b78972a..db29e56f2367 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn60/dcn60_hubbub.c
@@ -606,6 +606,7 @@ static bool hubbub60_program_watermarks(
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower)
{
bool wm_pending = false;
@@ -619,7 +620,7 @@ static bool hubbub60_program_watermarks(
if (hubbub60_program_pstate_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower))
wm_pending = true;
- hubbub1_allow_self_refresh_control(hubbub, !hubbub->ctx->dc->debug.disable_stutter);
+ hubbub1_allow_self_refresh_control(hubbub, !policy.disable_stutter);
return wm_pending;
}
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 f1d14b97977f..46a134301526 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
@@ -3420,6 +3420,7 @@ void dcn10_prepare_bandwidth(
{
struct dce_hwseq *hws = dc->hwseq;
struct hubbub *hubbub = dc->res_pool->hubbub;
+ struct hubbub_program_watermarks_params wm;
int min_fclk_khz, min_dcfclk_khz, socclk_khz;
if (dc->debug.sanity_checks)
@@ -3433,10 +3434,10 @@ void dcn10_prepare_bandwidth(
context,
false);
+ wm = hwss_prepare_hubbub_program_watermarks(dc, hubbub, &context->bw_ctx.bw.dcn.watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, true);
dc->optimized_required = hubbub->funcs->program_watermarks(hubbub,
- &context->bw_ctx.bw.dcn.watermarks,
- dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
- true);
+ wm.watermarks, wm.refclk_mhz, wm.policy, wm.safe_to_lower);
dcn10_stereo_hw_frame_pack_wa(dc, context);
if (dc->debug.pplib_wm_report_mode == WM_REPORT_OVERRIDE) {
@@ -3458,6 +3459,7 @@ void dcn10_optimize_bandwidth(
{
struct dce_hwseq *hws = dc->hwseq;
struct hubbub *hubbub = dc->res_pool->hubbub;
+ struct hubbub_program_watermarks_params wm;
int min_fclk_khz, min_dcfclk_khz, socclk_khz;
if (dc->debug.sanity_checks)
@@ -3471,10 +3473,10 @@ void dcn10_optimize_bandwidth(
context,
true);
+ wm = hwss_prepare_hubbub_program_watermarks(dc, hubbub, &context->bw_ctx.bw.dcn.watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, true);
hubbub->funcs->program_watermarks(hubbub,
- &context->bw_ctx.bw.dcn.watermarks,
- dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
- true);
+ wm.watermarks, wm.refclk_mhz, wm.policy, wm.safe_to_lower);
dcn10_stereo_hw_frame_pack_wa(dc, context);
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 636cbd947918..696c0d5b8132 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
@@ -2438,6 +2438,7 @@ void dcn20_prepare_bandwidth(
struct hubbub *hubbub = dc->res_pool->hubbub;
unsigned int compbuf_size_kb = 0;
unsigned int cache_wm_a = context->bw_ctx.bw.dcn.watermarks.a.cstate_pstate.pstate_change_ns;
+ struct hubbub_program_watermarks_params wm;
unsigned int i;
dc->clk_mgr->funcs->update_clocks(
@@ -2459,10 +2460,13 @@ void dcn20_prepare_bandwidth(
* For assigning optimized_required, use |= operator since we don't want
* to clear the value if the optimize has not happened yet
*/
+ wm = hwss_prepare_hubbub_program_watermarks(dc, hubbub, &context->bw_ctx.bw.dcn.watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, false);
+ /* Read after update_clocks(): P-state support is being lost, lower to dummy P-state now */
+ wm.policy.force_safe_to_lower = dc->clk_mgr->clks.prev_p_state_change_support &&
+ !dc->clk_mgr->clks.p_state_change_support;
dc->optimized_required |= hubbub->funcs->program_watermarks(hubbub,
- &context->bw_ctx.bw.dcn.watermarks,
- dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
- false);
+ wm.watermarks, wm.refclk_mhz, wm.policy, wm.safe_to_lower);
// Restore the real watermark so we can commit the value to DMCUB
// DMCUB uses the "original" watermark value in SubVP MCLK switch
@@ -2487,6 +2491,7 @@ void dcn20_optimize_bandwidth(
struct dc_state *context)
{
struct hubbub *hubbub = dc->res_pool->hubbub;
+ struct hubbub_program_watermarks_params wm;
unsigned int i;
for (i = 0; i < dc->res_pool->pipe_count; i++) {
@@ -2500,10 +2505,10 @@ void dcn20_optimize_bandwidth(
}
/* program dchubbub watermarks */
+ wm = hwss_prepare_hubbub_program_watermarks(dc, hubbub, &context->bw_ctx.bw.dcn.watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, true);
hubbub->funcs->program_watermarks(hubbub,
- &context->bw_ctx.bw.dcn.watermarks,
- dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
- true);
+ wm.watermarks, wm.refclk_mhz, wm.policy, wm.safe_to_lower);
if (dc->clk_mgr->dc_mode_softmax_enabled) {
int softmax_memclk_khz = dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000;
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 508749496826..f14cba867111 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
@@ -1421,6 +1421,8 @@ void dcn401_prepare_bandwidth(struct dc *dc,
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;
+ struct hubbub_program_watermarks_params wm;
+ struct hubbub_program_arbiter_params arb;
/* Any transition into P-State support should disable MCLK switching first to avoid hangs */
if (p_state_change_support) {
@@ -1446,19 +1448,25 @@ void dcn401_prepare_bandwidth(struct dc *dc,
* For assigning optimized_required, use |= operator since we don't want
* to clear the value if the optimize has not happened yet
*/
+ wm = hwss_prepare_hubbub_program_watermarks(dc, hubbub,
+ &context->bw_ctx.bw.dcn.watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, false);
dc->optimized_required |= hubbub->funcs->program_watermarks(hubbub,
- &context->bw_ctx.bw.dcn.watermarks,
- dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
- false);
+ wm.watermarks, wm.refclk_mhz, wm.policy, wm.safe_to_lower);
/* update timeout thresholds */
if (hubbub->funcs->program_arbiter) {
- dc->optimized_required |= hubbub->funcs->program_arbiter(hubbub, &context->bw_ctx.bw.dcn.arb_regs, false);
+ arb = hwss_prepare_hubbub_program_arbiter(hubbub,
+ &context->bw_ctx.bw.dcn.arb_regs, false);
+ dc->optimized_required |= hubbub->funcs->program_arbiter(hubbub,
+ arb.pstate_stall_threshold,
+ arb.allow_sdpif_rate_limit_when_cstate_req, arb.safe_to_lower);
}
/* decrease compbuf size */
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);
+ dc->optimized_required |= (compbuf_size !=
+ dc->current_state->bw_ctx.bw.dcn.arb_regs.compbuf_size);
hubbub->funcs->program_compbuf_segments(hubbub, compbuf_size, false);
}
@@ -1489,7 +1497,7 @@ void dcn401_prepare_bandwidth_sequence(struct dc *dc,
/* Any transition into P-State support should disable MCLK switching first to avoid hangs */
if (p_state_change_support) {
- dc->optimized_required = true;
+ hwss_add_dc_set_optimized_required(seq_state, true);
context->bw_ctx.bw.dcn.clk.p_state_change_support = false;
}
@@ -1510,12 +1518,13 @@ void dcn401_prepare_bandwidth_sequence(struct dc *dc,
false);
if (hubbub->funcs->program_arbiter)
- hwss_add_hubbub_program_arbiter(seq_state, dc, hubbub,
+ hwss_add_hubbub_program_arbiter(seq_state, 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);
+ if (compbuf_size != dc->current_state->bw_ctx.bw.dcn.arb_regs.compbuf_size)
+ hwss_add_dc_set_optimized_required(seq_state, true);
hwss_add_hubbub_program_compbuf_segments(seq_state, hubbub, compbuf_size, false);
}
@@ -1536,6 +1545,8 @@ void dcn401_optimize_bandwidth(
{
unsigned int i;
struct hubbub *hubbub = dc->res_pool->hubbub;
+ struct hubbub_program_watermarks_params wm;
+ struct hubbub_program_arbiter_params arb;
/* enable fams2 if needed */
if (dc->debug.fams2_config.bits.enable) {
@@ -1548,13 +1559,17 @@ void dcn401_optimize_bandwidth(
}
/* program dchubbub watermarks */
+ wm = hwss_prepare_hubbub_program_watermarks(dc, hubbub,
+ &context->bw_ctx.bw.dcn.watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, true);
hubbub->funcs->program_watermarks(hubbub,
- &context->bw_ctx.bw.dcn.watermarks,
- dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000,
- true);
+ wm.watermarks, wm.refclk_mhz, wm.policy, wm.safe_to_lower);
/* update timeout thresholds */
if (hubbub->funcs->program_arbiter) {
- hubbub->funcs->program_arbiter(hubbub, &context->bw_ctx.bw.dcn.arb_regs, true);
+ arb = hwss_prepare_hubbub_program_arbiter(hubbub,
+ &context->bw_ctx.bw.dcn.arb_regs, true);
+ hubbub->funcs->program_arbiter(hubbub, arb.pstate_stall_threshold,
+ arb.allow_sdpif_rate_limit_when_cstate_req, arb.safe_to_lower);
}
if (dc->clk_mgr->dc_mode_softmax_enabled) {
@@ -1611,7 +1626,7 @@ void dcn401_optimize_bandwidth_sequence(struct dc *dc,
true);
if (hubbub->funcs->program_arbiter)
- hwss_add_hubbub_program_arbiter(seq_state, dc, hubbub,
+ hwss_add_hubbub_program_arbiter(seq_state, hubbub,
&context->bw_ctx.bw.dcn.arb_regs, true);
if (dc->clk_mgr->dc_mode_softmax_enabled)
@@ -3300,7 +3315,7 @@ void dcn401_plane_atomic_disconnect_sequence(struct dc *dc,
}
/* Step 3: Set optimized required flag */
- hwss_add_dc_set_optimized_required(seq_state, dc, true);
+ hwss_add_dc_set_optimized_required(seq_state, true);
/* Step 4: Disconnect HUBP if function exists */
if (hubp->funcs->hubp_disconnect)
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 3042785cb8fc..d02834639ab3 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -732,7 +732,6 @@ struct mpc_remove_mpcc_params {
};
struct dc_set_optimized_required_params {
- struct dc *dc;
bool optimized_required;
};
@@ -1264,17 +1263,21 @@ struct dccg_initialize_clocks_params {
};
struct hubbub_program_watermarks_params {
- struct dc *dc;
struct hubbub *hubbub;
+ /* Workaround: watermarks by value would add ~160 B to every step (~158 KiB per dc_state).
+ * TODO: pass by value once block_sequence storage moves from dc_state to dc-owned scratch.
+ * The pointed-to watermarks must not change between build and execution.
+ */
union dcn_watermark_set *watermarks;
unsigned int refclk_mhz;
+ struct hubbub_watermark_policy policy;
bool safe_to_lower;
};
struct hubbub_program_arbiter_params {
- struct dc *dc;
struct hubbub *hubbub;
- struct dml2_display_arb_regs *arb_regs;
+ uint32_t pstate_stall_threshold;
+ bool allow_sdpif_rate_limit_when_cstate_req;
bool safe_to_lower;
};
@@ -2345,7 +2348,7 @@ void hwss_set_abm_immediate_disable(union block_sequence_params *params);
void hwss_mpc_remove_mpcc(union block_sequence_params *params);
-void hwss_dc_set_optimized_required(union block_sequence_params *params);
+bool hwss_dc_set_optimized_required(union block_sequence_params *params);
void hwss_hubp_disconnect(union block_sequence_params *params);
@@ -2664,7 +2667,7 @@ void hwss_add_dsc_disconnect(struct block_sequence_state *seq_state,
struct display_stream_compressor *dsc);
void hwss_add_dc_set_optimized_required(struct block_sequence_state *seq_state,
- struct dc *dc, bool optimized_required);
+ bool optimized_required);
void hwss_add_abm_set_immediate_disable(struct block_sequence_state *seq_state,
struct dc *dc, struct pipe_ctx *pipe_ctx);
@@ -3139,9 +3142,9 @@ void hwss_add_link_set_dpms_on(struct block_sequence_state *seq_state,
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);
+bool hwss_hubbub_program_watermarks(union block_sequence_params *params);
-void hwss_hubbub_program_arbiter(union block_sequence_params *params);
+bool hwss_hubbub_program_arbiter(union block_sequence_params *params);
void hwss_hubbub_program_compbuf_segments(union block_sequence_params *params);
@@ -3153,17 +3156,28 @@ void hwss_add_clk_mgr_set_max_memclk(struct block_sequence_state *seq_state,
void hwss_add_clk_mgr_update_clocks(struct block_sequence_state *seq_state,
struct clk_mgr *clk_mgr);
+struct hubbub_program_watermarks_params hwss_prepare_hubbub_program_watermarks(
+ const struct dc *dc,
+ struct hubbub *hubbub,
+ union dcn_watermark_set *watermarks,
+ unsigned int refclk_mhz,
+ bool safe_to_lower);
+
void hwss_add_hubbub_program_watermarks(struct block_sequence_state *seq_state,
- struct dc *dc,
+ const struct dc *dc,
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
bool safe_to_lower);
+struct hubbub_program_arbiter_params hwss_prepare_hubbub_program_arbiter(
+ struct hubbub *hubbub,
+ const struct dml2_display_arb_regs *arb_regs,
+ 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,
+ const struct dml2_display_arb_regs *arb_regs,
bool safe_to_lower);
void hwss_add_hubbub_program_compbuf_segments(struct block_sequence_state *seq_state,
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
index b33273b61abd..7280f57f75bf 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
@@ -35,6 +35,24 @@
#include "dc/dc_hw_types.h"
+/*
+ * Watermark programming policy resolved by the caller from dc state, clock manager
+ * state and debug options. Backends must not read mutable dc state (debug options,
+ * clock manager state); static ASIC identification from ctx is allowed.
+ */
+ struct hubbub_watermark_policy {
+ uint32_t urgent_watermark_override;
+ bool disable_stutter;
+ bool disable_stutter_for_program;
+ bool force_usr_allow;
+ /*
+ * P-state support is being lost, lower watermarks to dummy P-state in prepare.
+ * Not set by hwss_prepare_hubbub_program_watermarks(); set by the immediate prepare
+ * path after clk_mgr->funcs->update_clocks(). Consumed by hubbub2 only.
+ */
+ bool force_safe_to_lower;
+};
+
enum dcc_control {
dcc_control__256_256_xxx,
dcc_control__128_128_xxx,
@@ -207,6 +225,7 @@ struct hubbub_funcs {
struct hubbub *hubbub,
union dcn_watermark_set *watermarks,
unsigned int refclk_mhz,
+ struct hubbub_watermark_policy policy,
bool safe_to_lower);
bool (*is_allow_self_refresh_enabled)(struct hubbub *hubbub);
@@ -255,7 +274,8 @@ struct hubbub_funcs {
void (*program_det_segments)(struct hubbub *hubbub, int hubp_inst, unsigned det_buffer_size_seg);
void (*program_compbuf_segments)(struct hubbub *hubbub, unsigned compbuf_size_seg, bool safe_to_increase);
void (*wait_for_det_update)(struct hubbub *hubbub, int hubp_inst);
- bool (*program_arbiter)(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs, bool safe_to_lower);
+ bool (*program_arbiter)(struct hubbub *hubbub, uint32_t pstate_stall_threshold,
+ bool allow_sdpif_rate_limit_when_cstate_req, bool safe_to_lower);
void (*dchvm_init)(struct hubbub *hubbub);
void (*override_utm_client_qc_profile)(struct hubbub *hubbub, uint8_t qc_profile, int index);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 17/74] drm/amd/display: Resolve clk_mgr DPP DTO targets when the sequence is built
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (15 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 16/74] drm/amd/display: HWSS Refactor - Watermarks Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 18/74] drm/amd/display: Resolve clk_mgr DTBCLK " Wayne Lin
` (57 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Piotr Maziarz, Alvin Lee
From: Piotr Maziarz <piotr.maziarz@amd.com>
[WHY]
The DCN401 and DCN60 clk_mgr block sequences keep a dc_state pointer
in the DPPCLK DTO step and walk res_ctx, dc->res_pool and the pipe's DPP
when the step executes. The step therefore depends on state outside its
parameters, and on that state still being valid when the sequence runs.
[HOW]
Add clk_mgr_build_dpp_dto_targets(), which records each pipe's DPP instance
and DPP clock while the sequence is built, and clk_mgr_program_dpp_dto(),
which programs them. The step carries the targets instead of the dc_state.
The reference clock stays a pointer into clk_mgr's own clocks, because an
earlier step in the same sequence stores the PMFW-granted DPPCLK there.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Piotr Maziarz <piotr.maziarz@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c | 46 ++++++++++++++++
.../dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 54 ++++---------------
.../dc/clk_mgr/dcn401/dcn401_clk_mgr.h | 4 +-
.../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 54 ++++---------------
.../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h | 4 +-
.../amd/display/dc/inc/hw/clk_mgr_internal.h | 22 ++++++++
6 files changed, 90 insertions(+), 94 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
index bdc755be7bf6..cf1f1c8761c6 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
@@ -98,6 +98,52 @@ int clk_mgr_helper_get_active_plane_cnt(
return total_plane_count;
}
+void clk_mgr_build_dpp_dto_targets(
+ const struct dc *dc,
+ const struct dc_state *context,
+ bool safe_to_lower,
+ struct clk_mgr_dpp_dto_targets *targets)
+{
+ unsigned int i;
+
+ targets->safe_to_lower = safe_to_lower;
+ targets->count = 0;
+
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ const struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
+ int dppclk_khz = pipe->plane_res.bw.dppclk_khz;
+
+ if (!pipe->plane_res.dpp) {
+ /* A harvested pipe has neither a DPP nor a DPP clock. */
+ ASSERT(dppclk_khz == 0);
+ continue;
+ }
+
+ targets->pipes[targets->count].pipe_idx = i;
+ targets->pipes[targets->count].dpp_inst = pipe->plane_res.dpp->inst;
+ targets->pipes[targets->count].dppclk_khz = dppclk_khz;
+ targets->count++;
+ }
+}
+
+void clk_mgr_program_dpp_dto(
+ struct dccg *dccg,
+ int ref_dppclk_khz,
+ const struct clk_mgr_dpp_dto_targets *targets)
+{
+ unsigned int i;
+
+ dccg->ref_dppclk = ref_dppclk_khz;
+ for (i = 0; i < targets->count; i++) {
+ int dppclk_khz = targets->pipes[i].dppclk_khz;
+
+ /* Unless lowering is safe, only raise over what DCCG last programmed. */
+ if (targets->safe_to_lower ||
+ dccg->pipe_dppclk_khz[targets->pipes[i].pipe_idx] < dppclk_khz)
+ dccg->funcs->update_dpp_dto(dccg, targets->pipes[i].dpp_inst, dppclk_khz);
+ }
+}
+
void clk_mgr_exit_optimized_pwr_state(const struct dc *dc, struct clk_mgr *clk_mgr)
{
struct dc_link *edp_links[MAX_NUM_EDP];
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 03aec7f4df7b..d609f77d5298 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
@@ -560,40 +560,6 @@ static void dcn401_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr
}
}
-static void dcn401_update_clocks_update_dpp_dto(struct clk_mgr_internal *clk_mgr,
- struct dc_state *context, bool safe_to_lower, int ref_dppclk_khz)
-{
- uint32_t i;
-
- clk_mgr->dccg->ref_dppclk = ref_dppclk_khz;
- for (i = 0; i < clk_mgr->base.ctx->dc->res_pool->pipe_count; i++) {
- int dpp_inst = 0, dppclk_khz, prev_dppclk_khz;
-
- dppclk_khz = context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz;
-
- if (context->res_ctx.pipe_ctx[i].plane_res.dpp)
- dpp_inst = context->res_ctx.pipe_ctx[i].plane_res.dpp->inst;
- else if (!context->res_ctx.pipe_ctx[i].plane_res.dpp && dppclk_khz == 0) {
- /* dpp == NULL && dppclk_khz == 0 is valid because of pipe harvesting.
- * In this case just continue in loop
- */
- continue;
- } else if (!context->res_ctx.pipe_ctx[i].plane_res.dpp && dppclk_khz > 0) {
- /* The software state is not valid if dpp resource is NULL and
- * dppclk_khz > 0.
- */
- ASSERT(false);
- continue;
- }
-
- prev_dppclk_khz = clk_mgr->dccg->pipe_dppclk_khz[i];
-
- if (safe_to_lower || prev_dppclk_khz < dppclk_khz)
- clk_mgr->dccg->funcs->update_dpp_dto(
- clk_mgr->dccg, dpp_inst, dppclk_khz);
- }
-}
-
static int dcn401_set_hard_min_by_freq_optimized(struct clk_mgr_internal *clk_mgr, PPCLK_e clk, int requested_clk_khz)
{
if (!clk_mgr->smu_present || !dcn401_is_ppclk_dpm_enabled(clk_mgr, clk))
@@ -739,11 +705,9 @@ static void dcn401_execute_block_sequence(struct clk_mgr *clk_mgr_base, unsigned
params->indicate_drr_status_params.mod_drr_for_pstate);
break;
case CLK_MGR401_UPDATE_DPPCLK_DTO:
- dcn401_update_clocks_update_dpp_dto(
- clk_mgr_internal,
- params->update_dppclk_dto_params.context,
- params->update_dppclk_dto_params.safe_to_lower,
- *params->update_dppclk_dto_params.ref_dppclk_khz);
+ clk_mgr_program_dpp_dto(clk_mgr_internal->dccg,
+ *params->update_dppclk_dto_params.ref_dppclk_khz,
+ ¶ms->update_dppclk_dto_params.targets);
break;
case CLK_MGR401_UPDATE_DTBCLK_DTO:
dcn401_update_clocks_update_dtb_dto(
@@ -1186,9 +1150,9 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
if (dc->config.forced_clocks == false || (force_reset && safe_to_lower)) {
if (dppclk_lowered) {
/* if clock is being lowered, increase DTO before lowering refclk */
- block_sequence[num_steps].params.update_dppclk_dto_params.context = context;
block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.dppclk_khz;
- block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower;
+ clk_mgr_build_dpp_dto_targets(dc, context, safe_to_lower,
+ &block_sequence[num_steps].params.update_dppclk_dto_params.targets);
block_sequence[num_steps].func = CLK_MGR401_UPDATE_DPPCLK_DTO;
num_steps++;
@@ -1203,9 +1167,9 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
block_sequence[num_steps].func = CLK_MGR401_UPDATE_HARDMIN_PPCLK_OPTIMIZED;
num_steps++;
- block_sequence[num_steps].params.update_dppclk_dto_params.context = context;
block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.actual_dppclk_khz;
- block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower;
+ clk_mgr_build_dpp_dto_targets(dc, context, safe_to_lower,
+ &block_sequence[num_steps].params.update_dppclk_dto_params.targets);
block_sequence[num_steps].func = CLK_MGR401_UPDATE_DPPCLK_DTO;
num_steps++;
}
@@ -1225,9 +1189,9 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
num_steps++;
}
- block_sequence[num_steps].params.update_dppclk_dto_params.context = context;
block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.actual_dppclk_khz;
- block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower;
+ clk_mgr_build_dpp_dto_targets(dc, context, safe_to_lower,
+ &block_sequence[num_steps].params.update_dppclk_dto_params.targets);
block_sequence[num_steps].func = CLK_MGR401_UPDATE_DPPCLK_DTO;
num_steps++;
}
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 d4cd69a5a8dd..7f03a30546a2 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
@@ -53,9 +53,9 @@ union dcn401_clk_mgr_block_sequence_params {
} indicate_drr_status_params;
struct {
/* inputs */
- struct dc_state *context;
+ /* clk_mgr's own clocks; an earlier step may store the PMFW-granted DPPCLK there. */
int *ref_dppclk_khz;
- bool safe_to_lower;
+ struct clk_mgr_dpp_dto_targets targets;
} update_dppclk_dto_params;
struct {
/* inputs */
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
index 4242a4b5c902..6f16bc33b6af 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
@@ -183,40 +183,6 @@ static bool dcn60_check_native_scaling(struct pipe_ctx *pipe)
return is_native_scaling;
}
-static void dcn60_update_clocks_update_dpp_dto(struct clk_mgr_internal *clk_mgr,
- struct dc_state *context, bool safe_to_lower, int ref_dppclk_khz)
-{
- uint32_t i;
-
- clk_mgr->dccg->ref_dppclk = ref_dppclk_khz;
- for (i = 0; i < clk_mgr->base.ctx->dc->res_pool->pipe_count; i++) {
- int dpp_inst = 0, dppclk_khz, prev_dppclk_khz;
-
- dppclk_khz = context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz;
-
- if (context->res_ctx.pipe_ctx[i].plane_res.dpp)
- dpp_inst = context->res_ctx.pipe_ctx[i].plane_res.dpp->inst;
- else if (!context->res_ctx.pipe_ctx[i].plane_res.dpp && dppclk_khz == 0) {
- /* dpp == NULL && dppclk_khz == 0 is valid because of pipe harvesting.
- * In this case just continue in loop
- */
- continue;
- } else if (!context->res_ctx.pipe_ctx[i].plane_res.dpp && dppclk_khz > 0) {
- /* The software state is not valid if dpp resource is NULL and
- * dppclk_khz > 0.
- */
- ASSERT(false);
- continue;
- }
-
- prev_dppclk_khz = clk_mgr->dccg->pipe_dppclk_khz[i];
-
- if (safe_to_lower || prev_dppclk_khz < dppclk_khz)
- clk_mgr->dccg->funcs->update_dpp_dto(
- clk_mgr->dccg, dpp_inst, dppclk_khz);
- }
-}
-
static int dcn60_set_clock_freq_optimized(struct clk_mgr_internal *clk_mgr, PPCLK_e clk, int requested_clk_khz)
{
if (!clk_mgr->smu_present || !dcn60_is_ppclk_dpm_enabled(clk_mgr, clk))
@@ -383,9 +349,9 @@ static unsigned int dcn60_build_update_display_clocks_sequence(
if (dc->config.forced_clocks == false || (force_reset && safe_to_lower)) {
if (dppclk_lowered) {
/* if clock is being lowered, increase DTO before lowering refclk */
- block_sequence[num_steps].params.update_dppclk_dto_params.context = context;
block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.dppclk_khz;
- block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower;
+ clk_mgr_build_dpp_dto_targets(dc, context, safe_to_lower,
+ &block_sequence[num_steps].params.update_dppclk_dto_params.targets);
block_sequence[num_steps].func = CLK_MGR60_UPDATE_DPPCLK_DTO;
num_steps++;
@@ -396,9 +362,9 @@ static unsigned int dcn60_build_update_display_clocks_sequence(
block_sequence[num_steps].func = CLK_MGR60_SET_PPCLK_OPTIMIZED;
num_steps++;
- block_sequence[num_steps].params.update_dppclk_dto_params.context = context;
block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.actual_dppclk_khz;
- block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower;
+ clk_mgr_build_dpp_dto_targets(dc, context, safe_to_lower,
+ &block_sequence[num_steps].params.update_dppclk_dto_params.targets);
block_sequence[num_steps].func = CLK_MGR60_UPDATE_DPPCLK_DTO;
num_steps++;
}
@@ -412,9 +378,9 @@ static unsigned int dcn60_build_update_display_clocks_sequence(
num_steps++;
}
- block_sequence[num_steps].params.update_dppclk_dto_params.context = context;
block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.actual_dppclk_khz;
- block_sequence[num_steps].params.update_dppclk_dto_params.safe_to_lower = safe_to_lower;
+ clk_mgr_build_dpp_dto_targets(dc, context, safe_to_lower,
+ &block_sequence[num_steps].params.update_dppclk_dto_params.targets);
block_sequence[num_steps].func = CLK_MGR60_UPDATE_DPPCLK_DTO;
num_steps++;
}
@@ -1225,11 +1191,9 @@ static void dcn60_execute_block_sequence(struct clk_mgr *clk_mgr_base, unsigned
params->indicate_pstate_status_params.alt_ch_enable);
break;
case CLK_MGR60_UPDATE_DPPCLK_DTO:
- dcn60_update_clocks_update_dpp_dto(
- clk_mgr_internal,
- params->update_dppclk_dto_params.context,
- params->update_dppclk_dto_params.safe_to_lower,
- *params->update_dppclk_dto_params.ref_dppclk_khz);
+ clk_mgr_program_dpp_dto(clk_mgr_internal->dccg,
+ *params->update_dppclk_dto_params.ref_dppclk_khz,
+ ¶ms->update_dppclk_dto_params.targets);
break;
case CLK_MGR60_UPDATE_DTBCLK_DTO:
dcn60_update_clocks_update_dtb_dto(
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
index 0625f060e8c6..6d96ec8440a2 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
@@ -45,9 +45,9 @@ union dcn60_clk_mgr_block_sequence_params {
} indicate_pstate_status_params;
struct {
/* inputs */
- struct dc_state *context;
+ /* clk_mgr's own clocks; an earlier step may store the PMFW-granted DPPCLK there. */
int *ref_dppclk_khz;
- bool safe_to_lower;
+ struct clk_mgr_dpp_dto_targets targets;
} update_dppclk_dto_params;
struct {
/* inputs */
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
index 68fd92258e8c..c6c087197f82 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
@@ -602,6 +602,28 @@ int clk_mgr_helper_get_active_plane_cnt(
struct dc *dc,
struct dc_state *context);
+/* Per-pipe DPP DTO programming resolved from dc_state when a clock update is built. */
+struct clk_mgr_dpp_dto_targets {
+ bool safe_to_lower;
+ unsigned int count;
+ struct {
+ unsigned int pipe_idx;
+ int dpp_inst;
+ int dppclk_khz;
+ } pipes[MAX_PIPES];
+};
+
+void clk_mgr_build_dpp_dto_targets(
+ const struct dc *dc,
+ const struct dc_state *context,
+ bool safe_to_lower,
+ struct clk_mgr_dpp_dto_targets *targets);
+
+void clk_mgr_program_dpp_dto(
+ struct dccg *dccg,
+ int ref_dppclk_khz,
+ const struct clk_mgr_dpp_dto_targets *targets);
+
#endif //__DAL_CLK_MGR_INTERNAL_H__
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 18/74] drm/amd/display: Resolve clk_mgr DTBCLK DTO targets when the sequence is built
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (16 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 17/74] drm/amd/display: Resolve clk_mgr DPP DTO targets when the sequence is built Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 19/74] drm/amd/display: Resolve the DCN401 DENTIST divider " Wayne Lin
` (56 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Piotr Maziarz, Alvin Lee
From: Piotr Maziarz <piotr.maziarz@amd.com>
[WHY]
The DTBCLK DTO step of the DCN401 and DCN60 clk_mgr block sequences
keeps a dc_state pointer and, when it executes, walks the state's streams,
looks up each OTG master, decides whether it uses an HPO encoder and reads
its pixel clock parameters. The step also carries an unused pointer to the
reference DTBCLK.
[HOW]
Add clk_mgr_build_dtbclk_dto_target(), which resolves one HPO stream's
clock source, pixel clock parameters, PLL settings and link encoding while
the sequence is built, and clk_mgr_program_dtbclk_dto(), which programs it.
The builders emit one DTBCLK DTO step per HPO stream, and the clk_mgr
sequence capacity grows by MAX_PIPES - 1 to hold them.
The per-ASIC copies of the DTBCLK DTO loop are removed.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Piotr Maziarz <piotr.maziarz@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c | 37 +++++++++++++
.../dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 53 +++----------------
.../dc/clk_mgr/dcn401/dcn401_clk_mgr.h | 9 ++--
.../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 53 +++----------------
.../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h | 6 +--
.../amd/display/dc/inc/hw/clk_mgr_internal.h | 16 ++++++
6 files changed, 73 insertions(+), 101 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
index cf1f1c8761c6..28dc3eb3e472 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c
@@ -144,6 +144,43 @@ void clk_mgr_program_dpp_dto(
}
}
+bool clk_mgr_build_dtbclk_dto_target(
+ const struct dc *dc,
+ struct dc_state *context,
+ const struct dc_stream_state *stream,
+ struct clk_mgr_dtbclk_dto_target *target)
+{
+ struct pipe_ctx *otg_master = resource_get_otg_master_for_stream(&context->res_ctx, stream);
+
+ ASSERT(otg_master);
+ if (!otg_master)
+ return false;
+
+ ASSERT(otg_master->clock_source);
+ ASSERT(otg_master->clock_source->funcs->program_pix_clk);
+ ASSERT(otg_master->stream_res.pix_clk_params.controller_id >= CONTROLLER_ID_D0);
+
+ if (!dc->link_srv->dp_is_128b_132b_signal(otg_master) &&
+ !dc_is_hdmi_frl_signal(otg_master->stream->signal))
+ return false;
+
+ if (otg_master->stream_res.pix_clk_params.controller_id <= CONTROLLER_ID_UNDEFINED)
+ return false;
+
+ target->clock_source = otg_master->clock_source;
+ target->pix_clk_params = otg_master->stream_res.pix_clk_params;
+ target->pll_settings = otg_master->pll_settings;
+ target->encoding = dc->link_srv->dp_get_encoding_format(
+ &otg_master->link_config.dp_link_settings);
+ return true;
+}
+
+void clk_mgr_program_dtbclk_dto(struct clk_mgr_dtbclk_dto_target *target)
+{
+ target->clock_source->funcs->program_pix_clk(target->clock_source,
+ &target->pix_clk_params, target->encoding, &target->pll_settings);
+}
+
void clk_mgr_exit_optimized_pwr_state(const struct dc *dc, struct clk_mgr *clk_mgr)
{
struct dc_link *edp_links[MAX_NUM_EDP];
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 d609f77d5298..0785f70fb535 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
@@ -523,43 +523,6 @@ static void dcn401_auto_dpm_test_log(
}
}
-static void dcn401_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr,
- struct dc_state *context,
- int ref_dtbclk_khz)
-{
- (void)ref_dtbclk_khz;
- int i;
- struct dccg *dccg = clk_mgr->dccg;
- struct pipe_ctx *otg_master;
- bool use_hpo_encoder;
-
-
- for (i = 0; i < context->stream_count; i++) {
- otg_master = resource_get_otg_master_for_stream(
- &context->res_ctx, context->streams[i]);
- ASSERT(otg_master);
- if (!otg_master)
- continue;
-
- ASSERT(otg_master->clock_source);
- ASSERT(otg_master->clock_source->funcs->program_pix_clk);
- ASSERT(otg_master->stream_res.pix_clk_params.controller_id >= CONTROLLER_ID_D0);
-
- use_hpo_encoder = dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(otg_master);
- use_hpo_encoder |= dc_is_hdmi_frl_signal(otg_master->stream->signal);
- if (!use_hpo_encoder)
- continue;
-
- if (otg_master->stream_res.pix_clk_params.controller_id > CONTROLLER_ID_UNDEFINED)
- otg_master->clock_source->funcs->program_pix_clk(
- otg_master->clock_source,
- &otg_master->stream_res.pix_clk_params,
- dccg->ctx->dc->link_srv->dp_get_encoding_format(
- &otg_master->link_config.dp_link_settings),
- &otg_master->pll_settings);
- }
-}
-
static int dcn401_set_hard_min_by_freq_optimized(struct clk_mgr_internal *clk_mgr, PPCLK_e clk, int requested_clk_khz)
{
if (!clk_mgr->smu_present || !dcn401_is_ppclk_dpm_enabled(clk_mgr, clk))
@@ -710,10 +673,7 @@ static void dcn401_execute_block_sequence(struct clk_mgr *clk_mgr_base, unsigned
¶ms->update_dppclk_dto_params.targets);
break;
case CLK_MGR401_UPDATE_DTBCLK_DTO:
- dcn401_update_clocks_update_dtb_dto(
- clk_mgr_internal,
- params->update_dtbclk_dto_params.context,
- *params->update_dtbclk_dto_params.ref_dtbclk_khz);
+ clk_mgr_program_dtbclk_dto(¶ms->update_dtbclk_dto_params);
break;
case CLK_MGR401_UPDATE_DENTIST:
dcn401_update_clocks_update_dentist(
@@ -1119,10 +1079,13 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
num_steps++;
/* Update DTO in DCCG */
- block_sequence[num_steps].params.update_dtbclk_dto_params.context = context;
- block_sequence[num_steps].params.update_dtbclk_dto_params.ref_dtbclk_khz = &clk_mgr_base->clks.ref_dtbclk_khz;
- block_sequence[num_steps].func = CLK_MGR401_UPDATE_DTBCLK_DTO;
- num_steps++;
+ for (i = 0; i < context->stream_count; i++) {
+ if (!clk_mgr_build_dtbclk_dto_target(dc, context, context->streams[i],
+ &block_sequence[num_steps].params.update_dtbclk_dto_params))
+ continue;
+ block_sequence[num_steps].func = CLK_MGR401_UPDATE_DTBCLK_DTO;
+ num_steps++;
+ }
}
if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr_base->clks.dppclk_khz)) {
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 7f03a30546a2..a2ba8b665a14 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
@@ -5,7 +5,8 @@
#ifndef __DCN401_CLK_MGR_H_
#define __DCN401_CLK_MGR_H_
-#define DCN401_CLK_MGR_MAX_SEQUENCE_SIZE 30
+/* Leaves room for a DTBCLK DTO step per stream. */
+#define DCN401_CLK_MGR_MAX_SEQUENCE_SIZE (30 + MAX_PIPES - 1)
union dcn401_clk_mgr_block_sequence_params {
struct {
@@ -57,11 +58,7 @@ union dcn401_clk_mgr_block_sequence_params {
int *ref_dppclk_khz;
struct clk_mgr_dpp_dto_targets targets;
} update_dppclk_dto_params;
- struct {
- /* inputs */
- struct dc_state *context;
- int *ref_dtbclk_khz;
- } update_dtbclk_dto_params;
+ struct clk_mgr_dtbclk_dto_target update_dtbclk_dto_params;
struct {
/* inputs */
struct dc_state *context;
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
index 6f16bc33b6af..43d1932085b7 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
@@ -208,43 +208,6 @@ static int dcn60_set_clock_freq_optimized(struct clk_mgr_internal *clk_mgr, PPCL
return actual_clk_khz;
}
-static void dcn60_update_clocks_update_dtb_dto(struct clk_mgr_internal *clk_mgr,
- struct dc_state *context,
- int ref_dtbclk_khz)
-{
- (void)ref_dtbclk_khz;
- int i;
- struct dccg *dccg = clk_mgr->dccg;
- struct pipe_ctx *otg_master;
- bool use_hpo_encoder;
-
- for (i = 0; i < context->stream_count; i++) {
- otg_master = resource_get_otg_master_for_stream(
- &context->res_ctx, context->streams[i]);
-
- ASSERT(otg_master);
- if (!otg_master)
- continue;
-
- ASSERT(otg_master->clock_source);
- ASSERT(otg_master->clock_source->funcs->program_pix_clk);
- ASSERT(otg_master->stream_res.pix_clk_params.controller_id >= CONTROLLER_ID_D0);
-
- use_hpo_encoder = dccg->ctx->dc->link_srv->dp_is_128b_132b_signal(otg_master);
- use_hpo_encoder |= dc_is_hdmi_frl_signal(otg_master->stream->signal);
- if (!use_hpo_encoder)
- continue;
-
- if (otg_master->stream_res.pix_clk_params.controller_id > CONTROLLER_ID_UNDEFINED)
- otg_master->clock_source->funcs->program_pix_clk(
- otg_master->clock_source,
- &otg_master->stream_res.pix_clk_params,
- dccg->ctx->dc->link_srv->dp_get_encoding_format(
- &otg_master->link_config.dp_link_settings),
- &otg_master->pll_settings);
- }
-}
-
static unsigned int dcn60_build_update_display_clocks_sequence(
struct clk_mgr *clk_mgr_base,
struct dc_state *context,
@@ -316,10 +279,13 @@ static unsigned int dcn60_build_update_display_clocks_sequence(
num_steps++;
/* Update DTO in DCCG */
- block_sequence[num_steps].params.update_dtbclk_dto_params.context = context;
- block_sequence[num_steps].params.update_dtbclk_dto_params.ref_dtbclk_khz = &clk_mgr_base->clks.ref_dtbclk_khz;
- block_sequence[num_steps].func = CLK_MGR60_UPDATE_DTBCLK_DTO;
- num_steps++;
+ for (i = 0; i < context->stream_count; i++) {
+ if (!clk_mgr_build_dtbclk_dto_target(dc, context, context->streams[i],
+ &block_sequence[num_steps].params.update_dtbclk_dto_params))
+ continue;
+ block_sequence[num_steps].func = CLK_MGR60_UPDATE_DTBCLK_DTO;
+ num_steps++;
+ }
}
if (should_set_clock(safe_to_lower, new_clocks->dppclk_khz, clk_mgr_base->clks.dppclk_khz)) {
@@ -1196,10 +1162,7 @@ static void dcn60_execute_block_sequence(struct clk_mgr *clk_mgr_base, unsigned
¶ms->update_dppclk_dto_params.targets);
break;
case CLK_MGR60_UPDATE_DTBCLK_DTO:
- dcn60_update_clocks_update_dtb_dto(
- clk_mgr_internal,
- params->update_dtbclk_dto_params.context,
- *params->update_dtbclk_dto_params.ref_dtbclk_khz);
+ clk_mgr_program_dtbclk_dto(¶ms->update_dtbclk_dto_params);
break;
case CLK_MGR60_UPDATE_PSR_WAIT_LOOP:
params->update_psr_wait_loop_params.dmcu->funcs->set_psr_wait_loop(
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
index 6d96ec8440a2..bd61817a5ef9 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
@@ -49,11 +49,7 @@ union dcn60_clk_mgr_block_sequence_params {
int *ref_dppclk_khz;
struct clk_mgr_dpp_dto_targets targets;
} update_dppclk_dto_params;
- struct {
- /* inputs */
- struct dc_state *context;
- int *ref_dtbclk_khz;
- } update_dtbclk_dto_params;
+ struct clk_mgr_dtbclk_dto_target update_dtbclk_dto_params;
struct {
/* inputs */
struct dc_state *context;
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
index c6c087197f82..ff43c1814766 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h
@@ -624,6 +624,22 @@ void clk_mgr_program_dpp_dto(
int ref_dppclk_khz,
const struct clk_mgr_dpp_dto_targets *targets);
+/* DTBCLK DTO programming of one HPO stream, resolved from dc_state when a clock update is built. */
+struct clk_mgr_dtbclk_dto_target {
+ struct clock_source *clock_source;
+ struct pixel_clk_params pix_clk_params;
+ struct pll_settings pll_settings;
+ enum dp_link_encoding encoding;
+};
+
+bool clk_mgr_build_dtbclk_dto_target(
+ const struct dc *dc,
+ struct dc_state *context,
+ const struct dc_stream_state *stream,
+ struct clk_mgr_dtbclk_dto_target *target);
+
+void clk_mgr_program_dtbclk_dto(struct clk_mgr_dtbclk_dto_target *target);
+
#endif //__DAL_CLK_MGR_INTERNAL_H__
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 19/74] drm/amd/display: Resolve the DCN401 DENTIST divider when the sequence is built
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (17 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 18/74] drm/amd/display: Resolve clk_mgr DTBCLK " Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 20/74] drm/amd/display: Update default VCO frequency Wayne Lin
` (55 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Piotr Maziarz, Alvin Lee
From: Piotr Maziarz <piotr.maziarz@amd.com>
[WHY]
The DCN401 clk_mgr DENTIST step keeps an unused dc_state pointer and, when
it executes, reads the current DISPCLK from clk_mgr, the DENTIST VCO
frequency and dc->debug.override_dispclk_programming to decide whether
and how to program the divider. DCN60 still declares the same unused
dc_state-carrying parameters.
[HOW]
Compute the DISPCLK divider when the sequence is built and store it in the
step. Emit the step only when the debug override is set and DISPCLK is
known, which is when the executor used to program anything; the executor
keeps only the register read-back and update.
Drop the unused DENTIST parameters from the DCN60 sequence.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Piotr Maziarz <piotr.maziarz@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 63 ++++++++++---------
.../dc/clk_mgr/dcn401/dcn401_clk_mgr.h | 2 +-
.../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h | 4 --
3 files changed, 33 insertions(+), 36 deletions(-)
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 0785f70fb535..dccc9012c7fc 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
@@ -548,33 +548,18 @@ static int dcn401_set_hard_min_by_freq_optimized(struct clk_mgr_internal *clk_mg
static void dcn401_update_clocks_update_dentist(
struct clk_mgr_internal *clk_mgr,
- struct dc_state *context)
+ uint32_t new_dispclk_wdivider)
{
- (void)context;
- uint32_t new_disp_divider = 0;
- uint32_t new_dispclk_wdivider = 0;
uint32_t dentist_dispclk_wdivider_readback = 0;
- struct dc *dc = clk_mgr->base.ctx->dc;
-
- if (clk_mgr->base.clks.dispclk_khz == 0)
- return;
-
- new_disp_divider = DENTIST_DIVIDER_RANGE_SCALE_FACTOR
- * clk_mgr->base.dentist_vco_freq_khz / clk_mgr->base.clks.dispclk_khz;
- new_dispclk_wdivider = dentist_get_did_from_divider(new_disp_divider);
+ REG_GET(DENTIST_DISPCLK_CNTL,
+ DENTIST_DISPCLK_WDIVIDER, &dentist_dispclk_wdivider_readback);
- if (dc->debug.override_dispclk_programming) {
- REG_GET(DENTIST_DISPCLK_CNTL,
- DENTIST_DISPCLK_WDIVIDER, &dentist_dispclk_wdivider_readback);
-
- if (dentist_dispclk_wdivider_readback > new_dispclk_wdivider) {
- REG_UPDATE(DENTIST_DISPCLK_CNTL,
- DENTIST_DISPCLK_WDIVIDER, new_dispclk_wdivider);
- REG_WAIT(DENTIST_DISPCLK_CNTL, DENTIST_DISPCLK_CHG_DONE, 1, 50, 2000);
- }
+ if (dentist_dispclk_wdivider_readback > new_dispclk_wdivider) {
+ REG_UPDATE(DENTIST_DISPCLK_CNTL,
+ DENTIST_DISPCLK_WDIVIDER, new_dispclk_wdivider);
+ REG_WAIT(DENTIST_DISPCLK_CNTL, DENTIST_DISPCLK_CHG_DONE, 1, 50, 2000);
}
-
}
static void dcn401_execute_block_sequence(struct clk_mgr *clk_mgr_base, unsigned int num_steps)
@@ -678,7 +663,7 @@ static void dcn401_execute_block_sequence(struct clk_mgr *clk_mgr_base, unsigned
case CLK_MGR401_UPDATE_DENTIST:
dcn401_update_clocks_update_dentist(
clk_mgr_internal,
- params->update_dentist_params.context);
+ params->update_dentist_params.dispclk_wdivider);
break;
case CLK_MGR401_UPDATE_PSR_WAIT_LOOP:
params->update_psr_wait_loop_params.dmcu->funcs->set_psr_wait_loop(
@@ -1009,6 +994,27 @@ static unsigned int dcn401_build_update_bandwidth_clocks_sequence(
return num_steps;
}
+/* Outside the dispclk programming debug override, DENTIST is not reprogrammed here. */
+static unsigned int dcn401_add_update_dentist_step(
+ struct clk_mgr *clk_mgr_base,
+ struct dcn401_clk_mgr_block_sequence *block_sequence,
+ unsigned int num_steps)
+{
+ uint32_t new_disp_divider;
+
+ if (!clk_mgr_base->ctx->dc->debug.override_dispclk_programming ||
+ clk_mgr_base->clks.dispclk_khz == 0)
+ return num_steps;
+
+ new_disp_divider = DENTIST_DIVIDER_RANGE_SCALE_FACTOR
+ * clk_mgr_base->dentist_vco_freq_khz / clk_mgr_base->clks.dispclk_khz;
+
+ block_sequence[num_steps].params.update_dentist_params.dispclk_wdivider =
+ dentist_get_did_from_divider(new_disp_divider);
+ block_sequence[num_steps].func = CLK_MGR401_UPDATE_DENTIST;
+ return num_steps + 1;
+}
+
static unsigned int dcn401_build_update_display_clocks_sequence(
struct clk_mgr *clk_mgr_base,
struct dc_state *context,
@@ -1119,9 +1125,7 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
block_sequence[num_steps].func = CLK_MGR401_UPDATE_DPPCLK_DTO;
num_steps++;
- block_sequence[num_steps].params.update_dentist_params.context = context;
- block_sequence[num_steps].func = CLK_MGR401_UPDATE_DENTIST;
- num_steps++;
+ num_steps = dcn401_add_update_dentist_step(clk_mgr_base, block_sequence, num_steps);
if (dcn401_is_ppclk_dpm_enabled(clk_mgr_internal, PPCLK_DPPCLK)) {
block_sequence[num_steps].params.update_hardmin_optimized_params.ppclk = PPCLK_DPPCLK;
@@ -1146,11 +1150,8 @@ static unsigned int dcn401_build_update_display_clocks_sequence(
num_steps++;
}
- if (update_dppclk || update_dispclk) {
- block_sequence[num_steps].params.update_dentist_params.context = context;
- block_sequence[num_steps].func = CLK_MGR401_UPDATE_DENTIST;
- num_steps++;
- }
+ if (update_dppclk || update_dispclk)
+ num_steps = dcn401_add_update_dentist_step(clk_mgr_base, block_sequence, num_steps);
block_sequence[num_steps].params.update_dppclk_dto_params.ref_dppclk_khz = &clk_mgr_base->clks.actual_dppclk_khz;
clk_mgr_build_dpp_dto_targets(dc, context, safe_to_lower,
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 a2ba8b665a14..c87fb8e794a6 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
@@ -61,7 +61,7 @@ union dcn401_clk_mgr_block_sequence_params {
struct clk_mgr_dtbclk_dto_target update_dtbclk_dto_params;
struct {
/* inputs */
- struct dc_state *context;
+ uint32_t dispclk_wdivider;
} update_dentist_params;
struct {
/* inputs */
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
index bd61817a5ef9..9f945d2eb78a 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h
@@ -50,10 +50,6 @@ union dcn60_clk_mgr_block_sequence_params {
struct clk_mgr_dpp_dto_targets targets;
} update_dppclk_dto_params;
struct clk_mgr_dtbclk_dto_target update_dtbclk_dto_params;
- struct {
- /* inputs */
- struct dc_state *context;
- } update_dentist_params;
struct {
/* inputs */
struct dmcu *dmcu;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 20/74] drm/amd/display: Update default VCO frequency
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (18 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 19/74] drm/amd/display: Resolve the DCN401 DENTIST divider " Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 21/74] drm/amd/display: commit validated target when minimal transition cannot be built Wayne Lin
` (54 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Nevenko Stupar, Alvin Lee
From: Nevenko Stupar <Nevenko.Stupar@amd.com>
[Why & How]
No need to check for a specific variant. The default VCO frequency
is 4.1 GHz for all DCN6 variants when the register reads zero.
dprefclk_khz is 607407 for every DCN6 variant.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Nevenko Stupar <Nevenko.Stupar@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
index 43d1932085b7..8eee54c7d5d5 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
@@ -1753,7 +1753,6 @@ struct clk_mgr_internal *dcn60_clk_mgr_construct(
{
struct dcn60_clk_mgr *clk_mgr60 = kzalloc(sizeof(struct dcn60_clk_mgr), GFP_KERNEL);
struct clk_mgr_internal *clk_mgr;
- bool variant_clocks = ASICREV_IS_DCN6_VARIANT_LITE3(ctx->asic_id.hw_internal_rev);
if (!clk_mgr60)
return NULL;
@@ -1778,14 +1777,14 @@ struct clk_mgr_internal *dcn60_clk_mgr_construct(
* dcn60_dump_clk_registers from 4 * dentist_vco_freq_khz /
* dprefclk DID divider
*/
- clk_mgr->base.dprefclk_khz = variant_clocks ? 607407 : 720000;
+ clk_mgr->base.dprefclk_khz = 607407;
/* integer part is now VCO frequency in kHz */
clk_mgr->base.dentist_vco_freq_khz = dcn60_get_vco_frequency_from_reg(clk_mgr);
/* in case we don't get a value from the register, use default */
if (clk_mgr->base.dentist_vco_freq_khz == 0)
- clk_mgr->base.dentist_vco_freq_khz = variant_clocks ? 4100000 : 4500000;
+ clk_mgr->base.dentist_vco_freq_khz = 4100000;
clk_mgr->dfs_bypass_enabled = false;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 21/74] drm/amd/display: commit validated target when minimal transition cannot be built
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (19 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 20/74] drm/amd/display: Update default VCO frequency Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 22/74] drm/amd/display: Update DP HPO to use correct APG instance Wayne Lin
` (53 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
From: James Lin <PingLei.Lin@amd.com>
[why]
Before committing a non-seamless pipe-topology change,
dc_commit_streams() inserts a minimal (no-ODM) intermediate
transition so hardware can reach the target topology safely. A stream
that needs ODM combine to meet DISPCLK (e.g. 2560x1440@180 on
DCN4.0.1) cannot be validated under the minimal no-ODM pipe-split
policy, so commit_minimal_transition_state() cannot build that
intermediate state.
The call site then failed the whole commit and blanked the display,
even though the target context already passed full
MODE_AND_PROGRAMMING validation and is safe to program directly.
Because this runs in the atomic commit_tail after validation has
already succeeded, failing here leaves the display blank every time
the mode is set.
[how]
When the minimal transition cannot be built, log it and commit the
already-validated target context instead of failing the commit.
Cc: Sun peng Li <sunpeng.li@amd.com>
Signed-off-by: James Lin <PingLei.Lin@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/core/dc.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 9d871a821c8d..464a5b7810d9 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -2711,11 +2711,16 @@ enum dc_status dc_commit_streams(struct dc *dc, struct dc_commit_streams_params
*/
if (dc->hwss.is_pipe_topology_transition_seamless &&
!dc->hwss.is_pipe_topology_transition_seamless(dc, dc->current_state, context)) {
- res = commit_minimal_transition_state(dc, context);
- if (res != DC_OK) {
- BREAK_TO_DEBUGGER();
- goto fail;
- }
+ /*
+ * A minimal (no-ODM) intermediate transition could not be built
+ * for a stream that still passed full validation above, e.g. a
+ * high pixel clock mode that needs ODM combine to meet DISPCLK.
+ * In that case commit the validated target context directly
+ * instead of failing the whole commit and blanking the display.
+ */
+ if (!commit_minimal_transition_state(dc, context))
+ DC_LOG_WARNING("%s: minimal transition unavailable; committing target\n",
+ __func__);
}
res = dc_commit_state_no_check(dc, context);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 22/74] drm/amd/display: Update DP HPO to use correct APG instance
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (20 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 21/74] drm/amd/display: commit validated target when minimal transition cannot be built Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 23/74] drm/amd/display: cleanup tiling and address formats Wayne Lin
` (52 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Alvin Lee, Charlene Liu
From: Alvin Lee <Alvin.Lee2@amd.com>
[Description]
DP HPO uses APG instance 5 - 8. In total their are 9 APG instances
for DCN6.
Cc: Charlene Liu <charlene.liu@amd.com>
Signed-off-by: Alvin Lee <Alvin.Lee2@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dc/resource/dcn60/dcn60_resource.c | 26 +++++++++++--------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
index f192c95952ab..d068d5682619 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
@@ -291,7 +291,7 @@ static const struct dcn31_vpg_mask vpg_mask = {
#define apg_regs_init(id)\
APG_DCN31_REG_LIST_RI(id)
-static struct dcn31_apg_registers apg_regs[5];
+static struct dcn31_apg_registers apg_regs[9];
static const struct dcn31_apg_shift apg_shift = {
DCN31_APG_MASK_SH_LIST(__SHIFT)
@@ -1394,7 +1394,11 @@ static struct apg *dcn60_apg_create(
apg_regs_init(1),
apg_regs_init(2),
apg_regs_init(3),
- apg_regs_init(4);
+ apg_regs_init(4),
+ apg_regs_init(5),
+ apg_regs_init(6),
+ apg_regs_init(7),
+ apg_regs_init(8);
apg31_construct(apg60, ctx, inst,
&apg_regs[inst],
@@ -1525,20 +1529,20 @@ static struct hpo_dp_stream_encoder *dcn60_hpo_dp_stream_encoder_create(
hpo_dp_inst = eng_id - ENGINE_ID_HPO_DP_0;
/* Mapping of VPG register blocks to HPO DP block instance:
- * VPG[6] -> HPO_DP[0]
- * VPG[7] -> HPO_DP[1]
- * VPG[8] -> HPO_DP[2]
- * VPG[9] -> HPO_DP[3]
+ * VPG[5] -> HPO_DP[0]
+ * VPG[6] -> HPO_DP[1]
+ * VPG[7] -> HPO_DP[2]
+ * VPG[8] -> HPO_DP[3]
*/
vpg_inst = hpo_dp_inst + 5;
/* Mapping of APG register blocks to HPO DP block instance:
- * APG[0] -> HPO_DP[0]
- * APG[1] -> HPO_DP[1]
- * APG[2] -> HPO_DP[2]
- * APG[3] -> HPO_DP[3]
+ * APG[5] -> HPO_DP[0]
+ * APG[6] -> HPO_DP[1]
+ * APG[7] -> HPO_DP[2]
+ * APG[8] -> HPO_DP[3]
*/
- apg_inst = hpo_dp_inst;
+ apg_inst = hpo_dp_inst + 5;
/* allocate HPO stream encoder and create VPG sub-block */
hpo_dp_enc60 = kzalloc(sizeof(struct dcn31_hpo_dp_stream_encoder), GFP_KERNEL);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 23/74] drm/amd/display: cleanup tiling and address formats
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (21 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 22/74] drm/amd/display: Update DP HPO to use correct APG instance Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 24/74] drm/amd/display: Enable SST PCON FRL DSC passthrough Wayne Lin
` (51 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Dillon Varone, Aric Cyr
From: Dillon Varone <Dillon.Varone@amd.com>
[WHY&HOW]
Cleanup unused tiling and address cases and improve version enum.
Cc: Aric Cyr <aric.cyr@amd.com>
Signed-off-by: Dillon Varone <Dillon.Varone@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/dc_hw_types.h | 16 +++---
.../dc/dml2_wrapper/dml2_mall_phantom.c | 1 -
.../amd/display/dc/hubp/dcn50/dcn50_hubp.c | 52 ++-----------------
3 files changed, 13 insertions(+), 56 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h
index eb9effb22658..20504dfb1b13 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_hw_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_hw_types.h
@@ -390,14 +390,14 @@ enum swizzle_mode_addr3_values {
DC_ADDR3_SW_UNKNOWN = DC_ADDR3_SW_MAX
};
-enum dc_gfxversion {
- DcGfxVersion7 = 0,
- DcGfxVersion8,
- DcGfxVersion9,
- DcGfxVersion10,
- DcGfxVersion11,
- DcGfxAddr3,
- DcGfxVersionUnknown
+enum dc_gfx_addr_version {
+ DcGfxVersion7 = 0x0700,
+ DcGfxVersion8 = 0x0800,
+ DcGfxVersion9 = 0x0900,
+ DcGfxVersion10 = 0x0A00,
+ DcGfxVersion11 = 0x0B00,
+ DcGfxAddr3 = 0x0C00,
+ DcGfxVersionUnknown = 0xFFFF
};
struct dc_tiling_info {
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_mall_phantom.c b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_mall_phantom.c
index edc4dcdde952..bd2c2b82b176 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_mall_phantom.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_mall_phantom.c
@@ -808,7 +808,6 @@ static void enable_phantom_plane(struct dml2_context *ctx,
memcpy(&phantom_plane->tiling_info, &curr_pipe->plane_state->tiling_info,
sizeof(phantom_plane->tiling_info));
memcpy(&phantom_plane->dcc, &curr_pipe->plane_state->dcc, sizeof(phantom_plane->dcc));
- //phantom_plane->tiling_info.gfx10compatible.compat_level = curr_pipe->plane_state->tiling_info.gfx10compatible.compat_level;
phantom_plane->format = curr_pipe->plane_state->format;
phantom_plane->rotation = curr_pipe->plane_state->rotation;
phantom_plane->visible = curr_pipe->plane_state->visible;
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c
index ba86e30b1180..ceea0b766f5b 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn50/dcn50_hubp.c
@@ -217,57 +217,15 @@ static void hubp50_program_tiling(
const enum surface_pixel_format pixel_format)
{
(void)pixel_format;
- /* Tiling address-generation compatibility level programmed into the
- * DCSURF_TILING_CONFIG.COMPAT_LEVEL register field:
- * 0 - gfx8 bank_height == 1
- * 1 - gfx8 bank_height == 2
- * 2 - gfx9/10/11
- * 5 - gfx addr3
- */
- unsigned int compat_level = 0;
+
enum swizzle_mode_values swizzle_mode;
- switch (info->gfxversion) {
- case DcGfxVersion7:
- case DcGfxVersion8:
-
- REG_UPDATE_8(DCSURF_LEGACY_ADDR_CONFIG,
- LEGACY_NUM_BANKS, info->gfx8.num_banks,
- BANK_WIDTH, info->gfx8.bank_width,
- BANK_HEIGHT, info->gfx8.bank_height,
- MACRO_TILE_ASPECT, info->gfx8.tile_aspect,
- TILE_SPLIT, info->gfx8.tile_split,
- MICRO_TILE_MODE_NEW, info->gfx8.tile_mode,
- PIPE_CONFIG, info->gfx8.pipe_config,
- ARRAY_MODE, info->gfx8.array_mode);
- if (info->gfx8.bank_height == 1) {
- compat_level = 0;
- } else if (info->gfx8.bank_height == 2) {
- compat_level = 1;
- }
- break;
-
- case DcGfxVersion9:
- case DcGfxVersion10:
- case DcGfxVersion11:
- REG_UPDATE_4(DCSURF_ADDR_CONFIG,
- NUM_PIPES, log_2(info->gfx9.num_pipes),
- PIPE_INTERLEAVE, info->gfx9.pipe_interleave,
- MAX_COMPRESSED_FRAGS, log_2(info->gfx9.max_compressed_frags),
- NUM_PKRS, log_2(info->gfx9.num_pkrs));
- REG_UPDATE(DCSURF_TILING_CONFIG, SW_MODE, info->gfx9.swizzle);
- compat_level = 2;
-
- break;
- case DcGfxAddr3:
- swizzle_mode = hubp50_addr3_to_swizzle_mode_mapping(info->gfx_addr3.swizzle);
- REG_UPDATE(DCSURF_TILING_CONFIG, SW_MODE, swizzle_mode);
- compat_level = 5;
- break;
- }
+ ASSERT(info->gfxversion == DcGfxAddr3);
- REG_UPDATE(DCSURF_TILING_CONFIG, COMPAT_LEVEL, compat_level);
+ swizzle_mode = hubp50_addr3_to_swizzle_mode_mapping(info->gfx_addr3.swizzle);
+ REG_UPDATE(DCSURF_TILING_CONFIG, SW_MODE, swizzle_mode);
}
+
static void hubp50_program_pixel_format(
struct hubp *hubp,
enum surface_pixel_format format)
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 24/74] drm/amd/display: Enable SST PCON FRL DSC passthrough
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (22 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 23/74] drm/amd/display: cleanup tiling and address formats Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 25/74] drm/amd/display: Don't let HPD IRQ handler take dc_lock in reset Wayne Lin
` (50 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
From: Fangzhi Zuo <jerry.zuo@amd.com>
[Why]
The SST DSC passthrough to FRL endpoint is not activated yet,
that leads to an FRL sink received DSC_DECODING
instead of DSC_PASSTHROUGH even when the stream selected FRL DSC.
[How]
Make the SST PCON path unconditional and select DSC_PASSTHROUGH when
DSC is enabled for an FRL stream. Preserve the existing default
DSC_DECODING and DSC_DISABLE values for non-FRL enable and disable
operations.
Cc: Ivan Lipski <ivan.lipski@amd.com>
Signed-off-by: Fangzhi Zuo <jerry.zuo@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
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 3067ac5bb0cd..6192c0f6086d 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
@@ -1022,10 +1022,15 @@ bool dm_helpers_dp_write_dsc_enable(
"SST_DSC Send DSC %s to SST RX\n",
enable_dsc ? "enable" : "disable");
} else if (stream->sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
+ if (enable && stream->timing.dsc_cfg.is_frl)
+ enable_dsc = DSC_PASSTHROUGH;
+
ret = dm_helpers_dp_write_dpcd(ctx, stream->link, DP_DSC_ENABLE, &enable_dsc, 1);
drm_dbg_dp(dev,
- "SST_DSC Send DSC %s to DP-HDMI PCON\n",
- enable_dsc ? "enable" : "disable");
+ "SST_DSC Send DSC %s to %s\n",
+ enable_dsc ? "enable" : "disable",
+ stream->timing.dsc_cfg.is_frl ? "HDMI FRL RX" :
+ "DP-HDMI PCON");
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 25/74] drm/amd/display: Don't let HPD IRQ handler take dc_lock in reset
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (23 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 24/74] drm/amd/display: Enable SST PCON FRL DSC passthrough Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 26/74] drm/amd/display: Skip visual confirm color programming when FW owns it Wayne Lin
` (49 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, George Zhang
From: George Zhang <george.zhang@amd.com>
If an HPD event occurs during gpu reset, dm_suspend will take dc_lock.
dm_suspend then calls irq_suspend, which will flush the IRQ workqueue.
This will cause any pending HPD work to try to take dc_lock, causing a
deadlock.
Don't take the lock in the HPD handler if gpu is in reset.
Also fix some KUnit tests that now requires a non-NULL
adev->reset_domain.
Cc: Alex Hung <alex.hung@amd.com>
Signed-off-by: George Zhang <george.zhang@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 10 ++++++----
.../amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c | 6 ++++++
2 files changed, 12 insertions(+), 4 deletions(-)
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 5b4ff079cd55..18060a9ee890 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
@@ -1444,10 +1444,12 @@ STATIC_IFN_KUNIT void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnect
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 (!amdgpu_in_reset(adev))
+ mutex_lock(&adev->dm.dc_lock);
+ dc_exit_ips_for_hw_access(dc);
+ ret = dc_link_detect(aconnector->dc_link, reason);
+ if (!amdgpu_in_reset(adev))
+ mutex_unlock(&adev->dm.dc_lock);
if (ret) {
/* w/a delay for certain panels */
amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c
index c0c913d95aec..105e3ba38290 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c
@@ -2746,6 +2746,9 @@ static struct amdgpu_dm_connector *dm_test_setup_hpd_irq_helper(struct kunit *te
adev = dm_kunit_alloc_adev(test);
mutex_init(&adev->dm.dc_lock);
+ adev->reset_domain = kunit_kzalloc(test, sizeof(*adev->reset_domain),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->reset_domain);
aconn = dm_kunit_alloc_connector(test, adev, NULL);
mutex_init(&aconn->hpd_lock);
@@ -3462,6 +3465,9 @@ static void dm_test_dmub_hpd_callback_hpd_type(struct kunit *test)
adev = dm_kunit_alloc_adev(test);
mutex_init(&adev->dm.dc_lock);
+ adev->reset_domain = kunit_kzalloc(test, sizeof(*adev->reset_domain),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev->reset_domain);
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, dc);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 26/74] drm/amd/display: Skip visual confirm color programming when FW owns it
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (24 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 25/74] drm/amd/display: Don't let HPD IRQ handler take dc_lock in reset Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 27/74] drm/amd/display: Fix Stage Optimizer Parameter Naming Wayne Lin
` (48 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Ashwin Santhosh, Alvin Lee
From: Ashwin Santhosh <Ashwin.Santhosh@amd.com>
why:
For some visual confirm modes DMCUB programs the MPCC background color and
the driver leaves the pipe color all zero to signal that. The driver still
programmed that placeholder on every plane update, which stomped the color
firmware had written. Firmware only repaints across a modeset, so with
visual confirm set to FAMS2 the bar went black after resizing a window and
stayed black until the next mode change. The same stomp applies to the PSR,
FAMS, and REPLAY, which also leave the color to firmware.
how:
Add should_driver_program_visual_confirm, which returns false when visual
confirm is disabled or set to a mode whose color firmware owns. For FAMS2
the driver still paints all pipes grey while FAMS2 is not in use, so
that case checks dc_state_is_fams2_in_use rather than blocking the mode
outright.
Call the helper from dcn10_update_visual_confirm_color, which covers every
caller of the hwss hook, and from hwss_build_fast_sequence, which programs
the color directly and is the only path that bypasses the hook.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Ashwin Santhosh <Ashwin.Santhosh@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 24 ++++++++++++++++++-
.../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 3 +++
.../drm/amd/display/dc/hwss/hw_sequencer.h | 7 ++++++
3 files changed, 33 insertions(+), 1 deletion(-)
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 2384cbf16ee1..32a33d29835e 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
@@ -806,6 +806,28 @@ void get_fams2_visual_confirm_color(
}
}
+bool should_driver_program_visual_confirm(const struct dc *dc, const struct dc_state *context)
+{
+ switch (dc->debug.visual_confirm) {
+ case VISUAL_CONFIRM_DISABLE:
+ return false;
+ /* DMCUB owns the color for these modes. Programming ours stomps what FW
+ * wrote, and FW only repaints across a modeset, so the bar stays stale.
+ */
+ case VISUAL_CONFIRM_PSR:
+ case VISUAL_CONFIRM_FAMS:
+ case VISUAL_CONFIRM_REPLAY:
+ return false;
+ case VISUAL_CONFIRM_FAMS2:
+ /* FW owns the color only while FAMS2 runs. When it is not in use the
+ * driver paints all pipes grey, and FW does not cover that case.
+ */
+ return !dc_state_is_fams2_in_use(dc, context);
+ default:
+ return true;
+ }
+}
+
static bool get_update_dchubp_dpp_flags_status(struct pipe_ctx *pipe)
{
if (!pipe->plane_state) {
@@ -1644,7 +1666,7 @@ void hwss_build_fast_sequence(struct dc *dc,
block_sequence[*num_steps].func = DPP_SET_OUTPUT_TRANSFER_FUNC;
(*num_steps)++;
}
- if (dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE &&
+ if (should_driver_program_visual_confirm(dc, context) &&
dc->hwss.update_visual_confirm_color) {
block_sequence[*num_steps].params.update_visual_confirm_params.mpc = dc->res_pool->mpc;
block_sequence[*num_steps].params.update_visual_confirm_params.color = ¤t_mpc_pipe->visual_confirm_color;
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 46a134301526..79c0f55d11e8 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
@@ -2951,6 +2951,9 @@ void dcn10_update_visual_confirm_color(struct dc *dc,
{
struct mpc *mpc = dc->res_pool->mpc;
+ if (!should_driver_program_visual_confirm(dc, dc->current_state))
+ return;
+
if (mpc->funcs->set_bg_color) {
mpc->funcs->set_bg_color(mpc, &(pipe_ctx->visual_confirm_color), 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 d02834639ab3..a6c743d0cc58 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -2157,6 +2157,13 @@ void get_fams2_visual_confirm_color(
struct pipe_ctx *pipe_ctx,
struct tg_color *color);
+/* False for modes whose confirm color DMCUB programs, so the driver leaves
+ * MPCC_BG alone instead of stomping it. Pass the state being programmed.
+ */
+bool should_driver_program_visual_confirm(
+ const struct dc *dc,
+ const struct dc_state *context);
+
void get_mclk_switch_visual_confirm_color(
struct pipe_ctx *pipe_ctx,
struct tg_color *color);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 27/74] drm/amd/display: Fix Stage Optimizer Parameter Naming
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (25 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 26/74] drm/amd/display: Skip visual confirm color programming when FW owns it Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 28/74] drm/amd/display: Build the dcn401 seamless update in DC2 Wayne Lin
` (47 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Austin Zheng, Dillon Varone
From: Austin Zheng <Austin.Zheng@amd.com>
[Why/How]
Header declaration misspelled optimizer as optimer.
Use stages instead to match implementation.
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Austin Zheng <Austin.Zheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h | 4 ++--
.../display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h | 8 ++++----
.../dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h | 4 ++--
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h
index fdcc42b9bc32..ec41c06a4b1d 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h
@@ -6,8 +6,8 @@
#define __DML2_PMO_DCN5_H__
#include "dml2_internal_shared_types.h"
bool dml2_pmo_dcn5_initialize(struct dml2_pmo_initialize_in_out *in_out);
-int dml2_pmo_dcn5_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo, struct dml2_pmo_stage_optimizer **optimers);
-int dml2_pmo_dcn5_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo, struct dml2_pmo_stage_optimizer **optimers);
+int dml2_pmo_dcn5_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo, struct dml2_pmo_stage_optimizer **stages);
+int dml2_pmo_dcn5_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo, struct dml2_pmo_stage_optimizer **stages);
void dml2_pmo_dcn5_initialize_worksheet(struct dml2_pmo_instance *pmo,
const struct dml2_display_cfg *dispcfg,
struct dml2_optimization_worksheet *worksheet);
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h
index b3b297aca47a..cc29e80a975a 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h
@@ -9,14 +9,14 @@
void dml2_pmo_dcn6_assign_pstate_strategies(struct dml2_pmo_instance *pmo);
bool dml2_pmo_dcn6a_initialize(struct dml2_pmo_initialize_in_out *in_out);
int dml2_pmo_dcn6a_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo,
- struct dml2_pmo_stage_optimizer **optimers);
+ struct dml2_pmo_stage_optimizer **stages);
int dml2_pmo_dcn6a_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo,
- struct dml2_pmo_stage_optimizer **optimers);
+ struct dml2_pmo_stage_optimizer **stages);
bool dml2_pmo_dcn6b_initialize(struct dml2_pmo_initialize_in_out *in_out);
int dml2_pmo_dcn6b_get_ordered_mandatory_stage_optimizers(struct dml2_pmo_instance *pmo,
- struct dml2_pmo_stage_optimizer **optimers);
+ struct dml2_pmo_stage_optimizer **stages);
int dml2_pmo_dcn6b_get_ordered_optional_stages_optimizers(struct dml2_pmo_instance *pmo,
- struct dml2_pmo_stage_optimizer **optimers);
+ struct dml2_pmo_stage_optimizer **stages);
void dml2_pmo_dcn6_convert_worksheet_to_solution(struct dml2_pmo_instance *pmo,
const struct dml2_optimization_worksheet *worksheet,
struct dml2_display_solution *solution);
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 f06affe1fbbb..54ec7ef7cbae 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
@@ -1220,7 +1220,7 @@ struct dml2_pmo_instance {
* return - stage optimizer count
*/
int (*get_ordered_mandatory_stage_optimizers)(struct dml2_pmo_instance *pmo,
- struct dml2_pmo_stage_optimizer **optimers);
+ struct dml2_pmo_stage_optimizer **stages);
/*
* obtain a list of option stage optimizers ordered by priority, Caller must perform stage optimization in the
* same order. Caller must complete stage optimization for all mandatory stage optimizers before performing
@@ -1228,7 +1228,7 @@ struct dml2_pmo_instance {
* return - stage optimizer count
*/
int (*get_ordered_optional_stage_optimizers)(struct dml2_pmo_instance *pmo,
- struct dml2_pmo_stage_optimizer **optimers);
+ struct dml2_pmo_stage_optimizer **stages);
/*
* initialize an optimization worksheet based on the display config passed in.
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 28/74] drm/amd/display: Build the dcn401 seamless update in DC2
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (26 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 27/74] drm/amd/display: Fix Stage Optimizer Parameter Naming Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 29/74] drm/amd/display: Read accelerated mode through a query builder Wayne Lin
` (46 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
With DC2 selected, a full update that adds a plane to a stream has no
DC2 builder. dc_update_planes_and_stream rejects the update, and the
stream stays without a plane. A tail must run on the vsync after the
head, and DC2 has no wait for that vsync.
[How]
DC2 now runs only the head of a commit and rejects a builder that
emits a tail. The update goes through the API shim. DC2 accepts only
a full update on a new context. Add the block calls the plane add
makes, and a dcn401 builder that emits them in one head. The stream
checks reject a virtual signal, so DC2 builds no update yet. The
builder does not call floating-point-only code, and it checks the OTG
master before use.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/dc/bls_builder/bls_builder.h | 3 +-
.../dc/bls_builder/bls_builder_internal.h | 1 +
.../bls_builder/bls_builder_private_types.h | 52 +
.../dc/bls_builder/dcn401_commit_builder.c | 1242 ++++++++++++++++-
.../dc/bls_builder/dcn401_init_builder.c | 2 +
.../display/dc/bls_executor/bls_executor.c | 466 +++++++
drivers/gpu/drm/amd/display/dc/core/dc.c | 15 +-
.../gpu/drm/amd/display/dc/core2/dc_core2.c | 204 ++-
.../amd/display/dc/dc_api_dispatch/api_shim.c | 18 +
.../amd/display/dc/dc_api_dispatch/api_shim.h | 4 +
.../drm/amd/display/dc/hwss/hw_sequencer.h | 89 ++
.../amd/display/dc/inc/dc_core_interface.h | 7 +
12 files changed, 2003 insertions(+), 100 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
index 485e877df5d2..954adc590ed4 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
@@ -12,7 +12,7 @@
#include "bls_builder/dmub_abi.h"
#include "bls_builder/bls_builder_legacy_init.h"
-#define DC2_BLS_MAX_STEPS 64
+#define DC2_BLS_MAX_STEPS 128
enum bls_builder_result {
BLS_BUILD_SUCCESS,
@@ -25,6 +25,7 @@ enum bls_builder_name {
DCN_DECL_INITIALIZATION,
DCN_DECL_INITIALIZATION_LEGACY,
DCN_DECL_NON_SEAMLESS_COMMIT,
+ DCN_DECL_SEAMLESS_UPDATE,
BLS_BUILDER_NAME_COUNT,
};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_internal.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_internal.h
index a641941a2f2f..ebf315e31ad8 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_internal.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_internal.h
@@ -10,3 +10,4 @@
const struct bls_builder *dcn401_bls_builder_create(
enum bls_builder_name name);
const struct bls_builder *dcn401_non_seamless_commit_builder(void);
+const struct bls_builder *dcn401_seamless_update_builder(void);
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
index 025b2e3b62ad..2f3cb1457ebe 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
@@ -31,7 +31,55 @@ struct dcn401_commit_step_meta {
} hubbub_program_arbiter;
struct {
bool safe_to_lower;
+ /*
+ * Set: program the current state's compbuf size, as
+ * dcn401_program_outstanding_updates does. Clear:
+ * program the new state's size.
+ */
+ bool use_current_state_size;
} hubbub_program_compbuf_segments;
+ struct {
+ bool enable;
+ } hubbub_force_pstate_change_control;
+ struct {
+ bool enable;
+ } hubp_clk_cntl;
+ struct {
+ bool power_on;
+ } hubp_pg_control;
+ struct {
+ bool unblank;
+ } hubp_set_blank;
+ struct {
+ bool enable;
+ } dpp_dppclk_control;
+ struct {
+ bool unblank;
+ } opp_set_disp_pattern_generator;
+ struct {
+ /*
+ * Set: program the pipe's hubp_regs.det_size, which
+ * gives the HUBP its DET segments, as dcn401_program_pipe
+ * does. Clear: program size 0, which releases the
+ * HUBP's segments, as the DC1 plane disable does.
+ */
+ bool allocate;
+ } hubp_program_det_segments;
+ struct {
+ /*
+ * Set: program the MALL selection and cursor caching
+ * that dcn401_program_mall_pipe_config_sequence computes
+ * for the new state. Clear: program no MALL and no
+ * cursor caching, as the DC1 plane disable does.
+ */
+ bool from_new_state;
+ } hubp_update_mall_sel;
+ struct {
+ enum mpcc_gamut_remap_id block_id;
+ } mpc_set_gamut_remap_bypass;
+ struct {
+ enum MCM_LUT_ID id;
+ } mpc_program_lut_mode;
struct {
bool state;
} bios_set_scratch_critical_state;
@@ -63,6 +111,10 @@ struct dcn401_commit_step_meta {
bool stream_mode_unchanged;
/* The new state has a stream. */
bool new_state_has_stream;
+ /* dc->optimized_required is clear. */
+ bool optimized_not_required;
+ /* The plane has no address update, or skips the manual trigger. */
+ bool no_manual_trigger;
} skip_if;
};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
index 5fe4a1ec7166..b6208a0ad164 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
@@ -5,12 +5,15 @@
#include "dm_services.h"
#include "core_types.h"
+#include "resource.h"
#include "link_encoder.h"
#include "stream_encoder.h"
#include "clk_mgr.h"
#include "dccg.h"
#include "dc_bios_types.h"
#include "dc_dmub_srv.h"
+#include "custom_float.h"
+#include "dcn32/dcn32_resource.h"
#include "bls_builder/bls_builder_internal.h"
#include "bls_builder/dcn401_init_builder.h"
#include "bls_builder/bls_builder_private_types.h"
@@ -23,18 +26,35 @@
* DCN_DECL_NON_SEAMLESS_COMMIT for one virtual stream without planes.
* The new state has no stream, or one stream with no plane; the current
* state has no stream, or one stream with one plane. A plane is added
- * later by commit_planes, not by this commit.
+ * later by DCN_DECL_SEAMLESS_UPDATE, not by this commit.
*
* The sequence follows legacy_commit_state_hw on dcn401: the same leaf
- * calls in the same order, including the calls DC1 repeats. head_sequence
- * holds everything through the ODM double-buffer and blank waits.
- * tail_sequence is the deferred bandwidth lower, and it runs only after
- * that head has finished.
+ * calls in the same order, including the calls DC1 repeats. Everything is
+ * in head_sequence: the ODM double-buffer and blank waits come before the
+ * bandwidth lower, so no tail is needed.
+ *
+ * DCN_DECL_SEAMLESS_UPDATE adds one plane to that stream. The sequence
+ * follows the full update in commit_planes_for_stream on dcn401, all in
+ * head_sequence: the outstanding updates, prepare_bandwidth and the
+ * pending-update waits, then the locked front-end programming and
+ * post_unlock_program_front_end. The bandwidth lower stays with
+ * dc_post_update_surfaces_to_stream.
+ *
+ * Neither builder emits a tail_sequence. A tail runs only after a vsync,
+ * and Core has no wait-for-vsync imperative yet, so Core rejects a tail.
*/
#define DCN401_DPG_POLL_INTERVAL_US 100
#define DCN401_DPG_POLL_TIMEOUT_US 100000
#define DCN401_STATIC_SCREEN_NUM_FRAMES 2
+#define DCN401_PENDING_UPDATE_POLL_INTERVAL_US 1
+#define DCN401_PENDING_UPDATE_TIMEOUT_US 100000
+#define DCN401_FLIP_PENDING_POLL_INTERVAL_US 100
+#define DCN401_PLANE_ENABLE_DELAY_US 500
+/* 1.0 in the HDR multiplier and MPCC gain format. */
+#define DCN401_UNITY_GAIN 0x1f000
+#define DCN401_MALL_SEL_SS 2
+#define DCN401_CACHE_CURSOR_BYTES 16384
/*
* Each helper takes the same arguments. CHECK_CONDITION logs the reason
@@ -308,6 +328,16 @@ static const struct pipe_ctx *dcn401_checked_otg_master(
new_state), ¬_one_pipe);
}
+/* The OTG master of one state, or NULL when that state is not one pipe. */
+static const struct pipe_ctx *dcn401_otg_of(const struct dc_state *state)
+{
+ bool not_one_pipe = false;
+
+ if (!state)
+ return NULL;
+ return dcn401_state_otg_master(state, ¬_one_pipe);
+}
+
static bool check_one_otg_pipe(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
@@ -457,7 +487,8 @@ static bool check_link_enc_assignment(const struct dc *dc,
pipe->stream->link->link_enc;
}
-static enum bls_builder_result dcn401_check_commit_state_support(
+/* What both builders need of a state's stream, apart from its planes. */
+static enum bls_builder_result dcn401_check_stream_support(
const struct dc *dc, const struct dc_state *current_state,
const struct dc_state *new_state)
{
@@ -480,15 +511,11 @@ static enum bls_builder_result dcn401_check_commit_state_support(
CHECK_CONDITION(no_boot_dpms_writeback,
"seamless boot, dpms, or writeback is set");
CHECK_CONDITION(abm, "ABM level is set");
- CHECK_CONDITION(plane_count, "plane count does not match the state");
CHECK_CONDITION(one_otg_pipe, "stream is not on one pipe");
CHECK_RESOURCE(otg_master_present, "stream has no otg master");
CHECK_CONDITION(odm_combine, "odm is set");
CHECK_CONDITION(mpc_blend, "top or bottom pipe is set");
- CHECK_CONDITION(pipe_plane, "pipe plane does not match the state");
CHECK_RESOURCE(otg_master_blocks, "otg master is missing a block");
- CHECK_RESOURCE(plane_blocks, "plane is missing hubp or dpp");
- CHECK_CONDITION(mpcc_alone, "mpcc is not alone in the opp tree");
CHECK_CONDITION(pipe_audio, "audio is set");
CHECK_CONDITION(pipe_dsc, "dsc is set");
CHECK_CONDITION(pipe_gsl, "gsl is set");
@@ -496,6 +523,25 @@ static enum bls_builder_result dcn401_check_commit_state_support(
return BLS_BUILD_SUCCESS;
}
+static enum bls_builder_result dcn401_check_commit_state_support(
+ const struct dc *dc, const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ enum bls_builder_result result;
+
+ result = dcn401_check_stream_support(dc, current_state, new_state);
+ if (result != BLS_BUILD_SUCCESS ||
+ !dcn401_checked_state(current_state,
+ new_state)->stream_count)
+ return result;
+
+ CHECK_CONDITION(plane_count, "plane count does not match the state");
+ CHECK_CONDITION(pipe_plane, "pipe plane does not match the state");
+ CHECK_RESOURCE(plane_blocks, "plane is missing hubp or dpp");
+ CHECK_CONDITION(mpcc_alone, "mpcc is not alone in the opp tree");
+ return BLS_BUILD_SUCCESS;
+}
+
static bool check_stream_not_kept(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
@@ -546,6 +592,442 @@ static enum bls_builder_result dcn401_check_non_seamless_commit_support(
return BLS_BUILD_SUCCESS;
}
+/* The seamless update keeps the one stream and only adds its plane. */
+static bool check_stream_kept(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ return current_state->stream_count == 1 &&
+ new_state->stream_count == 1 &&
+ current_state->streams[0] == new_state->streams[0];
+}
+
+static bool check_plane_added(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ return !current_state->stream_status[0].plane_count &&
+ new_state->stream_status[0].plane_count == 1;
+}
+
+static bool check_same_otg_master_blocks(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *current_pipe = dcn401_otg_of(current_state);
+ const struct pipe_ctx *new_pipe = dcn401_otg_of(new_state);
+
+ (void)dc;
+ if (!current_pipe || !new_pipe)
+ return false;
+ return current_pipe->stream_res.tg == new_pipe->stream_res.tg &&
+ current_pipe->stream_res.opp == new_pipe->stream_res.opp;
+}
+
+static bool check_new_plane_blocks(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ (void)dc;
+ (void)current_state;
+ return pipe && pipe->plane_state && pipe->plane_res.hubp &&
+ pipe->plane_res.dpp && pipe->plane_res.dpp->funcs;
+}
+
+static bool check_pool_funcs(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return dc->res_pool->funcs && dc->res_pool->hubbub->funcs;
+}
+
+/* DC1 uses the HUBP instance as the MPCC and DPP instance. */
+static bool check_plane_instances(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+ int inst;
+
+ (void)dc;
+ (void)current_state;
+ if (!pipe || !pipe->plane_res.hubp || !pipe->plane_res.dpp)
+ return false;
+ inst = pipe->plane_res.hubp->inst;
+ return inst < MAX_MPCC && pipe->plane_res.mpcc_inst == inst &&
+ pipe->plane_res.dpp->inst == inst;
+}
+
+/* dcn401 programs DET by segments only. */
+static bool check_det_by_segments(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return !dc->res_pool->hubbub->funcs->program_det_size;
+}
+
+/* The update always emits the two triple-buffer calls DC1 makes with it. */
+static bool check_triple_buffer(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return dc->debug.enable_tri_buf;
+}
+
+/* With sanity_checks DC1 adds p-state verification the update lacks. */
+static bool check_no_sanity_checks(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return !dc->debug.sanity_checks;
+}
+
+static bool check_no_pipe_wait(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ return pipe && !pipe->wait_is_required;
+}
+
+/* dcn20_blank_pixel_data does nothing while a test pattern is on. */
+static bool check_no_test_pattern(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_stream_state *stream = new_state->streams[0];
+
+ (void)dc;
+ (void)current_state;
+ return !stream->link->test_pattern_enabled &&
+ stream->test_pattern.type == DP_TEST_PATTERN_VIDEO_MODE;
+}
+
+/* With force_dpg_enable DC1 unblanks to colour squares, not video. */
+static bool check_no_forced_dpg(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ (void)dc;
+ return true;
+}
+
+/* dcn20_blank_pixel_data sets the ABM pipe on unblank when there is one. */
+static bool check_no_abm_pipe(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ return pipe && !pipe->stream_res.abm;
+}
+
+/* enable_plane programs the system aperture when VM is valid. */
+static bool check_no_vm_aperture(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return !dc->vm_pa_config.valid;
+}
+
+/* enable_plane sets the flip interrupt when the plane asks for one. */
+static bool check_no_flip_int(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ return pipe && pipe->plane_state &&
+ !pipe->plane_state->flip_int_enabled;
+}
+
+/* With an empty tree, DC1 removes no MPCC before the insert. */
+static bool check_opp_tree_empty(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+ const struct output_pixel_processor *opp;
+ unsigned int i;
+
+ (void)dc;
+ (void)current_state;
+ if (!pipe)
+ return false;
+ opp = pipe->stream_res.opp;
+ if (!opp || opp->mpc_tree_params.opp_list)
+ return false;
+ for (i = 0; i < MAX_PIPES; i++) {
+ if (opp->mpcc_disconnect_pending[i])
+ return false;
+ }
+ return true;
+}
+
+/* The update bypasses the plane gamut remap. */
+static bool check_no_plane_gamut_remap(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ (void)dc;
+ (void)current_state;
+ return pipe && pipe->plane_state &&
+ !pipe->plane_state->gamut_remap_matrix.enable_remap;
+}
+
+/* The update bypasses the stream gamut remap. */
+static bool check_no_stream_gamut_remap(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ return !new_state->streams[0]->gamut_remap_matrix.enable_remap;
+}
+
+/* The update sets the default output CSC. */
+static bool check_no_output_csc(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ return !new_state->streams[0]->csc_color_matrix.enable_adjustment;
+}
+
+/* The update programs the surface and its address without DCC. */
+static bool check_no_dcc(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ return pipe && pipe->plane_state && !pipe->plane_state->dcc.enable;
+}
+
+/* The update programs a single graphics address, not stereo or video. */
+static bool check_graphics_address(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ return pipe && pipe->plane_state &&
+ pipe->plane_state->address.type == PLN_ADDR_TYPE_GRAPHICS;
+}
+
+/* DC1 programs triple buffering and the flip only for a queued flip. */
+static bool check_no_immediate_flip(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ return pipe && pipe->plane_state &&
+ !pipe->plane_state->flip_immediate;
+}
+
+/*
+ * DC1 translates a distributed-points curve into a LUT while it programs
+ * the plane. The update programs only the predefined and bypass curves.
+ */
+static bool check_no_degamma_curve(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ return pipe && pipe->plane_state &&
+ pipe->plane_state->in_transfer_func.type !=
+ TF_TYPE_DISTRIBUTED_POINTS;
+}
+
+/* dcn32_set_input_transfer_func calls dpp_set_pregam_state if present. */
+static bool check_no_dpp_pregam(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+
+ return pipe && pipe->plane_res.dpp && pipe->plane_res.dpp->funcs &&
+ !pipe->plane_res.dpp->funcs->dpp_set_pregam_state;
+}
+
+/* dcn401_set_mcm_luts then programs every MCM LUT off. */
+static bool check_no_mcm_luts(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+ const union dc_plane_cm_flags *flags;
+
+ (void)dc;
+ (void)current_state;
+ if (!pipe || !pipe->plane_state)
+ return false;
+ flags = &pipe->plane_state->cm.flags;
+ return !flags->bits.blend_enable && !flags->bits.shaper_enable &&
+ !flags->bits.lut3d_enable && !flags->bits.rmcm_enable;
+}
+
+static bool check_no_shaper_curve(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_transfer_func *shaper = new_state->streams[0]->func_shaper;
+
+ (void)dc;
+ (void)current_state;
+ return !shaper || shaper->type != TF_TYPE_DISTRIBUTED_POINTS;
+}
+
+static bool check_no_stream_3dlut(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_3dlut *lut3d = new_state->streams[0]->lut3d_func;
+
+ (void)dc;
+ (void)current_state;
+ return !lut3d || !lut3d->state.bits.initialized;
+}
+
+static bool check_no_regamma_curve(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ return new_state->streams[0]->out_transfer_func.type !=
+ TF_TYPE_DISTRIBUTED_POINTS;
+}
+
+static bool check_no_cursor(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ return !new_state->streams[0]->cursor_attributes.address.quad_part;
+}
+
+static bool check_no_dsc_change(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ return !new_state->streams[0]->update_flags.bits.dsc_changed;
+}
+
+/*
+ * The checks follow the update in the order it programs the pipe. Each one
+ * rejects a state for which DC1 would make a call the update does not emit.
+ */
+static enum bls_builder_result dcn401_check_seamless_update_support(
+ const struct dc *dc, const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ enum bls_builder_result result;
+
+ if (!dc || !current_state || !new_state)
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
+
+ CHECK_RESOURCE(dc_resources, "dc resources are missing");
+ CHECK_CONDITION(fams2_debug, "FAMS2 debug is enabled");
+ CHECK_CONDITION(cursor_offload, "cursor offload is enabled");
+ CHECK_CONDITION(dc_mode_softmax, "dc mode softmax is enabled");
+ CHECK_CONDITION(visual_confirm, "visual confirm, dpg, hblank borrow, or frame sync is set");
+ CHECK_CONDITION(dsc_power_gate, "DSC power gate is enabled");
+
+ result = dcn401_check_stream_support(dc, current_state, NULL);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+ result = dcn401_check_stream_support(dc, NULL, new_state);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+
+ /* The transition: one stream, kept, gains one plane on its OTG. */
+ CHECK_CONDITION(stream_kept, "stream is not kept across the update");
+ CHECK_CONDITION(plane_added, "update does not add exactly one plane");
+ CHECK_CONDITION(same_otg_master_blocks, "otg or opp changes");
+ CHECK_RESOURCE(new_plane_blocks, "plane is missing hubp or dpp");
+ CHECK_RESOURCE(pool_funcs, "resource pool or hubbub has no funcs");
+ CHECK_CONDITION(plane_instances, "mpcc or dpp is not the hubp instance");
+ CHECK_CONDITION(det_by_segments, "hubbub programs det size");
+
+ /* Under the lock, before the pipe is programmed. */
+ CHECK_CONDITION(triple_buffer, "triple buffer is disabled");
+ CHECK_CONDITION(no_sanity_checks, "sanity checks are enabled");
+ CHECK_CONDITION(no_pipe_wait, "pipe wait is required");
+
+ /* Unblank. */
+ CHECK_CONDITION(no_test_pattern, "test pattern is set");
+ CHECK_CONDITION(no_forced_dpg, "dpg is forced");
+ CHECK_CONDITION(no_abm_pipe, "abm is set");
+
+ /* enable_plane. */
+ CHECK_CONDITION(no_vm_aperture, "vm system aperture is valid");
+ CHECK_CONDITION(no_flip_int, "flip interrupt is enabled");
+
+ /* update_dchubp_dpp. */
+ CHECK_CONDITION(opp_tree_empty, "opp tree is not empty");
+ CHECK_CONDITION(no_plane_gamut_remap, "plane gamut remap is set");
+ CHECK_CONDITION(no_stream_gamut_remap, "stream gamut remap is set");
+ CHECK_CONDITION(no_output_csc, "output csc is set");
+ CHECK_CONDITION(no_dcc, "dcc is enabled");
+ CHECK_CONDITION(graphics_address, "address is not graphics");
+ CHECK_CONDITION(no_immediate_flip, "flip is immediate");
+
+ /* Input, MCM and output transfer functions. */
+ CHECK_CONDITION(no_degamma_curve, "plane degamma is distributed points");
+ CHECK_CONDITION(no_dpp_pregam, "dpp has a pregam state");
+ CHECK_CONDITION(no_mcm_luts, "an mcm lut is enabled");
+ CHECK_CONDITION(no_shaper_curve, "shaper is distributed points");
+ CHECK_CONDITION(no_stream_3dlut, "stream 3dlut is set");
+ CHECK_CONDITION(no_regamma_curve, "regamma is distributed points");
+
+ /* Not programmed by this update. */
+ CHECK_CONDITION(no_cursor, "cursor is set");
+ CHECK_CONDITION(no_dsc_change, "dsc changes");
+ return BLS_BUILD_SUCCESS;
+}
+
static struct pipe_ctx *get_otg_master_from_state_by_stream(
struct dc_state *state, const struct dc_stream_state *stream)
{
@@ -681,7 +1163,7 @@ static const struct dcn401_commit_step
* the head's double buffering. legacy_commit_state_hw sets up stereo for
* a mode-changed stream, then dc_enable_stereo sets it up again for every
* stream; dcn10_setup_stereo with no 3D format is the two STEREO calls.
- * The deferred tail starts only after these waits.
+ * The bandwidth lower starts only after these waits.
*/
static const struct dcn401_commit_step
bls_head_per_stream_per_otg_master_pipe_new_front_end[] = {
@@ -710,7 +1192,7 @@ static const struct dcn401_commit_step
* Net effect: release the forced p-state when no OTG master did, then
* dcn401_optimize_bandwidth without FAMS2, SubVP or DC-mode softmax.
*/
-static const struct dcn401_commit_step bls_tail_per_state_new[] = {
+static const struct dcn401_commit_step bls_head_per_state_new_postamble[] = {
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
.meta.skip_if.new_state_has_stream = true },
{ .func = HUBBUB_PROGRAM_WATERMARKS,
@@ -728,21 +1210,187 @@ static const struct dcn401_commit_step bls_tail_per_state_new[] = {
* state, and never one in both.
*/
static_assert(ARRAY_SIZE(bls_head_per_state_new) +
- ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_current) <=
+ ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_current) +
+ ARRAY_SIZE(bls_head_per_state_new_postamble) <=
DC2_BLS_MAX_STEPS,
"dcn401 commit stream-off head exceeds DC2_BLS_MAX_STEPS");
static_assert(ARRAY_SIZE(bls_head_per_state_new) +
ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_new) +
ARRAY_SIZE(
- bls_head_per_stream_per_otg_master_pipe_new_front_end) <=
+ bls_head_per_stream_per_otg_master_pipe_new_front_end) +
+ ARRAY_SIZE(bls_head_per_state_new_postamble) <=
DC2_BLS_MAX_STEPS,
"dcn401 commit enable head exceeds DC2_BLS_MAX_STEPS");
-static_assert(ARRAY_SIZE(bls_tail_per_state_new) <= DC2_BLS_MAX_STEPS,
- "dcn401 commit tail sequence exceeds DC2_BLS_MAX_STEPS");
+
+/*
+ * Net effect: hwss_process_outstanding_hw_updates on the current state,
+ * which commit_planes_for_stream runs while optimized_required is set.
+ * The OTG master has no plane yet, so no MPCC disconnect is pending.
+ */
+static const struct dcn401_commit_step
+ bls_update_head_per_stream_per_otg_master_pipe_preamble[] = {
+ { .func = TG_WAIT_FOR_ALL_PENDING_UPDATES,
+ .meta.skip_if.optimized_not_required = true },
+ { .func = TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR,
+ .meta.skip_if.optimized_not_required = true },
+ { .func = OPP_WAIT_FOR_BLANK_COMPLETE,
+ .meta.skip_if.optimized_not_required = true },
+};
+
+/* Net effect: dcn401_program_outstanding_updates on the current state. */
+static const struct dcn401_commit_step bls_update_head_global_preamble[] = {
+ { .func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS,
+ .meta.hubbub_program_compbuf_segments.safe_to_lower = true,
+ .meta.hubbub_program_compbuf_segments.use_current_state_size = true,
+ .meta.skip_if.optimized_not_required = true },
+};
+
+/*
+ * Net effect: hwss_wait_for_outstanding_hw_updates on the current state,
+ * which a full update runs before it takes the pipe lock.
+ */
+static const struct dcn401_commit_step
+ bls_update_head_per_stream_per_otg_master_pipe_current_pending[] = {
+ { .func = TG_WAIT_FOR_ALL_PENDING_UPDATES },
+ { .func = TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR },
+ { .func = OPP_WAIT_FOR_BLANK_COMPLETE },
+};
+
+/*
+ * After the waits, the head adds the plane. Each table runs once per
+ * stream, on the OTG master of the new state, and is named for the DC1
+ * call it replaces in commit_planes_for_stream and dcn401_program_pipe.
+ *
+ * Pipe lock: the two triple-buffer offs, from commit_planes_for_stream and
+ * dcn401_program_front_end_for_ctx, then the forced p-state and delay for
+ * the first HUBP.
+ */
+static const struct dcn401_commit_step bls_update_head_new_lock[] = {
+ { .func = TG_LOCK, .meta.tg_lock.lock = true },
+ { .func = HUBP_PROGRAM_TRIPLEBUFFER },
+ { .func = HUBP_PROGRAM_TRIPLEBUFFER },
+ { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
+ .meta.hubbub_force_pstate_change_control.enable = true },
+ { .func = DC_UDELAY },
+};
+
+/* blank_pixel_data (unblank), dcn401_program_tg and update_odm. */
+static const struct dcn401_commit_step bls_update_head_new_stream[] = {
+ { .func = OPP_SET_DISP_PATTERN_GENERATOR,
+ .meta.opp_set_disp_pattern_generator.unblank = true },
+ { .func = TG_PROGRAM_GLOBAL_SYNC },
+ { .func = TG_WAIT_FOR_STATE },
+ { .func = TG_SET_VTG_PARAMS },
+ { .func = TG_SETUP_VERTICAL_INTERRUPT2 },
+ { .func = OPTC_SET_ODM_BYPASS },
+ { .func = OPP_PIPE_CLOCK_CONTROL },
+ { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
+};
+
+/* enable_plane, then the watermark propagate and the DET segments. */
+static const struct dcn401_commit_step bls_update_head_new_enable_plane[] = {
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true },
+ { .func = HUBP_PG_CONTROL, .meta.hubp_pg_control.power_on = true },
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false },
+ { .func = HUBP_CLK_CNTL, .meta.hubp_clk_cntl.enable = true },
+ { .func = HUBP_INIT },
+ { .func = OPP_PIPE_CLOCK_CONTROL },
+ { .func = HUBBUB_FORCE_WM_PROPAGATE_TO_PIPES },
+ { .func = HUBP_PROGRAM_DET_SEGMENTS,
+ .meta.hubp_program_det_segments.allocate = true },
+};
+
+/* update_dchubp_dpp, with the MPC insert from dcn401_update_mpcc. */
+static const struct dcn401_commit_step
+ bls_update_head_new_update_dchubp_dpp[] = {
+ { .func = DPP_DPPCLK_CONTROL, .meta.dpp_dppclk_control.enable = true },
+ { .func = DCCG_UPDATE_DPP_DTO },
+ { .func = HUBP_VTG_SEL },
+ { .func = HUBP_SETUP2 },
+ { .func = HUBP_SET_UNBOUNDED_REQUESTING },
+ { .func = HUBP_SETUP_INTERDEPENDENT2 },
+ { .func = DPP_SETUP_DPP },
+ { .func = DPP_SET_CURSOR_MATRIX },
+ { .func = DPP_PROGRAM_BIAS_AND_SCALE },
+ /* insert_plane programs the blending; no update_blending follows. */
+ { .func = MPC_INSERT_PLANE },
+ { .func = MPC_UPDATE_VISUAL_CONFIRM },
+ { .func = DPP_SET_SCALER },
+ { .func = HUBP_MEM_PROGRAM_VIEWPORT },
+ { .func = HUBP_PROGRAM_MCACHE_ID },
+ { .func = MPC_SET_GAMUT_REMAP_BYPASS,
+ .meta.mpc_set_gamut_remap_bypass.block_id =
+ MPCC_MCM_FIRST_GAMUT_REMAP },
+ { .func = MPC_SET_GAMUT_REMAP_BYPASS,
+ .meta.mpc_set_gamut_remap_bypass.block_id =
+ MPCC_MCM_SECOND_GAMUT_REMAP },
+ { .func = MPC_SET_GAMUT_REMAP_BYPASS,
+ .meta.mpc_set_gamut_remap_bypass.block_id = MPCC_OGAM_GAMUT_REMAP },
+ { .func = MPC_POWER_ON_MPC_MEM_PWR },
+ { .func = MPC_SET_OCSC_DEFAULT },
+ { .func = HUBP_PROGRAM_SURFACE_CONFIG },
+ { .func = HUBP_UPDATE_PLANE_ADDR },
+ { .func = HUBP_SET_BLANK, .meta.hubp_set_blank.unblank = true },
+};
+
+/*
+ * set_hdr_multiplier, the input and MCM transfer functions, the output
+ * transfer function and the OPP format.
+ */
+static const struct dcn401_commit_step
+ bls_update_head_new_transfer_funcs[] = {
+ { .func = DPP_SET_HDR_MULTIPLIER },
+ { .func = DPP_SET_PRE_DEGAM },
+ { .func = DPP_PROGRAM_GAMCOR_LUT },
+ { .func = MPC_SET_MOVABLE_CM_LOCATION },
+ { .func = MPC_PROGRAM_LUT_MODE,
+ .meta.mpc_program_lut_mode.id = MCM_LUT_1DLUT },
+ { .func = MPC_PROGRAM_LUT_MODE,
+ .meta.mpc_program_lut_mode.id = MCM_LUT_SHAPER },
+ { .func = MPC_PROGRAM_LUT_MODE,
+ .meta.mpc_program_lut_mode.id = MCM_LUT_3DLUT },
+ { .func = HUBP_PROGRAM_3DLUT_FL_CONFIG },
+ { .func = HUBP_SET_3DLUT_FL },
+ { .func = MPC_SET_OUTPUT_GAMMA },
+ { .func = OPP_SET_DYN_EXPANSION },
+ { .func = OPP_PROGRAM_FMT },
+};
+
+/*
+ * The pipe unlock, then dcn401_post_unlock_program_front_end: the
+ * flip-pending wait, the p-state release and the MALL selection. Last,
+ * the manual trigger.
+ */
+static const struct dcn401_commit_step bls_update_head_new_unlock[] = {
+ { .func = TG_LOCK, .meta.tg_lock.lock = false },
+ { .func = HUBP_WAIT_FLIP_PENDING },
+ { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
+ { .func = HUBP_UPDATE_MALL_SEL,
+ .meta.hubp_update_mall_sel.from_new_state = true },
+ { .func = OPTC_PROGRAM_MANUAL_TRIGGER,
+ .meta.skip_if.no_manual_trigger = true },
+};
+
+/* The support check allows one stream, kept across the update. */
+static_assert(
+ ARRAY_SIZE(
+ bls_update_head_per_stream_per_otg_master_pipe_preamble) +
+ ARRAY_SIZE(bls_update_head_global_preamble) +
+ ARRAY_SIZE(bls_head_per_state_new) +
+ ARRAY_SIZE(
+ bls_update_head_per_stream_per_otg_master_pipe_current_pending) +
+ ARRAY_SIZE(bls_update_head_new_lock) +
+ ARRAY_SIZE(bls_update_head_new_stream) +
+ ARRAY_SIZE(bls_update_head_new_enable_plane) +
+ ARRAY_SIZE(bls_update_head_new_update_dchubp_dpp) +
+ ARRAY_SIZE(bls_update_head_new_transfer_funcs) +
+ ARRAY_SIZE(bls_update_head_new_unlock) <= DC2_BLS_MAX_STEPS,
+ "dcn401 seamless update head exceeds DC2_BLS_MAX_STEPS");
/* pipe is NULL only for the bandwidth and p-state steps. */
struct dcn401_commit_step_args {
struct dc *dc;
+ struct dc_state *current_state;
struct dc_state *new_state;
struct pipe_ctx *pipe;
};
@@ -787,15 +1435,19 @@ static uint32_t dcn401_early_cntl(const struct pipe_ctx *pipe)
return early_cntl ? early_cntl : lane_count;
}
-/* dcn20_blank_pixel_data(blank): sRGB black over the whole OTG. */
+/*
+ * dcn20_blank_pixel_data: sRGB black over the whole OTG, or the video
+ * on unblank.
+ */
static void dcn401_opp_blank_params(const struct dc *dc,
- const struct pipe_ctx *pipe,
+ const struct pipe_ctx *pipe, bool unblank,
struct opp_set_disp_pattern_generator_params *p)
{
const struct dc_crtc_timing *timing = &pipe->stream->timing;
p->opp = pipe->stream_res.opp;
- p->test_pattern = CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR;
+ p->test_pattern = unblank ? CONTROLLER_DP_TEST_PATTERN_VIDEOMODE :
+ CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR;
p->color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED;
p->color_depth = timing->display_color_depth;
p->use_solid_color = true;
@@ -848,6 +1500,111 @@ static void dcn401_update_clocks_params(const struct dc *dc,
context->bw_ctx.bw.dcn.clk.p_state_change_support;
}
+/* dcn401_update_mpcc_sequence's blending for the plane. */
+static void dcn401_blnd_cfg(const struct dc_plane_state *plane,
+ struct mpcc_blnd_cfg *cfg)
+{
+ cfg->overlap_only = false;
+ cfg->global_gain = 0xff;
+ if (plane->per_pixel_alpha) {
+ cfg->pre_multiplied_alpha = plane->pre_multiplied_alpha;
+ if (plane->global_alpha) {
+ cfg->alpha_mode =
+ MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA_COMBINED_GLOBAL_GAIN;
+ cfg->global_gain = plane->global_alpha_value;
+ } else {
+ cfg->alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA;
+ }
+ } else {
+ cfg->pre_multiplied_alpha = false;
+ cfg->alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA;
+ }
+ cfg->global_alpha = plane->global_alpha ?
+ plane->global_alpha_value : 0xff;
+ cfg->background_color_bpc = 4;
+ cfg->bottom_gain_mode = 0;
+ cfg->top_gain = DCN401_UNITY_GAIN;
+ cfg->bottom_inside_gain = DCN401_UNITY_GAIN;
+ cfg->bottom_outside_gain = DCN401_UNITY_GAIN;
+ if (plane->format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA)
+ cfg->pre_multiplied_alpha = false;
+}
+
+/* dcn401_set_hdr_multiplier_sequence: 1.0 unless the plane sets one. */
+static uint32_t dcn401_hdr_mult(const struct dc_plane_state *plane)
+{
+ struct custom_float_format fmt = {
+ .exponenta_bits = 6,
+ .mantissa_bits = 12,
+ .sign = true,
+ };
+ uint32_t hw_mult = DCN401_UNITY_GAIN;
+
+ if (!dc_fixpt_eq(plane->hdr_mult, dc_fixpt_from_int(0)) &&
+ !convert_to_custom_float_format(plane->hdr_mult, &fmt,
+ &hw_mult))
+ hw_mult = DCN401_UNITY_GAIN;
+ return hw_mult;
+}
+
+/* dcn401_program_mall_pipe_config_sequence for one non-phantom pipe. */
+static void dcn401_mall_sel_params(struct dc *dc, struct dc_state *state,
+ struct pipe_ctx *pipe, struct hubp_update_mall_sel_params *p)
+{
+ const struct hubp *hubp = pipe->plane_res.hubp;
+ const struct dc_plane_address *address = &pipe->plane_state->address;
+ int cursor_size = hubp->curs_attr.pitch * hubp->curs_attr.height;
+ uint32_t mall_ss_size_bytes;
+ uint32_t num_ways = 0;
+
+ switch (hubp->curs_attr.color_format) {
+ case CURSOR_MODE_MONO:
+ cursor_size /= 2;
+ break;
+ case CURSOR_MODE_COLOR_1BIT_AND:
+ case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA:
+ case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA:
+ cursor_size *= 4;
+ break;
+ default:
+ cursor_size *= 8;
+ break;
+ }
+
+ /*
+ * dcn401_calculate_cab_allocation also adds the cursor mall bytes.
+ * This builder rejects a cursor, so those bytes are zero. The helper
+ * that computes them is built only with CONFIG_DRM_AMD_DC_FP.
+ */
+ mall_ss_size_bytes = state->bw_ctx.bw.dcn.mall_ss_size_bytes;
+ if (dc->debug.force_mall_ss_num_ways > 0)
+ num_ways = dc->debug.force_mall_ss_num_ways;
+ else if (dc->res_pool->funcs->calculate_mall_ways_from_bytes)
+ num_ways = dc->res_pool->funcs->calculate_mall_ways_from_bytes(
+ dc, mall_ss_size_bytes);
+
+ p->hubp = pipe->plane_res.hubp;
+ p->cache_cursor = cursor_size > DCN401_CACHE_CURSOR_BYTES;
+ p->mall_sel = num_ways <= dc->caps.cache_num_ways &&
+ pipe->stream->link->psr_settings.psr_version ==
+ DC_PSR_VERSION_UNSUPPORTED &&
+ address->type != PLN_ADDR_TYPE_GRPH_STEREO &&
+ !address->tmz_surface ? DCN401_MALL_SEL_SS : 0;
+}
+
+/* dcn401_set_mcm_luts clears the HUBP 3DLUT fast load with this. */
+static const struct dc_3dlut_dma dcn401_3dlut_dma_off = {0};
+
+/* dcn401_set_mcm_luts sizes the off 3DLUT by the plane's LUT. */
+static enum dc_cm_lut_size dcn401_lut_mode_size(
+ const struct dc_plane_state *plane, enum MCM_LUT_ID id)
+{
+ if (id != MCM_LUT_3DLUT)
+ return CM_LUT_SIZE_NONE;
+ return plane->cm.lut3d_func.lut_3d.use_tetrahedral_9 ?
+ CM_LUT_SIZE_999 : CM_LUT_SIZE_171717;
+}
+
/* Writes one entry; build sized every sequence before adding to it. */
static void dcn401_add_step(struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *args,
@@ -858,11 +1615,65 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
union block_sequence_params *p = &entry->params;
struct dc *dc = args->dc;
struct dcn_bw_output *bw = &args->new_state->bw_ctx.bw.dcn;
- struct pipe_ctx *pipe = args->pipe;
+ struct pipe_ctx *pipe;
+ struct dpp *dpp;
+ struct dc_plane_state *plane;
const struct dcn401_commit_step_meta *m = &step->meta;
memset(entry, 0, sizeof(*entry));
entry->func = step->func;
+ /*
+ * Bandwidth and p-state steps have no pipe. Handle them before the
+ * pipe is used, then every later case can use the pipe directly.
+ */
+ if (!args->pipe) {
+ switch (step->func) {
+ case CLK_MGR_UPDATE_CLOCKS_FOR_STATE:
+ dcn401_update_clocks_params(dc, args->new_state,
+ m->clk_mgr_update_clocks_for_state
+ .safe_to_lower,
+ &p->clk_mgr_update_clocks_for_state_params);
+ return;
+ case HUBBUB_PROGRAM_WATERMARKS:
+ p->hubbub_program_watermarks_params =
+ hwss_prepare_hubbub_program_watermarks(dc,
+ dc->res_pool->hubbub, &bw->watermarks,
+ dc->res_pool->ref_clocks.dchub_ref_clock_inKhz /
+ 1000,
+ m->hubbub_program_watermarks.safe_to_lower);
+ return;
+ case HUBBUB_PROGRAM_ARBITER:
+ p->hubbub_program_arbiter_params =
+ hwss_prepare_hubbub_program_arbiter(
+ dc->res_pool->hubbub, &bw->arb_regs,
+ m->hubbub_program_arbiter.safe_to_lower);
+ return;
+ case HUBBUB_PROGRAM_COMPBUF_SEGMENTS:
+ p->hubbub_program_compbuf_segments_params.hubbub =
+ dc->res_pool->hubbub;
+ p->hubbub_program_compbuf_segments_params.compbuf_size =
+ m->hubbub_program_compbuf_segments
+ .use_current_state_size ?
+ args->current_state->bw_ctx.bw.dcn.arb_regs
+ .compbuf_size : bw->arb_regs.compbuf_size;
+ p->hubbub_program_compbuf_segments_params.safe_to_lower =
+ m->hubbub_program_compbuf_segments
+ .safe_to_lower;
+ return;
+ case HUBBUB_FORCE_PSTATE_CHANGE_CONTROL:
+ p->hubbub_force_pstate_change_control_params.hubbub =
+ dc->res_pool->hubbub;
+ p->hubbub_force_pstate_change_control_params.enable =
+ m->hubbub_force_pstate_change_control.enable;
+ return;
+ default:
+ (*sequence->num_steps)--;
+ return;
+ }
+ }
+ pipe = args->pipe;
+ dpp = pipe->plane_res.dpp;
+ plane = pipe->plane_state;
switch (step->func) {
case CLK_MGR_UPDATE_CLOCKS_FOR_STATE:
dcn401_update_clocks_params(dc, args->new_state,
@@ -888,7 +1699,10 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
p->hubbub_program_compbuf_segments_params.hubbub =
dc->res_pool->hubbub;
p->hubbub_program_compbuf_segments_params.compbuf_size =
- bw->arb_regs.compbuf_size;
+ m->hubbub_program_compbuf_segments
+ .use_current_state_size ?
+ args->current_state->bw_ctx.bw.dcn.arb_regs
+ .compbuf_size : bw->arb_regs.compbuf_size;
p->hubbub_program_compbuf_segments_params.safe_to_lower =
m->hubbub_program_compbuf_segments
.safe_to_lower;
@@ -896,6 +1710,8 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
case HUBBUB_FORCE_PSTATE_CHANGE_CONTROL:
p->hubbub_force_pstate_change_control_params.hubbub =
dc->res_pool->hubbub;
+ p->hubbub_force_pstate_change_control_params.enable =
+ m->hubbub_force_pstate_change_control.enable;
break;
case BIOS_SET_SCRATCH_CRITICAL_STATE:
p->bios_set_scratch_critical_state_params.dcb =
@@ -996,6 +1812,7 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
break;
case OPP_SET_DISP_PATTERN_GENERATOR:
dcn401_opp_blank_params(dc, pipe,
+ m->opp_set_disp_pattern_generator.unblank,
&p->opp_set_disp_pattern_generator_params);
break;
case OPP_WAIT_FOR_BLANK_COMPLETE:
@@ -1084,6 +1901,9 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
p->program_det_segments_params.hubbub = dc->res_pool->hubbub;
p->program_det_segments_params.hubp_inst =
pipe->plane_res.hubp->inst;
+ if (m->hubp_program_det_segments.allocate)
+ p->program_det_segments_params.det_size =
+ pipe->hubp_regs.det_size;
break;
case MPC_REMOVE_MPCC:
p->mpc_remove_mpcc_params.mpc = dc->res_pool->mpc;
@@ -1100,19 +1920,27 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
break;
case HUBP_SET_BLANK:
p->hubp_set_blank_params.hubp = pipe->plane_res.hubp;
- p->hubp_set_blank_params.blank = true;
+ p->hubp_set_blank_params.blank = !m->hubp_set_blank.unblank;
break;
case HUBP_UPDATE_MALL_SEL:
- p->hubp_update_mall_sel_params.hubp = pipe->plane_res.hubp;
+ if (m->hubp_update_mall_sel.from_new_state)
+ dcn401_mall_sel_params(dc, args->new_state, pipe,
+ &p->hubp_update_mall_sel_params);
+ else
+ p->hubp_update_mall_sel_params.hubp =
+ pipe->plane_res.hubp;
break;
case HUBP_SET_FLIP_CONTROL_GSL:
p->set_flip_control_gsl_params.hubp = pipe->plane_res.hubp;
break;
case HUBP_CLK_CNTL:
p->hubp_clk_cntl_params.hubp = pipe->plane_res.hubp;
+ p->hubp_clk_cntl_params.enable = m->hubp_clk_cntl.enable;
break;
case DPP_DPPCLK_CONTROL:
- p->dpp_dppclk_control_params.dpp = pipe->plane_res.dpp;
+ p->dpp_dppclk_control_params.dpp = dpp;
+ p->dpp_dppclk_control_params.enable =
+ m->dpp_dppclk_control.enable;
break;
case DC_IP_REQUEST_CNTL:
p->dc_ip_request_cntl_params.hws = dc->hwseq;
@@ -1123,13 +1951,240 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
p->hubp_pg_control_params.hws = dc->hwseq;
p->hubp_pg_control_params.hubp_inst =
pipe->plane_res.hubp->inst;
- p->hubp_pg_control_params.power_on = false;
+ p->hubp_pg_control_params.power_on =
+ m->hubp_pg_control.power_on;
break;
case HUBP_RESET:
p->hubp_reset_params.hubp = pipe->plane_res.hubp;
break;
case DPP_RESET:
- p->dpp_reset_params.dpp = pipe->plane_res.dpp;
+ p->dpp_reset_params.dpp = dpp;
+ break;
+ case TG_WAIT_FOR_ALL_PENDING_UPDATES:
+ p->tg_wait_for_all_pending_updates_params.tg =
+ pipe->stream_res.tg;
+ p->tg_wait_for_all_pending_updates_params.include_pipe_update =
+ plane != NULL;
+ p->tg_wait_for_all_pending_updates_params.polling_interval_us =
+ DCN401_PENDING_UPDATE_POLL_INTERVAL_US;
+ p->tg_wait_for_all_pending_updates_params.timeout_us =
+ DCN401_PENDING_UPDATE_TIMEOUT_US;
+ break;
+ case HUBP_PROGRAM_TRIPLEBUFFER:
+ p->program_triplebuffer_params.hubp = pipe->plane_res.hubp;
+ p->program_triplebuffer_params.enableTripleBuffer =
+ plane->triplebuffer_flips;
+ break;
+ case DC_UDELAY:
+ p->dc_udelay_params.us = DCN401_PLANE_ENABLE_DELAY_US;
+ break;
+ case TG_PROGRAM_GLOBAL_SYNC:
+ p->tg_program_global_sync_params.tg = pipe->stream_res.tg;
+ p->tg_program_global_sync_params.vready_offset =
+ pipe->global_sync.dcn4x.vready_offset_pixels;
+ p->tg_program_global_sync_params.vstartup_lines =
+ pipe->global_sync.dcn4x.vstartup_lines;
+ p->tg_program_global_sync_params.vupdate_offset_pixels =
+ pipe->global_sync.dcn4x.vupdate_offset_pixels;
+ p->tg_program_global_sync_params.vupdate_vupdate_width_pixels =
+ pipe->global_sync.dcn4x
+ .vupdate_vupdate_width_pixels;
+ p->tg_program_global_sync_params.pstate_keepout_start_lines =
+ pipe->global_sync.dcn4x.pstate_keepout_start_lines;
+ break;
+ case TG_WAIT_FOR_STATE:
+ p->tg_wait_for_state_params.tg = pipe->stream_res.tg;
+ p->tg_wait_for_state_params.state = CRTC_STATE_VACTIVE;
+ break;
+ case TG_SET_VTG_PARAMS:
+ p->tg_set_vtg_params_params.tg = pipe->stream_res.tg;
+ p->tg_set_vtg_params_params.timing = &pipe->stream->timing;
+ p->tg_set_vtg_params_params.program_fp2 = true;
+ break;
+ case HUBP_INIT:
+ p->hubp_init_params.hubp = pipe->plane_res.hubp;
+ break;
+ case HUBBUB_FORCE_WM_PROPAGATE_TO_PIPES:
+ p->hubbub_force_wm_propagate_to_pipes_params.hubbub =
+ dc->res_pool->hubbub;
+ break;
+ case DCCG_UPDATE_DPP_DTO:
+ p->dccg_update_dpp_dto_params.dccg = dc->res_pool->dccg;
+ p->dccg_update_dpp_dto_params.dpp_inst = dpp->inst;
+ p->dccg_update_dpp_dto_params.dppclk_khz =
+ pipe->plane_res.bw.dppclk_khz;
+ break;
+ case HUBP_VTG_SEL:
+ p->hubp_vtg_sel_params.hubp = pipe->plane_res.hubp;
+ p->hubp_vtg_sel_params.otg_inst = pipe->stream_res.tg->inst;
+ break;
+ case HUBP_SETUP2:
+ p->hubp_setup2_params.hubp = pipe->plane_res.hubp;
+ p->hubp_setup2_params.hubp_regs = &pipe->hubp_regs;
+ p->hubp_setup2_params.global_sync = &pipe->global_sync;
+ p->hubp_setup2_params.timing = &pipe->stream->timing;
+ break;
+ case HUBP_SET_UNBOUNDED_REQUESTING:
+ p->hubp_set_unbounded_requesting_params.hubp = pipe->plane_res.hubp;
+ p->hubp_set_unbounded_requesting_params.unbounded_req =
+ pipe->unbounded_req;
+ break;
+ case HUBP_SETUP_INTERDEPENDENT2:
+ p->hubp_setup_interdependent2_params.hubp = pipe->plane_res.hubp;
+ p->hubp_setup_interdependent2_params.hubp_regs =
+ &pipe->hubp_regs;
+ break;
+ case DPP_SETUP_DPP:
+ p->setup_dpp_params.dpp = dpp;
+ p->setup_dpp_params.format = plane->format;
+ p->setup_dpp_params.input_csc_color_matrix =
+ plane->input_csc_color_matrix;
+ p->setup_dpp_params.color_space = plane->color_space;
+ p->setup_dpp_params.upsp_mode = resource_is_upsp_required(
+ plane->format, plane->scaling_linearity);
+ break;
+ case DPP_SET_CURSOR_MATRIX:
+ p->dpp_set_cursor_matrix_params.dpp = dpp;
+ p->dpp_set_cursor_matrix_params.color_space =
+ plane->color_space;
+ p->dpp_set_cursor_matrix_params.cursor_csc_color_matrix =
+ &plane->cursor_csc_color_matrix;
+ break;
+ case DPP_PROGRAM_BIAS_AND_SCALE:
+ p->program_bias_and_scale_params.dpp = dpp;
+ p->program_bias_and_scale_params.bias_and_scale =
+ plane->bias_and_scale;
+ break;
+ case MPC_INSERT_PLANE:
+ p->mpc_insert_plane_params.mpc = dc->res_pool->mpc;
+ p->mpc_insert_plane_params.mpc_tree_params =
+ &pipe->stream_res.opp->mpc_tree_params;
+ dcn401_blnd_cfg(plane, &p->mpc_insert_plane_params.blnd_cfg);
+ p->mpc_insert_plane_params.dpp_id = pipe->plane_res.hubp->inst;
+ p->mpc_insert_plane_params.mpcc_id = pipe->plane_res.hubp->inst;
+ break;
+ case MPC_UPDATE_VISUAL_CONFIRM:
+ p->update_visual_confirm_params.mpc = dc->res_pool->mpc;
+ p->update_visual_confirm_params.color =
+ &pipe->visual_confirm_color;
+ p->update_visual_confirm_params.mpcc_id = pipe->plane_res.hubp->inst;
+ break;
+ case DPP_SET_SCALER:
+ p->dpp_set_scaler_params.dpp = dpp;
+ p->dpp_set_scaler_params.scl_data = &pipe->plane_res.scl_data;
+ break;
+ case HUBP_MEM_PROGRAM_VIEWPORT:
+ p->hubp_mem_program_viewport_params.hubp = pipe->plane_res.hubp;
+ p->hubp_mem_program_viewport_params.viewport =
+ &pipe->plane_res.scl_data.viewport;
+ p->hubp_mem_program_viewport_params.viewport_c =
+ &pipe->plane_res.scl_data.viewport_c;
+ break;
+ case HUBP_PROGRAM_MCACHE_ID:
+ p->program_mcache_id_and_split_coordinate.hubp = pipe->plane_res.hubp;
+ p->program_mcache_id_and_split_coordinate.mcache_regs =
+ &pipe->mcache_regs;
+ break;
+ case MPC_SET_GAMUT_REMAP_BYPASS:
+ p->mpc_set_gamut_remap_bypass_params.mpc = dc->res_pool->mpc;
+ p->mpc_set_gamut_remap_bypass_params.mpcc_id =
+ pipe->plane_res.mpcc_inst;
+ p->mpc_set_gamut_remap_bypass_params.block_id =
+ m->mpc_set_gamut_remap_bypass.block_id;
+ break;
+ case MPC_POWER_ON_MPC_MEM_PWR:
+ p->power_on_mpc_mem_pwr_params.mpc = dc->res_pool->mpc;
+ p->power_on_mpc_mem_pwr_params.mpcc_id = pipe->plane_res.hubp->inst;
+ p->power_on_mpc_mem_pwr_params.power_on = true;
+ break;
+ case MPC_SET_OCSC_DEFAULT:
+ p->set_ocsc_default_params.mpc = dc->res_pool->mpc;
+ p->set_ocsc_default_params.opp_id = pipe->stream_res.opp->inst;
+ p->set_ocsc_default_params.color_space =
+ pipe->stream->output_color_space;
+ p->set_ocsc_default_params.ocsc_mode = MPC_OUTPUT_CSC_COEF_A;
+ break;
+ case HUBP_PROGRAM_SURFACE_CONFIG:
+ p->program_surface_config_params.hubp = pipe->plane_res.hubp;
+ p->program_surface_config_params.format = plane->format;
+ p->program_surface_config_params.tiling_info =
+ &plane->tiling_info;
+ p->program_surface_config_params.plane_size =
+ plane->plane_size;
+ p->program_surface_config_params.plane_size.surface_size =
+ pipe->plane_res.scl_data.viewport;
+ p->program_surface_config_params.rotation = plane->rotation;
+ p->program_surface_config_params.dcc = &plane->dcc;
+ p->program_surface_config_params.horizontal_mirror =
+ plane->horizontal_mirror;
+ break;
+ case HUBP_UPDATE_PLANE_ADDR:
+ p->update_plane_addr_params.hubp = pipe->plane_res.hubp;
+ p->update_plane_addr_params.address = plane->address;
+ p->update_plane_addr_params.flip_immediate =
+ plane->flip_immediate;
+ p->update_plane_addr_params.dcc = plane->dcc.enable;
+ break;
+ case DPP_SET_HDR_MULTIPLIER:
+ p->dpp_set_hdr_multiplier_params.dpp = dpp;
+ p->dpp_set_hdr_multiplier_params.hw_mult =
+ dcn401_hdr_mult(plane);
+ break;
+ case DPP_SET_PRE_DEGAM:
+ p->dpp_set_pre_degam_params.dpp = dpp;
+ p->dpp_set_pre_degam_params.in_transfer_func =
+ &plane->in_transfer_func;
+ break;
+ case DPP_PROGRAM_GAMCOR_LUT:
+ p->dpp_program_gamcor_lut_params.dpp = dpp;
+ if (plane->in_transfer_func.type == TF_TYPE_HWPWL)
+ p->dpp_program_gamcor_lut_params.params =
+ &plane->in_transfer_func.pwl;
+ break;
+ case MPC_SET_MOVABLE_CM_LOCATION:
+ p->mpc_set_movable_cm_location_params.mpc = dc->res_pool->mpc;
+ p->mpc_set_movable_cm_location_params.location =
+ MPCC_MOVABLE_CM_LOCATION_BEFORE;
+ p->mpc_set_movable_cm_location_params.mpcc_id =
+ pipe->plane_res.mpcc_inst;
+ break;
+ case MPC_PROGRAM_LUT_MODE:
+ /*
+ * Every LUT is off. DC1 reads the bank back with get_lut_mode;
+ * the build reads no hardware, so it assumes bank A for an
+ * MPCC that held no plane.
+ */
+ p->mpc_program_lut_mode_params.mpc = dc->res_pool->mpc;
+ p->mpc_program_lut_mode_params.id = m->mpc_program_lut_mode.id;
+ p->mpc_program_lut_mode_params.lut_bank_a = true;
+ p->mpc_program_lut_mode_params.size = dcn401_lut_mode_size(
+ plane, m->mpc_program_lut_mode.id);
+ p->mpc_program_lut_mode_params.mpcc_id =
+ pipe->plane_res.mpcc_inst;
+ break;
+ case HUBP_PROGRAM_3DLUT_FL_CONFIG:
+ p->hubp_program_3dlut_fl_config_params.hubp = pipe->plane_res.hubp;
+ p->hubp_program_3dlut_fl_config_params.config =
+ &dcn401_3dlut_dma_off;
+ break;
+ case HUBP_SET_3DLUT_FL:
+ p->hubp_set_3dlut_fl_params.hubp = pipe->plane_res.hubp;
+ break;
+ case MPC_SET_OUTPUT_GAMMA:
+ p->mpc_set_output_gamma_params.mpc = dc->res_pool->mpc;
+ p->mpc_set_output_gamma_params.mpcc_id =
+ pipe->plane_res.mpcc_inst;
+ if (pipe->stream->out_transfer_func.type == TF_TYPE_HWPWL)
+ p->mpc_set_output_gamma_params.params =
+ &pipe->stream->out_transfer_func.pwl;
+ break;
+ case HUBP_WAIT_FLIP_PENDING:
+ p->hubp_wait_flip_pending_params.hubp = pipe->plane_res.hubp;
+ p->hubp_wait_flip_pending_params.polling_interval_us =
+ DCN401_FLIP_PENDING_POLL_INTERVAL_US;
+ break;
+ case OPTC_PROGRAM_MANUAL_TRIGGER:
+ p->program_manual_trigger_params.tg = pipe->stream_res.tg;
break;
default:
/* The step tables use only the funcs above. */
@@ -1147,6 +2202,13 @@ static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
if (step->meta.skip_if.new_state_has_stream &&
args->new_state->stream_count)
return true;
+ if (step->meta.skip_if.optimized_not_required &&
+ !args->dc->optimized_required)
+ return true;
+ if (step->meta.skip_if.no_manual_trigger &&
+ (!args->pipe->plane_state->update_bits.addr_update ||
+ args->pipe->plane_state->skip_manual_trigger))
+ return true;
return false;
}
@@ -1164,15 +2226,14 @@ static void dcn401_add_steps(struct block_sequence_state *sequence,
/* The support check found an OTG master for each stream of state. */
static void dcn401_add_steps_per_stream_per_otg_master_pipe(
- struct block_sequence_state *sequence, struct dc *dc,
- struct dc_state *new_state, struct dc_state *state,
+ struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *state_args,
+ struct dc_state *state,
const struct dcn401_commit_step *steps, unsigned int count)
{
- struct dcn401_commit_step_args args = {0};
+ struct dcn401_commit_step_args args = *state_args;
unsigned int i;
- args.dc = dc;
- args.new_state = new_state;
for (i = 0; i < state->stream_count; i++) {
args.pipe = get_otg_master_from_state_by_stream(state,
state->streams[i]);
@@ -1182,20 +2243,37 @@ static void dcn401_add_steps_per_stream_per_otg_master_pipe(
#define DCN401_ADD_STEPS(sequence, args, steps) \
dcn401_add_steps(sequence, args, steps, ARRAY_SIZE(steps))
-#define DCN401_ADD_STEPS_PER_STREAM(sequence, dc, new_state, state, steps) \
- dcn401_add_steps_per_stream_per_otg_master_pipe(sequence, dc, \
- new_state, state, steps, ARRAY_SIZE(steps))
+#define DCN401_ADD_STEPS_PER_STREAM(sequence, args, state, steps) \
+ dcn401_add_steps_per_stream_per_otg_master_pipe(sequence, args, \
+ state, steps, ARRAY_SIZE(steps))
+
+/*
+ * Points the step arguments at the states and the sequence at
+ * head_sequence, both empty. The entries point into the states; the build
+ * writes neither.
+ */
+static void dcn401_init_and_unpack_params(
+ struct bls_builder_declarative_params *params,
+ struct dcn401_commit_step_args *state_args,
+ struct block_sequence_state *head)
+{
+ state_args->dc = params->dc;
+ state_args->current_state = (struct dc_state *)params->current_state;
+ state_args->new_state = (struct dc_state *)params->new_state;
+ state_args->pipe = NULL;
+
+ params->head_num_steps = 0;
+ params->tail_num_steps = 0;
+ head->steps = params->head_sequence;
+ head->num_steps = ¶ms->head_num_steps;
+}
static enum bls_builder_result dcn401_build_non_seamless_commit(
struct bls_builder_declarative_params *params)
{
- struct dcn401_commit_step_args state_args = {0};
+ struct dcn401_commit_step_args state_args;
struct block_sequence_state head;
- struct block_sequence_state tail;
- struct dc_state *current_state;
- struct dc_state *new_state;
enum bls_builder_result result;
- struct dc *dc;
if (!params)
return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
@@ -1204,28 +2282,58 @@ static enum bls_builder_result dcn401_build_non_seamless_commit(
if (result != BLS_BUILD_SUCCESS)
return result;
- /* The entries point into the states. The build writes neither. */
- dc = params->dc;
- current_state = (struct dc_state *)params->current_state;
- new_state = (struct dc_state *)params->new_state;
- state_args.dc = dc;
- state_args.new_state = new_state;
-
- params->head_num_steps = 0;
- params->tail_num_steps = 0;
- head.steps = params->head_sequence;
- head.num_steps = ¶ms->head_num_steps;
- tail.steps = params->tail_sequence;
- tail.num_steps = ¶ms->tail_num_steps;
-
+ dcn401_init_and_unpack_params(params, &state_args, &head);
DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new);
- DCN401_ADD_STEPS_PER_STREAM(&head, dc, new_state, current_state,
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ state_args.current_state,
bls_head_per_stream_per_otg_master_pipe_current);
- DCN401_ADD_STEPS_PER_STREAM(&head, dc, new_state, new_state,
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
bls_head_per_stream_per_otg_master_pipe_new);
- DCN401_ADD_STEPS_PER_STREAM(&head, dc, new_state, new_state,
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
bls_head_per_stream_per_otg_master_pipe_new_front_end);
- DCN401_ADD_STEPS(&tail, &state_args, bls_tail_per_state_new);
+ DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new_postamble);
+
+ return BLS_BUILD_SUCCESS;
+}
+
+static enum bls_builder_result dcn401_build_seamless_update(
+ struct bls_builder_declarative_params *params)
+{
+ struct dcn401_commit_step_args state_args;
+ struct block_sequence_state head;
+ enum bls_builder_result result;
+
+ if (!params)
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
+ result = dcn401_check_seamless_update_support(params->dc,
+ params->current_state, params->new_state);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+
+ dcn401_init_and_unpack_params(params, &state_args, &head);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ state_args.current_state,
+ bls_update_head_per_stream_per_otg_master_pipe_preamble);
+ DCN401_ADD_STEPS(&head, &state_args,
+ bls_update_head_global_preamble);
+ DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ state_args.current_state,
+ bls_update_head_per_stream_per_otg_master_pipe_current_pending);
+
+ /* One stream, so adding each table per stream keeps the DC1 order. */
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ bls_update_head_new_lock);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ bls_update_head_new_stream);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ bls_update_head_new_enable_plane);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ bls_update_head_new_update_dchubp_dpp);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ bls_update_head_new_transfer_funcs);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ bls_update_head_new_unlock);
return BLS_BUILD_SUCCESS;
}
@@ -1249,3 +2357,23 @@ const struct bls_builder *dcn401_non_seamless_commit_builder(void)
return &builder;
}
+
+static bool dcn401_seamless_update_check_capability(
+ const struct bls_builder_check_capability_params *params)
+{
+ return params && dcn401_check_seamless_update_support(params->dc,
+ params->current_state, params->new_state) ==
+ BLS_BUILD_SUCCESS;
+}
+
+const struct bls_builder *dcn401_seamless_update_builder(void)
+{
+ static const struct bls_builder builder = {
+ .name = DCN_DECL_SEAMLESS_UPDATE,
+ .kind = BLS_BUILDER_DECLARATIVE,
+ .check_capability = dcn401_seamless_update_check_capability,
+ .build.declarative = dcn401_build_seamless_update,
+ };
+
+ return &builder;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
index 4c0043c84907..75fd4be3690a 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
@@ -409,6 +409,8 @@ const struct bls_builder *dcn401_bls_builder_create(
return &legacy_initialization_builder;
case DCN_DECL_NON_SEAMLESS_COMMIT:
return dcn401_non_seamless_commit_builder();
+ case DCN_DECL_SEAMLESS_UPDATE:
+ return dcn401_seamless_update_builder();
default:
return NULL;
}
diff --git a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
index 3a80c9261710..6a875add72e2 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
@@ -486,6 +486,151 @@ static noinline_for_stack bool dc2_bls_step_supported(const struct dc2 *dc2,
return mpc && mpc->funcs && mpc->funcs->remove_mpcc &&
step->params.mpc_remove_mpcc_params.mpc_tree_params &&
step->params.mpc_remove_mpcc_params.mpcc_to_remove;
+ case MPC_INSERT_PLANE:
+ mpc = step->params.mpc_insert_plane_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->insert_plane &&
+ step->params.mpc_insert_plane_params.mpc_tree_params;
+ case MPC_UPDATE_BLENDING:
+ mpc = step->params.mpc_update_blending_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->update_blending;
+ case MPC_POWER_ON_MPC_MEM_PWR:
+ mpc = step->params.power_on_mpc_mem_pwr_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->power_on_mpc_mem_pwr;
+ case MPC_SET_OCSC_DEFAULT:
+ mpc = step->params.set_ocsc_default_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->set_ocsc_default;
+ case DPP_SET_SCALER:
+ dpp = step->params.dpp_set_scaler_params.dpp;
+ return dpp && dpp->funcs && dpp->funcs->dpp_set_scaler &&
+ step->params.dpp_set_scaler_params.scl_data;
+ case DPP_PROGRAM_BIAS_AND_SCALE:
+ dpp = step->params.program_bias_and_scale_params.dpp;
+ return dpp && dpp->funcs &&
+ dpp->funcs->dpp_program_bias_and_scale;
+ case DPP_SET_HDR_MULTIPLIER:
+ dpp = step->params.dpp_set_hdr_multiplier_params.dpp;
+ return dpp && dpp->funcs && dpp->funcs->dpp_set_hdr_multiplier;
+ case HUBP_INIT:
+ hubp = step->params.hubp_init_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->hubp_init;
+ case HUBP_VTG_SEL:
+ hubp = step->params.hubp_vtg_sel_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->hubp_vtg_sel;
+ case HUBP_SET_UNBOUNDED_REQUESTING:
+ hubp = step->params.hubp_set_unbounded_requesting_params.hubp;
+ return hubp && hubp->funcs &&
+ hubp->funcs->set_unbounded_requesting;
+ case HUBP_MEM_PROGRAM_VIEWPORT:
+ hubp = step->params.hubp_mem_program_viewport_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->mem_program_viewport &&
+ step->params.hubp_mem_program_viewport_params.viewport;
+ case HUBP_PROGRAM_TRIPLEBUFFER:
+ hubp = step->params.program_triplebuffer_params.hubp;
+ return hubp && hubp->funcs &&
+ hubp->funcs->hubp_enable_tripleBuffer;
+ case OPTC_PROGRAM_MANUAL_TRIGGER:
+ tg = step->params.program_manual_trigger_params.tg;
+ return tg && tg->funcs && tg->funcs->program_manual_trigger;
+ case DCCG_UPDATE_DPP_DTO:
+ dccg = step->params.dccg_update_dpp_dto_params.dccg;
+ return dccg && dccg->funcs && dccg->funcs->update_dpp_dto;
+ case TG_PROGRAM_GLOBAL_SYNC:
+ tg = step->params.tg_program_global_sync_params.tg;
+ return tg && tg->funcs && tg->funcs->program_global_sync;
+ case TG_SET_VTG_PARAMS:
+ tg = step->params.tg_set_vtg_params_params.tg;
+ return tg && tg->funcs && tg->funcs->set_vtg_params &&
+ step->params.tg_set_vtg_params_params.timing;
+ case TG_WAIT_FOR_ALL_PENDING_UPDATES: {
+ const struct tg_wait_for_all_pending_updates_params *p =
+ &step->params.tg_wait_for_all_pending_updates_params;
+
+ return p->tg && p->tg->funcs && p->tg->funcs->is_tg_enabled &&
+ p->polling_interval_us;
+ }
+ case DC_UDELAY:
+ return true;
+ case HUBBUB_FORCE_WM_PROPAGATE_TO_PIPES:
+ hubbub = step->params.hubbub_force_wm_propagate_to_pipes_params
+ .hubbub;
+ return hubbub && hubbub->funcs &&
+ hubbub->funcs->force_wm_propagate_to_pipes;
+ case HUBP_SETUP2: {
+ const struct hubp_setup2_params *p =
+ &step->params.hubp_setup2_params;
+
+ return p->hubp && p->hubp->funcs && p->hubp->funcs->hubp_setup2 &&
+ p->hubp_regs && p->global_sync && p->timing;
+ }
+ case HUBP_SETUP_INTERDEPENDENT2:
+ hubp = step->params.hubp_setup_interdependent2_params.hubp;
+ return hubp && hubp->funcs &&
+ hubp->funcs->hubp_setup_interdependent2 &&
+ step->params.hubp_setup_interdependent2_params
+ .hubp_regs;
+ case HUBP_PROGRAM_MCACHE_ID:
+ hubp = step->params.program_mcache_id_and_split_coordinate.hubp;
+ return hubp && hubp->funcs &&
+ hubp->funcs->hubp_program_mcache_id_and_split_coordinate &&
+ step->params.program_mcache_id_and_split_coordinate
+ .mcache_regs;
+ case HUBP_PROGRAM_SURFACE_CONFIG:
+ hubp = step->params.program_surface_config_params.hubp;
+ return hubp && hubp->funcs &&
+ hubp->funcs->hubp_program_surface_config &&
+ step->params.program_surface_config_params
+ .tiling_info &&
+ step->params.program_surface_config_params.dcc;
+ case HUBP_UPDATE_PLANE_ADDR:
+ hubp = step->params.update_plane_addr_params.hubp;
+ return hubp && hubp->funcs &&
+ hubp->funcs->hubp_program_surface_flip_and_addr;
+ case HUBP_WAIT_FLIP_PENDING:
+ hubp = step->params.hubp_wait_flip_pending_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->hubp_is_flip_pending &&
+ step->params.hubp_wait_flip_pending_params
+ .polling_interval_us;
+ case HUBP_PROGRAM_3DLUT_FL_CONFIG:
+ hubp = step->params.hubp_program_3dlut_fl_config_params.hubp;
+ return hubp && hubp->funcs &&
+ hubp->funcs->hubp_program_3dlut_fl_config &&
+ step->params.hubp_program_3dlut_fl_config_params
+ .config;
+ case HUBP_SET_3DLUT_FL:
+ hubp = step->params.hubp_set_3dlut_fl_params.hubp;
+ return hubp && hubp->funcs && hubp->funcs->hubp_enable_3dlut_fl;
+ case DPP_SETUP_DPP:
+ dpp = step->params.setup_dpp_params.dpp;
+ return dpp && dpp->funcs && dpp->funcs->dpp_setup;
+ case DPP_SET_CURSOR_MATRIX:
+ dpp = step->params.dpp_set_cursor_matrix_params.dpp;
+ return dpp && dpp->funcs && dpp->funcs->set_cursor_matrix &&
+ step->params.dpp_set_cursor_matrix_params
+ .cursor_csc_color_matrix;
+ case DPP_SET_PRE_DEGAM:
+ dpp = step->params.dpp_set_pre_degam_params.dpp;
+ return dpp && dpp->funcs && dpp->funcs->dpp_set_pre_degam &&
+ step->params.dpp_set_pre_degam_params
+ .in_transfer_func;
+ case DPP_PROGRAM_GAMCOR_LUT:
+ dpp = step->params.dpp_program_gamcor_lut_params.dpp;
+ return dpp && dpp->funcs && dpp->funcs->dpp_program_gamcor_lut;
+ case MPC_UPDATE_VISUAL_CONFIRM:
+ mpc = step->params.update_visual_confirm_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->set_bg_color &&
+ step->params.update_visual_confirm_params.color;
+ case MPC_SET_GAMUT_REMAP_BYPASS:
+ mpc = step->params.mpc_set_gamut_remap_bypass_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->set_gamut_remap;
+ case MPC_SET_MOVABLE_CM_LOCATION:
+ mpc = step->params.mpc_set_movable_cm_location_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->set_movable_cm_location;
+ case MPC_PROGRAM_LUT_MODE:
+ mpc = step->params.mpc_program_lut_mode_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->program_lut_mode;
+ case MPC_SET_OUTPUT_GAMMA:
+ mpc = step->params.mpc_set_output_gamma_params.mpc;
+ return mpc && mpc->funcs && mpc->funcs->set_output_gamma;
default:
return false;
}
@@ -564,6 +709,64 @@ static noinline_for_stack bool dc2_bls_wait_for_blank_complete(
return w->opp->funcs->dpg_is_blanked(w->opp);
}
+static noinline_for_stack void dc2_bls_wait_for_all_pending_updates(
+ const struct tg_wait_for_all_pending_updates_params *w)
+{
+ struct timing_generator *tg = w->tg;
+ unsigned int waited_us;
+ bool pending;
+
+ if (!tg->funcs->is_tg_enabled(tg))
+ return;
+
+ for (waited_us = 0; waited_us < w->timeout_us;
+ waited_us += w->polling_interval_us) {
+ pending = false;
+ if (tg->funcs->get_optc_double_buffer_pending)
+ pending |= tg->funcs->get_optc_double_buffer_pending(tg);
+ if (tg->funcs->get_otg_double_buffer_pending)
+ pending |= tg->funcs->get_otg_double_buffer_pending(tg);
+ if (w->include_pipe_update && tg->funcs->get_pipe_update_pending)
+ pending |= tg->funcs->get_pipe_update_pending(tg);
+ if (!pending)
+ return;
+ udelay(w->polling_interval_us);
+ }
+}
+
+static noinline_for_stack void dc2_bls_wait_flip_pending(
+ const struct hubp_wait_flip_pending_params *w)
+{
+ const unsigned int timeout_us = 100000U;
+ unsigned int j;
+
+ for (j = 0; j < timeout_us / w->polling_interval_us &&
+ w->hubp->funcs->hubp_is_flip_pending(w->hubp); j++)
+ udelay(w->polling_interval_us);
+}
+
+static noinline_for_stack void dc2_bls_set_gamut_remap_bypass(
+ const struct mpc_set_gamut_remap_bypass_params *p)
+{
+ struct mpc_grph_gamut_adjustment adjust = {
+ .gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS,
+ .mpcc_gamut_remap_block_id = p->block_id,
+ };
+
+ p->mpc->funcs->set_gamut_remap(p->mpc, p->mpcc_id, &adjust);
+}
+
+static noinline_for_stack void dc2_bls_program_surface_config(
+ const struct program_surface_config_params *p)
+{
+ struct plane_size size = p->plane_size;
+
+ p->hubp->funcs->hubp_program_surface_config(p->hubp, p->format,
+ p->tiling_info, &size, p->rotation, p->dcc,
+ p->horizontal_mirror, p->compat_level);
+ p->hubp->power_gated = false;
+}
+
enum dc2_bls_exec_result dc2_bls_execute(
struct dc2 *dc2,
const struct block_sequence *steps,
@@ -1278,6 +1481,269 @@ enum dc2_bls_exec_result dc2_bls_execute(
p->mpcc_to_remove);
break;
}
+ case MPC_INSERT_PLANE: {
+ const struct mpc_insert_plane_params *p =
+ &entry->params.mpc_insert_plane_params;
+ struct mpcc_blnd_cfg blnd_cfg = p->blnd_cfg;
+
+ p->mpc->funcs->insert_plane(p->mpc, p->mpc_tree_params,
+ &blnd_cfg, p->sm_cfg, p->insert_above_mpcc,
+ p->dpp_id, p->mpcc_id);
+ break;
+ }
+ case MPC_UPDATE_BLENDING: {
+ const struct mpc_update_blending_params *p =
+ &entry->params.mpc_update_blending_params;
+ struct mpcc_blnd_cfg blnd_cfg = p->blnd_cfg;
+
+ p->mpc->funcs->update_blending(p->mpc, &blnd_cfg,
+ p->mpcc_id);
+ break;
+ }
+ case MPC_POWER_ON_MPC_MEM_PWR: {
+ const struct power_on_mpc_mem_pwr_params *p =
+ &entry->params.power_on_mpc_mem_pwr_params;
+
+ p->mpc->funcs->power_on_mpc_mem_pwr(p->mpc, p->mpcc_id,
+ p->power_on);
+ break;
+ }
+ case MPC_SET_OCSC_DEFAULT: {
+ const struct set_ocsc_default_params *p =
+ &entry->params.set_ocsc_default_params;
+
+ p->mpc->funcs->set_ocsc_default(p->mpc, p->opp_id,
+ p->color_space, p->ocsc_mode);
+ break;
+ }
+ case DPP_SET_SCALER: {
+ const struct dpp_set_scaler_params *p =
+ &entry->params.dpp_set_scaler_params;
+
+ p->dpp->funcs->dpp_set_scaler(p->dpp, p->scl_data);
+ break;
+ }
+ case DPP_PROGRAM_BIAS_AND_SCALE: {
+ const struct program_bias_and_scale_params *p =
+ &entry->params.program_bias_and_scale_params;
+ struct dc_bias_and_scale bias_and_scale = p->bias_and_scale;
+
+ p->dpp->funcs->dpp_program_bias_and_scale(p->dpp,
+ &bias_and_scale);
+ break;
+ }
+ case DPP_SET_HDR_MULTIPLIER: {
+ const struct dpp_set_hdr_multiplier_params *p =
+ &entry->params.dpp_set_hdr_multiplier_params;
+
+ p->dpp->funcs->dpp_set_hdr_multiplier(p->dpp, p->hw_mult);
+ break;
+ }
+ case HUBP_INIT:
+ hubp = entry->params.hubp_init_params.hubp;
+ hubp->funcs->hubp_init(hubp);
+ break;
+ case HUBP_VTG_SEL: {
+ const struct hubp_vtg_sel_params *p =
+ &entry->params.hubp_vtg_sel_params;
+
+ p->hubp->funcs->hubp_vtg_sel(p->hubp, p->otg_inst);
+ break;
+ }
+ case HUBP_SET_UNBOUNDED_REQUESTING: {
+ const struct hubp_set_unbounded_requesting_params *p =
+ &entry->params.hubp_set_unbounded_requesting_params;
+
+ p->hubp->funcs->set_unbounded_requesting(p->hubp,
+ p->unbounded_req);
+ break;
+ }
+ case HUBP_MEM_PROGRAM_VIEWPORT: {
+ const struct hubp_mem_program_viewport_params *p =
+ &entry->params.hubp_mem_program_viewport_params;
+
+ p->hubp->funcs->mem_program_viewport(p->hubp, p->viewport,
+ p->viewport_c);
+ break;
+ }
+ case HUBP_PROGRAM_TRIPLEBUFFER: {
+ const struct program_triplebuffer_params *p =
+ &entry->params.program_triplebuffer_params;
+
+ p->hubp->funcs->hubp_enable_tripleBuffer(p->hubp,
+ p->enableTripleBuffer);
+ break;
+ }
+ case OPTC_PROGRAM_MANUAL_TRIGGER:
+ tg = entry->params.program_manual_trigger_params.tg;
+ tg->funcs->program_manual_trigger(tg);
+ break;
+ case DCCG_UPDATE_DPP_DTO: {
+ const struct dccg_update_dpp_dto_params *p =
+ &entry->params.dccg_update_dpp_dto_params;
+
+ p->dccg->funcs->update_dpp_dto(p->dccg, p->dpp_inst,
+ p->dppclk_khz);
+ break;
+ }
+ case TG_PROGRAM_GLOBAL_SYNC: {
+ const struct tg_program_global_sync_params *p =
+ &entry->params.tg_program_global_sync_params;
+
+ p->tg->funcs->program_global_sync(p->tg, p->vready_offset,
+ p->vstartup_lines, p->vupdate_offset_pixels,
+ p->vupdate_vupdate_width_pixels,
+ p->pstate_keepout_start_lines);
+ break;
+ }
+ case TG_SET_VTG_PARAMS: {
+ const struct tg_set_vtg_params_params *p =
+ &entry->params.tg_set_vtg_params_params;
+
+ p->tg->funcs->set_vtg_params(p->tg, p->timing,
+ p->program_fp2);
+ break;
+ }
+ case TG_WAIT_FOR_ALL_PENDING_UPDATES:
+ dc2_bls_wait_for_all_pending_updates(&entry->params
+ .tg_wait_for_all_pending_updates_params);
+ break;
+ case DC_UDELAY:
+ udelay(entry->params.dc_udelay_params.us);
+ break;
+ case HUBBUB_FORCE_WM_PROPAGATE_TO_PIPES: {
+ struct hubbub *hubbub = entry->params
+ .hubbub_force_wm_propagate_to_pipes_params
+ .hubbub;
+
+ hubbub->funcs->force_wm_propagate_to_pipes(hubbub);
+ break;
+ }
+ case HUBP_SETUP2: {
+ const struct hubp_setup2_params *p =
+ &entry->params.hubp_setup2_params;
+
+ p->hubp->funcs->hubp_setup2(p->hubp, p->hubp_regs,
+ p->global_sync, p->timing);
+ break;
+ }
+ case HUBP_SETUP_INTERDEPENDENT2: {
+ const struct hubp_setup_interdependent2_params *p =
+ &entry->params.hubp_setup_interdependent2_params;
+
+ p->hubp->funcs->hubp_setup_interdependent2(p->hubp,
+ p->hubp_regs);
+ break;
+ }
+ case HUBP_PROGRAM_MCACHE_ID: {
+ const struct program_mcache_id_and_split_coordinate *p =
+ &entry->params
+ .program_mcache_id_and_split_coordinate;
+
+ p->hubp->funcs->hubp_program_mcache_id_and_split_coordinate(
+ p->hubp, p->mcache_regs);
+ break;
+ }
+ case HUBP_PROGRAM_SURFACE_CONFIG:
+ dc2_bls_program_surface_config(&entry->params
+ .program_surface_config_params);
+ break;
+ case HUBP_UPDATE_PLANE_ADDR: {
+ const struct update_plane_addr_params *p =
+ &entry->params.update_plane_addr_params;
+
+ p->hubp->funcs->hubp_program_surface_flip_and_addr(p->hubp,
+ &p->address, p->flip_immediate, p->dcc);
+ break;
+ }
+ case HUBP_WAIT_FLIP_PENDING:
+ dc2_bls_wait_flip_pending(&entry->params
+ .hubp_wait_flip_pending_params);
+ break;
+ case HUBP_PROGRAM_3DLUT_FL_CONFIG: {
+ const struct hubp_program_3dlut_fl_config_params *p =
+ &entry->params
+ .hubp_program_3dlut_fl_config_params;
+
+ p->hubp->funcs->hubp_program_3dlut_fl_config(p->hubp,
+ p->config);
+ break;
+ }
+ case HUBP_SET_3DLUT_FL:
+ hubp = entry->params.hubp_set_3dlut_fl_params.hubp;
+ hubp->funcs->hubp_enable_3dlut_fl(hubp,
+ entry->params.hubp_set_3dlut_fl_params.enable);
+ break;
+ case DPP_SETUP_DPP: {
+ const struct setup_dpp_params *p =
+ &entry->params.setup_dpp_params;
+
+ p->dpp->funcs->dpp_setup(p->dpp, p->format,
+ EXPANSION_MODE_ZERO,
+ p->input_csc_color_matrix, p->color_space,
+ NULL, p->upsp_mode);
+ break;
+ }
+ case DPP_SET_CURSOR_MATRIX: {
+ const struct dpp_set_cursor_matrix_params *p =
+ &entry->params.dpp_set_cursor_matrix_params;
+
+ p->dpp->funcs->set_cursor_matrix(p->dpp, p->color_space,
+ *p->cursor_csc_color_matrix);
+ break;
+ }
+ case DPP_SET_PRE_DEGAM: {
+ const struct dpp_set_pre_degam_params *p =
+ &entry->params.dpp_set_pre_degam_params;
+
+ p->dpp->funcs->dpp_set_pre_degam(p->dpp,
+ p->in_transfer_func->type == TF_TYPE_PREDEFINED ?
+ p->in_transfer_func->tf :
+ TRANSFER_FUNCTION_UNITY);
+ break;
+ }
+ case DPP_PROGRAM_GAMCOR_LUT:
+ dpp = entry->params.dpp_program_gamcor_lut_params.dpp;
+ dpp->funcs->dpp_program_gamcor_lut(dpp,
+ entry->params.dpp_program_gamcor_lut_params
+ .params);
+ break;
+ case MPC_UPDATE_VISUAL_CONFIRM: {
+ const struct update_visual_confirm_params *p =
+ &entry->params.update_visual_confirm_params;
+
+ p->mpc->funcs->set_bg_color(p->mpc, p->color, p->mpcc_id);
+ break;
+ }
+ case MPC_SET_GAMUT_REMAP_BYPASS:
+ dc2_bls_set_gamut_remap_bypass(&entry->params
+ .mpc_set_gamut_remap_bypass_params);
+ break;
+ case MPC_SET_MOVABLE_CM_LOCATION: {
+ const struct mpc_set_movable_cm_location_params *p =
+ &entry->params
+ .mpc_set_movable_cm_location_params;
+
+ p->mpc->funcs->set_movable_cm_location(p->mpc, p->location,
+ p->mpcc_id);
+ break;
+ }
+ case MPC_PROGRAM_LUT_MODE: {
+ const struct mpc_program_lut_mode_params *p =
+ &entry->params.mpc_program_lut_mode_params;
+
+ p->mpc->funcs->program_lut_mode(p->mpc, p->id, p->enable,
+ p->lut_bank_a, p->size, p->mpcc_id);
+ break;
+ }
+ case MPC_SET_OUTPUT_GAMMA: {
+ const struct mpc_set_output_gamma_params *p =
+ &entry->params.mpc_set_output_gamma_params;
+
+ p->mpc->funcs->set_output_gamma(p->mpc, p->mpcc_id,
+ p->params);
+ break;
+ }
default:
ASSERT(false);
return DC2_BLS_EXEC_ERROR;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 464a5b7810d9..85de12c558ad 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -6790,6 +6790,7 @@ static const struct dc2_funcs dc2_legacy_funcs_table = {
.hardware_init = legacy_hardware_init,
.hardware_release = legacy_hardware_release,
.commit_state_hw = legacy_commit_state_hw,
+ .update_state_execute = legacy_update_state_execute,
};
const struct dc2_funcs *dc2_legacy_funcs(void)
@@ -8512,6 +8513,9 @@ bool dc_update_state_prepare(struct dc_update_scratch_space *scratch)
if (!dc_update_probes_prepare(scratch))
goto release_scratch;
+ if (!api_shim_update_state_supported(scratch))
+ goto release_scratch;
+
return true;
release_scratch:
@@ -8520,9 +8524,7 @@ bool dc_update_state_prepare(struct dc_update_scratch_space *scratch)
return false;
}
-void dc_update_state_execute(
- const struct dc_update_scratch_space *scratch
-)
+void legacy_update_state_execute(const struct dc_update_scratch_space *scratch)
{
if (scratch->stream)
scratch->update_v3
@@ -8533,6 +8535,13 @@ void dc_update_state_execute(
dc_update_probes_execute(scratch);
}
+void dc_update_state_execute(
+ const struct dc_update_scratch_space *scratch
+)
+{
+ api_shim_update_state_execute(scratch);
+}
+
bool dc_update_state_cleanup(
struct dc_update_scratch_space *scratch
)
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
index 43bb5f461ef2..052f29df1797 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
@@ -7,6 +7,7 @@
#include "dc.h"
#include "core_types.h"
#include "resource.h"
+#include "vm_helper.h"
#include "dc_bios_types.h"
#include "bls_builder/bls_builder.h"
#include "bls_executor/bls_executor.h"
@@ -20,10 +21,17 @@ static void core2_hardware_release(struct dc *dc)
dc2_legacy_funcs()->hardware_release(dc);
}
+static bool core2_update_state_supported(
+ const struct dc_update_scratch_space *scratch);
+static void core2_update_state_execute(
+ const struct dc_update_scratch_space *scratch);
+
static const struct dc2_funcs core2_funcs_table = {
.hardware_init = core2_hardware_init,
.hardware_release = core2_hardware_release,
.commit_state_hw = core2_commit_state_hw,
+ .update_state_supported = core2_update_state_supported,
+ .update_state_execute = core2_update_state_execute,
};
const struct dc2_funcs *core2_funcs(void)
@@ -136,27 +144,69 @@ static void core2_commit_disable_done(struct dc *dc, struct pipe_ctx *pipe)
dc->optimized_required = true;
}
-/* Never falls back to legacy_commit_state_hw: DC2 must not call DC1. */
-enum dc_status core2_commit_state_hw(struct dc *dc, struct dc_state *context)
+/* Builds and runs one declarative builder from dc->current_state to new_state. */
+static enum dc_status core2_build_and_run(struct dc *dc,
+ enum bls_builder_name name, struct dc_state *new_state)
{
- const struct bls_builder *builder =
- TO_DC2(dc)->builders[DCN_DECL_NON_SEAMLESS_COMMIT];
+ const struct bls_builder *builder = TO_DC2(dc)->builders[name];
const struct workaround_manager *wam = dc2_workaround_manager_get();
+ struct bls_builder_declarative_params *params;
+ enum dc_status status = DC_ERROR_UNEXPECTED;
+
+ /* 256 block-sequence entries; keep them off the kernel stack. */
+ params = kvzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return DC_ERROR_UNEXPECTED;
+ params->dc = dc;
+ params->current_state = dc->current_state;
+ params->new_state = new_state;
+
+ /*
+ * A tail runs on the vsync after the head, so Core would have to wait
+ * for that vsync on the stream first. Core has no wait-for-vsync
+ * imperative yet, so a builder that emits a tail is not supported.
+ */
+ if (builder->build.declarative(params) != BLS_BUILD_SUCCESS ||
+ params->tail_num_steps) {
+ status = DC_NOT_SUPPORTED;
+ goto out;
+ }
+ if (!core2_run_commit_sequence(dc, builder, wam,
+ params->head_sequence, ¶ms->head_num_steps))
+ goto out;
+ status = DC_OK;
+
+out:
+ kvfree(params);
+ return status;
+}
+
+static bool core2_builder_supports(struct dc *dc, enum bls_builder_name name,
+ struct dc_state *new_state)
+{
+ const struct bls_builder *builder = TO_DC2(dc)->builders[name];
struct bls_builder_check_capability_params check = {
.dc = dc,
.current_state = dc->current_state,
- .new_state = context,
+ .new_state = new_state,
};
- struct bls_builder_declarative_params *params;
+
+ return builder && builder->check_capability &&
+ builder->kind == BLS_BUILDER_DECLARATIVE &&
+ builder->build.declarative &&
+ dc2_workaround_manager_get() &&
+ builder->check_capability(&check) &&
+ core2_is_accelerated_mode(dc);
+}
+
+/* Never falls back to legacy_commit_state_hw: DC2 must not call DC1. */
+enum dc_status core2_commit_state_hw(struct dc *dc, struct dc_state *context)
+{
struct pipe_ctx *old_pipe;
struct pipe_ctx *new_pipe;
- enum dc_status status = DC_ERROR_UNEXPECTED;
+ enum dc_status status;
- if (!builder || !builder->check_capability ||
- builder->kind != BLS_BUILDER_DECLARATIVE ||
- !builder->build.declarative || !wam ||
- !builder->check_capability(&check) ||
- !core2_is_accelerated_mode(dc))
+ if (!core2_builder_supports(dc, DCN_DECL_NON_SEAMLESS_COMMIT, context))
return DC_NOT_SUPPORTED;
old_pipe = core2_otg_master(dc->current_state);
@@ -170,38 +220,114 @@ enum dc_status core2_commit_state_hw(struct dc *dc, struct dc_state *context)
if (new_pipe)
resource_build_info_frame(new_pipe);
- /* 128 block-sequence entries; keep them off the kernel stack. */
- params = kvzalloc(sizeof(*params), GFP_KERNEL);
- if (!params)
- return DC_ERROR_UNEXPECTED;
- params->dc = dc;
- params->current_state = dc->current_state;
- params->new_state = context;
-
- if (builder->build.declarative(params) != BLS_BUILD_SUCCESS) {
- status = DC_NOT_SUPPORTED;
- goto out;
- }
- if (!core2_run_commit_sequence(dc, builder, wam,
- params->head_sequence, ¶ms->head_num_steps))
- goto out;
- /*
- * The head ends with the ODM double-buffer and blank waits when it
- * programs an OTG. The tail is deferred until that head returns, so
- * it runs after double buffering completes.
- */
- if (!core2_run_commit_sequence(dc, builder, wam,
- params->tail_sequence, ¶ms->tail_num_steps))
- goto out;
+ status = core2_build_and_run(dc, DCN_DECL_NON_SEAMLESS_COMMIT, context);
+ if (status != DC_OK)
+ return status;
core2_commit_prepare_done(dc, context);
if (old_pipe)
core2_commit_disable_done(dc, old_pipe);
if (new_pipe)
core2_commit_enable_done(new_pipe);
- status = DC_OK;
+ return DC_OK;
+}
-out:
- kvfree(params);
- return status;
+/*
+ * The stream update may move the stream's source and destination
+ * rectangles, which the new context already holds. Anything else asks
+ * for stream programming that the full update does not build.
+ */
+static bool core2_stream_update_supported(
+ const struct dc_stream_update *update)
+{
+ if (!update)
+ return true;
+ return !update->hdr_static_metadata && !update->abm_level &&
+ !update->periodic_interrupt &&
+ !update->vrr_infopacket && !update->vsc_infopacket &&
+ !update->vsp_infopacket && !update->hfvsif_infopacket &&
+ !update->vtem_infopacket &&
+ !update->adaptive_sync_infopacket &&
+ !update->avi_infopacket && !update->dpms_off &&
+ !update->gamut_remap && !update->output_color_space &&
+ !update->dither_option &&
+ !update->output_csc_transform && !update->dsc_config &&
+ !update->mst_bw_update && !update->func_shaper &&
+ !update->lut3d_func && !update->pending_test_pattern &&
+ !update->crtc_timing_adjust &&
+ !update->cursor_attributes && !update->cursor_position &&
+ !(update->wb_update && update->wb_update->num_wb_info);
+}
+
+static bool core2_update_state_supported(
+ const struct dc_update_scratch_space *scratch)
+{
+ /* Probes and v2 updates have no DC2 path. */
+ if (!scratch->stream || !scratch->update_v3 || scratch->probe_updates)
+ return false;
+ /*
+ * Only a full update on a new context has a builder. A context-full
+ * flow keeps dc->current_state, so its update type is below FULL.
+ */
+ if (scratch->update_type != UPDATE_TYPE_FULL ||
+ scratch->flow != UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS)
+ return false;
+ /* The full-update builder adds one plane. */
+ if (scratch->surface_count != 1)
+ return false;
+ if (!core2_stream_update_supported(scratch->stream_update))
+ return false;
+ return core2_builder_supports(scratch->dc, DCN_DECL_SEAMLESS_UPDATE,
+ scratch->new_context);
+}
+
+/*
+ * The software state commit_planes_for_stream writes while it programs
+ * the plane. The swap to the new context stays in
+ * dc_update_state_cleanup.
+ */
+static void core2_update_done(struct dc *dc, struct dc_state *context)
+{
+ struct pipe_ctx *pipe = core2_otg_master(context);
+ struct dc_plane_state *plane;
+ struct hubp *hubp;
+
+ if (!pipe || !pipe->plane_state || !pipe->plane_res.hubp ||
+ !pipe->stream_res.opp)
+ return;
+ plane = pipe->plane_state;
+ hubp = pipe->plane_res.hubp;
+
+ vm_helper_mark_vmid_used(dc->vm_helper, plane->address.vmid,
+ (uint8_t)hubp->inst);
+ plane->status.requested_address = plane->address;
+ hubp->opp_id = pipe->stream_res.opp->inst;
+ hubp->mpcc_id = hubp->inst;
+ hubp->power_gated = false;
+ core2_commit_prepare_done(dc, context);
+}
+
+static void core2_update_state_execute(
+ const struct dc_update_scratch_space *scratch)
+{
+ struct dc *dc = scratch->dc;
+ struct dc_state *context = scratch->new_context;
+ struct pipe_ctx *pipe = core2_otg_master(context);
+
+ /*
+ * The values DC1 computes into the context before it programs the
+ * plane. The builder reads them from the context.
+ */
+ if (!pipe || !pipe->plane_state)
+ return;
+ if (dc->res_pool->funcs->prepare_mcache_programming)
+ dc->res_pool->funcs->prepare_mcache_programming(dc, context);
+ pipe->plane_res.scl_data.lb_params.alpha_en =
+ pipe->plane_state->per_pixel_alpha;
+
+ if (core2_build_and_run(dc, DCN_DECL_SEAMLESS_UPDATE, context) != DC_OK) {
+ ASSERT(false);
+ return;
+ }
+ core2_update_done(dc, context);
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.c b/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.c
index eca9b2de92fa..5d7117501428 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.c
@@ -5,6 +5,7 @@
#include "dm_services.h"
#include "dc.h"
+#include "core_types.h"
#include "dc_api_dispatch/api_shim.h"
#include "core2/dc_core2.h"
#include "inc/dc_core_interface.h"
@@ -37,3 +38,20 @@ enum dc_status api_shim_commit_state_hw(struct dc *dc, struct dc_state *context)
return core2_funcs()->commit_state_hw(dc, context);
return legacy_commit_state_hw(dc, context);
}
+
+bool api_shim_update_state_supported(
+ const struct dc_update_scratch_space *scratch)
+{
+ if (TO_DC2(scratch->dc)->selection == DC2_SELECTION_CORE2)
+ return core2_funcs()->update_state_supported(scratch);
+ return true;
+}
+
+void api_shim_update_state_execute(
+ const struct dc_update_scratch_space *scratch)
+{
+ if (TO_DC2(scratch->dc)->selection == DC2_SELECTION_CORE2)
+ core2_funcs()->update_state_execute(scratch);
+ else
+ legacy_update_state_execute(scratch);
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.h b/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.h
index 6d9ad4192296..93dd2d47c8dc 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_api_dispatch/api_shim.h
@@ -24,3 +24,7 @@ struct dc2 {
void api_shim_construct(struct dc *dc, const struct dc_init_data *init_params);
enum dc_status api_shim_commit_state_hw(struct dc *dc, struct dc_state *context);
+bool api_shim_update_state_supported(
+ const struct dc_update_scratch_space *scratch);
+void api_shim_update_state_execute(
+ const struct dc_update_scratch_space *scratch);
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 a6c743d0cc58..6996ce12a17d 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -423,6 +423,68 @@ struct bios_set_scratch_critical_state_params {
bool state;
};
+struct tg_wait_for_all_pending_updates_params {
+ struct timing_generator *tg;
+ bool include_pipe_update;
+ unsigned int polling_interval_us;
+ unsigned int timeout_us;
+};
+
+struct dc_udelay_params {
+ unsigned int us;
+};
+
+struct hubbub_force_wm_propagate_to_pipes_params {
+ struct hubbub *hubbub;
+};
+
+struct mpc_set_gamut_remap_bypass_params {
+ struct mpc *mpc;
+ int mpcc_id;
+ enum mpcc_gamut_remap_id block_id;
+};
+
+struct mpc_set_movable_cm_location_params {
+ struct mpc *mpc;
+ enum mpcc_movable_cm_location location;
+ int mpcc_id;
+};
+
+struct mpc_program_lut_mode_params {
+ struct mpc *mpc;
+ enum MCM_LUT_ID id;
+ bool enable;
+ bool lut_bank_a;
+ enum dc_cm_lut_size size;
+ int mpcc_id;
+};
+
+struct mpc_set_output_gamma_params {
+ struct mpc *mpc;
+ int mpcc_id;
+ const struct pwl_params *params;
+};
+
+struct dpp_set_pre_degam_params {
+ struct dpp *dpp;
+ const struct dc_transfer_func *in_transfer_func;
+};
+
+struct dpp_program_gamcor_lut_params {
+ struct dpp *dpp;
+ const struct pwl_params *params;
+};
+
+struct hubp_program_3dlut_fl_config_params {
+ struct hubp *hubp;
+ const struct dc_3dlut_dma *config;
+};
+
+struct hubp_set_3dlut_fl_params {
+ struct hubp *hubp;
+ bool enable;
+};
+
struct lsdma_send_pio_copy_params {
struct dc_dmub_srv *dc_dmub_srv;
uint64_t src_addr;
@@ -1525,6 +1587,22 @@ union block_sequence_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;
+ struct tg_wait_for_all_pending_updates_params
+ tg_wait_for_all_pending_updates_params;
+ struct dc_udelay_params dc_udelay_params;
+ struct hubbub_force_wm_propagate_to_pipes_params
+ hubbub_force_wm_propagate_to_pipes_params;
+ struct mpc_set_gamut_remap_bypass_params
+ mpc_set_gamut_remap_bypass_params;
+ struct mpc_set_movable_cm_location_params
+ mpc_set_movable_cm_location_params;
+ struct mpc_program_lut_mode_params mpc_program_lut_mode_params;
+ struct mpc_set_output_gamma_params mpc_set_output_gamma_params;
+ struct dpp_set_pre_degam_params dpp_set_pre_degam_params;
+ struct dpp_program_gamcor_lut_params dpp_program_gamcor_lut_params;
+ struct hubp_program_3dlut_fl_config_params
+ hubp_program_3dlut_fl_config_params;
+ struct hubp_set_3dlut_fl_params hubp_set_3dlut_fl_params;
};
enum block_sequence_func {
@@ -1754,6 +1832,17 @@ enum block_sequence_func {
DIO_LINK_ENC_ENABLE_DP_OUTPUT,
DIO_LINK_ENC_DISABLE_OUTPUT,
BIOS_SET_SCRATCH_CRITICAL_STATE,
+ TG_WAIT_FOR_ALL_PENDING_UPDATES,
+ DC_UDELAY,
+ HUBBUB_FORCE_WM_PROPAGATE_TO_PIPES,
+ MPC_SET_GAMUT_REMAP_BYPASS,
+ MPC_SET_MOVABLE_CM_LOCATION,
+ MPC_PROGRAM_LUT_MODE,
+ MPC_SET_OUTPUT_GAMMA,
+ DPP_SET_PRE_DEGAM,
+ DPP_PROGRAM_GAMCOR_LUT,
+ HUBP_PROGRAM_3DLUT_FL_CONFIG,
+ HUBP_SET_3DLUT_FL,
/* This must be the last value in this enum, add new ones above */
HWSS_BLOCK_SEQUENCE_FUNC_COUNT
};
diff --git a/drivers/gpu/drm/amd/display/dc/inc/dc_core_interface.h b/drivers/gpu/drm/amd/display/dc/inc/dc_core_interface.h
index 383d14af91f1..4cd01e34a959 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/dc_core_interface.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/dc_core_interface.h
@@ -9,6 +9,7 @@
struct dc;
struct dc_state;
+struct dc_update_scratch_space;
/*
* Construction-time Core implementation selection. Zero is LEGACY so existing
@@ -28,9 +29,15 @@ struct dc2_funcs {
void (*hardware_init)(struct dc *dc);
void (*hardware_release)(struct dc *dc);
enum dc_status (*commit_state_hw)(struct dc *dc, struct dc_state *context);
+ /* Checked in prepare; execute returns void, so it cannot reject. */
+ bool (*update_state_supported)(
+ const struct dc_update_scratch_space *scratch);
+ void (*update_state_execute)(
+ const struct dc_update_scratch_space *scratch);
};
void legacy_hardware_init(struct dc *dc);
void legacy_hardware_release(struct dc *dc);
enum dc_status legacy_commit_state_hw(struct dc *dc, struct dc_state *context);
+void legacy_update_state_execute(const struct dc_update_scratch_space *scratch);
const struct dc2_funcs *dc2_legacy_funcs(void);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 29/74] drm/amd/display: Read accelerated mode through a query builder
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (27 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 28/74] drm/amd/display: Build the dcn401 seamless update in DC2 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 30/74] drm/amd/display: Ensure exit IPS before checking flip status Wayne Lin
` (45 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
DC2 read the VBIOS accelerated-mode bit with a direct BIOS call. The
core layer must not access hardware.
[How]
Add a query builder that emits one accelerated-mode read into an output
target. The core layer builds and executes it in place before it accepts
a commit or an update.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/dc/bls_builder/bls_builder.h | 2 +
.../dc/bls_builder/dcn401_init_builder.c | 32 +++++++++++
.../gpu/drm/amd/display/dc/core2/dc_core2.c | 53 +++++++++++++------
3 files changed, 72 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
index 954adc590ed4..9a3907bf950a 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
@@ -26,6 +26,7 @@ enum bls_builder_name {
DCN_DECL_INITIALIZATION_LEGACY,
DCN_DECL_NON_SEAMLESS_COMMIT,
DCN_DECL_SEAMLESS_UPDATE,
+ DCN_DECL_QUERY_ACCELERATED_MODE,
BLS_BUILDER_NAME_COUNT,
};
@@ -51,6 +52,7 @@ struct bls_builder_output_targets {
union {
struct bls_builder_output_init *DCN_DECL_INITIALIZATION;
struct bls_builder_legacy_hw_init_info *DCN_DECL_INITIALIZATION_LEGACY;
+ bool *DCN_DECL_QUERY_ACCELERATED_MODE;
};
};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
index 75fd4be3690a..1dc502c0839c 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_init_builder.c
@@ -386,6 +386,31 @@ static enum bls_builder_result dcn401_build_initialization(
return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
}
+/* Reads the VBIOS accelerated-mode bit into the caller's out-target. */
+static enum bls_builder_result dcn401_build_accelerated_mode_query(
+ struct bls_builder_declarative_params *params)
+{
+ union block_sequence_params bls_params = {0};
+ struct block_sequence_state sequence;
+ struct dc_bios *dcb;
+
+ if (!params || !params->dc || !params->dc->ctx || !params->out_targets ||
+ !params->out_targets->DCN_DECL_QUERY_ACCELERATED_MODE)
+ return BLS_BUILD_ERROR_RESOURCE_MAPPING;
+ dcb = params->dc->ctx->dc_bios;
+ if (!dcb || !dcb->funcs || !dcb->funcs->is_accelerated_mode)
+ return BLS_BUILD_ERROR_RESOURCE_MAPPING;
+
+ sequence.steps = params->head_sequence;
+ sequence.num_steps = ¶ms->head_num_steps;
+ bls_params.bios_is_accelerated_mode_params.dcb = dcb;
+ bls_params.bios_is_accelerated_mode_params.out_accelerated_mode =
+ params->out_targets->DCN_DECL_QUERY_ACCELERATED_MODE;
+ if (!bls_append(&sequence, BIOS_IS_ACCELERATED_MODE, &bls_params))
+ return BLS_BUILD_ERROR_RESOURCE_MAPPING;
+ return BLS_BUILD_SUCCESS;
+}
+
const struct bls_builder *dcn401_bls_builder_create(
enum bls_builder_name name)
{
@@ -401,6 +426,11 @@ const struct bls_builder *dcn401_bls_builder_create(
.check_capability = dcn401_dc1_initialization_check_capability,
.build.declarative = dcn401_build_dc1_initialization,
};
+ static const struct bls_builder accelerated_mode_query_builder = {
+ .name = DCN_DECL_QUERY_ACCELERATED_MODE,
+ .kind = BLS_BUILDER_DECLARATIVE,
+ .build.declarative = dcn401_build_accelerated_mode_query,
+ };
switch (name) {
case DCN_DECL_INITIALIZATION:
@@ -411,6 +441,8 @@ const struct bls_builder *dcn401_bls_builder_create(
return dcn401_non_seamless_commit_builder();
case DCN_DECL_SEAMLESS_UPDATE:
return dcn401_seamless_update_builder();
+ case DCN_DECL_QUERY_ACCELERATED_MODE:
+ return &accelerated_mode_query_builder;
default:
return NULL;
}
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
index 052f29df1797..295d2d8b277e 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
@@ -53,20 +53,6 @@ void core2_destruct(struct dc *dc)
(void)dc;
}
-/*
- * The VBIOS scratch read legacy_commit_state_hw makes
- * before it enables accelerated mode. The commit has no accelerated-mode
- * entries, so it runs only after init has left VBIOS mode. Remove when a
- * BLC exposes the accelerated-mode bit.
- */
-static bool core2_is_accelerated_mode(struct dc *dc)
-{
- struct dc_bios *dcb = dc->ctx->dc_bios;
-
- return dcb && dcb->funcs && dcb->funcs->is_accelerated_mode &&
- dcb->funcs->is_accelerated_mode(dcb);
-}
-
static struct pipe_ctx *core2_otg_master(struct dc_state *state)
{
unsigned int i;
@@ -105,6 +91,43 @@ static bool core2_run_commit_sequence(struct dc *dc,
result == DC2_BLS_EXEC_CONTINUED_ON_ERROR;
}
+/*
+ * legacy_commit_state_hw reads this bit before it enables accelerated
+ * mode. The commit and the update have no accelerated-mode entries, so
+ * they run only after init has left VBIOS mode.
+ */
+static bool core2_query_accelerated_mode(struct dc *dc)
+{
+ const struct bls_builder *builder =
+ TO_DC2(dc)->builders[DCN_DECL_QUERY_ACCELERATED_MODE];
+ const struct workaround_manager *wam = dc2_workaround_manager_get();
+ struct bls_builder_output_targets out_targets = {0};
+ struct bls_builder_declarative_params *params;
+ bool accelerated_mode = false;
+
+ if (!builder || builder->kind != BLS_BUILDER_DECLARATIVE ||
+ !builder->build.declarative || !wam)
+ return false;
+
+ /* 256 block-sequence entries; keep them off the kernel stack. */
+ params = kvzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return false;
+ params->dc = dc;
+ params->current_state = dc->current_state;
+ out_targets.DCN_DECL_QUERY_ACCELERATED_MODE = &accelerated_mode;
+ params->out_targets = &out_targets;
+
+ if (builder->build.declarative(params) != BLS_BUILD_SUCCESS ||
+ params->tail_num_steps ||
+ !core2_run_commit_sequence(dc, builder, wam,
+ params->head_sequence, ¶ms->head_num_steps))
+ accelerated_mode = false;
+
+ kvfree(params);
+ return accelerated_mode;
+}
+
/*
* dcn401_prepare_bandwidth's optimized_required writes. The Executor ORs
* in the watermark and arbiter results.
@@ -196,7 +219,7 @@ static bool core2_builder_supports(struct dc *dc, enum bls_builder_name name,
builder->build.declarative &&
dc2_workaround_manager_get() &&
builder->check_capability(&check) &&
- core2_is_accelerated_mode(dc);
+ core2_query_accelerated_mode(dc);
}
/* Never falls back to legacy_commit_state_hw: DC2 must not call DC1. */
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 30/74] drm/amd/display: Ensure exit IPS before checking flip status
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (28 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 29/74] drm/amd/display: Read accelerated mode through a query builder Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 31/74] drm/amd/display: Updating missing assignments for DCN6 Wayne Lin
` (44 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Aric Cyr, Nicholas Kazlauskas
From: Aric Cyr <Aric.Cyr@amd.com>
[why]
In dc_post_update_surfaces_to_stream flip pending is checked, but HW
could be in IPS. Accessing it could lead to a hang.
[how]
Enforce exiting IPS before touching hardware.
Cc: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Signed-off-by: Aric Cyr <Aric.Cyr@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/core/dc.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 85de12c558ad..fd382363a0ea 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -2850,6 +2850,8 @@ void dc_post_update_surfaces_to_stream(struct dc *dc)
else {
TRACE_DCN_CLOCK_STATE(&context->bw_ctx.bw.dcn.clk);
+ dc_exit_ips_for_hw_access(dc);
+
if (is_flip_pending_in_pipes(dc, context))
return;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 31/74] drm/amd/display: Updating missing assignments for DCN6
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (29 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 30/74] drm/amd/display: Ensure exit IPS before checking flip status Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 32/74] drm/amd/display: update dcn42b based on dcn42 Wayne Lin
` (43 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Alvin Lee, Nevenko Stupar
From: Alvin Lee <Alvin.Lee2@amd.com>
[Description]
The cap for limiting vtotal by FP2 is missing on DCN6.
Add back the cap. Also assign the function pointer
for getting vstartup_start.
Cc: Nevenko Stupar <nevenko.stupar@amd.com>
Signed-off-by: Alvin Lee <Alvin.Lee2@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
index d068d5682619..1d7074451ac1 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn60/dcn60_resource.c
@@ -1958,7 +1958,8 @@ static struct resource_funcs dcn60_res_pool_funcs = {
.build_pipe_pix_clk_params = dcn60_build_pipe_pix_clk_params,
.get_power_profile = dcn60_get_power_profile,
.program_mcache_pipe_config = dcn60_program_mcache_pipe_config,
- .get_default_tiling_info = dcn401_get_default_tiling_info
+ .get_default_tiling_info = dcn401_get_default_tiling_info,
+ .get_vstartup_for_pipe = dcn401_get_vstartup_for_pipe
};
static uint32_t read_pipe_fuses(struct dc_context *ctx)
@@ -2057,6 +2058,7 @@ static bool dcn60_resource_construct(
dc->caps.dmcub_support = true;
dc->caps.utm_support = true;
dc->caps.max_v_total = (1 << 15) - 1;
+ dc->caps.vtotal_limited_by_fp2 = true;
pool->base.ctx = ctx;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 32/74] drm/amd/display: update dcn42b based on dcn42
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (30 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 31/74] drm/amd/display: Updating missing assignments for DCN6 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 33/74] drm/amd/display: Build the kept-plane surface update in DC2 Wayne Lin
` (42 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Charlene Liu, Gabe Teeger
From: Charlene Liu <Charlene.Liu@amd.com>
Align DCN42B clocks and power gating with DCN42:
1. On DCN42, re-read DTBCLK while it is already enabled and turn it
back on when the measured frequency drops below 590 MHz.
2. Align DCN42B HUBP/DPP power gating with DCN42 by disabling both.
3. Remove the unused DCN42B 300 MHz AC DCFCLK macro and the comment
that described a wall-power floor. The function still only clamps
a user force-min override to 200-600 MHz.
Cc: Gabe Teeger <gabe.teeger@amd.com>
Signed-off-by: Charlene Liu <Charlene.Liu@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c | 24 +++++++++----------
.../dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c | 11 +++------
.../dc/resource/dcn42b/dcn42b_resource.c | 4 ++--
3 files changed, 17 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
index 405145889ba2..d125ea196cb2 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
@@ -258,21 +258,21 @@ void dcn42_update_clocks(struct clk_mgr *clk_mgr_base,
dcn42_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW);
clk_mgr_base->clks.zstate_support = new_clocks->zstate_support;
}
- /* Only attempt to enable dtbclk if currently disabled AND new state requests it.
- * For dcn42b (no dtbclk hardware), init_clk_states sets dtbclk_en=false and
- * new_clocks->dtbclk_en should always be false, so this block never executes.
+ /* PMFW may stop dtbclk on idle, so re-read the actual frequency even when
+ * clks.dtbclk_en is already set, and re-enable it if it has dropped.
*/
- actual_dtbclk = dcn42_get_clock_freq_from_clkip(clk_mgr_base, clock_type_dtbclk);
-
- if (new_clocks->dtbclk_en && actual_dtbclk < 590000) {
-
- dcn42_update_clocks_update_dtb_dto(clk_mgr, context, new_clocks->ref_dtbclk_khz);
- dcn42_smu_set_dtbclk(clk_mgr, true);
+ if (new_clocks->dtbclk_en) {
actual_dtbclk = dcn42_get_clock_freq_from_clkip(clk_mgr_base, clock_type_dtbclk);
- if (actual_dtbclk > 590000) {
- clk_mgr_base->clks.ref_dtbclk_khz = new_clocks->ref_dtbclk_khz;
- clk_mgr_base->clks.dtbclk_en = new_clocks->dtbclk_en;
+ if (actual_dtbclk < 590000) {
+ dcn42_update_clocks_update_dtb_dto(clk_mgr, context, new_clocks->ref_dtbclk_khz);
+ dcn42_smu_set_dtbclk(clk_mgr, true);
+ actual_dtbclk = dcn42_get_clock_freq_from_clkip(clk_mgr_base, clock_type_dtbclk);
+
+ if (actual_dtbclk > 590000) {
+ clk_mgr_base->clks.ref_dtbclk_khz = new_clocks->ref_dtbclk_khz;
+ clk_mgr_base->clks.dtbclk_en = new_clocks->dtbclk_en;
+ }
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
index 33f0099075c0..e18d705103ba 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
@@ -393,18 +393,13 @@ uint32_t dcn42b_get_clock_freq_from_clkip(struct clk_mgr *clk_mgr_base, enum clo
#define DCN42B_MIN_DCFCLK_MHZ 200
#define DCN42B_MAX_DCFCLK_MHZ 600
-#define DCN42B_AC_MIN_DCFCLK_MHZ 300
/*
* dcn42b_update_clocks - DCN42B wrapper around dcn42_update_clocks.
*
- * Establishes the DCN42B force-min-DCFCLK floor consumed by the shared
- * dcn42_update_clocks(), which applies it to new_clocks->dcfclk_khz:
- * - a user override (dc->debug.force_min_dcfclk_mhz)
- * wins, sanitized to the [200, 600] MHz supported range;
- * - otherwise DCFCLK is floored at 300 MHz on wall power, and left
- * unconstrained on battery.
- * dc->debug is restored afterwards so the override survives AC/DC transitions.
+ * Sanitizes the user force-min-DCFCLK override (dc->debug.force_min_dcfclk_mhz)
+ * to the [200, 600] MHz range supported by DCN42B before the shared
+ * dcn42_update_clocks() applies it to new_clocks->dcfclk_khz.
*/
static void dcn42b_update_clocks(struct clk_mgr *clk_mgr_base,
struct dc_state *context,
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 784cce09b89b..338c506588d9 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
@@ -803,8 +803,8 @@ static const struct dc_debug_options debug_defaults_drv = {
.clock_trace = true,
.disable_pplib_clock_request = false,
.ignore_pg = false,
- .disable_dpp_power_gate = false,
- .disable_hubp_power_gate = false,
+ .disable_dpp_power_gate = true,
+ .disable_hubp_power_gate = true,
.disable_optc_power_gate = true,
.disable_dsc_power_gate = false,
.disable_dio_power_gate = true,
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 33/74] drm/amd/display: Build the kept-plane surface update in DC2
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (31 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 32/74] drm/amd/display: update dcn42b based on dcn42 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 34/74] drm/amd/display: Skip first-enable steps on a kept plane Wayne Lin
` (41 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
With DC2 selected, a full update that keeps one plane and changes its
surface or address has no builder.
[How]
Accept a kept plane on the virtual stream. Emit the surface and address
steps without enable_plane. Reject a scaling or viewport change.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../bls_builder/bls_builder_private_types.h | 2 +
.../dc/bls_builder/dcn401_commit_builder.c | 165 +++++++++++++++---
.../gpu/drm/amd/display/dc/core2/dc_core2.c | 2 +-
3 files changed, 142 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
index 2f3cb1457ebe..a3b27a179f20 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
@@ -115,6 +115,8 @@ struct dcn401_commit_step_meta {
bool optimized_not_required;
/* The plane has no address update, or skips the manual trigger. */
bool no_manual_trigger;
+ /* The OPP MPC tree has no plane yet. */
+ bool opp_tree_empty;
} skip_if;
};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
index b6208a0ad164..a636e2bfe042 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
@@ -33,11 +33,13 @@
* in head_sequence: the ODM double-buffer and blank waits come before the
* bandwidth lower, so no tail is needed.
*
- * DCN_DECL_SEAMLESS_UPDATE adds one plane to that stream. The sequence
- * follows the full update in commit_planes_for_stream on dcn401, all in
+ * DCN_DECL_SEAMLESS_UPDATE either adds one plane to that stream, or keeps
+ * one plane and programs its surface and address. The sequence follows
+ * the full update in commit_planes_for_stream on dcn401, all in
* head_sequence: the outstanding updates, prepare_bandwidth and the
* pending-update waits, then the locked front-end programming and
- * post_unlock_program_front_end. The bandwidth lower stays with
+ * post_unlock_program_front_end. A kept-plane update skips enable_plane
+ * and the stream unblank. The bandwidth lower stays with
* dc_post_update_surfaces_to_stream.
*
* Neither builder emits a tail_sequence. A tail runs only after a vsync,
@@ -592,7 +594,7 @@ static enum bls_builder_result dcn401_check_non_seamless_commit_support(
return BLS_BUILD_SUCCESS;
}
-/* The seamless update keeps the one stream and only adds its plane. */
+/* The seamless update keeps the one stream across the update. */
static bool check_stream_kept(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
@@ -603,15 +605,33 @@ static bool check_stream_kept(const struct dc *dc,
current_state->streams[0] == new_state->streams[0];
}
-static bool check_plane_added(const struct dc *dc,
- const struct dc_state *current_state,
+static bool dcn401_is_plane_add(const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
return !current_state->stream_status[0].plane_count &&
new_state->stream_status[0].plane_count == 1;
}
+static bool dcn401_is_plane_kept(const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ return current_state->stream_status[0].plane_count == 1 &&
+ new_state->stream_status[0].plane_count == 1;
+}
+
+/*
+ * The seamless update either adds the first plane, or keeps one plane and
+ * programs its surface and address. Scaling and viewport stay rejected.
+ */
+static bool check_plane_add_or_kept(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ return dcn401_is_plane_add(current_state, new_state) ||
+ dcn401_is_plane_kept(current_state, new_state);
+}
+
static bool check_same_otg_master_blocks(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
@@ -765,7 +785,11 @@ static bool check_no_flip_int(const struct dc *dc,
}
/* With an empty tree, DC1 removes no MPCC before the insert. */
-static bool check_opp_tree_empty(const struct dc *dc,
+/*
+ * Plane add needs an empty OPP tree. A kept-plane update removes the MPCC
+ * that is already there, so a non-empty tree is fine.
+ */
+static bool check_opp_tree_for_update(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
@@ -774,17 +798,47 @@ static bool check_opp_tree_empty(const struct dc *dc,
unsigned int i;
(void)dc;
- (void)current_state;
if (!pipe)
return false;
opp = pipe->stream_res.opp;
- if (!opp || opp->mpc_tree_params.opp_list)
+ if (!opp)
return false;
for (i = 0; i < MAX_PIPES; i++) {
if (opp->mpcc_disconnect_pending[i])
return false;
}
- return true;
+ if (dcn401_is_plane_kept(current_state, new_state))
+ return true;
+ return !opp->mpc_tree_params.opp_list;
+}
+
+/*
+ * A kept-plane update does not yet emit scaler or viewport steps. Plane
+ * add has no current plane, so those rects are not compared.
+ */
+static bool check_no_scale_or_viewport(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *current_pipe = dcn401_otg_of(current_state);
+ const struct pipe_ctx *new_pipe = dcn401_otg_of(new_state);
+ const struct dc_plane_state *current_plane;
+ const struct dc_plane_state *new_plane;
+
+ (void)dc;
+ if (dcn401_is_plane_add(current_state, new_state))
+ return true;
+ if (!current_pipe || !new_pipe || !current_pipe->plane_state ||
+ !new_pipe->plane_state)
+ return false;
+ current_plane = current_pipe->plane_state;
+ new_plane = new_pipe->plane_state;
+ return !memcmp(¤t_plane->src_rect, &new_plane->src_rect,
+ sizeof(current_plane->src_rect)) &&
+ !memcmp(¤t_plane->dst_rect, &new_plane->dst_rect,
+ sizeof(current_plane->dst_rect)) &&
+ !memcmp(¤t_plane->clip_rect, &new_plane->clip_rect,
+ sizeof(current_plane->clip_rect));
}
/* The update bypasses the plane gamut remap. */
@@ -982,31 +1036,33 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
if (result != BLS_BUILD_SUCCESS)
return result;
- /* The transition: one stream, kept, gains one plane on its OTG. */
+ /* The transition: one stream, kept; add a plane or keep one plane. */
CHECK_CONDITION(stream_kept, "stream is not kept across the update");
- CHECK_CONDITION(plane_added, "update does not add exactly one plane");
+ CHECK_CONDITION(plane_add_or_kept,
+ "update is not a plane add or a kept-plane surface update");
CHECK_CONDITION(same_otg_master_blocks, "otg or opp changes");
CHECK_RESOURCE(new_plane_blocks, "plane is missing hubp or dpp");
CHECK_RESOURCE(pool_funcs, "resource pool or hubbub has no funcs");
CHECK_CONDITION(plane_instances, "mpcc or dpp is not the hubp instance");
CHECK_CONDITION(det_by_segments, "hubbub programs det size");
+ CHECK_CONDITION(no_scale_or_viewport,
+ "scaling or viewport changes");
/* Under the lock, before the pipe is programmed. */
CHECK_CONDITION(triple_buffer, "triple buffer is disabled");
CHECK_CONDITION(no_sanity_checks, "sanity checks are enabled");
CHECK_CONDITION(no_pipe_wait, "pipe wait is required");
- /* Unblank. */
+ /* Unblank / enable_plane: only the plane-add path emits these. */
CHECK_CONDITION(no_test_pattern, "test pattern is set");
CHECK_CONDITION(no_forced_dpg, "dpg is forced");
CHECK_CONDITION(no_abm_pipe, "abm is set");
-
- /* enable_plane. */
CHECK_CONDITION(no_vm_aperture, "vm system aperture is valid");
CHECK_CONDITION(no_flip_int, "flip interrupt is enabled");
/* update_dchubp_dpp. */
- CHECK_CONDITION(opp_tree_empty, "opp tree is not empty");
+ CHECK_CONDITION(opp_tree_for_update,
+ "opp tree is not ready for the update");
CHECK_CONDITION(no_plane_gamut_remap, "plane gamut remap is set");
CHECK_CONDITION(no_stream_gamut_remap, "stream gamut remap is set");
CHECK_CONDITION(no_output_csc, "output csc is set");
@@ -1333,6 +1389,35 @@ static const struct dcn401_commit_step
{ .func = HUBP_SET_BLANK, .meta.hubp_set_blank.unblank = true },
};
+/*
+ * update_dchubp_dpp for a kept plane: surface and address only. No
+ * enable_plane, no scaler, no viewport. DC1 remove+insert when the OPP
+ * tree already holds the MPCC.
+ */
+static const struct dcn401_commit_step
+ bls_update_head_kept_update_dchubp_dpp[] = {
+ { .func = DPP_SETUP_DPP },
+ { .func = DPP_SET_CURSOR_MATRIX },
+ { .func = DPP_PROGRAM_BIAS_AND_SCALE },
+ { .func = MPC_REMOVE_MPCC,
+ .meta.skip_if.opp_tree_empty = true },
+ { .func = MPC_INSERT_PLANE },
+ { .func = MPC_UPDATE_VISUAL_CONFIRM },
+ { .func = HUBP_PROGRAM_MCACHE_ID },
+ { .func = MPC_SET_GAMUT_REMAP_BYPASS,
+ .meta.mpc_set_gamut_remap_bypass.block_id =
+ MPCC_MCM_FIRST_GAMUT_REMAP },
+ { .func = MPC_SET_GAMUT_REMAP_BYPASS,
+ .meta.mpc_set_gamut_remap_bypass.block_id =
+ MPCC_MCM_SECOND_GAMUT_REMAP },
+ { .func = MPC_SET_GAMUT_REMAP_BYPASS,
+ .meta.mpc_set_gamut_remap_bypass.block_id = MPCC_OGAM_GAMUT_REMAP },
+ { .func = MPC_POWER_ON_MPC_MEM_PWR },
+ { .func = MPC_SET_OCSC_DEFAULT },
+ { .func = HUBP_PROGRAM_SURFACE_CONFIG },
+ { .func = HUBP_UPDATE_PLANE_ADDR },
+};
+
/*
* set_hdr_multiplier, the input and MCM transfer functions, the output
* transfer function and the OPP format.
@@ -1385,7 +1470,18 @@ static_assert(
ARRAY_SIZE(bls_update_head_new_update_dchubp_dpp) +
ARRAY_SIZE(bls_update_head_new_transfer_funcs) +
ARRAY_SIZE(bls_update_head_new_unlock) <= DC2_BLS_MAX_STEPS,
- "dcn401 seamless update head exceeds DC2_BLS_MAX_STEPS");
+ "dcn401 seamless plane-add head exceeds DC2_BLS_MAX_STEPS");
+static_assert(
+ ARRAY_SIZE(
+ bls_update_head_per_stream_per_otg_master_pipe_preamble) +
+ ARRAY_SIZE(bls_update_head_global_preamble) +
+ ARRAY_SIZE(bls_head_per_state_new) +
+ ARRAY_SIZE(
+ bls_update_head_per_stream_per_otg_master_pipe_current_pending) +
+ ARRAY_SIZE(bls_update_head_new_lock) +
+ ARRAY_SIZE(bls_update_head_kept_update_dchubp_dpp) +
+ ARRAY_SIZE(bls_update_head_new_unlock) <= DC2_BLS_MAX_STEPS,
+ "dcn401 seamless kept-plane head exceeds DC2_BLS_MAX_STEPS");
/* pipe is NULL only for the bandwidth and p-state steps. */
struct dcn401_commit_step_args {
@@ -2209,6 +2305,10 @@ static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
(!args->pipe->plane_state->update_bits.addr_update ||
args->pipe->plane_state->skip_manual_trigger))
return true;
+ if (step->meta.skip_if.opp_tree_empty &&
+ (!args->pipe->stream_res.opp ||
+ !args->pipe->stream_res.opp->mpc_tree_params.opp_list))
+ return true;
return false;
}
@@ -2302,6 +2402,7 @@ static enum bls_builder_result dcn401_build_seamless_update(
struct dcn401_commit_step_args state_args;
struct block_sequence_state head;
enum bls_builder_result result;
+ bool plane_add;
if (!params)
return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
@@ -2310,6 +2411,8 @@ static enum bls_builder_result dcn401_build_seamless_update(
if (result != BLS_BUILD_SUCCESS)
return result;
+ plane_add = dcn401_is_plane_add(params->current_state,
+ params->new_state);
dcn401_init_and_unpack_params(params, &state_args, &head);
DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
state_args.current_state,
@@ -2324,14 +2427,24 @@ static enum bls_builder_result dcn401_build_seamless_update(
/* One stream, so adding each table per stream keeps the DC1 order. */
DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
bls_update_head_new_lock);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
- bls_update_head_new_stream);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
- bls_update_head_new_enable_plane);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
- bls_update_head_new_update_dchubp_dpp);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
- bls_update_head_new_transfer_funcs);
+ if (plane_add) {
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ state_args.new_state,
+ bls_update_head_new_stream);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ state_args.new_state,
+ bls_update_head_new_enable_plane);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ state_args.new_state,
+ bls_update_head_new_update_dchubp_dpp);
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ state_args.new_state,
+ bls_update_head_new_transfer_funcs);
+ } else {
+ DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ state_args.new_state,
+ bls_update_head_kept_update_dchubp_dpp);
+ }
DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
bls_update_head_new_unlock);
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
index 295d2d8b277e..85d9aa1614f6 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
@@ -295,7 +295,7 @@ static bool core2_update_state_supported(
if (scratch->update_type != UPDATE_TYPE_FULL ||
scratch->flow != UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS)
return false;
- /* The full-update builder adds one plane. */
+ /* The full-update builder adds or keeps one plane. */
if (scratch->surface_count != 1)
return false;
if (!core2_stream_update_supported(scratch->stream_update))
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 34/74] drm/amd/display: Skip first-enable steps on a kept plane
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (32 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 33/74] drm/amd/display: Build the kept-plane surface update in DC2 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 35/74] drm/amd/display: Enable DRR stutter constraint relaxation on DCN42 Wayne Lin
` (40 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
The seamless builder branched on an internal plane-add check, and
rejected an update because the OPP tree was non-empty. A builder
check has to name a logical state change. Reconfiguring a HUBP/DPP
is a subset of enabling it.
[How]
Drop the OPP-tree support check and the plane-add branch. One step
table covers both. skip_if leaves out first-enable steps when the
HUBP/DPP is already on, and skips the MPCC remove when the tree is
empty. Rename the per-stream adder to add per OTG master.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../bls_builder/bls_builder_private_types.h | 5 +
.../dc/bls_builder/dcn401_commit_builder.c | 254 ++++++++----------
2 files changed, 117 insertions(+), 142 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
index a3b27a179f20..df86ff7d4867 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
@@ -117,6 +117,11 @@ struct dcn401_commit_step_meta {
bool no_manual_trigger;
/* The OPP MPC tree has no plane yet. */
bool opp_tree_empty;
+ /*
+ * The HUBP/DPP block is already on. The step is part of
+ * enabling that block the first time, not of reconfiguring it.
+ */
+ bool hubp_dpp_already_enabled;
} skip_if;
};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
index a636e2bfe042..d071768bda03 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
@@ -784,34 +784,6 @@ static bool check_no_flip_int(const struct dc *dc,
!pipe->plane_state->flip_int_enabled;
}
-/* With an empty tree, DC1 removes no MPCC before the insert. */
-/*
- * Plane add needs an empty OPP tree. A kept-plane update removes the MPCC
- * that is already there, so a non-empty tree is fine.
- */
-static bool check_opp_tree_for_update(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
- const struct output_pixel_processor *opp;
- unsigned int i;
-
- (void)dc;
- if (!pipe)
- return false;
- opp = pipe->stream_res.opp;
- if (!opp)
- return false;
- for (i = 0; i < MAX_PIPES; i++) {
- if (opp->mpcc_disconnect_pending[i])
- return false;
- }
- if (dcn401_is_plane_kept(current_state, new_state))
- return true;
- return !opp->mpc_tree_params.opp_list;
-}
-
/*
* A kept-plane update does not yet emit scaler or viewport steps. Plane
* add has no current plane, so those rects are not compared.
@@ -1061,8 +1033,6 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
CHECK_CONDITION(no_flip_int, "flip interrupt is enabled");
/* update_dchubp_dpp. */
- CHECK_CONDITION(opp_tree_for_update,
- "opp tree is not ready for the update");
CHECK_CONDITION(no_plane_gamut_remap, "plane gamut remap is set");
CHECK_CONDITION(no_stream_gamut_remap, "stream gamut remap is set");
CHECK_CONDITION(no_output_csc, "output csc is set");
@@ -1330,79 +1300,82 @@ static const struct dcn401_commit_step bls_update_head_new_lock[] = {
{ .func = DC_UDELAY },
};
-/* blank_pixel_data (unblank), dcn401_program_tg and update_odm. */
+/*
+ * blank_pixel_data (unblank), dcn401_program_tg and update_odm.
+ * First enable of the HUBP/DPP only.
+ */
static const struct dcn401_commit_step bls_update_head_new_stream[] = {
{ .func = OPP_SET_DISP_PATTERN_GENERATOR,
- .meta.opp_set_disp_pattern_generator.unblank = true },
- { .func = TG_PROGRAM_GLOBAL_SYNC },
- { .func = TG_WAIT_FOR_STATE },
- { .func = TG_SET_VTG_PARAMS },
- { .func = TG_SETUP_VERTICAL_INTERRUPT2 },
- { .func = OPTC_SET_ODM_BYPASS },
- { .func = OPP_PIPE_CLOCK_CONTROL },
- { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
+ .meta.opp_set_disp_pattern_generator.unblank = true,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = TG_PROGRAM_GLOBAL_SYNC,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = TG_WAIT_FOR_STATE,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = TG_SET_VTG_PARAMS,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = TG_SETUP_VERTICAL_INTERRUPT2,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = OPTC_SET_ODM_BYPASS,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = OPP_PIPE_CLOCK_CONTROL,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
};
/* enable_plane, then the watermark propagate and the DET segments. */
static const struct dcn401_commit_step bls_update_head_new_enable_plane[] = {
- { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true },
- { .func = HUBP_PG_CONTROL, .meta.hubp_pg_control.power_on = true },
- { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false },
- { .func = HUBP_CLK_CNTL, .meta.hubp_clk_cntl.enable = true },
- { .func = HUBP_INIT },
- { .func = OPP_PIPE_CLOCK_CONTROL },
- { .func = HUBBUB_FORCE_WM_PROPAGATE_TO_PIPES },
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_PG_CONTROL, .meta.hubp_pg_control.power_on = true,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_CLK_CNTL, .meta.hubp_clk_cntl.enable = true,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_INIT,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = OPP_PIPE_CLOCK_CONTROL,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBBUB_FORCE_WM_PROPAGATE_TO_PIPES,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = HUBP_PROGRAM_DET_SEGMENTS,
- .meta.hubp_program_det_segments.allocate = true },
-};
-
-/* update_dchubp_dpp, with the MPC insert from dcn401_update_mpcc. */
-static const struct dcn401_commit_step
- bls_update_head_new_update_dchubp_dpp[] = {
- { .func = DPP_DPPCLK_CONTROL, .meta.dpp_dppclk_control.enable = true },
- { .func = DCCG_UPDATE_DPP_DTO },
- { .func = HUBP_VTG_SEL },
- { .func = HUBP_SETUP2 },
- { .func = HUBP_SET_UNBOUNDED_REQUESTING },
- { .func = HUBP_SETUP_INTERDEPENDENT2 },
- { .func = DPP_SETUP_DPP },
- { .func = DPP_SET_CURSOR_MATRIX },
- { .func = DPP_PROGRAM_BIAS_AND_SCALE },
- /* insert_plane programs the blending; no update_blending follows. */
- { .func = MPC_INSERT_PLANE },
- { .func = MPC_UPDATE_VISUAL_CONFIRM },
- { .func = DPP_SET_SCALER },
- { .func = HUBP_MEM_PROGRAM_VIEWPORT },
- { .func = HUBP_PROGRAM_MCACHE_ID },
- { .func = MPC_SET_GAMUT_REMAP_BYPASS,
- .meta.mpc_set_gamut_remap_bypass.block_id =
- MPCC_MCM_FIRST_GAMUT_REMAP },
- { .func = MPC_SET_GAMUT_REMAP_BYPASS,
- .meta.mpc_set_gamut_remap_bypass.block_id =
- MPCC_MCM_SECOND_GAMUT_REMAP },
- { .func = MPC_SET_GAMUT_REMAP_BYPASS,
- .meta.mpc_set_gamut_remap_bypass.block_id = MPCC_OGAM_GAMUT_REMAP },
- { .func = MPC_POWER_ON_MPC_MEM_PWR },
- { .func = MPC_SET_OCSC_DEFAULT },
- { .func = HUBP_PROGRAM_SURFACE_CONFIG },
- { .func = HUBP_UPDATE_PLANE_ADDR },
- { .func = HUBP_SET_BLANK, .meta.hubp_set_blank.unblank = true },
+ .meta.hubp_program_det_segments.allocate = true,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
};
/*
- * update_dchubp_dpp for a kept plane: surface and address only. No
- * enable_plane, no scaler, no viewport. DC1 remove+insert when the OPP
- * tree already holds the MPCC.
+ * update_dchubp_dpp. First enable runs the whole table. A reconfigure
+ * skips the steps that only bring the HUBP/DPP up, and removes the MPCC
+ * when the OPP tree already holds one.
*/
static const struct dcn401_commit_step
- bls_update_head_kept_update_dchubp_dpp[] = {
+ bls_update_head_new_update_dchubp_dpp[] = {
+ { .func = DPP_DPPCLK_CONTROL, .meta.dpp_dppclk_control.enable = true,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = DCCG_UPDATE_DPP_DTO,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_VTG_SEL,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_SETUP2,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_SET_UNBOUNDED_REQUESTING,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_SETUP_INTERDEPENDENT2,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = DPP_SETUP_DPP },
{ .func = DPP_SET_CURSOR_MATRIX },
{ .func = DPP_PROGRAM_BIAS_AND_SCALE },
{ .func = MPC_REMOVE_MPCC,
.meta.skip_if.opp_tree_empty = true },
+ /* insert_plane programs the blending; no update_blending follows. */
{ .func = MPC_INSERT_PLANE },
{ .func = MPC_UPDATE_VISUAL_CONFIRM },
+ { .func = DPP_SET_SCALER,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_MEM_PROGRAM_VIEWPORT,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = HUBP_PROGRAM_MCACHE_ID },
{ .func = MPC_SET_GAMUT_REMAP_BYPASS,
.meta.mpc_set_gamut_remap_bypass.block_id =
@@ -1416,29 +1389,43 @@ static const struct dcn401_commit_step
{ .func = MPC_SET_OCSC_DEFAULT },
{ .func = HUBP_PROGRAM_SURFACE_CONFIG },
{ .func = HUBP_UPDATE_PLANE_ADDR },
+ { .func = HUBP_SET_BLANK, .meta.hubp_set_blank.unblank = true,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
};
/*
* set_hdr_multiplier, the input and MCM transfer functions, the output
- * transfer function and the OPP format.
+ * transfer function and the OPP format. First enable only.
*/
static const struct dcn401_commit_step
bls_update_head_new_transfer_funcs[] = {
- { .func = DPP_SET_HDR_MULTIPLIER },
- { .func = DPP_SET_PRE_DEGAM },
- { .func = DPP_PROGRAM_GAMCOR_LUT },
- { .func = MPC_SET_MOVABLE_CM_LOCATION },
+ { .func = DPP_SET_HDR_MULTIPLIER,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = DPP_SET_PRE_DEGAM,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = DPP_PROGRAM_GAMCOR_LUT,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = MPC_SET_MOVABLE_CM_LOCATION,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = MPC_PROGRAM_LUT_MODE,
- .meta.mpc_program_lut_mode.id = MCM_LUT_1DLUT },
+ .meta.mpc_program_lut_mode.id = MCM_LUT_1DLUT,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = MPC_PROGRAM_LUT_MODE,
- .meta.mpc_program_lut_mode.id = MCM_LUT_SHAPER },
+ .meta.mpc_program_lut_mode.id = MCM_LUT_SHAPER,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = MPC_PROGRAM_LUT_MODE,
- .meta.mpc_program_lut_mode.id = MCM_LUT_3DLUT },
- { .func = HUBP_PROGRAM_3DLUT_FL_CONFIG },
- { .func = HUBP_SET_3DLUT_FL },
- { .func = MPC_SET_OUTPUT_GAMMA },
- { .func = OPP_SET_DYN_EXPANSION },
- { .func = OPP_PROGRAM_FMT },
+ .meta.mpc_program_lut_mode.id = MCM_LUT_3DLUT,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_PROGRAM_3DLUT_FL_CONFIG,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = HUBP_SET_3DLUT_FL,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = MPC_SET_OUTPUT_GAMMA,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = OPP_SET_DYN_EXPANSION,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
+ { .func = OPP_PROGRAM_FMT,
+ .meta.skip_if.hubp_dpp_already_enabled = true },
};
/*
@@ -1470,18 +1457,7 @@ static_assert(
ARRAY_SIZE(bls_update_head_new_update_dchubp_dpp) +
ARRAY_SIZE(bls_update_head_new_transfer_funcs) +
ARRAY_SIZE(bls_update_head_new_unlock) <= DC2_BLS_MAX_STEPS,
- "dcn401 seamless plane-add head exceeds DC2_BLS_MAX_STEPS");
-static_assert(
- ARRAY_SIZE(
- bls_update_head_per_stream_per_otg_master_pipe_preamble) +
- ARRAY_SIZE(bls_update_head_global_preamble) +
- ARRAY_SIZE(bls_head_per_state_new) +
- ARRAY_SIZE(
- bls_update_head_per_stream_per_otg_master_pipe_current_pending) +
- ARRAY_SIZE(bls_update_head_new_lock) +
- ARRAY_SIZE(bls_update_head_kept_update_dchubp_dpp) +
- ARRAY_SIZE(bls_update_head_new_unlock) <= DC2_BLS_MAX_STEPS,
- "dcn401 seamless kept-plane head exceeds DC2_BLS_MAX_STEPS");
+ "dcn401 seamless update head exceeds DC2_BLS_MAX_STEPS");
/* pipe is NULL only for the bandwidth and p-state steps. */
struct dcn401_commit_step_args {
@@ -2309,6 +2285,10 @@ static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
(!args->pipe->stream_res.opp ||
!args->pipe->stream_res.opp->mpc_tree_params.opp_list))
return true;
+ if (step->meta.skip_if.hubp_dpp_already_enabled &&
+ args->current_state->stream_count &&
+ args->current_state->stream_status[0].plane_count)
+ return true;
return false;
}
@@ -2325,7 +2305,7 @@ static void dcn401_add_steps(struct block_sequence_state *sequence,
}
/* The support check found an OTG master for each stream of state. */
-static void dcn401_add_steps_per_stream_per_otg_master_pipe(
+static void dcn401_add_per_otg_master(
struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *state_args,
struct dc_state *state,
@@ -2343,9 +2323,9 @@ static void dcn401_add_steps_per_stream_per_otg_master_pipe(
#define DCN401_ADD_STEPS(sequence, args, steps) \
dcn401_add_steps(sequence, args, steps, ARRAY_SIZE(steps))
-#define DCN401_ADD_STEPS_PER_STREAM(sequence, args, state, steps) \
- dcn401_add_steps_per_stream_per_otg_master_pipe(sequence, args, \
- state, steps, ARRAY_SIZE(steps))
+#define DCN401_ADD_PER_OTG_MASTER(sequence, args, state, steps) \
+ dcn401_add_per_otg_master(sequence, args, state, steps, \
+ ARRAY_SIZE(steps))
/*
* Points the step arguments at the states and the sequence at
@@ -2384,12 +2364,12 @@ static enum bls_builder_result dcn401_build_non_seamless_commit(
dcn401_init_and_unpack_params(params, &state_args, &head);
DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
state_args.current_state,
bls_head_per_stream_per_otg_master_pipe_current);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
bls_head_per_stream_per_otg_master_pipe_new);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
bls_head_per_stream_per_otg_master_pipe_new_front_end);
DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new_postamble);
@@ -2402,7 +2382,6 @@ static enum bls_builder_result dcn401_build_seamless_update(
struct dcn401_commit_step_args state_args;
struct block_sequence_state head;
enum bls_builder_result result;
- bool plane_add;
if (!params)
return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
@@ -2411,41 +2390,32 @@ static enum bls_builder_result dcn401_build_seamless_update(
if (result != BLS_BUILD_SUCCESS)
return result;
- plane_add = dcn401_is_plane_add(params->current_state,
- params->new_state);
dcn401_init_and_unpack_params(params, &state_args, &head);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
state_args.current_state,
bls_update_head_per_stream_per_otg_master_pipe_preamble);
DCN401_ADD_STEPS(&head, &state_args,
bls_update_head_global_preamble);
DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
state_args.current_state,
bls_update_head_per_stream_per_otg_master_pipe_current_pending);
- /* One stream, so adding each table per stream keeps the DC1 order. */
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ /*
+ * One stream. skip_if drops first-enable steps when the HUBP/DPP
+ * is already on, so a kept-plane surface update is the same table.
+ */
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
bls_update_head_new_lock);
- if (plane_add) {
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
- state_args.new_state,
- bls_update_head_new_stream);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
- state_args.new_state,
- bls_update_head_new_enable_plane);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
- state_args.new_state,
- bls_update_head_new_update_dchubp_dpp);
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
- state_args.new_state,
- bls_update_head_new_transfer_funcs);
- } else {
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args,
- state_args.new_state,
- bls_update_head_kept_update_dchubp_dpp);
- }
- DCN401_ADD_STEPS_PER_STREAM(&head, &state_args, state_args.new_state,
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
+ bls_update_head_new_stream);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
+ bls_update_head_new_enable_plane);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
+ bls_update_head_new_update_dchubp_dpp);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
+ bls_update_head_new_transfer_funcs);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
bls_update_head_new_unlock);
return BLS_BUILD_SUCCESS;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 35/74] drm/amd/display: Enable DRR stutter constraint relaxation on DCN42
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (33 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 34/74] drm/amd/display: Skip first-enable steps on a kept plane Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 36/74] drm/amd/display: detect static screen state and propagate Wayne Lin
` (39 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Dillon Varone
From: Dillon Varone <Dillon.Varone@amd.com>
why:
Fixed-refresh DRR extends blanking and can use a relaxed stutter
constraint to improve Z8 optimization eligibility.
how:
Add the wrapper PMO option for DRR stutter constraint relaxation.
Pass the option into DML21 initialization and enable it for DCN42.
Signed-off-by: Dillon Varone <Dillon.Varone@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c | 3 +++
drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_wrapper.h | 1 +
drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c | 2 ++
3 files changed, 6 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
index 6a9f822b2b33..dc65750a7fe3 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
@@ -40,6 +40,9 @@ static void dml21_populate_pmo_options(struct dml2_pmo_options *pmo_options,
pmo_options->force_mandatory_uclk_pstate_support = config->pmo.force_mandatory_uclk_pstate_support;
pmo_options->force_optional_uclk_pstate_support = in_dc->debug.force_optional_uclk_pstate_support;
+
+ // stutter
+ pmo_options->relax_stutter_constraint_for_drr = config->pmo.relax_stutter_constraint_for_drr;
}
static enum dml2_project_id dml21_dcn_revision_to_dml2_project_id(const struct dc *in_dc)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_wrapper.h b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_wrapper.h
index 68ae013b026e..c1eb8cdfc9a4 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_wrapper.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml2_wrapper.h
@@ -242,6 +242,7 @@ struct dml2_configuration_options {
struct {
bool force_mandatory_uclk_pstate_support;
bool force_pstate_method_enable;
+ bool relax_stutter_constraint_for_drr;
enum dml2_force_pstate_methods force_pstate_method_values[MAX_PIPES];
} pmo;
bool map_dc_pipes_with_callbacks;
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 c01614005281..a6fd956d12e9 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
@@ -2468,6 +2468,8 @@ static bool dcn42_resource_construct(
dc->dml2_options.pmo.force_mandatory_uclk_pstate_support = true;
+ dc->dml2_options.pmo.relax_stutter_constraint_for_drr = true;
+
/* SPL */
dc->caps.scl_caps.sharpener_support = true;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 36/74] drm/amd/display: detect static screen state and propagate
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (34 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 35/74] drm/amd/display: Enable DRR stutter constraint relaxation on DCN42 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 37/74] drm/amd/display: Allow DC_PSR_MASK to enable PSR without DMUB PSR cap Wayne Lin
` (38 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Sreeja Golui, Dillon Varone
From: Sreeja Golui <Sreeja.Golui@amd.com>
Why:
Need an extra flag to track when vblank is being stretched in idle
scenarios
How:
Track static screen with the new flag and pass it to dml layer.
Requires the DML DRR member and stutter-gate change in dml.
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Sreeja Golui <Sreeja.Golui@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/core/dc.c | 7 +++++++
drivers/gpu/drm/amd/display/dc/dc_stream.h | 3 +++
.../dml2_wrapper/dml21_wrapper/dml21_translation_helper.c | 1 +
3 files changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index fd382363a0ea..6f2f219a928a 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -3300,6 +3300,10 @@ static struct dc_update_descriptor check_update_surfaces_for_stream(
stream_update->vrr_active_variable || stream_update->vrr_active_fixed))
su_flags->bits.fams_changed = 1;
+ /* Static-screen transitions change DML stutter constraints even if VRR stays variable. */
+ if (stream_update->vrr_active_idle_static_screen)
+ su_flags->bits.fams_changed = 1;
+
if (stream_update->scaler_sharpener_update) {
su_flags->bits.scaler_sharpener = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_STREAM);
@@ -3669,6 +3673,9 @@ static void copy_stream_update_to_stream(struct dc *dc,
if (update->vrr_active_fixed)
stream->vrr_active_fixed = *update->vrr_active_fixed;
+ if (update->vrr_active_idle_static_screen)
+ stream->vrr_active_idle_static_screen = *update->vrr_active_idle_static_screen;
+
if (update->crtc_timing_adjust) {
if (stream->adjust.v_total_min != update->crtc_timing_adjust->v_total_min ||
stream->adjust.v_total_max != update->crtc_timing_adjust->v_total_max)
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index 7999a7d84a77..94eccb87cbde 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -256,6 +256,8 @@ struct dc_stream_state {
bool vrr_active_variable;
bool freesync_on_desktop;
bool vrr_active_fixed;
+ /* Ramless Idle static screen; variable VRR still permits cursor boosting. */
+ bool vrr_active_idle_static_screen;
bool converter_disable_audio;
uint8_t qs_bit;
@@ -367,6 +369,7 @@ struct dc_stream_update {
bool *allow_freesync;
bool *vrr_active_variable;
bool *vrr_active_fixed;
+ bool *vrr_active_idle_static_screen;
struct colorspace_transform *gamut_remap;
enum dc_color_space *output_color_space;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
index dc65750a7fe3..2e3ad7152f3f 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
@@ -170,6 +170,7 @@ static void populate_dml21_timing_config_from_stream_state(struct dml2_timing_cf
timing->drr_config.enabled = stream->ignore_msa_timing_param;
timing->drr_config.drr_active_variable = stream->vrr_active_variable;
timing->drr_config.drr_active_fixed = stream->vrr_active_fixed;
+ timing->drr_config.drr_active_idle_static_screen = stream->vrr_active_idle_static_screen;
timing->drr_config.disallowed = !stream->allow_freesync;
/* limit min refresh rate to DC cap */
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 37/74] drm/amd/display: Allow DC_PSR_MASK to enable PSR without DMUB PSR cap
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (35 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 36/74] drm/amd/display: detect static screen state and propagate Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 38/74] drm/amd/display: Fix DSC teardown gating in post-unlock BLS sequence Wayne Lin
` (37 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Ray Wu, Hugo Sales
From: Ray Wu <ray.wu@amd.com>
[Why]
amdgpu_dm_set_psr_caps() requires dc->caps.dmub_caps.psr, which is only
populated on DCN IPs that query DMUB feature caps in init_hw. DCN 1.x,
2.x and 3.0.1 never do, so PSR can no longer be enabled there even with
amdgpu.dcfeaturemask=0x8.
[How]
Skip the DMUB PSR cap check when DC_PSR_MASK is set, and add a KUnit
case for this path.
Tested-by: Hugo Sales <hugo@hsal.es>
Cc: ChiaHsuan Chung <chiahsuan.chung@amd.com>
Signed-off-by: Ray Wu <ray.wu@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c | 3 +-
.../amdgpu_dm/tests/amdgpu_dm_psr_test.c | 35 ++++++++++++++++++-
2 files changed, 36 insertions(+), 2 deletions(-)
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 dbad0a6d3a2b..295b1c307fa3 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
@@ -111,7 +111,8 @@ bool amdgpu_dm_set_psr_caps(struct dc_link *link, struct amdgpu_dm_connector *ac
/* Reset psr version first */
link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
- if (!dc->caps.dmub_caps.psr)
+ /* DCN IPs that never query DMUB feature caps rely on DC_PSR_MASK to opt in */
+ if (!dc->caps.dmub_caps.psr && !(amdgpu_dc_feature_mask & DC_PSR_MASK))
return false;
if (!(link->connector_signal & SIGNAL_TYPE_EDP))
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 0a484ea7c0e8..c084e4f85f95 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
@@ -230,15 +230,47 @@ 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);
+ unsigned int old_feature_mask;
+ bool ret;
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));
+ old_feature_mask = amdgpu_dm_psr_get_dc_feature_mask();
+ amdgpu_dm_psr_set_dc_feature_mask(old_feature_mask & ~DC_PSR_MASK);
+
+ ret = amdgpu_dm_set_psr_caps(link, aconnector);
+ amdgpu_dm_psr_set_dc_feature_mask(old_feature_mask);
+
+ KUNIT_EXPECT_FALSE(test, ret);
KUNIT_EXPECT_EQ(test, link->psr_settings.psr_version,
DC_PSR_VERSION_UNSUPPORTED);
}
+/**
+ * dm_test_set_psr_caps_feature_mask_no_dmub_psr() - DC_PSR_MASK opts in without DMUB PSR cap.
+ * @test: KUnit test context.
+ */
+static void dm_test_set_psr_caps_feature_mask_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);
+ unsigned int old_feature_mask;
+ bool ret;
+
+ link->ctx->dc->caps.dmub_caps.psr = false;
+
+ old_feature_mask = amdgpu_dm_psr_get_dc_feature_mask();
+ amdgpu_dm_psr_set_dc_feature_mask(old_feature_mask | DC_PSR_MASK);
+
+ ret = amdgpu_dm_set_psr_caps(link, aconnector);
+ amdgpu_dm_psr_set_dc_feature_mask(old_feature_mask);
+
+ KUNIT_EXPECT_TRUE(test, ret);
+ KUNIT_EXPECT_NE(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.
@@ -712,6 +744,7 @@ static struct kunit_case dm_psr_test_cases[] = {
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_feature_mask_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),
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 38/74] drm/amd/display: Fix DSC teardown gating in post-unlock BLS sequence
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (36 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 37/74] drm/amd/display: Allow DC_PSR_MASK to enable PSR without DMUB PSR cap Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 39/74] drm/amd/display: Enable HDCP on DCN6 Wayne Lin
` (36 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Rafal Ostrowski, Alvin Lee
From: Rafal Ostrowski <rafal.ostrowski@amd.com>
[Why]
- The builder passed a NULL is_ungated, so these steps never programmed
under BLS: DSC_WAIT_DISCONNECT_PENDING_CLEAR, DSC_DISABLE,
DCCG_SET_REF_DSCCLK. The immediate path does program them.
[How]
- Store the DSC_PG_STATUS result in the new dsc->pg_ungated; the three
steps read it at execute time.
- Pass the DSC pointer to the four adders instead of dsc_inst and
is_ungated.
- hwss_add_dsc_pg_status returns whether it emitted a step; if not, the
builder emits no dependent steps.
- Adders validate their arguments and hooks; executors only program.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Rafal Ostrowski <rafal.ostrowski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 81 +++++++++----------
drivers/gpu/drm/amd/display/dc/dc_dsc.h | 1 +
.../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 20 +++--
.../amd/display/dc/hwss/dcn60/dcn60_hwseq.c | 9 +++
.../amd/display/dc/hwss/dcn60/dcn60_hwseq.h | 1 +
.../amd/display/dc/hwss/dcn60/dcn60_init.c | 2 +-
.../drm/amd/display/dc/hwss/hw_sequencer.h | 22 ++---
7 files changed, 66 insertions(+), 70 deletions(-)
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 32a33d29835e..c5e8a88d051d 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
@@ -4033,22 +4033,16 @@ void hwss_disable_phantom_crtc(union block_sequence_params *params)
void hwss_dsc_pg_status(union block_sequence_params *params)
{
struct dce_hwseq *hws = params->dsc_pg_status_params.hws;
- int dsc_inst = params->dsc_pg_status_params.dsc_inst;
+ struct display_stream_compressor *dsc = params->dsc_pg_status_params.dsc;
- if (hws && hws->funcs.dsc_pg_status)
- params->dsc_pg_status_params.is_ungated = hws->funcs.dsc_pg_status(hws, dsc_inst);
+ dsc->pg_ungated = hws->funcs.dsc_pg_status(hws, dsc->inst);
}
void hwss_dsc_wait_disconnect_pending_clear(union block_sequence_params *params)
{
struct display_stream_compressor *dsc = params->dsc_wait_disconnect_pending_clear_params.dsc;
- if (!params->dsc_wait_disconnect_pending_clear_params.is_ungated)
- return;
- if (*params->dsc_wait_disconnect_pending_clear_params.is_ungated == false)
- return;
-
- if (dsc && dsc->funcs->dsc_wait_disconnect_pending_clear)
+ if (dsc->pg_ungated)
dsc->funcs->dsc_wait_disconnect_pending_clear(dsc);
}
@@ -4056,27 +4050,17 @@ void hwss_dsc_disable(union block_sequence_params *params)
{
struct display_stream_compressor *dsc = params->dsc_disable_params.dsc;
- if (!params->dsc_disable_params.is_ungated)
- return;
- if (*params->dsc_disable_params.is_ungated == false)
- return;
-
- if (dsc && dsc->funcs->dsc_disable)
+ if (dsc->pg_ungated)
dsc->funcs->dsc_disable(dsc);
}
void hwss_dccg_set_ref_dscclk(union block_sequence_params *params)
{
struct dccg *dccg = params->dccg_set_ref_dscclk_params.dccg;
- int dsc_inst = params->dccg_set_ref_dscclk_params.dsc_inst;
-
- if (!params->dccg_set_ref_dscclk_params.is_ungated)
- return;
- if (*params->dccg_set_ref_dscclk_params.is_ungated == false)
- return;
+ const struct display_stream_compressor *dsc = params->dccg_set_ref_dscclk_params.dsc;
- if (dccg && dccg->funcs->set_ref_dscclk)
- dccg->funcs->set_ref_dscclk(dccg, dsc_inst);
+ if (dsc->pg_ungated)
+ dccg->funcs->set_ref_dscclk(dccg, dsc->inst);
}
void hwss_dpp_pg_control(union block_sequence_params *params)
@@ -5457,54 +5441,65 @@ void hwss_add_disable_phantom_crtc(struct block_sequence_state *seq_state,
}
}
-void hwss_add_dsc_pg_status(struct block_sequence_state *seq_state,
+bool hwss_add_dsc_pg_status(struct block_sequence_state *seq_state,
struct dce_hwseq *hws,
- int dsc_inst,
- bool is_ungated)
+ struct display_stream_compressor *dsc)
{
- if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
- seq_state->steps[*seq_state->num_steps].func = DSC_PG_STATUS;
- seq_state->steps[*seq_state->num_steps].params.dsc_pg_status_params.hws = hws;
- seq_state->steps[*seq_state->num_steps].params.dsc_pg_status_params.dsc_inst = dsc_inst;
- seq_state->steps[*seq_state->num_steps].params.dsc_pg_status_params.is_ungated = is_ungated;
- (*seq_state->num_steps)++;
- }
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps ||
+ !hws || !hws->funcs.dsc_pg_status || !dsc)
+ return false;
+
+ if (*seq_state->num_steps >= MAX_HWSS_BLOCK_SEQUENCE_SIZE)
+ return false;
+
+ seq_state->steps[*seq_state->num_steps].func = DSC_PG_STATUS;
+ seq_state->steps[*seq_state->num_steps].params.dsc_pg_status_params.hws = hws;
+ seq_state->steps[*seq_state->num_steps].params.dsc_pg_status_params.dsc = dsc;
+ (*seq_state->num_steps)++;
+
+ return true;
}
void hwss_add_dsc_wait_disconnect_pending_clear(struct block_sequence_state *seq_state,
- struct display_stream_compressor *dsc,
- bool *is_ungated)
+ struct display_stream_compressor *dsc)
{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps ||
+ !dsc || !dsc->funcs || !dsc->funcs->dsc_wait_disconnect_pending_clear)
+ return;
+
if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
seq_state->steps[*seq_state->num_steps].func = DSC_WAIT_DISCONNECT_PENDING_CLEAR;
seq_state->steps[*seq_state->num_steps].params.dsc_wait_disconnect_pending_clear_params.dsc = dsc;
- seq_state->steps[*seq_state->num_steps].params.dsc_wait_disconnect_pending_clear_params.is_ungated = is_ungated;
(*seq_state->num_steps)++;
}
}
void hwss_add_dsc_disable(struct block_sequence_state *seq_state,
- struct display_stream_compressor *dsc,
- bool *is_ungated)
+ struct display_stream_compressor *dsc)
{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps ||
+ !dsc || !dsc->funcs || !dsc->funcs->dsc_disable)
+ return;
+
if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
seq_state->steps[*seq_state->num_steps].func = DSC_DISABLE;
seq_state->steps[*seq_state->num_steps].params.dsc_disable_params.dsc = dsc;
- seq_state->steps[*seq_state->num_steps].params.dsc_disable_params.is_ungated = is_ungated;
(*seq_state->num_steps)++;
}
}
void hwss_add_dccg_set_ref_dscclk(struct block_sequence_state *seq_state,
struct dccg *dccg,
- int dsc_inst,
- bool *is_ungated)
+ const struct display_stream_compressor *dsc)
{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps ||
+ !dccg || !dccg->funcs || !dccg->funcs->set_ref_dscclk || !dsc)
+ return;
+
if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
seq_state->steps[*seq_state->num_steps].func = DCCG_SET_REF_DSCCLK;
seq_state->steps[*seq_state->num_steps].params.dccg_set_ref_dscclk_params.dccg = dccg;
- seq_state->steps[*seq_state->num_steps].params.dccg_set_ref_dscclk_params.dsc_inst = dsc_inst;
- seq_state->steps[*seq_state->num_steps].params.dccg_set_ref_dscclk_params.is_ungated = is_ungated;
+ seq_state->steps[*seq_state->num_steps].params.dccg_set_ref_dscclk_params.dsc = dsc;
(*seq_state->num_steps)++;
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dsc.h b/drivers/gpu/drm/amd/display/dc/dc_dsc.h
index de169ed3b3b7..99592d5bb3c4 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dsc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_dsc.h
@@ -43,6 +43,7 @@ struct display_stream_compressor {
const struct dsc_funcs *funcs;
struct dc_context *ctx;
int inst;
+ bool pg_ungated;
};
struct dc_dsc_policy {
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 f14cba867111..cc78b4794cef 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
@@ -1828,7 +1828,8 @@ static void dcn401_add_dsc_sequence_for_odm_change(struct dc *dc, struct dc_stat
if (otg_master->stream_res.dsc && otg_master->stream->timing.flags.DSC) {
if (!hwss_add_dsc_sequence_for_stream(seq_state, otg_master,
otg_master->dsc_padding_params.dsc_hactive_padding, NULL)) {
- ASSERT(false);
+ /* DSC sequence is deliberately skipped while the stream is dpms off */
+ ASSERT(otg_master->stream->dpms_off);
return;
}
} else if (otg_master->stream_res.dsc && !otg_master->stream->timing.flags.DSC) {
@@ -3635,12 +3636,10 @@ void dcn401_post_unlock_reset_opp_sequence(
if (dc->hwss.wait_for_mpcc_disconnect_sequence)
dc->hwss.wait_for_mpcc_disconnect_sequence(dc, dc->res_pool, opp_head, seq_state);
- if (dsc) {
- bool *is_ungated = NULL;
- /* Check DSC power gate status */
- if (dc->hwseq && dc->hwseq->funcs.dsc_pg_status)
- hwss_add_dsc_pg_status(seq_state, dc->hwseq, dsc->inst, false);
-
+ /* Check DSC power gate status; the result is kept in dsc->pg_ungated and
+ * gates the steps below at execute time.
+ */
+ if (dsc && hwss_add_dsc_pg_status(seq_state, dc->hwseq, dsc)) {
/* Seamless update specific where we will postpone non
* double buffered DSCCLK disable logic in post unlock
* sequence after DSC is disconnected from OPP but not
@@ -3648,14 +3647,13 @@ void dcn401_post_unlock_reset_opp_sequence(
*/
/* DSC wait disconnect pending clear */
- hwss_add_dsc_wait_disconnect_pending_clear(seq_state, dsc, is_ungated);
+ hwss_add_dsc_wait_disconnect_pending_clear(seq_state, dsc);
/* DSC disable */
- hwss_add_dsc_disable(seq_state, dsc, is_ungated);
+ hwss_add_dsc_disable(seq_state, dsc);
/* Set reference DSCCLK */
- if (dccg && dccg->funcs->set_ref_dscclk)
- hwss_add_dccg_set_ref_dscclk(seq_state, dccg, dsc->inst, 0);
+ hwss_add_dccg_set_ref_dscclk(seq_state, dccg, dsc);
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c
index f8254dfe1ce7..8a38b7f0bef0 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.c
@@ -1332,3 +1332,12 @@ bool dcn60_apply_idle_power_optimizations(struct dc *dc, bool enable)
return true;
}
+
+bool dcn60_dsc_pg_status(struct dce_hwseq *hws, unsigned int dsc_inst)
+{
+ (void)hws;
+ (void)dsc_inst;
+
+ /* DCN6.0 has no DSC power-gate domain, so the DSC is always ungated. */
+ return true;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h
index b880988ff1fc..b88f4304b1b9 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_hwseq.h
@@ -29,6 +29,7 @@ void dcn60_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_idx
void dcn60_program_perfmon(struct dc *dc, struct dc_state *context);
bool dcn60_apply_idle_power_optimizations(struct dc *dc, bool enable);
+bool dcn60_dsc_pg_status(struct dce_hwseq *hws, unsigned int dsc_inst);
bool dcn60_set_rmcm_luts(struct set_input_transfer_func_params *params);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c
index 9fde7d1be3e5..d5c8dcc86a9f 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn60/dcn60_init.c
@@ -159,7 +159,7 @@ static const struct hwseq_private_funcs dcn60_private_funcs = {
.update_odm = dcn401_update_odm,
.update_odm_sequence = dcn401_update_odm_sequence,
.dsc_pg_control = NULL,
- .dsc_pg_status = NULL,
+ .dsc_pg_status = dcn60_dsc_pg_status,
.set_hdr_multiplier = dcn10_set_hdr_multiplier,
.set_hdr_multiplier_sequence = dcn401_set_hdr_multiplier_sequence,
.wait_for_blank_complete = dcn20_wait_for_blank_complete,
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 6996ce12a17d..c4f3ec532572 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -1037,24 +1037,20 @@ struct dc_ip_request_cntl_params {
struct dsc_pg_status_params {
struct dce_hwseq *hws;
- int dsc_inst;
- bool is_ungated;
+ struct display_stream_compressor *dsc;
};
struct dsc_wait_disconnect_pending_clear_params {
struct display_stream_compressor *dsc;
- bool *is_ungated;
};
struct dsc_disable_params {
struct display_stream_compressor *dsc;
- bool *is_ungated;
};
struct dccg_set_ref_dscclk_params {
struct dccg *dccg;
- int dsc_inst;
- bool *is_ungated;
+ const struct display_stream_compressor *dsc;
};
struct dccg_update_dpp_dto_params {
@@ -2919,23 +2915,19 @@ void hwss_add_dpp_dppclk_control(struct block_sequence_state *seq_state,
void hwss_add_disable_phantom_crtc(struct block_sequence_state *seq_state,
struct timing_generator *tg);
-void hwss_add_dsc_pg_status(struct block_sequence_state *seq_state,
+bool hwss_add_dsc_pg_status(struct block_sequence_state *seq_state,
struct dce_hwseq *hws,
- int dsc_inst,
- bool is_ungated);
+ struct display_stream_compressor *dsc);
void hwss_add_dsc_wait_disconnect_pending_clear(struct block_sequence_state *seq_state,
- struct display_stream_compressor *dsc,
- bool *is_ungated);
+ struct display_stream_compressor *dsc);
void hwss_add_dsc_disable(struct block_sequence_state *seq_state,
- struct display_stream_compressor *dsc,
- bool *is_ungated);
+ struct display_stream_compressor *dsc);
void hwss_add_dccg_set_ref_dscclk(struct block_sequence_state *seq_state,
struct dccg *dccg,
- int dsc_inst,
- bool *is_ungated);
+ const struct display_stream_compressor *dsc);
bool hwss_is_dpp_root_clock_control_allowed(struct dce_hwseq *hws, bool clock_on);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 39/74] drm/amd/display: Enable HDCP on DCN6
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (37 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 38/74] drm/amd/display: Fix DSC teardown gating in post-unlock BLS sequence Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 40/74] drm/amd/display: enable DF C-state vote on DCN42B Wayne Lin
` (35 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Alvin Lee
From: Ivan Lipski <ivan.lipski@amd.com>
[Why&How]
Enable HDCP on DCN6 by
- Enabling dc_is_dmub_outbox_supported if fused_io (fw locality)
is supported
- Plumbing DTM TA v3 support to it.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 3 ++-
.../amdgpu_dm/tests/amdgpu_dm_hdcp_test.c | 27 +++++++++++++++++++
drivers/gpu/drm/amd/display/dc/core/dc.c | 4 +++
3 files changed, 33 insertions(+), 1 deletion(-)
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 47cbc826ed45..3cf96cc38575 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
@@ -883,7 +883,8 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,
dc->ctx->dce_version == DCN_VERSION_3_5 ||
dc->ctx->dce_version == DCN_VERSION_3_51 ||
dc->ctx->dce_version == DCN_VERSION_3_6 ||
- dc->ctx->dce_version == DCN_VERSION_4_01)
+ dc->ctx->dce_version == DCN_VERSION_4_01 ||
+ dc->ctx->dce_version == DCN_VERSION_6_0)
config->psp.caps.dtm_v3_supported = 1;
config->ddc.handle = dc_get_link_at_index(dc, i);
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 5af6ae47bb21..b0691a83d3da 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
@@ -1247,6 +1247,32 @@ static void dm_test_hdcp_create_workqueue_sets_dtm_v3_for_dcn31(struct kunit *te
hdcp_destroy(&adev->dev->kobj, work);
}
+/**
+ * dm_test_hdcp_create_workqueue_sets_dtm_v3_for_dcn60 - DCN 6.0 enables DTM v3
+ * @test: KUnit test context
+ *
+ * On a DCN 6.0 device, hdcp_create_workqueue() must set the per-link
+ * dtm_v3_supported cap.
+ */
+static void dm_test_hdcp_create_workqueue_sets_dtm_v3_for_dcn60(struct kunit *test)
+{
+ struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
+ struct cp_psp cp_psp = {0};
+ struct dc *dc = dm_kunit_alloc_dc_with_ctx(test);
+ struct hdcp_workqueue *work;
+
+ adev->dev = adev->ddev.dev;
+ dc->caps.max_links = 1;
+ dc->ctx->dce_version = DCN_VERSION_6_0;
+
+ work = hdcp_create_workqueue(adev, &cp_psp, dc);
+
+ KUNIT_ASSERT_NOT_NULL(test, work);
+ KUNIT_EXPECT_EQ(test, work[0].hdcp.config.psp.caps.dtm_v3_supported, 1);
+
+ hdcp_destroy(&adev->dev->kobj, work);
+}
+
/**
* dm_test_hdcp_create_workqueue_initializes_all_links - loop covers every link
* @test: KUnit test context
@@ -2564,6 +2590,7 @@ static struct kunit_case dm_hdcp_test_cases[] = {
KUNIT_CASE(dm_test_hdcp_create_workqueue_zero_max_links_returns_null),
KUNIT_CASE(dm_test_hdcp_create_workqueue_initializes_work),
KUNIT_CASE(dm_test_hdcp_create_workqueue_sets_dtm_v3_for_dcn31),
+ KUNIT_CASE(dm_test_hdcp_create_workqueue_sets_dtm_v3_for_dcn60),
KUNIT_CASE(dm_test_hdcp_create_workqueue_initializes_all_links),
/* hdcp_destroy() */
KUNIT_CASE(dm_test_hdcp_destroy_frees_and_removes_sysfs),
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 6f2f219a928a..656a941b1ba0 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -6850,6 +6850,10 @@ bool dc_is_dmub_outbox_supported(struct dc *dc)
break;
}
+ /* Fused IO replies (e.g. HDCP FW locality check) arrive via outbox notifications */
+ if (dc->caps.fused_io_supported)
+ return true;
+
/* dmub aux needs dmub notifications to be enabled */
return dc->debug.enable_dmub_aux_for_legacy_ddc;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 40/74] drm/amd/display: enable DF C-state vote on DCN42B
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (38 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 39/74] drm/amd/display: Enable HDCP on DCN6 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 41/74] drm/amd/display: Increase the DCN42 DDR5 stutter latency Wayne Lin
` (34 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Charlene Liu, Gabe Teeger
From: Charlene Liu <Charlene.Liu@amd.com>
why:
DCN42B shares the DCN42 hardware sequencer, so dcn42_prepare_bandwidth,
dcn42_hardware_release and dc_power_down_on_boot already try to vote DCN's
DF C-state policy through clk_mgr_funcs::notify_cstate_disable. The DCN42B
clk_mgr vtable never populated that callback, so every one of those call
sites was silently a no-op and DCN was never registered as a client of
PMFW's DF C-state arbiter on DCN42B. The PMFW side of the contract
(DALSMC_MSG_DfCstateDisable) is now releasing, so DCN42B can join the
arbiter instead of relying on the local DCHUBBUB watermark force selector.
how:
DCN42B already reuses the DCN42 SMU interface, so no new PMFW plumbing is
needed - only the DCN42B clk_mgr needs to opt in:
- hook dcn42_notify_cstate_disable into dcn42b_funcs
- clear dc_clocks.cstate_allow in dcn42b_init_clocks so D0 entry resyncs
DAL with PMFW without sending a message, matching dcn42_init_clocks
- record the newly shared function in the dcn42b_clk_mgr.h reuse list
Also reword the DF C-state comment in dcn42_init_clocks, and its new DCN42B
copy. Comment only, no functional change.
No behaviour change for DCN42 or for DCN42B parts without SMU, since
dcn42_smu_set_df_cstate_disable() is a no-op when smu_present is false.
Cc: Gabe Teeger <gabe.teeger@amd.com>
Signed-off-by: Charlene Liu <Charlene.Liu@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c | 4 ++--
.../drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c | 10 ++++++++++
.../drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.h | 1 +
3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
index d125ea196cb2..b1fbadd0d885 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
@@ -628,8 +628,8 @@ DC_LOGGER_INIT(clk_mgr_base->ctx->logger);
* DF C-state policy
* D0 entry must NOT send a PMFW message, but must unconditionally clear
* the cached vote so the next allow-side transition (prepare_bandwidth
- * or dc_power_down_on_boot) is guaranteed to issue a fresh
- * DfCstateDisable(Allow) and resync DAL with PMFW.
+ * or dc_power_down_on_boot) always issues a fresh DfCstateDisable(Allow)
+ * and resyncs DAL with PMFW.
*/
clk_mgr_base->clks.cstate_allow = false;
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
index e18d705103ba..57291c10c917 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
@@ -233,6 +233,15 @@ DC_LOGGER_INIT(clk_mgr_base->ctx->logger);
init_clk_states(clk_mgr_base);
+ /*
+ * DF C-state policy
+ * D0 entry must NOT send a PMFW message, but must unconditionally clear
+ * the cached vote so the next allow-side transition (prepare_bandwidth
+ * or dc_power_down_on_boot) always issues a fresh DfCstateDisable(Allow)
+ * and resyncs DAL with PMFW.
+ */
+ clk_mgr_base->clks.cstate_allow = false;
+
// to adjust dp_dto reference clock if ssc is enable otherwise to apply dprefclk
if (dcn42_is_spll_ssc_enabled(clk_mgr_base))
clk_mgr_base->dp_dto_source_clock_in_khz =
@@ -433,6 +442,7 @@ static struct clk_mgr_funcs dcn42b_funcs = {
.get_max_clock_khz = dcn42_get_max_clock_khz,
.get_dispclk_from_dentist = dcn42_get_dispclk_from_dentist,
.is_smu_present = dcn42_is_smu_present,
+ .notify_cstate_disable = dcn42_notify_cstate_disable,
};
typedef struct {
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.h
index 52ab87cdc7b6..ae871d43dbcf 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.h
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.h
@@ -13,6 +13,7 @@
* - dcn42_get_dpm_table_from_smu (identical, only SMU calls)
* - dcn42_are_clock_states_equal
* - dcn42_enable_pme_wa
+ * - dcn42_notify_cstate_disable (same DALSMC DfCstateDisable message)
* - dcn42_update_clocks_update_dpp_dto
* - dcn42_update_clocks_update_dtb_dto
* - dcn42_build_watermark_ranges
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 41/74] drm/amd/display: Increase the DCN42 DDR5 stutter latency
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (39 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 40/74] drm/amd/display: enable DF C-state vote on DCN42B Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 42/74] drm/amd/display: [FW Promotion] Release 0.1.83.0 Wayne Lin
` (33 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Charlene Liu
From: Charlene Liu <Charlene.Liu@amd.com>
Raise the DCN42 DDR5 stutter enter and exit latency so the bandwidth
model budgets enough time to avoid underflow.
Cc: Roman Li <roman.li@amd.com>
Signed-off-by: Charlene Liu <Charlene.Liu@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h
index 139e0152fcd6..876a6f1cad74 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42_soc_bb.h
@@ -224,8 +224,8 @@ static struct dml2_soc_power_management_parameters dcn42_ddr5_power_management_p
.dram_clk_change_blackout_us = 36,
.fclk_change_blackout_us = 0,
.g7_ppt_blackout_us = 0,
- .stutter_enter_plus_exit_latency_us = 25.0,
- .stutter_exit_latency_us = 23.0,
+ .stutter_enter_plus_exit_latency_us = 28.0,
+ .stutter_exit_latency_us = 26.0,
.z8_stutter_enter_plus_exit_latency_us = 300,
.z8_stutter_exit_latency_us = 200,
};
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 42/74] drm/amd/display: [FW Promotion] Release 0.1.83.0
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (40 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 41/74] drm/amd/display: Increase the DCN42 DDR5 stutter latency Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 43/74] drm/amd/display: Promote DC to 3.2.406 Wayne Lin
` (32 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Taimur Hassan
From: Taimur Hassan <Syed.Hassan@amd.com>
Updated DMUB interface
- Extend the SoC bounding box with urgent bandwidth targets, pipe and
output counts, return-buffer sizes, and FAMS2 delay and timeout values
- Add RAMLESS_LOBF commands to copy static settings and to enable,
disable, or wake the feature
- Carry a seamless-boot eDP request in the DCN init command
Signed-off-by: Taimur Hassan <Syed.Hassan@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 203 +++++++++++++++++-
1 file changed, 197 insertions(+), 6 deletions(-)
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 8684b1398d4e..c3aa0d0a74be 100644
--- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
+++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
@@ -408,6 +408,42 @@ struct dmub_soc_bb_params {
uint32_t vmin_limit_dispclk_khz;
uint32_t vmin_limit_dcfclk_khz;
uint32_t g7_temperature_read_blackout_ns;
+ uint32_t fraction_of_urgent_bandwidth_nominal_target;
+ uint32_t fraction_of_urgent_bandwidth_flip_target;
+ uint32_t max_outstanding_reqs;
+ uint32_t return_bus_width_bytes;
+ uint32_t alternate_dram_carveout_size_mb;
+ uint32_t pipe_count;
+ uint32_t otg_count;
+ uint32_t num_dsc;
+ uint32_t TDLUT_33cube_count;
+ uint32_t max_num_dp2p0_streams;
+ uint32_t max_num_hdmi_frl_outputs;
+ uint32_t max_num_dp2p0_outputs;
+ uint32_t rob_buffer_size_kbytes;
+ uint32_t config_return_buffer_size_in_kbytes;
+ uint32_t config_return_buffer_segment_size_in_kbytes;
+ uint32_t meta_fifo_size_in_kentries;
+ uint32_t compressed_buffer_segment_size_in_kbytes;
+ uint32_t cursor_buffer_size;
+ uint32_t max_flip_time_us;
+ uint32_t max_flip_time_lines;
+ uint32_t hostvm_mode;
+ uint32_t max_vactive_det_fill_delay_us;
+ uint32_t ppt_max_allow_delay_us;
+ uint32_t temp_read_max_allow_delay_us;
+ uint32_t vblank_nom_default_us;
+ uint32_t fams2_max_allow_delay_us;
+ uint32_t fams2_scheduling_delay_us;
+ uint32_t fams2_vertical_interrupt_ack_delay_us;
+ uint32_t fams2_allow_programming_delay_us;
+ uint32_t fams2_min_allow_width_us;
+ uint32_t fams2_subvp_df_throttle_delay_us;
+ uint32_t fams2_subvp_programming_delay_us;
+ uint32_t fams2_drr_programming_delay_us;
+ uint32_t fams2_lock_timeout_us;
+ uint32_t fams2_recovery_timeout_us;
+ uint32_t fams2_flip_programming_delay_us;
};
#pragma pack(pop)
@@ -1193,7 +1229,7 @@ enum dmub_dc_bls_command_type {
*/
DMUB_CMD__DC_BLS_DCHVM_INIT = 0,
/**
- * Runs the fixed DCN hardware init sequence.
+ * Initializes the DCN hardware.
*/
DMUB_CMD__DC_BLS_DCN_INIT = 1,
};
@@ -2071,6 +2107,11 @@ enum dmub_cmd_type {
*/
DMUB_CMD__NBIF_AZ_PME_RESTORE = 99,
+ /**
+ * Command type use for all RAMLESS_LOBF commands.
+ */
+ DMUB_CMD__RAMLESS_LOBF = 100,
+
/**
* Command type use for VBIOS shared commands.
*/
@@ -7732,15 +7773,157 @@ struct dmub_rb_cmd_boot_time_crc_init {
struct dmub_cmd_boot_time_crc_init_data data;
};
+enum ramless_lobf_enable_cmd {
+ LOBF_DISABLE = 0, /**< Disable Ramless LOBF */
+ LOBF_ENABLE = 1, /**< Enable Ramless LOBF */
+ LOBF_IMMEDIATE_WAKE = 2, /**< Immediate wake (program_manual_trigger) */
+ LOBF_ENTER_IDLE_SCREEN = 3, /**< Enter idle screen */
+ LOBF_EXIT_IDLE_SCREEN = 4, /**< Exit idle screen */
+};
+
+/**
+ * enum ramless_lobf_phy_mode - PHY power-state mode for Ramless LOBF.
+ */
+enum ramless_lobf_phy_mode {
+ LOBF_PHY_MODE_P0_ON = 0, /**< PHY P0: lanes and PLL fully powered (normal operation) */
+ LOBF_PHY_MODE_P2_PLL_ON = 1, /**< PHY P2: lanes off, PLL still running (shallow sleep) */
+ LOBF_PHY_MODE_P3_PG = 2, /**< PHY P3: lanes and PLL power-gated (deep sleep) */
+};
+
+/**
+ * enum dmub_cmd_ramless_lobf_type - Sub-command types for DMUB_CMD__RAMLESS_LOBF.
+ */
+enum dmub_cmd_ramless_lobf_type {
+ DMUB_CMD__RAMLESS_LOBF_COPY_SETTINGS = 0, /**< Copy static settings from driver to FW */
+ DMUB_CMD__RAMLESS_LOBF_ENABLE = 1, /**< Enable or disable Ramless LOBF */
+};
+
+/**
+ * Data passed from driver to FW in a DMUB_CMD__RAMLESS_LOBF_ENABLE command.
+ */
+struct dmub_cmd_ramless_lobf_enable_data {
+ /**
+ * @enable: 1 to enable LOBF, 0 to disable, 2 to force immediate wake (program_manual_trigger).
+ */
+ uint8_t enable_command;
+ /**
+ * @panel_inst: Panel instance index.
+ */
+ uint8_t panel_inst;
+ /**
+ * @pad: Align structure to 4-byte boundary.
+ */
+ uint8_t pad[2];
+
+ uint32_t wake_source;
+};
+
+/**
+ * Definition of a DMUB_CMD__RAMLESS_LOBF_ENABLE command.
+ */
+struct dmub_rb_cmd_ramless_lobf_enable {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+ /**
+ * Data for enable/disable.
+ */
+ struct dmub_cmd_ramless_lobf_enable_data data;
+};
+
+/**
+ * Data passed from driver to FW in a DMUB_CMD__RAMLESS_LOBF_COPY_SETTINGS command.
+ */
+struct dmub_cmd_ramless_lobf_copy_settings_data {
+ /**
+ * @panel_inst: Panel instance index.
+ */
+ uint8_t panel_inst;
+ /**
+ * @otg_inst: OTG hardware instance.
+ */
+ uint8_t otg_inst;
+ /**
+ * @digfe_inst: DIG front-end hardware instance.
+ */
+ uint8_t digfe_inst;
+ /**
+ * @digbe_inst: DIG back-end hardware instance.
+ */
+ uint8_t digbe_inst;
+ /**
+ * @dpphy_inst: DP PHY hardware instance.
+ */
+ uint8_t dpphy_inst;
+ /**
+ * @aux_inst: AUX hardware instance.
+ */
+ uint8_t aux_inst;
+ /**
+ * @phy_mode: PHY power-state mode. Use values from enum ramless_lobf_phy_mode.
+ */
+ uint8_t phy_mode;
+ /**
+ * @enable_scenarios: Bitmask of enabled scenarios for Ramless LOBF.
+ */
+ uint8_t enable_scenarios;
+ /**
+ * @line_time_in_ns: Total length of one horizontal line (active + blanking) in
+ * nanoseconds. Used to calculate wake up timer IRQ.
+ */
+ uint32_t line_time_in_ns;
+ /**
+ * @vactive: Number of active video lines in the display mode.
+ */
+ uint32_t vactive;
+
+ uint16_t reserved_0;
+ uint16_t lobf_min_sleep_time_us; /* if < min, then we don't want LOBF to sleep */
+ uint8_t debug_options;
+ uint8_t vmin_force_default_frames;
+ uint8_t visual_confirm;
+ uint8_t reserved[1];
+ /**
+ * Use for AUX-less ALPM LFPS wake operation
+ */
+ struct dmub_alpm_auxless_data auxless_alpm_data;
+};
+
+/**
+ * Definition of a DMUB_CMD__RAMLESS_LOBF_COPY_SETTINGS command.
+ */
+struct dmub_rb_cmd_ramless_lobf_copy_settings {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+ /**
+ * Data passed from driver to FW.
+ */
+ struct dmub_cmd_ramless_lobf_copy_settings_data data;
+};
+
+/**
+ * Data passed from driver to FW in a DMUB_CMD__DC_BLS_DCN_INIT command.
+ */
+struct dmub_cmd_dc_bls_dcn_init_data {
+ /** 1 to keep the boot eDP stream for seamless boot. */
+ uint8_t seamless_boot_edp_requested : 1;
+ /** Reserved. Must be zero. */
+ uint8_t reserved_bits : 7;
+ /** Reserved. Must be zero. */
+ uint8_t reserved[3];
+};
+
/**
* Definition of a DMUB_CMD__DC_BLS_DCN_INIT command.
- *
- * Runs a fixed DCN hardware init sequence after the pre-OS to post-OS
- * transition. Carries no payload; anything driver-configurable is
- * programmed by the driver after this command completes.
*/
struct dmub_rb_cmd_dc_bls_dcn_init {
- struct dmub_cmd_header header; /**< Command header */
+ /** Command header. */
+ struct dmub_cmd_header header;
+ /** Data passed from driver to FW in a DMUB_CMD__DC_BLS_DCN_INIT command. */
+ struct dmub_cmd_dc_bls_dcn_init_data data;
};
/**
@@ -8164,6 +8347,14 @@ union dmub_rb_cmd {
struct dmub_rb_cmd_panel_polarity_reset panel_polarity_reset;
struct dmub_rb_cmd_dc_bls_dchvm_init dc_bls_dchvm_init;
+ /**
+ * Definition of a DMUB_CMD__RAMLESS_LOBF_COPY_SETTINGS command.
+ */
+ struct dmub_rb_cmd_ramless_lobf_copy_settings ramless_lobf_copy_settings;
+ /**
+ * Definition of a DMUB_CMD__RAMLESS_LOBF_ENABLE command.
+ */
+ struct dmub_rb_cmd_ramless_lobf_enable ramless_lobf_enable;
};
/**
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 43/74] drm/amd/display: Promote DC to 3.2.406
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (41 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 42/74] drm/amd/display: [FW Promotion] Release 0.1.83.0 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 44/74] drm/amd/display: Fix KUnit tiled EDID size Wayne Lin
` (31 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Taimur Hassan
From: Taimur Hassan <Syed.Hassan@amd.com>
This version brings along the following updates:
- Build DCN401 virtual commits and seamless plane updates in DC2, and
dispatch the hardware span of a commit through dc2_funcs.
- Add the leaf block-sequence entries a virtual commit needs, and read
the accelerated-mode bit through a query builder.
- Resolve watermark policy and the DPP, DTBCLK and DCN401 DENTIST
targets when the sequence is built.
- Fix DSC teardown gating in the post-unlock sequence.
- Move HDMI FRL status polling outside dc_lock and stop it on fini.
- Relax stutter constraints for DRR streams, including ramless idle,
track static-screen idle, and enable the relaxation on DCN42. Raise
the DCN42 DDR5 stutter latency.
- Let DC_PSR_MASK enable PSR without a DMUB PSR cap.
- Enable HDCP on DCN6 and the DF C-state vote on DCN42B.
- Enable SST PCON FRL DSC passthrough and program the correct DP HPO
APG instance.
- Exit IPS before checking flip pending, and keep the HPD IRQ reset
path from taking dc_lock.
- Skip UPSP programming when the registers are absent, and skip
visual confirm color programming when firmware owns it.
- Align DCN42B with DCN42 and extend the IOMMU mismatch workaround to
DCN42B. Restore the missing DCN6 vtotal cap and vstartup assignment.
- Update the DMUB interface to 0.1.83.0 with an extended SoC bounding
box, RAMLESS_LOBF commands, and a seamless-boot eDP request.
- Initialize the DMUB LSDMA ring, accept a 1932-byte PSP footer, and
update the default VCO frequency.
- Fix the FFE link-rate change path and the p-state enable sequence.
Commit the validated target when a minimal transition cannot be
built.
- Various cleanups.
Signed-off-by: Taimur Hassan <Syed.Hassan@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/dc.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 916fa87d0dbc..2dff1b271416 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -69,7 +69,7 @@ struct dcn_dsc_reg_state;
struct dcn_optc_reg_state;
struct dcn_dccg_reg_state;
-#define DC_VER "3.2.405"
+#define DC_VER "3.2.406"
/**
* MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 44/74] drm/amd/display: Fix KUnit tiled EDID size
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (42 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 43/74] drm/amd/display: Promote DC to 3.2.406 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-12 7:17 ` Eren Özen
2026-11-11 1:28 ` [PATCH 45/74] drm/amd/display: fix hubbub read state logging Wayne Lin
` (30 subsequent siblings)
74 siblings, 1 reply; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Qi
From: Alex Hung <alex.hung@amd.com>
[WHAT]
dm_test_detect_hides_secondary_tile fails because its synthesized
DisplayID tiled display block is 21 bytes, built around an 8-byte
topology ID. Upstream commit faaa1e115583 ("drm/edid: fix OOB read in
drm_parse_tiled_block()") now ignores tiled blocks shorter than the
spec's 22 bytes, so has_tile stays false and detect reports connected.
[HOW]
Grow the tiled block to 22 bytes with a 9-byte topology ID, bump the
DisplayID section length and recompute its checksum.
Cc: Tony Qi <haoming.qi@amd.com>
Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../display/amdgpu_dm/tests/amdgpu_dm_connector_test.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
index addc407841a2..805c1bc2ad9e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
@@ -4894,7 +4894,8 @@ static void dm_test_detect_no_sink(struct kunit *test)
* A 256-byte EDID whose DisplayID extension carries a tiled display
* topology block placing this connector at tile location (h=1, v=0), i.e.
* a secondary tile. drm_edid_connector_update() parses it to set has_tile
- * and the non-origin tile location.
+ * and the non-origin tile location. The block carries the full 22-byte
+ * payload (9-byte topology ID); DRM ignores shorter tiled blocks.
*/
static const u8 dm_test_detect_tile_edid[256] = {
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x00, 0x00,
@@ -4907,10 +4908,10 @@ static const u8 dm_test_detect_tile_edid[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x70, 0x12, 0x18, 0x00,
- 0x00, 0x12, 0x00, 0x15, 0x00, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x70, 0x12, 0x19, 0x00,
+ 0x00, 0x12, 0x00, 0x16, 0x00, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 45/74] drm/amd/display: fix hubbub read state logging
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (43 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 44/74] drm/amd/display: Fix KUnit tiled EDID size Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 46/74] drm/amd/display: Keep panel power across link-loss retrain Wayne Lin
` (29 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Karen Chen, Ovidiu Bunea
From: Karen Chen <Karen.Chen@amd.com>
[how & why]
In function hubbub2_read_state, it stores the LSB
into the MSB field and never fills vm_fault_addr_lsb.
Cc: Ovidiu Bunea <ovidiu.bunea@amd.com>
Signed-off-by: Karen Chen <Karen.Chen@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c
index 46e43a51ab22..464fc5924a56 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn20/dcn20_hubbub.c
@@ -634,7 +634,7 @@ void hubbub2_read_state(struct hubbub *hubbub, struct dcn_hubbub_state *hubbub_s
hubbub_state->vm_fault_addr_msb = REG_READ(DCN_VM_FAULT_ADDR_MSB);
if (REG(DCN_VM_FAULT_ADDR_LSB))
- hubbub_state->vm_fault_addr_msb = REG_READ(DCN_VM_FAULT_ADDR_LSB);
+ hubbub_state->vm_fault_addr_lsb = REG_READ(DCN_VM_FAULT_ADDR_LSB);
if (REG(DCN_VM_FAULT_CNTL))
REG_GET(DCN_VM_FAULT_CNTL, DCN_VM_ERROR_STATUS_MODE, &hubbub_state->vm_error_mode);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 46/74] drm/amd/display: Keep panel power across link-loss retrain
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (44 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 45/74] drm/amd/display: fix hubbub read state logging Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 47/74] drm/amd/display: Don't shrink zero-area recout in full viewport visual confirm Wayne Lin
` (28 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Charlene Liu, Leo Chen
From: Charlene Liu <Charlene.Liu@amd.com>
[Why]
Link-loss retrain turns the link off and back on. Powering the eDP
panel off here drops LCDVDD and the panel must wait before the
picture can return.
[How]
dp_handle_hpd_rx_irq() sets skip_implict_edp_power_control around
dp_handle_link_loss() and restores the old value. The flag also skips
backlight-off and the DPCD sink power-down. The path runs for non-eDP
link loss, and for eDP only when replay ESD recovery is needed.
Cc: Leo Chen <leo.chen@amd.com>
Signed-off-by: Charlene Liu <Charlene.Liu@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/dc/link/protocols/link_dp_irq_handler.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
index da679fb7d89c..59f9150edf30 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
@@ -576,8 +576,14 @@ bool dp_handle_hpd_rx_irq(struct dc_link *link,
if (defer_handling && has_left_work)
*has_left_work = true;
- else
+ else {
+ bool skip_implict_edp_power_control = link->skip_implict_edp_power_control;
+
+ /*skip LCDVDD power off for retraining*/
+ link->skip_implict_edp_power_control = true;
dp_handle_link_loss(link);
+ link->skip_implict_edp_power_control = skip_implict_edp_power_control;
+ }
status = false;
if (out_link_loss)
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 47/74] drm/amd/display: Don't shrink zero-area recout in full viewport visual confirm
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (45 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 46/74] drm/amd/display: Keep panel power across link-loss retrain Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 48/74] drm/amd/display: Keep patched HDMI panels on FRL across detect Wayne Lin
` (27 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Alvin Lee, Samson Tam
From: Alvin Lee <Alvin.Lee2@amd.com>
The visual confirm overlay shrinks the recout height by a base/dpp offset.
Prior to visual_confirm_full_viewport, this shrink ran only inside the
overlap > 0 branch of calculate_recout; a pipe with no overlap on its ODM
slice (e.g. an MPO window entirely on a single slice) had its recout zeroed
and was left untouched, so both RECOUT_WIDTH and RECOUT_HEIGHT programmed 0.
The full viewport path moved the shrink to a common tail that runs
unconditionally for every pipe. For a disabled pipe the recout is {0,0,0,0},
so subtracting the height offset drove RECOUT_HEIGHT negative (non-zero)
while RECOUT_WIDTH stayed 0.
Only apply the visual confirm shrink when the recout has real area
(width > 0 && height > 0), for both scl_data.recout and the SPL
dscl_prog_data.recout, preserving the zeroed disabled-pipe behavior.
Cc: Samson Tam <samson.tam@amd.com>
Signed-off-by: Alvin Lee <Alvin.Lee2@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/core/dc_resource.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
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 832286f0c0b1..3e87ec8ad6a5 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -1747,11 +1747,19 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx)
* recout before the viewport was computed.
*/
if (pipe_ctx->stream->ctx->dc->debug.visual_confirm_full_viewport) {
- adjust_recout_for_visual_confirm(
- &pipe_ctx->plane_res.scl_data.recout, pipe_ctx);
+ /* A zero-area recout is the disabled-pipe sentinel (no overlap with this
+ * ODM slice); only shrink a recout that actually draws, else the height
+ * offset drives it negative and the pipe no longer reads as disabled.
+ */
+ if (pipe_ctx->plane_res.scl_data.recout.width > 0 &&
+ pipe_ctx->plane_res.scl_data.recout.height > 0)
+ adjust_recout_for_visual_confirm(
+ &pipe_ctx->plane_res.scl_data.recout, pipe_ctx);
/* SPL programs the HW recout from dscl_prog_data, not scl_data.recout */
if (pipe_ctx->stream->ctx->dc->config.use_spl &&
- !pipe_ctx->stream->ctx->dc->debug.disable_spl)
+ !pipe_ctx->stream->ctx->dc->debug.disable_spl &&
+ pipe_ctx->plane_res.scl_data.dscl_prog_data.recout.width > 0 &&
+ pipe_ctx->plane_res.scl_data.dscl_prog_data.recout.height > 0)
adjust_recout_for_visual_confirm((struct rect *)
&pipe_ctx->plane_res.scl_data.dscl_prog_data.recout,
pipe_ctx);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 48/74] drm/amd/display: Keep patched HDMI panels on FRL across detect
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (46 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 47/74] drm/amd/display: Don't shrink zero-area recout in full viewport visual confirm Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 49/74] drm/amd/display: Use expanded DMUB-provided SoC BB and IP caps Wayne Lin
` (26 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Charlene Liu, Chris Park
From: Charlene Liu <Charlene.Liu@amd.com>
[Why]
A patched panel sets force_frl so update_stream_signal() keeps an
FRL stream instead of dropping it to TMDS. The flags stay on the
link, so a later sink with no FRL rate can keep the previous
panel's force_frl_always. Setting the flag on an encoder that
cannot drive FRL also picks a signal the ASIC cannot output.
[How]
verify_link_capability() sets force_frl_always, force_frl_max and
force_frl_dsc from the current panel patch when the encoder is FRL
capable and max_frl_rate is non-zero. It does not clear a flag the
patch leaves unset. Otherwise it clears those three flags.
hdmi_frl_verify_link_cap() applies force_frl only when the encoder
is FRL capable.
Cc: Chris Park <chris.park@amd.com>
Signed-off-by: Charlene Liu <Charlene.Liu@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../drm/amd/display/dc/link/link_detection.c | 26 +++++++++++++++++++
.../display/dc/link/protocols/link_hdmi_frl.c | 7 ++++-
2 files changed, 32 insertions(+), 1 deletion(-)
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 b4546f93af0c..5b257f003f13 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
@@ -1006,6 +1006,32 @@ static bool should_verify_link_capability_destructively(struct dc_link *link,
static void verify_link_capability(struct dc_link *link,
enum dc_detect_reason reason)
{
+ /* The FRL force flags are only fully recomputed on the destructive path
+ * (hdmi_frl_verify_link_cap), so re-derive the sink driven part on every
+ * detect. Without this a sink can either inherit flags from a previously
+ * connected monitor, or stay cleared when a patched monitor is detected
+ * through one of the non destructive paths (FRL already in use,
+ * skip_frl_pre_training on an active link, or skip_detection_link_training).
+ * Setting them is additive so SCDC test request results survive, and the
+ * destructive path overwrites all of them anyway.
+ */
+ if (link->link_enc &&
+ link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE &&
+ link->local_sink &&
+ link->local_sink->edid_caps.max_frl_rate != 0) {
+ if (link->local_sink->edid_caps.panel_patch.force_frl)
+ link->frl_flags.force_frl_always = true;
+
+ if (link->local_sink->edid_caps.panel_patch.hdmi_comp_auto) {
+ link->frl_flags.force_frl_max = true;
+ link->frl_flags.force_frl_dsc = true;
+ }
+ } else {
+ link->frl_flags.force_frl_always = false;
+ link->frl_flags.force_frl_max = false;
+ link->frl_flags.force_frl_dsc = false;
+ }
+
if (should_verify_link_capability_destructively(link, reason))
verify_link_capability_destructive(link, reason);
else
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
index c31be4900e8d..3f687603a70c 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
@@ -902,8 +902,13 @@ void hdmi_frl_verify_link_cap(struct dc_link *link,
if (link->frl_flags.force_frl_rate == 0xF)
return;
+ /* Only honour the force FRL monitor patch when VBIOS reports the encoder
+ * as FRL capable, otherwise the stream would be forced to a signal type
+ * the ASIC cannot drive.
+ */
if (link->local_sink &&
- link->local_sink->edid_caps.panel_patch.force_frl)
+ link->local_sink->edid_caps.panel_patch.force_frl &&
+ link->link_enc->features.flags.bits.IS_HDMI_FRL_CAPABLE)
link->frl_flags.force_frl_always = true;
if (!link->frl_flags.force_frl_max &&
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 49/74] drm/amd/display: Use expanded DMUB-provided SoC BB and IP caps
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (47 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 48/74] drm/amd/display: Keep patched HDMI panels on FRL across detect Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 50/74] drm/amd/display: Only force P-State on first plane if TTU is below WM Wayne Lin
` (25 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Dillon Varone
From: Ivan Lipski <ivan.lipski@amd.com>
[Why]
Some SoC bounding box and IP capability values must be provided by
VBIOS through DMUB for the display to light up.
[How]
- Pass dml2_configuration_options to get_ip_caps so translators can
read the DMUB bounding box, and update all implementations.
- Take the urgent bandwidth targets, max outstanding requests, return
bus width and alternate DRAM carveout size from DMUB when provided.
- Override each IP cap with its DMUB value when provided (non-zero);
otherwise keep the default.
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dml21_wrapper/dml21_translation_helper.c | 2 +-
.../display/dc/inc/soc_and_ip_translator.h | 4 +-
.../dcn401/dcn401_soc_and_ip_translator.c | 4 +-
.../dcn42/dcn42_soc_and_ip_translator.c | 4 +-
.../dcn42b/dcn42b_soc_and_ip_translator.c | 4 +-
.../dcn60/dcn60_soc_and_ip_translator.c | 96 ++++++++++++++++++-
6 files changed, 108 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
index 2e3ad7152f3f..a7d6ff59870a 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
@@ -79,7 +79,7 @@ void dml21_populate_dml_init_params(struct dml2_initialize_instance_in_out *dml_
if (config->use_native_soc_bb_construction) {
in_dc->soc_and_ip_translator->translator_funcs->get_soc_bb(&dml_init->soc_bb, in_dc, config);
- in_dc->soc_and_ip_translator->translator_funcs->get_ip_caps(&dml_init->ip_caps);
+ in_dc->soc_and_ip_translator->translator_funcs->get_ip_caps(&dml_init->ip_caps, config);
} else {
dml_init->soc_bb = config->external_socbb_ip_params->soc_bb;
dml_init->ip_caps = config->external_socbb_ip_params->ip_params;
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 5dcb9f8f4daf..13d59cc082f0 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
@@ -13,7 +13,9 @@ struct soc_and_ip_translator_funcs {
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);
+ void (*get_ip_caps)(
+ struct dml2_ip_capabilities *dml_ip_caps,
+ const struct dml2_configuration_options *config);
};
struct soc_and_ip_translator {
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.c
index c3dfcf2a73e3..a0181bbb2865 100644
--- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.c
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.c
@@ -307,8 +307,10 @@ void dcn401_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const st
apply_soc_bb_updates(soc_bb, dc, config);
}
-static void dcn401_get_ip_caps(struct dml2_ip_capabilities *ip_caps)
+static void dcn401_get_ip_caps(struct dml2_ip_capabilities *ip_caps,
+ const struct dml2_configuration_options *config)
{
+ (void)config;
*ip_caps = dml2_dcn401_max_ip_caps;
}
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 c6c1b19b7370..0e9fd4d177d3 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
@@ -180,8 +180,10 @@ void dcn42_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const str
dcn42_apply_soc_bb_updates(soc_bb, dc, config);
}
-static void dcn42_get_ip_caps(struct dml2_ip_capabilities *ip_caps)
+static void dcn42_get_ip_caps(struct dml2_ip_capabilities *ip_caps,
+ const struct dml2_configuration_options *config)
{
+ (void)config;
*ip_caps = dml2_dcn42_max_ip_caps;
}
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
index 50669f458e23..e7efca1f0ab1 100644
--- 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
@@ -26,8 +26,10 @@ void dcn42b_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const st
dcn42_apply_soc_bb_updates(soc_bb, dc, config);
}
-static void dcn42b_get_ip_caps(struct dml2_ip_capabilities *ip_caps)
+static void dcn42b_get_ip_caps(struct dml2_ip_capabilities *ip_caps,
+ const struct dml2_configuration_options *config)
{
+ (void)config;
*ip_caps = dml2_dcn42b_max_ip_caps;
}
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.c
index aef527c7584b..f33e818d1b1e 100644
--- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.c
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn60/dcn60_soc_and_ip_translator.c
@@ -65,6 +65,19 @@ static void dcn60_update_soc_bb_with_values_from_dmub(struct dml2_soc_bb *soc_bb
if (dmub_bb_params->g7_temperature_read_blackout_ns > 0)
soc_bb->power_management_parameters.g7_temperature_read_blackout_us =
(double) dmub_bb_params->g7_temperature_read_blackout_ns / 1000.0;
+ if (dmub_bb_params->fraction_of_urgent_bandwidth_nominal_target > 0)
+ soc_bb->fraction_of_urgent_bandwidth_nominal_target =
+ (double) dmub_bb_params->fraction_of_urgent_bandwidth_nominal_target / 1000.0;
+ if (dmub_bb_params->fraction_of_urgent_bandwidth_flip_target > 0)
+ soc_bb->fraction_of_urgent_bandwidth_flip_target =
+ (double) dmub_bb_params->fraction_of_urgent_bandwidth_flip_target / 1000.0;
+ if (dmub_bb_params->max_outstanding_reqs > 0)
+ soc_bb->max_outstanding_reqs = dmub_bb_params->max_outstanding_reqs;
+ if (dmub_bb_params->return_bus_width_bytes > 0)
+ soc_bb->return_bus_width_bytes = dmub_bb_params->return_bus_width_bytes;
+ if (dmub_bb_params->alternate_dram_carveout_size_mb > 0)
+ soc_bb->power_management_parameters.alternate_dram_carveout_size_mb =
+ dmub_bb_params->alternate_dram_carveout_size_mb;
/* populate alt-channel info */
for (i = 0; i < 2; i++) {
@@ -141,9 +154,90 @@ static void dcn60_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, co
}
-static void dcn60_get_ip_caps(struct dml2_ip_capabilities *ip_caps)
+static void dcn60_update_ip_caps_with_values_from_dmub(struct dml2_ip_capabilities *ip_caps,
+ const struct dml2_configuration_options *config)
+{
+ const struct dmub_soc_bb_params *dmub_ip =
+ (const struct dmub_soc_bb_params *)config->bb_from_dmub;
+
+ if (dmub_ip == NULL)
+ return;
+
+ //only update if value is provided
+ if (dmub_ip->pipe_count > 0)
+ ip_caps->pipe_count = dmub_ip->pipe_count;
+ if (dmub_ip->otg_count > 0)
+ ip_caps->otg_count = dmub_ip->otg_count;
+ if (dmub_ip->num_dsc > 0)
+ ip_caps->num_dsc = dmub_ip->num_dsc;
+ if (dmub_ip->TDLUT_33cube_count > 0)
+ ip_caps->TDLUT_33cube_count = dmub_ip->TDLUT_33cube_count;
+ if (dmub_ip->max_num_dp2p0_streams > 0)
+ ip_caps->max_num_dp2p0_streams = dmub_ip->max_num_dp2p0_streams;
+ if (dmub_ip->max_num_hdmi_frl_outputs > 0)
+ ip_caps->max_num_hdmi_frl_outputs = dmub_ip->max_num_hdmi_frl_outputs;
+ if (dmub_ip->max_num_dp2p0_outputs > 0)
+ ip_caps->max_num_dp2p0_outputs = dmub_ip->max_num_dp2p0_outputs;
+ if (dmub_ip->rob_buffer_size_kbytes > 0)
+ ip_caps->rob_buffer_size_kbytes = dmub_ip->rob_buffer_size_kbytes;
+ if (dmub_ip->config_return_buffer_size_in_kbytes > 0)
+ ip_caps->config_return_buffer_size_in_kbytes =
+ dmub_ip->config_return_buffer_size_in_kbytes;
+ if (dmub_ip->config_return_buffer_segment_size_in_kbytes > 0)
+ ip_caps->config_return_buffer_segment_size_in_kbytes =
+ dmub_ip->config_return_buffer_segment_size_in_kbytes;
+ if (dmub_ip->meta_fifo_size_in_kentries > 0)
+ ip_caps->meta_fifo_size_in_kentries = dmub_ip->meta_fifo_size_in_kentries;
+ if (dmub_ip->compressed_buffer_segment_size_in_kbytes > 0)
+ ip_caps->compressed_buffer_segment_size_in_kbytes =
+ dmub_ip->compressed_buffer_segment_size_in_kbytes;
+ if (dmub_ip->cursor_buffer_size > 0)
+ ip_caps->cursor_buffer_size = dmub_ip->cursor_buffer_size;
+ if (dmub_ip->max_flip_time_us > 0)
+ ip_caps->max_flip_time_us = dmub_ip->max_flip_time_us;
+ if (dmub_ip->max_flip_time_lines > 0)
+ ip_caps->max_flip_time_lines = dmub_ip->max_flip_time_lines;
+ /* 0 is a valid hostvm_mode */
+ ip_caps->hostvm_mode = dmub_ip->hostvm_mode;
+ if (dmub_ip->max_vactive_det_fill_delay_us > 0)
+ ip_caps->max_vactive_det_fill_delay_us = dmub_ip->max_vactive_det_fill_delay_us;
+ if (dmub_ip->ppt_max_allow_delay_us > 0)
+ ip_caps->ppt_max_allow_delay_us = dmub_ip->ppt_max_allow_delay_us;
+ if (dmub_ip->temp_read_max_allow_delay_us > 0)
+ ip_caps->temp_read_max_allow_delay_us = dmub_ip->temp_read_max_allow_delay_us;
+ if (dmub_ip->vblank_nom_default_us > 0)
+ ip_caps->vblank_nom_default_us = dmub_ip->vblank_nom_default_us;
+
+ if (dmub_ip->fams2_max_allow_delay_us > 0)
+ ip_caps->fams2.max_allow_delay_us = dmub_ip->fams2_max_allow_delay_us;
+ if (dmub_ip->fams2_scheduling_delay_us > 0)
+ ip_caps->fams2.scheduling_delay_us = dmub_ip->fams2_scheduling_delay_us;
+ if (dmub_ip->fams2_vertical_interrupt_ack_delay_us > 0)
+ ip_caps->fams2.vertical_interrupt_ack_delay_us =
+ dmub_ip->fams2_vertical_interrupt_ack_delay_us;
+ if (dmub_ip->fams2_allow_programming_delay_us > 0)
+ ip_caps->fams2.allow_programming_delay_us = dmub_ip->fams2_allow_programming_delay_us;
+ if (dmub_ip->fams2_min_allow_width_us > 0)
+ ip_caps->fams2.min_allow_width_us = dmub_ip->fams2_min_allow_width_us;
+ if (dmub_ip->fams2_subvp_df_throttle_delay_us > 0)
+ ip_caps->fams2.subvp_df_throttle_delay_us = dmub_ip->fams2_subvp_df_throttle_delay_us;
+ if (dmub_ip->fams2_subvp_programming_delay_us > 0)
+ ip_caps->fams2.subvp_programming_delay_us = dmub_ip->fams2_subvp_programming_delay_us;
+ if (dmub_ip->fams2_drr_programming_delay_us > 0)
+ ip_caps->fams2.drr_programming_delay_us = dmub_ip->fams2_drr_programming_delay_us;
+ if (dmub_ip->fams2_lock_timeout_us > 0)
+ ip_caps->fams2.lock_timeout_us = dmub_ip->fams2_lock_timeout_us;
+ if (dmub_ip->fams2_recovery_timeout_us > 0)
+ ip_caps->fams2.recovery_timeout_us = dmub_ip->fams2_recovery_timeout_us;
+ if (dmub_ip->fams2_flip_programming_delay_us > 0)
+ ip_caps->fams2.flip_programming_delay_us = dmub_ip->fams2_flip_programming_delay_us;
+}
+
+static void dcn60_get_ip_caps(struct dml2_ip_capabilities *ip_caps,
+ const struct dml2_configuration_options *config)
{
dcn6_test_initialize_ip_caps(ip_caps);
+ dcn60_update_ip_caps_with_values_from_dmub(ip_caps, config);
}
static struct soc_and_ip_translator_funcs dcn60_translator_funcs = {
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 50/74] drm/amd/display: Only force P-State on first plane if TTU is below WM
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (48 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 49/74] drm/amd/display: Use expanded DMUB-provided SoC BB and IP caps Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 51/74] drm/amd/display: Program the kept-plane scaler in DC2 Wayne Lin
` (24 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Ovidiu Bunea, Dillon Varone
From: Ovidiu Bunea <ovidiu.bunea@amd.com>
[Why]
When the first plane is enabled, P-State is forced to block UCLK
switching while the new HUBP is brought up. This is only needed when
the HUBP MIN_TTU_VBLANK is below UCLK_PSTATE_CHANGE_WATERMARK_A.
Otherwise the force is unnecessary and adds a 500us delay.
[How]
Track the smallest MIN_TTU_VBLANK across the new pipes and compare it
against watermark A. prepare_bandwidth only raises watermarks, so HW
holds the max of the old and new values; compare against that. Only
force P-State and wait when MIN_TTU_VBLANK is below the watermark.
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Ovidiu Bunea <ovidiu.bunea@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 25 +++++++++++++++----
1 file changed, 20 insertions(+), 5 deletions(-)
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 cc78b4794cef..bac9c5e18e6a 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
@@ -2591,6 +2591,7 @@ void dcn401_program_front_end_for_ctx(
unsigned int i;
unsigned int prev_hubp_count = 0;
unsigned int hubp_count = 0;
+ unsigned int min_ttu_vblank = UINT_MAX;
struct dce_hwseq *hws = dc->hwseq;
struct pipe_ctx *pipe = NULL;
@@ -2623,17 +2624,31 @@ void dcn401_program_front_end_for_ctx(
}
for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ pipe = &context->res_ctx.pipe_ctx[i];
+
if (dc->current_state->res_ctx.pipe_ctx[i].plane_state)
prev_hubp_count++;
- if (context->res_ctx.pipe_ctx[i].plane_state)
+ if (pipe->plane_state) {
hubp_count++;
+ min_ttu_vblank = min(min_ttu_vblank,
+ pipe->hubp_regs.ttu_regs.min_ttu_vblank);
+ }
}
if (prev_hubp_count == 0 && hubp_count > 0) {
- if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
- dc->res_pool->hubbub->funcs->force_pstate_change_control(
- dc->res_pool->hubbub, true, false);
- udelay(500);
+ struct hubbub *hubbub = dc->res_pool->hubbub;
+ unsigned int pstate_wm_a;
+
+ /* WM_A is only raised in prepare_bandwidth, so HW holds the max of old and new */
+ pstate_wm_a = max(context->bw_ctx.bw.dcn.watermarks.dcn4x.a.uclk_pstate,
+ dc->current_state->bw_ctx.bw.dcn.watermarks.dcn4x.a.uclk_pstate);
+
+ /* MIN_TTU_VBLANK and UCLK_PSTATE_CHANGE_WATERMARK_A are both in refclk cycles */
+ if (min_ttu_vblank < pstate_wm_a) {
+ if (hubbub->funcs->force_pstate_change_control)
+ hubbub->funcs->force_pstate_change_control(hubbub, true, false);
+ udelay(500);
+ }
}
/* Set pipe update flags and lock pipes */
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 51/74] drm/amd/display: Program the kept-plane scaler in DC2
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (49 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 50/74] drm/amd/display: Only force P-State on first plane if TTU is below WM Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 52/74] drm/amd/display: Add and remove two MPO planes " Wayne Lin
` (23 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
A full update that keeps one plane and changes its scaling or its
viewport is rejected on DCN4. The builder rejects every rect change.
[How]
Remove the rect change check. Program the DPP scaler and the HUBP
viewport when the plane scaling, the plane position or the stream
scaling changes. A full update sets every plane update bit, so a new
plane also programs both.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../bls_builder/bls_builder_private_types.h | 6 +++
.../dc/bls_builder/dcn401_commit_builder.c | 54 ++++++-------------
2 files changed, 21 insertions(+), 39 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
index df86ff7d4867..0e6733eba83d 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
@@ -122,6 +122,12 @@ struct dcn401_commit_step_meta {
* enabling that block the first time, not of reconfiguring it.
*/
bool hubp_dpp_already_enabled;
+ /*
+ * Neither the plane scaling, the plane position nor the
+ * stream scaling changed. A full update sets every plane bit,
+ * so a plane add always programs the scaler and viewport.
+ */
+ bool scaling_unchanged;
} skip_if;
};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
index d071768bda03..ab84e8fded1d 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
@@ -34,10 +34,10 @@
* bandwidth lower, so no tail is needed.
*
* DCN_DECL_SEAMLESS_UPDATE either adds one plane to that stream, or keeps
- * one plane and programs its surface and address. The sequence follows
- * the full update in commit_planes_for_stream on dcn401, all in
- * head_sequence: the outstanding updates, prepare_bandwidth and the
- * pending-update waits, then the locked front-end programming and
+ * one plane and programs its surface, address, scaler and viewport. The
+ * sequence follows the full update in commit_planes_for_stream on dcn401,
+ * all in head_sequence: the outstanding updates, prepare_bandwidth and
+ * the pending-update waits, then the locked front-end programming and
* post_unlock_program_front_end. A kept-plane update skips enable_plane
* and the stream unblank. The bandwidth lower stays with
* dc_post_update_surfaces_to_stream.
@@ -621,7 +621,7 @@ static bool dcn401_is_plane_kept(const struct dc_state *current_state,
/*
* The seamless update either adds the first plane, or keeps one plane and
- * programs its surface and address. Scaling and viewport stay rejected.
+ * programs its surface, address, scaler and viewport.
*/
static bool check_plane_add_or_kept(const struct dc *dc,
const struct dc_state *current_state,
@@ -784,34 +784,6 @@ static bool check_no_flip_int(const struct dc *dc,
!pipe->plane_state->flip_int_enabled;
}
-/*
- * A kept-plane update does not yet emit scaler or viewport steps. Plane
- * add has no current plane, so those rects are not compared.
- */
-static bool check_no_scale_or_viewport(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- const struct pipe_ctx *current_pipe = dcn401_otg_of(current_state);
- const struct pipe_ctx *new_pipe = dcn401_otg_of(new_state);
- const struct dc_plane_state *current_plane;
- const struct dc_plane_state *new_plane;
-
- (void)dc;
- if (dcn401_is_plane_add(current_state, new_state))
- return true;
- if (!current_pipe || !new_pipe || !current_pipe->plane_state ||
- !new_pipe->plane_state)
- return false;
- current_plane = current_pipe->plane_state;
- new_plane = new_pipe->plane_state;
- return !memcmp(¤t_plane->src_rect, &new_plane->src_rect,
- sizeof(current_plane->src_rect)) &&
- !memcmp(¤t_plane->dst_rect, &new_plane->dst_rect,
- sizeof(current_plane->dst_rect)) &&
- !memcmp(¤t_plane->clip_rect, &new_plane->clip_rect,
- sizeof(current_plane->clip_rect));
-}
/* The update bypasses the plane gamut remap. */
static bool check_no_plane_gamut_remap(const struct dc *dc,
@@ -1017,8 +989,6 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
CHECK_RESOURCE(pool_funcs, "resource pool or hubbub has no funcs");
CHECK_CONDITION(plane_instances, "mpcc or dpp is not the hubp instance");
CHECK_CONDITION(det_by_segments, "hubbub programs det size");
- CHECK_CONDITION(no_scale_or_viewport,
- "scaling or viewport changes");
/* Under the lock, before the pipe is programmed. */
CHECK_CONDITION(triple_buffer, "triple buffer is disabled");
@@ -1347,8 +1317,9 @@ static const struct dcn401_commit_step bls_update_head_new_enable_plane[] = {
/*
* update_dchubp_dpp. First enable runs the whole table. A reconfigure
- * skips the steps that only bring the HUBP/DPP up, and removes the MPCC
- * when the OPP tree already holds one.
+ * skips the steps that only bring the HUBP/DPP up, and programs the
+ * scaler and viewport only when scaling or position changed. The MPCC is
+ * removed when the OPP tree already holds one.
*/
static const struct dcn401_commit_step
bls_update_head_new_update_dchubp_dpp[] = {
@@ -1373,9 +1344,9 @@ static const struct dcn401_commit_step
{ .func = MPC_INSERT_PLANE },
{ .func = MPC_UPDATE_VISUAL_CONFIRM },
{ .func = DPP_SET_SCALER,
- .meta.skip_if.hubp_dpp_already_enabled = true },
+ .meta.skip_if.scaling_unchanged = true },
{ .func = HUBP_MEM_PROGRAM_VIEWPORT,
- .meta.skip_if.hubp_dpp_already_enabled = true },
+ .meta.skip_if.scaling_unchanged = true },
{ .func = HUBP_PROGRAM_MCACHE_ID },
{ .func = MPC_SET_GAMUT_REMAP_BYPASS,
.meta.mpc_set_gamut_remap_bypass.block_id =
@@ -2289,6 +2260,11 @@ static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
args->current_state->stream_count &&
args->current_state->stream_status[0].plane_count)
return true;
+ if (step->meta.skip_if.scaling_unchanged &&
+ !args->pipe->plane_state->update_bits.scaling_change &&
+ !args->pipe->plane_state->update_bits.position_change &&
+ !args->pipe->stream->update_flags.bits.scaling)
+ return true;
return false;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 52/74] drm/amd/display: Add and remove two MPO planes in DC2
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (50 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 51/74] drm/amd/display: Program the kept-plane scaler in DC2 Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 53/74] drm/amd/display: Add DC2 state facts and split the builders Wayne Lin
` (22 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
The DC2 update on DCN4 supports only one plane. An update with two
MPO planes falls back to the old commit path.
[How]
Let the seamless update add one plane or two blended planes to a kept
stream. Let the non-seamless commit remove a stream with one or two
planes. Program each DPP pipe from one step list, top pipe first.
Write the software state for each DPP pipe.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dc/bls_builder/dcn401_commit_builder.c | 450 +++++++++++++-----
.../gpu/drm/amd/display/dc/core2/dc_core2.c | 46 +-
2 files changed, 350 insertions(+), 146 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
index ab84e8fded1d..19765f7dcd11 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
@@ -25,16 +25,18 @@
/*
* DCN_DECL_NON_SEAMLESS_COMMIT for one virtual stream without planes.
* The new state has no stream, or one stream with no plane; the current
- * state has no stream, or one stream with one plane. A plane is added
- * later by DCN_DECL_SEAMLESS_UPDATE, not by this commit.
+ * state has no stream, or one stream with one or two planes, one per DPP
+ * pipe. Planes are added later by DCN_DECL_SEAMLESS_UPDATE, not by this
+ * commit.
*
* The sequence follows legacy_commit_state_hw on dcn401: the same leaf
* calls in the same order, including the calls DC1 repeats. Everything is
* in head_sequence: the ODM double-buffer and blank waits come before the
* bandwidth lower, so no tail is needed.
*
- * DCN_DECL_SEAMLESS_UPDATE either adds one plane to that stream, or keeps
- * one plane and programs its surface, address, scaler and viewport. The
+ * DCN_DECL_SEAMLESS_UPDATE either adds one plane, or two MPC-blended
+ * planes, to that stream, or keeps one plane and programs its surface,
+ * address, scaler and viewport. The
* sequence follows the full update in commit_planes_for_stream on dcn401,
* all in head_sequence: the outstanding updates, prepare_bandwidth and
* the pending-update waits, then the locked front-end programming and
@@ -282,23 +284,11 @@ static bool check_abm(const struct dc *dc,
stream->abm_level == ABM_LEVEL_IMMEDIATE_DISABLE;
}
-static bool check_plane_count(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- const struct dc_state *state = dcn401_checked_state(current_state,
- new_state);
- /*
- * The current state carries the removed plane. The new state has
- * none.
- */
- bool has_plane = current_state != NULL;
-
- (void)dc;
- return state->stream_status[0].plane_count == (has_plane ? 1 : 0);
-}
-
-/* The pipe for this state's one stream, or NULL when it is not exactly one. */
+/*
+ * The OTG master for this state's one stream, or NULL when the stream has
+ * more than one. An MPC bottom pipe of the stream is not an OTG master;
+ * check_mpc_blend bounds it.
+ */
static const struct pipe_ctx *dcn401_state_otg_master(
const struct dc_state *state, bool *not_one_pipe)
{
@@ -311,6 +301,8 @@ static const struct pipe_ctx *dcn401_state_otg_master(
if (!pipe->stream)
continue;
+ if (pipe->stream == state->streams[0] && pipe->top_pipe)
+ continue;
if (pipe->stream != state->streams[0] || otg_master) {
*not_one_pipe = true;
return NULL;
@@ -340,6 +332,51 @@ static const struct pipe_ctx *dcn401_otg_of(const struct dc_state *state)
return dcn401_state_otg_master(state, ¬_one_pipe);
}
+/* The OTG master and its MPC bottom pipe, top to bottom. */
+static unsigned int dcn401_dpp_pipe_count(const struct pipe_ctx *otg_master)
+{
+ unsigned int count = 0;
+
+ for (; otg_master; otg_master = otg_master->bottom_pipe)
+ count++;
+ return count;
+}
+
+typedef bool (*dcn401_pipe_check)(const struct pipe_ctx *pipe);
+
+/* False when state has no OTG master, or check fails on one of its pipes. */
+static bool dcn401_each_dpp_pipe(const struct dc_state *state,
+ dcn401_pipe_check check)
+{
+ const struct pipe_ctx *pipe = dcn401_otg_of(state);
+
+ if (!pipe)
+ return false;
+ for (; pipe; pipe = pipe->bottom_pipe) {
+ if (!check(pipe))
+ return false;
+ }
+ return true;
+}
+
+static bool check_plane_count(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_state *state = dcn401_checked_state(current_state,
+ new_state);
+ unsigned int planes = state->stream_status[0].plane_count;
+
+ (void)dc;
+ /*
+ * The current state carries the removed planes, one per DPP pipe.
+ * The new state has none.
+ */
+ if (!current_state)
+ return !planes;
+ return planes && planes == dcn401_dpp_pipe_count(dcn401_otg_of(state));
+}
+
static bool check_one_otg_pipe(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
@@ -378,24 +415,45 @@ static bool check_mpc_blend(const struct dc *dc,
const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
new_state);
+ const struct pipe_ctx *bottom;
+
(void)dc;
- return pipe && !pipe->top_pipe && !pipe->bottom_pipe;
+ if (!pipe || pipe->top_pipe)
+ return false;
+ /* At most one bottom pipe, blended on the master's OTG and OPP. */
+ bottom = pipe->bottom_pipe;
+ if (!bottom)
+ return true;
+ return bottom->top_pipe == pipe && !bottom->bottom_pipe &&
+ !bottom->next_odm_pipe && !bottom->prev_odm_pipe &&
+ bottom->stream == pipe->stream &&
+ bottom->stream_res.tg == pipe->stream_res.tg &&
+ bottom->stream_res.opp == pipe->stream_res.opp;
+}
+
+static bool dcn401_pipe_has_plane(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state != NULL;
+}
+
+static bool dcn401_pipe_has_no_plane(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state == NULL;
}
static bool check_pipe_plane(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
+ (void)dc;
/*
- * The current state carries the removed plane. The new state has
+ * The current state carries the removed planes. The new state has
* none.
*/
- bool has_plane = current_state != NULL;
-
- (void)dc;
- return pipe && !!pipe->plane_state == has_plane;
+ if (current_state)
+ return dcn401_each_dpp_pipe(current_state,
+ dcn401_pipe_has_plane);
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_has_no_plane);
}
static bool check_otg_master_blocks(const struct dc *dc,
@@ -410,21 +468,25 @@ static bool check_otg_master_blocks(const struct dc *dc,
pipe->stream_res.stream_enc && pipe->clock_source;
}
+static bool dcn401_pipe_plane_blocks(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_res.hubp && pipe->plane_res.dpp &&
+ pipe->plane_res.mpcc_inst < MAX_MPCC;
+}
+
static bool check_plane_blocks(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
-
(void)dc;
+ (void)new_state;
if (!current_state)
return true;
- return pipe && pipe->plane_res.hubp && pipe->plane_res.dpp &&
- pipe->plane_res.mpcc_inst < MAX_MPCC;
+ return dcn401_each_dpp_pipe(current_state, dcn401_pipe_plane_blocks);
}
-static bool check_mpcc_alone(const struct dc *dc,
+/* The OPP's tree holds exactly the removed MPCCs, in pipe order. */
+static bool check_mpcc_tree(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
@@ -436,11 +498,15 @@ static bool check_mpcc_alone(const struct dc *dc,
return true;
if (!pipe || !dc->res_pool || !dc->res_pool->mpc)
return false;
- mpcc = &dc->res_pool->mpc->mpcc_array[pipe->plane_res.mpcc_inst];
- /* The removed MPCC must be the only one in the OPP's tree. */
- return pipe->stream_res.opp->mpc_tree_params.opp_list == mpcc &&
- !mpcc->mpcc_bot &&
- mpcc->dpp_id == pipe->plane_res.dpp->inst;
+ mpcc = pipe->stream_res.opp->mpc_tree_params.opp_list;
+ for (; pipe; pipe = pipe->bottom_pipe) {
+ if (mpcc != &dc->res_pool->mpc->mpcc_array[
+ pipe->plane_res.mpcc_inst] ||
+ mpcc->dpp_id != pipe->plane_res.dpp->inst)
+ return false;
+ mpcc = mpcc->mpcc_bot;
+ }
+ return !mpcc;
}
static bool check_pipe_audio(const struct dc *dc,
@@ -516,7 +582,8 @@ static enum bls_builder_result dcn401_check_stream_support(
CHECK_CONDITION(one_otg_pipe, "stream is not on one pipe");
CHECK_RESOURCE(otg_master_present, "stream has no otg master");
CHECK_CONDITION(odm_combine, "odm is set");
- CHECK_CONDITION(mpc_blend, "top or bottom pipe is set");
+ CHECK_CONDITION(mpc_blend,
+ "more than one bottom pipe, or it is off the otg or opp");
CHECK_RESOURCE(otg_master_blocks, "otg master is missing a block");
CHECK_CONDITION(pipe_audio, "audio is set");
CHECK_CONDITION(pipe_dsc, "dsc is set");
@@ -540,7 +607,7 @@ static enum bls_builder_result dcn401_check_commit_state_support(
CHECK_CONDITION(plane_count, "plane count does not match the state");
CHECK_CONDITION(pipe_plane, "pipe plane does not match the state");
CHECK_RESOURCE(plane_blocks, "plane is missing hubp or dpp");
- CHECK_CONDITION(mpcc_alone, "mpcc is not alone in the opp tree");
+ CHECK_CONDITION(mpcc_tree, "opp tree does not match the pipes");
return BLS_BUILD_SUCCESS;
}
@@ -605,11 +672,14 @@ static bool check_stream_kept(const struct dc *dc,
current_state->streams[0] == new_state->streams[0];
}
+/* Both planes of an MPO state are added together, by one update. */
static bool dcn401_is_plane_add(const struct dc_state *current_state,
const struct dc_state *new_state)
{
+ unsigned int planes = new_state->stream_status[0].plane_count;
+
return !current_state->stream_status[0].plane_count &&
- new_state->stream_status[0].plane_count == 1;
+ (planes == 1 || planes == 2);
}
static bool dcn401_is_plane_kept(const struct dc_state *current_state,
@@ -620,16 +690,20 @@ static bool dcn401_is_plane_kept(const struct dc_state *current_state,
}
/*
- * The seamless update either adds the first plane, or keeps one plane and
- * programs its surface, address, scaler and viewport.
+ * The seamless update either adds the first one or two planes, or keeps
+ * one plane and programs its surface, address, scaler and viewport. Each
+ * plane has its own DPP pipe.
*/
static bool check_plane_add_or_kept(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
(void)dc;
- return dcn401_is_plane_add(current_state, new_state) ||
- dcn401_is_plane_kept(current_state, new_state);
+ if (!dcn401_is_plane_add(current_state, new_state) &&
+ !dcn401_is_plane_kept(current_state, new_state))
+ return false;
+ return dcn401_dpp_pipe_count(dcn401_otg_of(new_state)) ==
+ new_state->stream_status[0].plane_count;
}
static bool check_same_otg_master_blocks(const struct dc *dc,
@@ -646,16 +720,19 @@ static bool check_same_otg_master_blocks(const struct dc *dc,
current_pipe->stream_res.opp == new_pipe->stream_res.opp;
}
+static bool dcn401_pipe_new_plane_blocks(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state && pipe->plane_res.hubp &&
+ pipe->plane_res.dpp && pipe->plane_res.dpp->funcs;
+}
+
static bool check_new_plane_blocks(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
-
(void)dc;
(void)current_state;
- return pipe && pipe->plane_state && pipe->plane_res.hubp &&
- pipe->plane_res.dpp && pipe->plane_res.dpp->funcs;
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_new_plane_blocks);
}
static bool check_pool_funcs(const struct dc *dc,
@@ -667,23 +744,27 @@ static bool check_pool_funcs(const struct dc *dc,
return dc->res_pool->funcs && dc->res_pool->hubbub->funcs;
}
-/* DC1 uses the HUBP instance as the MPCC and DPP instance. */
-static bool check_plane_instances(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
+static bool dcn401_pipe_plane_instances(const struct pipe_ctx *pipe)
{
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
int inst;
- (void)dc;
- (void)current_state;
- if (!pipe || !pipe->plane_res.hubp || !pipe->plane_res.dpp)
+ if (!pipe->plane_res.hubp || !pipe->plane_res.dpp)
return false;
inst = pipe->plane_res.hubp->inst;
return inst < MAX_MPCC && pipe->plane_res.mpcc_inst == inst &&
pipe->plane_res.dpp->inst == inst;
}
+/* DC1 uses the HUBP instance as the MPCC and DPP instance. */
+static bool check_plane_instances(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_plane_instances);
+}
+
/* dcn401 programs DET by segments only. */
static bool check_det_by_segments(const struct dc *dc,
const struct dc_state *current_state,
@@ -714,15 +795,18 @@ static bool check_no_sanity_checks(const struct dc *dc,
return !dc->debug.sanity_checks;
}
+static bool dcn401_pipe_no_wait(const struct pipe_ctx *pipe)
+{
+ return !pipe->wait_is_required;
+}
+
static bool check_no_pipe_wait(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
(void)dc;
(void)current_state;
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
-
- return pipe && !pipe->wait_is_required;
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_wait);
}
/* dcn20_blank_pixel_data does nothing while a test pattern is on. */
@@ -750,15 +834,18 @@ static bool check_no_forced_dpg(const struct dc *dc,
}
/* dcn20_blank_pixel_data sets the ABM pipe on unblank when there is one. */
+static bool dcn401_pipe_no_abm(const struct pipe_ctx *pipe)
+{
+ return !pipe->stream_res.abm;
+}
+
static bool check_no_abm_pipe(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
(void)dc;
(void)current_state;
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
-
- return pipe && !pipe->stream_res.abm;
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_abm);
}
/* enable_plane programs the system aperture when VM is valid. */
@@ -772,30 +859,36 @@ static bool check_no_vm_aperture(const struct dc *dc,
}
/* enable_plane sets the flip interrupt when the plane asks for one. */
+static bool dcn401_pipe_no_flip_int(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state && !pipe->plane_state->flip_int_enabled;
+}
+
static bool check_no_flip_int(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
(void)dc;
(void)current_state;
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
-
- return pipe && pipe->plane_state &&
- !pipe->plane_state->flip_int_enabled;
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_flip_int);
}
+static bool dcn401_pipe_no_plane_gamut_remap(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state &&
+ !pipe->plane_state->gamut_remap_matrix.enable_remap;
+}
+
/* The update bypasses the plane gamut remap. */
static bool check_no_plane_gamut_remap(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
-
(void)dc;
(void)current_state;
- return pipe && pipe->plane_state &&
- !pipe->plane_state->gamut_remap_matrix.enable_remap;
+ return dcn401_each_dpp_pipe(new_state,
+ dcn401_pipe_no_plane_gamut_remap);
}
/* The update bypasses the stream gamut remap. */
@@ -819,15 +912,24 @@ static bool check_no_output_csc(const struct dc *dc,
}
/* The update programs the surface and its address without DCC. */
+static bool dcn401_pipe_no_dcc(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state && !pipe->plane_state->dcc.enable;
+}
+
static bool check_no_dcc(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
(void)dc;
(void)current_state;
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_dcc);
+}
- return pipe && pipe->plane_state && !pipe->plane_state->dcc.enable;
+static bool dcn401_pipe_graphics_address(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state &&
+ pipe->plane_state->address.type == PLN_ADDR_TYPE_GRAPHICS;
}
/* The update programs a single graphics address, not stereo or video. */
@@ -837,10 +939,12 @@ static bool check_graphics_address(const struct dc *dc,
{
(void)dc;
(void)current_state;
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_graphics_address);
+}
- return pipe && pipe->plane_state &&
- pipe->plane_state->address.type == PLN_ADDR_TYPE_GRAPHICS;
+static bool dcn401_pipe_no_immediate_flip(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state && !pipe->plane_state->flip_immediate;
}
/* DC1 programs triple buffering and the flip only for a queued flip. */
@@ -850,27 +954,33 @@ static bool check_no_immediate_flip(const struct dc *dc,
{
(void)dc;
(void)current_state;
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
-
- return pipe && pipe->plane_state &&
- !pipe->plane_state->flip_immediate;
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_immediate_flip);
}
/*
* DC1 translates a distributed-points curve into a LUT while it programs
* the plane. The update programs only the predefined and bypass curves.
*/
+static bool dcn401_pipe_no_degamma_curve(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_state &&
+ pipe->plane_state->in_transfer_func.type !=
+ TF_TYPE_DISTRIBUTED_POINTS;
+}
+
static bool check_no_degamma_curve(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
(void)dc;
(void)current_state;
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_degamma_curve);
+}
- return pipe && pipe->plane_state &&
- pipe->plane_state->in_transfer_func.type !=
- TF_TYPE_DISTRIBUTED_POINTS;
+static bool dcn401_pipe_no_dpp_pregam(const struct pipe_ctx *pipe)
+{
+ return pipe->plane_res.dpp && pipe->plane_res.dpp->funcs &&
+ !pipe->plane_res.dpp->funcs->dpp_set_pregam_state;
}
/* dcn32_set_input_transfer_func calls dpp_set_pregam_state if present. */
@@ -880,10 +990,18 @@ static bool check_no_dpp_pregam(const struct dc *dc,
{
(void)dc;
(void)current_state;
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_dpp_pregam);
+}
- return pipe && pipe->plane_res.dpp && pipe->plane_res.dpp->funcs &&
- !pipe->plane_res.dpp->funcs->dpp_set_pregam_state;
+static bool dcn401_pipe_no_mcm_luts(const struct pipe_ctx *pipe)
+{
+ const union dc_plane_cm_flags *flags;
+
+ if (!pipe->plane_state)
+ return false;
+ flags = &pipe->plane_state->cm.flags;
+ return !flags->bits.blend_enable && !flags->bits.shaper_enable &&
+ !flags->bits.lut3d_enable && !flags->bits.rmcm_enable;
}
/* dcn401_set_mcm_luts then programs every MCM LUT off. */
@@ -891,16 +1009,9 @@ static bool check_no_mcm_luts(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_otg_of(new_state);
- const union dc_plane_cm_flags *flags;
-
(void)dc;
(void)current_state;
- if (!pipe || !pipe->plane_state)
- return false;
- flags = &pipe->plane_state->cm.flags;
- return !flags->bits.blend_enable && !flags->bits.shaper_enable &&
- !flags->bits.lut3d_enable && !flags->bits.rmcm_enable;
+ return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_mcm_luts);
}
static bool check_no_shaper_curve(const struct dc *dc,
@@ -1030,7 +1141,8 @@ static struct pipe_ctx *get_otg_master_from_state_by_stream(
unsigned int i;
for (i = 0; i < MAX_PIPES; i++) {
- if (state->res_ctx.pipe_ctx[i].stream == stream)
+ if (state->res_ctx.pipe_ctx[i].stream == stream &&
+ !state->res_ctx.pipe_ctx[i].top_pipe)
return &state->res_ctx.pipe_ctx[i];
}
return NULL;
@@ -1059,14 +1171,28 @@ static const struct dcn401_commit_step bls_head_per_state_new[] = {
* disable_link, which disables the output as DisplayPort.
* - dcn401_reset_back_end_for_pipe after link_set_dpms_off, then the
* clock source power-down from dcn401_reset_hw_ctx_wrap.
+ * The front end disconnects every MPCC under the lock, then powers down
+ * each DPP pipe after the unlock; the _dpp tables run once per DPP pipe.
*/
static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_current[] = {
+ bls_head_per_stream_per_otg_master_pipe_current_lock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = true },
{ .func = OPP_SET_DISP_PATTERN_GENERATOR },
+};
+
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_dpp_pipe_current_disconnect[] = {
{ .func = HUBP_PROGRAM_DET_SEGMENTS },
{ .func = MPC_REMOVE_MPCC },
+};
+
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_current_unlock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = false },
+};
+
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_dpp_pipe_current_power_down[] = {
{ .func = MPC_ASSERT_IDLE_MPCC },
{ .func = HUBP_SET_BLANK },
{ .func = HUBP_UPDATE_MALL_SEL },
@@ -1082,6 +1208,10 @@ static const struct dcn401_commit_step
{ .func = HUBP_RESET },
{ .func = DPP_RESET },
{ .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false },
+};
+
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_current_stream_off[] = {
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
{ .func = DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
@@ -1201,12 +1331,23 @@ static const struct dcn401_commit_step bls_head_per_state_new_postamble[] = {
.meta.clk_mgr_update_clocks_for_state.safe_to_lower = true },
};
+/* The support check allows the OTG master and one MPC bottom pipe. */
+#define DCN401_MAX_DPP_PIPES 2
+
/*
* Bounds on what build adds. The support check allows one stream per
* state, and never one in both.
*/
static_assert(ARRAY_SIZE(bls_head_per_state_new) +
- ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_current) +
+ ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_current_lock) +
+ DCN401_MAX_DPP_PIPES * ARRAY_SIZE(
+ bls_head_per_stream_per_dpp_pipe_current_disconnect) +
+ ARRAY_SIZE(
+ bls_head_per_stream_per_otg_master_pipe_current_unlock) +
+ DCN401_MAX_DPP_PIPES * ARRAY_SIZE(
+ bls_head_per_stream_per_dpp_pipe_current_power_down) +
+ ARRAY_SIZE(
+ bls_head_per_stream_per_otg_master_pipe_current_stream_off) +
ARRAY_SIZE(bls_head_per_state_new_postamble) <=
DC2_BLS_MAX_STEPS,
"dcn401 commit stream-off head exceeds DC2_BLS_MAX_STEPS");
@@ -1263,8 +1404,14 @@ static const struct dcn401_commit_step
*/
static const struct dcn401_commit_step bls_update_head_new_lock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = true },
+};
+
+static const struct dcn401_commit_step bls_update_head_new_dpp_lock[] = {
{ .func = HUBP_PROGRAM_TRIPLEBUFFER },
{ .func = HUBP_PROGRAM_TRIPLEBUFFER },
+};
+
+static const struct dcn401_commit_step bls_update_head_new_first_hubp[] = {
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
.meta.hubbub_force_pstate_change_control.enable = true },
{ .func = DC_UDELAY },
@@ -1313,16 +1460,12 @@ static const struct dcn401_commit_step bls_update_head_new_enable_plane[] = {
{ .func = HUBP_PROGRAM_DET_SEGMENTS,
.meta.hubp_program_det_segments.allocate = true,
.meta.skip_if.hubp_dpp_already_enabled = true },
-};
-
-/*
- * update_dchubp_dpp. First enable runs the whole table. A reconfigure
- * skips the steps that only bring the HUBP/DPP up, and programs the
- * scaler and viewport only when scaling or position changed. The MPCC is
- * removed when the OPP tree already holds one.
- */
-static const struct dcn401_commit_step
- bls_update_head_new_update_dchubp_dpp[] = {
+ /*
+ * update_dchubp_dpp. First enable runs the whole table. A reconfigure
+ * skips the steps that only bring the HUBP/DPP up, and programs the
+ * scaler and viewport only when scaling or position changed. The MPCC is
+ * removed when the OPP tree already holds one.
+ */
{ .func = DPP_DPPCLK_CONTROL, .meta.dpp_dppclk_control.enable = true,
.meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = DCCG_UPDATE_DPP_DTO,
@@ -1362,14 +1505,9 @@ static const struct dcn401_commit_step
{ .func = HUBP_UPDATE_PLANE_ADDR },
{ .func = HUBP_SET_BLANK, .meta.hubp_set_blank.unblank = true,
.meta.skip_if.hubp_dpp_already_enabled = true },
-};
-
-/*
- * set_hdr_multiplier, the input and MCM transfer functions, the output
- * transfer function and the OPP format. First enable only.
- */
-static const struct dcn401_commit_step
- bls_update_head_new_transfer_funcs[] = {
+ /* set_hdr_multiplier, the input and MCM transfer functions, the
+ * output transfer function and the OPP format. First enable only.
+ */
{ .func = DPP_SET_HDR_MULTIPLIER,
.meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = DPP_SET_PRE_DEGAM,
@@ -1406,10 +1544,22 @@ static const struct dcn401_commit_step
*/
static const struct dcn401_commit_step bls_update_head_new_unlock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = false },
+};
+
+static const struct dcn401_commit_step bls_update_head_new_dpp_flip[] = {
{ .func = HUBP_WAIT_FLIP_PENDING },
+};
+
+static const struct dcn401_commit_step bls_update_head_new_pstate[] = {
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
+};
+
+static const struct dcn401_commit_step bls_update_head_new_dpp_mall[] = {
{ .func = HUBP_UPDATE_MALL_SEL,
.meta.hubp_update_mall_sel.from_new_state = true },
+};
+
+static const struct dcn401_commit_step bls_update_head_new_trigger[] = {
{ .func = OPTC_PROGRAM_MANUAL_TRIGGER,
.meta.skip_if.no_manual_trigger = true },
};
@@ -1423,11 +1573,16 @@ static_assert(
ARRAY_SIZE(
bls_update_head_per_stream_per_otg_master_pipe_current_pending) +
ARRAY_SIZE(bls_update_head_new_lock) +
+ ARRAY_SIZE(bls_update_head_new_first_hubp) +
ARRAY_SIZE(bls_update_head_new_stream) +
+ ARRAY_SIZE(bls_update_head_new_unlock) +
+ ARRAY_SIZE(bls_update_head_new_pstate) +
+ ARRAY_SIZE(bls_update_head_new_trigger) +
+ DCN401_MAX_DPP_PIPES * (
+ ARRAY_SIZE(bls_update_head_new_dpp_lock) +
ARRAY_SIZE(bls_update_head_new_enable_plane) +
- ARRAY_SIZE(bls_update_head_new_update_dchubp_dpp) +
- ARRAY_SIZE(bls_update_head_new_transfer_funcs) +
- ARRAY_SIZE(bls_update_head_new_unlock) <= DC2_BLS_MAX_STEPS,
+ ARRAY_SIZE(bls_update_head_new_dpp_flip) +
+ ARRAY_SIZE(bls_update_head_new_dpp_mall)) <= DC2_BLS_MAX_STEPS,
"dcn401 seamless update head exceeds DC2_BLS_MAX_STEPS");
/* pipe is NULL only for the bandwidth and p-state steps. */
@@ -2297,11 +2452,32 @@ static void dcn401_add_per_otg_master(
}
}
+/* Each DPP pipe of each stream, the OTG master first. */
+static void dcn401_add_per_dpp_pipe(
+ struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *state_args,
+ struct dc_state *state,
+ const struct dcn401_commit_step *steps, unsigned int count)
+{
+ struct dcn401_commit_step_args args = *state_args;
+ unsigned int i;
+
+ for (i = 0; i < state->stream_count; i++) {
+ for (args.pipe = get_otg_master_from_state_by_stream(state,
+ state->streams[i]); args.pipe;
+ args.pipe = args.pipe->bottom_pipe)
+ dcn401_add_steps(sequence, &args, steps, count);
+ }
+}
+
#define DCN401_ADD_STEPS(sequence, args, steps) \
dcn401_add_steps(sequence, args, steps, ARRAY_SIZE(steps))
#define DCN401_ADD_PER_OTG_MASTER(sequence, args, state, steps) \
dcn401_add_per_otg_master(sequence, args, state, steps, \
ARRAY_SIZE(steps))
+#define DCN401_ADD_PER_DPP_PIPE(sequence, args, state, steps) \
+ dcn401_add_per_dpp_pipe(sequence, args, state, steps, \
+ ARRAY_SIZE(steps))
/*
* Points the step arguments at the states and the sequence at
@@ -2342,7 +2518,17 @@ static enum bls_builder_result dcn401_build_non_seamless_commit(
DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
state_args.current_state,
- bls_head_per_stream_per_otg_master_pipe_current);
+ bls_head_per_stream_per_otg_master_pipe_current_lock);
+ DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.current_state,
+ bls_head_per_stream_per_dpp_pipe_current_disconnect);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
+ state_args.current_state,
+ bls_head_per_stream_per_otg_master_pipe_current_unlock);
+ DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.current_state,
+ bls_head_per_stream_per_dpp_pipe_current_power_down);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
+ state_args.current_state,
+ bls_head_per_stream_per_otg_master_pipe_current_stream_off);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
bls_head_per_stream_per_otg_master_pipe_new);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
@@ -2383,16 +2569,28 @@ static enum bls_builder_result dcn401_build_seamless_update(
*/
DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
bls_update_head_new_lock);
+ DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.new_state,
+ bls_update_head_new_dpp_lock);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_stream);
+ bls_update_head_new_first_hubp);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
+ bls_update_head_new_stream);
+ /*
+ * One step list per DPP pipe, top pipe first, so the top MPCC is
+ * inserted before the bottom one.
+ */
+ DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.new_state,
bls_update_head_new_enable_plane);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_update_dchubp_dpp);
+ bls_update_head_new_unlock);
+ DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.new_state,
+ bls_update_head_new_dpp_flip);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_transfer_funcs);
+ bls_update_head_new_pstate);
+ DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.new_state,
+ bls_update_head_new_dpp_mall);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_unlock);
+ bls_update_head_new_trigger);
return BLS_BUILD_SUCCESS;
}
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
index 85d9aa1614f6..33c52ef9b728 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
@@ -60,7 +60,8 @@ static struct pipe_ctx *core2_otg_master(struct dc_state *state)
if (!state->stream_count)
return NULL;
for (i = 0; i < MAX_PIPES; i++) {
- if (state->res_ctx.pipe_ctx[i].stream == state->streams[0])
+ if (state->res_ctx.pipe_ctx[i].stream == state->streams[0] &&
+ !state->res_ctx.pipe_ctx[i].top_pipe)
return &state->res_ctx.pipe_ctx[i];
}
return NULL;
@@ -159,8 +160,10 @@ static void core2_commit_enable_done(struct pipe_ctx *pipe)
static void core2_commit_disable_done(struct dc *dc, struct pipe_ctx *pipe)
{
struct dc_link *link = pipe->stream->link;
+ struct pipe_ctx *dpp_pipe;
- pipe->plane_res.hubp->power_gated = true;
+ for (dpp_pipe = pipe; dpp_pipe; dpp_pipe = dpp_pipe->bottom_pipe)
+ dpp_pipe->plane_res.hubp->power_gated = true;
link->link_status.link_active = false;
link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
pipe->stream->timing_adjust_pending = true;
@@ -295,8 +298,8 @@ static bool core2_update_state_supported(
if (scratch->update_type != UPDATE_TYPE_FULL ||
scratch->flow != UPDATE_V3_FLOW_NEW_CONTEXT_SEAMLESS)
return false;
- /* The full-update builder adds or keeps one plane. */
- if (scratch->surface_count != 1)
+ /* The full-update builder adds one or two planes, or keeps one. */
+ if (scratch->surface_count != 1 && scratch->surface_count != 2)
return false;
if (!core2_stream_update_supported(scratch->stream_update))
return false;
@@ -311,22 +314,24 @@ static bool core2_update_state_supported(
*/
static void core2_update_done(struct dc *dc, struct dc_state *context)
{
- struct pipe_ctx *pipe = core2_otg_master(context);
+ struct pipe_ctx *pipe;
struct dc_plane_state *plane;
struct hubp *hubp;
- if (!pipe || !pipe->plane_state || !pipe->plane_res.hubp ||
- !pipe->stream_res.opp)
- return;
- plane = pipe->plane_state;
- hubp = pipe->plane_res.hubp;
-
- vm_helper_mark_vmid_used(dc->vm_helper, plane->address.vmid,
- (uint8_t)hubp->inst);
- plane->status.requested_address = plane->address;
- hubp->opp_id = pipe->stream_res.opp->inst;
- hubp->mpcc_id = hubp->inst;
- hubp->power_gated = false;
+ for (pipe = core2_otg_master(context); pipe; pipe = pipe->bottom_pipe) {
+ if (!pipe->plane_state || !pipe->plane_res.hubp ||
+ !pipe->stream_res.opp)
+ return;
+ plane = pipe->plane_state;
+ hubp = pipe->plane_res.hubp;
+
+ vm_helper_mark_vmid_used(dc->vm_helper, plane->address.vmid,
+ (uint8_t)hubp->inst);
+ plane->status.requested_address = plane->address;
+ hubp->opp_id = pipe->stream_res.opp->inst;
+ hubp->mpcc_id = hubp->inst;
+ hubp->power_gated = false;
+ }
core2_commit_prepare_done(dc, context);
}
@@ -339,14 +344,15 @@ static void core2_update_state_execute(
/*
* The values DC1 computes into the context before it programs the
- * plane. The builder reads them from the context.
+ * planes. The builder reads them from the context.
*/
if (!pipe || !pipe->plane_state)
return;
if (dc->res_pool->funcs->prepare_mcache_programming)
dc->res_pool->funcs->prepare_mcache_programming(dc, context);
- pipe->plane_res.scl_data.lb_params.alpha_en =
- pipe->plane_state->per_pixel_alpha;
+ for (; pipe; pipe = pipe->bottom_pipe)
+ pipe->plane_res.scl_data.lb_params.alpha_en =
+ pipe->plane_state->per_pixel_alpha;
if (core2_build_and_run(dc, DCN_DECL_SEAMLESS_UPDATE, context) != DC_OK) {
ASSERT(false);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 53/74] drm/amd/display: Add DC2 state facts and split the builders
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (51 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 52/74] drm/amd/display: Add and remove two MPO planes " Wayne Lin
@ 2026-11-11 1:28 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 54/74] drm/amd/display: Program DC2 commit stereo once Wayne Lin
` (21 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:28 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
Each DCN4 DC2 builder has its own helpers that read the stream, pipe
and plane state. The same checks are in several files with different
names. The two builders also share one file.
[How]
Add a state facts component that reads a state once into trait masks.
Make the builders ask their questions of these masks. Give each trait
a name that tells what the object has. Remove the traits that are the
inverse of other traits. Remove the HUBP, DPP and MPCC instance check,
because the resource pool assigns these blocks together. Remove the
DPP pregam check, because DCN4 has no pregam.
Put the seamless and non-seamless builders in their own files. Each
file has its own step tables. A shared header has only the support
checks and the step helpers.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/Makefile | 4 +-
.../dc/bls_builder/dcn401_builder_internal.h | 107 +++
.../bls_builder/dcn401_non_seamless_builder.c | 447 +++++++++
...it_builder.c => dcn401_seamless_builder.c} | 883 ++++--------------
.../amd/display/dc/state_facts/state_facts.c | 294 ++++++
.../amd/display/dc/state_facts/state_facts.h | 132 +++
6 files changed, 1162 insertions(+), 705 deletions(-)
create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h
create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
rename drivers/gpu/drm/amd/display/dc/bls_builder/{dcn401_commit_builder.c => dcn401_seamless_builder.c} (72%)
create mode 100644 drivers/gpu/drm/amd/display/dc/state_facts/state_facts.c
create mode 100644 drivers/gpu/drm/amd/display/dc/state_facts/state_facts.h
diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile
index ec3ef6ed6ed4..a7dd6a7ba492 100644
--- a/drivers/gpu/drm/amd/display/dc/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/Makefile
@@ -86,9 +86,11 @@ FILES += core2/dc_core2.o
FILES += core2/dc_core2_init.o
FILES += core2/dc_core2_legacy_init.o
FILES += bls_builder/bls_builder.o
-FILES += bls_builder/dcn401_commit_builder.o
+FILES += bls_builder/dcn401_seamless_builder.o
FILES += bls_builder/dcn401_init_builder.o
FILES += bls_builder/dcn401_init_builder_legacy.o
+FILES += bls_builder/dcn401_non_seamless_builder.o
+FILES += state_facts/state_facts.o
FILES += bls_executor/bls_executor.o
FILES += workaround_manager/workaround_manager.o
FILES += dmub_messenger/dmub_messenger.o
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h
new file mode 100644
index 000000000000..cc531a7eadf7
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#pragma once
+
+#include "bls_builder/bls_builder.h"
+#include "bls_builder/bls_builder_private_types.h"
+
+/*
+ * Shared by the dcn401 non-seamless commit and seamless update builders:
+ * the support checks both make, and the step walkers that turn a step
+ * table into BLS entries.
+ */
+
+/* The support check allows the OTG master and one MPC bottom pipe. */
+#define DCN401_MAX_DPP_PIPES 2
+
+void dcn401_commit_check_failed(const struct dc *dc, const char *msg);
+
+/*
+ * Each check_<name> helper takes the same arguments. CHECK_CONDITION logs
+ * the reason and returns. current_state or new_state is NULL when the
+ * helper looks at only one of them.
+ */
+#define CHECK_CONDITION(name, msg) \
+ do { \
+ if (!check_##name(dc, current_state, new_state)) { \
+ dcn401_commit_check_failed(dc, msg); \
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE; \
+ } \
+ } while (0)
+
+#define CHECK_RESOURCE(name, msg) \
+ do { \
+ if (!check_##name(dc, current_state, new_state)) { \
+ dcn401_commit_check_failed(dc, msg); \
+ return BLS_BUILD_ERROR_RESOURCE_MAPPING; \
+ } \
+ } while (0)
+
+static inline const struct dc_state *dcn401_checked_state(
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ return current_state ? current_state : new_state;
+}
+
+/* The OTG master of state's one stream, or NULL when it is not one pipe. */
+const struct pipe_ctx *dcn401_otg_of(const struct dc *dc,
+ const struct dc_state *state);
+const struct pipe_ctx *dcn401_checked_otg_master(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state);
+
+/* Each DPP pipe under the OTG master of state, top pipe first. */
+#define DCN401_FOR_EACH_DPP_PIPE(dc, state, pipe) \
+ for ((pipe) = dcn401_otg_of(dc, state); (pipe); \
+ (pipe) = (pipe)->bottom_pipe)
+
+/* What both builders need of the dc, before either state. */
+enum bls_builder_result dcn401_check_dc_support(const struct dc *dc);
+/* What both builders need of a state's stream, apart from its planes. */
+enum bls_builder_result dcn401_check_stream_support(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state);
+
+/* pipe is NULL only for the bandwidth and p-state steps. */
+struct dcn401_commit_step_args {
+ struct dc *dc;
+ struct dc_state *current_state;
+ struct dc_state *new_state;
+ struct pipe_ctx *pipe;
+};
+
+void dcn401_add_steps(struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *args,
+ const struct dcn401_commit_step *steps, unsigned int count);
+/* The support check found an OTG master for each stream of state. */
+void dcn401_add_per_otg_master(struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *state_args,
+ struct dc_state *state,
+ const struct dcn401_commit_step *steps, unsigned int count);
+/* Each DPP pipe of each stream, the OTG master first. */
+void dcn401_add_per_dpp_pipe(struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *state_args,
+ struct dc_state *state,
+ const struct dcn401_commit_step *steps, unsigned int count);
+#define DCN401_ADD_STEPS(sequence, args, steps) \
+ dcn401_add_steps(sequence, args, steps, ARRAY_SIZE(steps))
+#define DCN401_ADD_PER_OTG_MASTER(sequence, args, state, steps) \
+ dcn401_add_per_otg_master(sequence, args, state, steps, \
+ ARRAY_SIZE(steps))
+#define DCN401_ADD_PER_DPP_PIPE(sequence, args, state, steps) \
+ dcn401_add_per_dpp_pipe(sequence, args, state, steps, \
+ ARRAY_SIZE(steps))
+
+/*
+ * Points the step arguments at the states and the sequence at
+ * head_sequence, both empty. The entries point into the states; the build
+ * writes neither.
+ */
+void dcn401_init_and_unpack_params(
+ struct bls_builder_declarative_params *params,
+ struct dcn401_commit_step_args *state_args,
+ struct block_sequence_state *head);
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
new file mode 100644
index 000000000000..e379793a8efd
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
@@ -0,0 +1,447 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include "dm_services.h"
+#include "core_types.h"
+#include "bls_builder/bls_builder_internal.h"
+#include "bls_builder/dcn401_builder_internal.h"
+#include "state_facts/state_facts.h"
+
+/*
+ * DCN_DECL_NON_SEAMLESS_COMMIT for one virtual stream without planes.
+ * The new state has no stream, or one stream with no plane; the current
+ * state has no stream, or one stream with one or two planes, one per DPP
+ * pipe. Planes are added later by DCN_DECL_SEAMLESS_UPDATE, not by this
+ * commit.
+ *
+ * The sequence follows legacy_commit_state_hw on dcn401: the same leaf
+ * calls in the same order, including the calls DC1 repeats. Everything is
+ * in head_sequence: the ODM double-buffer and blank waits come before the
+ * bandwidth lower, so no tail is needed.
+ */
+
+static bool check_stream_config_update(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)current_state;
+ (void)new_state;
+ return !dc->ctx->cp_psp.funcs.update_stream_config;
+}
+
+static bool check_plane_count(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct dc_state *state = dcn401_checked_state(current_state,
+ new_state);
+ unsigned int planes = state->stream_status[0].plane_count;
+ struct dc_state_facts facts;
+
+ /*
+ * The current state carries the removed planes, one per DPP pipe.
+ * The new state has none.
+ */
+ (void)dc;
+ if (!current_state)
+ return !planes;
+ dc_state_facts_derive(state, &facts);
+ return planes && planes ==
+ facts.global.blend_tree[0].dpp_pipe_count;
+}
+
+static bool check_pipe_plane(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ /*
+ * The current state carries the removed planes. The new state has
+ * none.
+ */
+ struct dc_state_facts facts;
+ const struct dc_state *state = current_state ? current_state : new_state;
+
+ (void)dc;
+ dc_state_facts_derive(state, &facts);
+ if (!facts.pipe.count)
+ return false;
+ if (current_state)
+ return dc_state_facts_all_pipe(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE, 0));
+ return !dc_state_facts_any_pipe(&facts, 0,
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE, 0));
+}
+
+static bool check_plane_blocks(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ struct dc_state_facts facts;
+
+ (void)new_state;
+ (void)dc;
+ if (!current_state)
+ return true;
+ dc_state_facts_derive(current_state, &facts);
+ return facts.pipe.count &&
+ dc_state_facts_all_pipe(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE_RESOURCES, 0));
+}
+
+/* The OPP's tree holds exactly the removed MPCCs, in pipe order. */
+static bool check_mpcc_tree(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ current_state, new_state);
+ const struct mpcc *mpcc;
+
+ if (!current_state)
+ return true;
+ if (!pipe || !dc->res_pool || !dc->res_pool->mpc)
+ return false;
+ mpcc = pipe->stream_res.opp->mpc_tree_params.opp_list;
+ for (; pipe; pipe = pipe->bottom_pipe) {
+ if (mpcc != &dc->res_pool->mpc->mpcc_array[
+ pipe->plane_res.mpcc_inst] ||
+ mpcc->dpp_id != pipe->plane_res.dpp->inst)
+ return false;
+ mpcc = mpcc->mpcc_bot;
+ }
+ return !mpcc;
+}
+
+static enum bls_builder_result dcn401_check_commit_state_support(
+ const struct dc *dc, const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ enum bls_builder_result result;
+
+ result = dcn401_check_stream_support(dc, current_state, new_state);
+ if (result != BLS_BUILD_SUCCESS ||
+ !dcn401_checked_state(current_state,
+ new_state)->stream_count)
+ return result;
+
+ CHECK_CONDITION(plane_count, "plane count does not match the state");
+ CHECK_CONDITION(pipe_plane, "pipe plane does not match the state");
+ CHECK_RESOURCE(plane_blocks, "plane is missing hubp or dpp");
+ CHECK_CONDITION(mpcc_tree, "opp tree does not match the pipes");
+ return BLS_BUILD_SUCCESS;
+}
+
+static bool check_stream_not_kept(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ /* A stream is either removed or added, never kept. */
+ return !current_state->stream_count || !new_state->stream_count;
+}
+
+static bool check_link_not_active(const struct dc *dc,
+ const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ (void)dc;
+ (void)current_state;
+ if (!new_state->stream_count)
+ return true;
+ return !new_state->streams[0]->link->link_status.link_active;
+}
+
+static enum bls_builder_result dcn401_check_non_seamless_commit_support(
+ const struct dc *dc, const struct dc_state *current_state,
+ const struct dc_state *new_state)
+{
+ enum bls_builder_result result;
+
+ if (!dc || !current_state || !new_state)
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
+
+ result = dcn401_check_dc_support(dc);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+ CHECK_CONDITION(stream_config_update, "stream config update is set");
+
+ result = dcn401_check_commit_state_support(dc, current_state, NULL);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+ result = dcn401_check_commit_state_support(dc, NULL, new_state);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+
+ CHECK_CONDITION(stream_not_kept, "stream is kept across the commit");
+ CHECK_CONDITION(link_not_active, "link is already active");
+ return BLS_BUILD_SUCCESS;
+}
+
+/*
+ * Net effect: dcn401_prepare_bandwidth without FAMS2, SubVP or DC-mode
+ * softmax.
+ */
+static const struct dcn401_commit_step
+bls_head_per_state_new_prepare_bandwidth[] = {
+ { .func = CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
+ .meta.clk_mgr_update_clocks_for_state.safe_to_lower = false },
+ { .func = HUBBUB_PROGRAM_WATERMARKS,
+ .meta.hubbub_program_watermarks.safe_to_lower = false },
+ { .func = HUBBUB_PROGRAM_ARBITER,
+ .meta.hubbub_program_arbiter.safe_to_lower = false },
+ { .func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS,
+ .meta.hubbub_program_compbuf_segments.safe_to_lower = false },
+};
+
+/*
+ * Net effect: disable the stream of the current state.
+ * - disable_dangling_plane: disconnect the MPCC under the OTG lock, then
+ * dcn20_disable_plane.
+ * - link_set_dpms_off for the virtual signal: dcn401_disable_stream, then
+ * disable_link, which disables the output as DisplayPort.
+ * - dcn401_reset_back_end_for_pipe after link_set_dpms_off, then the
+ * clock source power-down from dcn401_reset_hw_ctx_wrap.
+ * The front end disconnects every MPCC under the lock, then powers down
+ * each DPP pipe after the unlock; the _dpp tables run once per DPP pipe.
+ */
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_current_lock[] = {
+ { .func = TG_LOCK, .meta.tg_lock.lock = true },
+ { .func = OPP_SET_DISP_PATTERN_GENERATOR },
+};
+
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_dpp_pipe_current_disconnect[] = {
+ { .func = HUBP_PROGRAM_DET_SEGMENTS },
+ { .func = MPC_REMOVE_MPCC },
+};
+
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_current_unlock[] = {
+ { .func = TG_LOCK, .meta.tg_lock.lock = false },
+};
+
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_dpp_pipe_current_power_down[] = {
+ { .func = MPC_ASSERT_IDLE_MPCC },
+ { .func = HUBP_SET_BLANK },
+ { .func = HUBP_UPDATE_MALL_SEL },
+ { .func = HUBP_SET_FLIP_CONTROL_GSL },
+ { .func = HUBP_CLK_CNTL },
+ { .func = DPP_DPPCLK_CONTROL },
+ /*
+ * dcn10_plane_atomic_power_down sets IP_REQUEST_EN only when it
+ * reads 0. Every writer clears it again, so it always reads 0 here.
+ */
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true },
+ { .func = HUBP_PG_CONTROL },
+ { .func = HUBP_RESET },
+ { .func = DPP_RESET },
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false },
+};
+
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_current_stream_off[] = {
+ { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
+
+ { .func = DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
+ .meta.link_enc_connect_dig_be_to_fe.connect = false },
+ { .func = DCCG_DISABLE_SYMCLK_SE },
+ { .func = DIO_LINK_ENC_DISABLE_OUTPUT },
+
+ { .func = TG_DISABLE_CRTC },
+ { .func = TG_ENABLE_OPTC_CLOCK,
+ .meta.tg_enable_optc_clock.enable = false },
+ { .func = OPTC_SET_ODM_BYPASS },
+ { .func = TG_SET_DRR },
+ /* link_set_dpms_off disabled the output already. */
+ { .func = DCCG_SET_DTBCLK_P_SRC,
+ .meta.dccg_set_dtbclk_p_src.src = REFCLK },
+ { .func = CLK_SRC_POWER_DOWN },
+};
+
+/*
+ * Net effect: enable the stream of the new state. Between the BIOS
+ * critical-state calls:
+ * - dcn401_enable_stream_timing plus the dce110_apply_single_controller
+ * calls around it, up to the DIG connect.
+ * - link_set_dpms_on for the virtual signal: enable_link_virtual, then
+ * dcn401_enable_stream. The virtual stream encoder has no enable, map,
+ * input-mode or FIFO funcs, and update_info_frame programs nothing.
+ * DC1 skips dig_connect_to_otg for the virtual signal.
+ */
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_new[] = {
+ { .func = BIOS_SET_SCRATCH_CRITICAL_STATE,
+ .meta.bios_set_scratch_critical_state.state = true },
+
+ { .func = OPP_PROGRAM_FMT },
+ { .func = OPP_SET_DYN_EXPANSION },
+ { .func = DCCG_SET_PIXEL_RATE_DIV,
+ .meta.dccg_set_pixel_rate_div.k1 = PIXEL_RATE_DIV_BY_4 },
+ { .func = DCCG_SET_DTBCLK_P_SRC,
+ .meta.dccg_set_dtbclk_p_src.src = DPREFCLK },
+ { .func = TG_ENABLE_OPTC_CLOCK,
+ .meta.tg_enable_optc_clock.enable = true },
+ { .func = CLK_SRC_PROGRAM_PIX_CLK },
+ { .func = TG_PROGRAM_TIMING },
+ { .func = TG_SET_H_TIMING_DIV_MANUAL_MODE },
+ { .func = OPP_PIPE_CLOCK_CONTROL },
+ { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
+ { .func = OPP_PIPE_CLOCK_CONTROL },
+ { .func = OPP_SET_DISP_PATTERN_GENERATOR },
+ { .func = TG_ENABLE_CRTC },
+ { .func = OPP_WAIT_FOR_BLANK_COMPLETE },
+ { .func = TG_SET_DRR, .meta.tg_set_drr.from_adjust = true,
+ .meta.tg_set_drr.with_mid = true },
+ { .func = TG_SET_STATIC_SCREEN_CONTROL },
+ { .func = TG_SETUP_VERTICAL_INTERRUPT2 },
+ { .func = TG_SET_DRR, .meta.tg_set_drr.from_adjust = true },
+ { .func = TG_SET_STATIC_SCREEN_CONTROL },
+
+ { .func = TG_SET_OUT_MUX },
+ { .func = DIO_LINK_ENC_ENABLE_DP_OUTPUT },
+ { .func = DCCG_ENABLE_SYMCLK_SE },
+ /* dcn401_enable_stream divides by one unless the signal is HDMI or DVI. */
+ { .func = DCCG_SET_PIXEL_RATE_DIV,
+ .meta.dccg_set_pixel_rate_div.k1 = PIXEL_RATE_DIV_BY_1 },
+ { .func = DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
+ .meta.link_enc_connect_dig_be_to_fe.connect = true },
+ { .func = TG_SET_EARLY_CONTROL },
+
+ { .func = BIOS_SET_SCRATCH_CRITICAL_STATE,
+ .meta.bios_set_scratch_critical_state.state = false },
+};
+
+/*
+ * Net effect: finish the head for the new OTG master. program_front_end
+ * for a master with no plane, then stereo, then the waits that complete
+ * the head's double buffering. legacy_commit_state_hw sets up stereo for
+ * a mode-changed stream, then dc_enable_stereo sets it up again for every
+ * stream; dcn10_setup_stereo with no 3D format is the two STEREO calls.
+ * The bandwidth lower starts only after these waits.
+ */
+static const struct dcn401_commit_step
+ bls_head_per_stream_per_otg_master_pipe_new_front_end[] = {
+ { .func = TG_LOCK, .meta.tg_lock.lock = true },
+ { .func = OPTC_SET_ODM_BYPASS },
+ { .func = OPP_PIPE_CLOCK_CONTROL },
+ { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
+ { .func = OPP_SET_DYN_EXPANSION },
+ { .func = OPP_PROGRAM_FMT },
+ { .func = OPP_SET_DISP_PATTERN_GENERATOR },
+ { .func = TG_LOCK, .meta.tg_lock.lock = false },
+ { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
+
+ { .func = OPP_PROGRAM_STEREO,
+ .meta.skip_if.stream_mode_unchanged = true },
+ { .func = TG_PROGRAM_STEREO,
+ .meta.skip_if.stream_mode_unchanged = true },
+ { .func = OPP_PROGRAM_STEREO },
+ { .func = TG_PROGRAM_STEREO },
+
+ { .func = TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR },
+ { .func = OPP_WAIT_FOR_BLANK_COMPLETE },
+};
+
+/*
+ * Net effect: release the forced p-state when no OTG master did, then
+ * dcn401_optimize_bandwidth without FAMS2, SubVP or DC-mode softmax.
+ */
+static const struct dcn401_commit_step bls_head_per_state_new_postamble[] = { { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
+ .meta.skip_if.new_state_has_stream = true },
+ { .func = HUBBUB_PROGRAM_WATERMARKS,
+ .meta.hubbub_program_watermarks.safe_to_lower = true },
+ { .func = HUBBUB_PROGRAM_ARBITER,
+ .meta.hubbub_program_arbiter.safe_to_lower = true },
+ { .func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS,
+ .meta.hubbub_program_compbuf_segments.safe_to_lower = true },
+ { .func = CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
+ .meta.clk_mgr_update_clocks_for_state.safe_to_lower = true },
+};
+
+/*
+ * Bounds on what build adds. The support check allows one stream per
+ * state, and never one in both.
+ */
+static_assert(ARRAY_SIZE(bls_head_per_state_new_prepare_bandwidth) +
+ ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_current_lock) +
+ DCN401_MAX_DPP_PIPES * ARRAY_SIZE(
+ bls_head_per_stream_per_dpp_pipe_current_disconnect) +
+ ARRAY_SIZE(
+ bls_head_per_stream_per_otg_master_pipe_current_unlock) +
+ DCN401_MAX_DPP_PIPES * ARRAY_SIZE(
+ bls_head_per_stream_per_dpp_pipe_current_power_down) +
+ ARRAY_SIZE(
+ bls_head_per_stream_per_otg_master_pipe_current_stream_off) +
+ ARRAY_SIZE(bls_head_per_state_new_postamble) <=
+ DC2_BLS_MAX_STEPS,
+ "dcn401 commit stream-off head exceeds DC2_BLS_MAX_STEPS");
+static_assert(ARRAY_SIZE(bls_head_per_state_new_prepare_bandwidth) +
+ ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_new) +
+ ARRAY_SIZE(
+ bls_head_per_stream_per_otg_master_pipe_new_front_end) +
+ ARRAY_SIZE(bls_head_per_state_new_postamble) <=
+ DC2_BLS_MAX_STEPS,
+ "dcn401 commit enable head exceeds DC2_BLS_MAX_STEPS");
+
+static enum bls_builder_result dcn401_build_non_seamless_commit(
+ struct bls_builder_declarative_params *params)
+{
+ struct dcn401_commit_step_args state_args;
+ struct block_sequence_state head;
+ enum bls_builder_result result;
+
+ if (!params)
+ return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
+ result = dcn401_check_non_seamless_commit_support(params->dc,
+ params->current_state, params->new_state);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
+
+ dcn401_init_and_unpack_params(params, &state_args, &head);
+ DCN401_ADD_STEPS(&head, &state_args,
+ bls_head_per_state_new_prepare_bandwidth);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
+ state_args.current_state,
+ bls_head_per_stream_per_otg_master_pipe_current_lock);
+ DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.current_state,
+ bls_head_per_stream_per_dpp_pipe_current_disconnect);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
+ state_args.current_state,
+ bls_head_per_stream_per_otg_master_pipe_current_unlock);
+ DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.current_state,
+ bls_head_per_stream_per_dpp_pipe_current_power_down);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
+ state_args.current_state,
+ bls_head_per_stream_per_otg_master_pipe_current_stream_off);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
+ bls_head_per_stream_per_otg_master_pipe_new);
+ DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
+ bls_head_per_stream_per_otg_master_pipe_new_front_end);
+ DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new_postamble);
+
+ return BLS_BUILD_SUCCESS;
+}
+
+static bool dcn401_non_seamless_commit_check_capability(
+ const struct bls_builder_check_capability_params *params)
+{
+ return params && dcn401_check_non_seamless_commit_support(params->dc,
+ params->current_state, params->new_state) ==
+ BLS_BUILD_SUCCESS;
+}
+
+const struct bls_builder *dcn401_non_seamless_commit_builder(void)
+{
+ static const struct bls_builder builder = {
+ .name = DCN_DECL_NON_SEAMLESS_COMMIT,
+ .kind = BLS_BUILDER_DECLARATIVE,
+ .check_capability = dcn401_non_seamless_commit_check_capability,
+ .build.declarative = dcn401_build_non_seamless_commit,
+ };
+
+ return &builder;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
similarity index 72%
rename from drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
rename to drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
index 19765f7dcd11..b7f6909d69e0 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_commit_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
@@ -17,33 +17,29 @@
#include "bls_builder/bls_builder_internal.h"
#include "bls_builder/dcn401_init_builder.h"
#include "bls_builder/bls_builder_private_types.h"
+#include "bls_builder/dcn401_builder_internal.h"
+#include "state_facts/state_facts.h"
#undef DC_LOGGER
#define DC_LOGGER \
dc->ctx->logger
/*
- * DCN_DECL_NON_SEAMLESS_COMMIT for one virtual stream without planes.
- * The new state has no stream, or one stream with no plane; the current
- * state has no stream, or one stream with one or two planes, one per DPP
- * pipe. Planes are added later by DCN_DECL_SEAMLESS_UPDATE, not by this
- * commit.
- *
- * The sequence follows legacy_commit_state_hw on dcn401: the same leaf
- * calls in the same order, including the calls DC1 repeats. Everything is
- * in head_sequence: the ODM double-buffer and blank waits come before the
- * bandwidth lower, so no tail is needed.
- *
- * DCN_DECL_SEAMLESS_UPDATE either adds one plane, or two MPC-blended
- * planes, to that stream, or keeps one plane and programs its surface,
- * address, scaler and viewport. The
- * sequence follows the full update in commit_planes_for_stream on dcn401,
- * all in head_sequence: the outstanding updates, prepare_bandwidth and
- * the pending-update waits, then the locked front-end programming and
+ * DCN_DECL_SEAMLESS_UPDATE for the one virtual stream that
+ * DCN_DECL_NON_SEAMLESS_COMMIT (dcn401_non_seamless_builder.c) enabled.
+ * The update either adds one plane, or two MPC-blended planes, to that
+ * stream, or keeps one plane and programs its surface, address, scaler
+ * and viewport. The sequence follows the full update in
+ * commit_planes_for_stream on dcn401, all in head_sequence: the
+ * outstanding updates, prepare_bandwidth and the pending-update waits,
+ * then the locked front-end programming and
* post_unlock_program_front_end. A kept-plane update skips enable_plane
* and the stream unblank. The bandwidth lower stays with
* dc_post_update_surfaces_to_stream.
*
+ * This file also holds what both builders share: the dc and stream
+ * support checks, and the step walkers that turn a table into BLS entries.
+ *
* Neither builder emits a tail_sequence. A tail runs only after a vsync,
* and Core has no wait-for-vsync imperative yet, so Core rejects a tail.
*/
@@ -60,41 +56,12 @@
#define DCN401_MALL_SEL_SS 2
#define DCN401_CACHE_CURSOR_BYTES 16384
-/*
- * Each helper takes the same arguments. CHECK_CONDITION logs the reason
- * and returns. current_state or new_state is NULL when the helper looks
- * at only one of them.
- */
-
-static void dcn401_commit_check_failed(const struct dc *dc, const char *msg)
+void dcn401_commit_check_failed(const struct dc *dc, const char *msg)
{
if (dc && dc->ctx && dc->ctx->logger)
DC_LOG_DEBUG("%s\n", msg);
}
-#define CHECK_CONDITION(name, msg) \
- do { \
- if (!check_##name(dc, current_state, new_state)) { \
- dcn401_commit_check_failed(dc, msg); \
- return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE; \
- } \
- } while (0)
-
-#define CHECK_RESOURCE(name, msg) \
- do { \
- if (!check_##name(dc, current_state, new_state)) { \
- dcn401_commit_check_failed(dc, msg); \
- return BLS_BUILD_ERROR_RESOURCE_MAPPING; \
- } \
- } while (0)
-
-static const struct dc_state *dcn401_checked_state(
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- return current_state ? current_state : new_state;
-}
-
static bool check_dc_resources(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
@@ -155,13 +122,19 @@ static bool check_dsc_power_gate(const struct dc *dc,
return dc->debug.disable_dsc_power_gate;
}
-static bool check_stream_config_update(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
+enum bls_builder_result dcn401_check_dc_support(const struct dc *dc)
{
- (void)current_state;
- (void)new_state;
- return !dc->ctx->cp_psp.funcs.update_stream_config;
+ const struct dc_state *current_state = NULL;
+ const struct dc_state *new_state = NULL;
+
+ CHECK_RESOURCE(dc_resources, "dc resources are missing");
+ CHECK_CONDITION(fams2_debug, "FAMS2 debug is enabled");
+ CHECK_CONDITION(cursor_offload, "cursor offload is enabled");
+ CHECK_CONDITION(dc_mode_softmax, "dc mode softmax is enabled");
+ CHECK_CONDITION(visual_confirm,
+ "visual confirm, dpg, hblank borrow, or frame sync is set");
+ CHECK_CONDITION(dsc_power_gate, "DSC power gate is enabled");
+ return BLS_BUILD_SUCCESS;
}
static bool check_one_stream(const struct dc *dc,
@@ -289,12 +262,13 @@ static bool check_abm(const struct dc *dc,
* more than one. An MPC bottom pipe of the stream is not an OTG master;
* check_mpc_blend bounds it.
*/
-static const struct pipe_ctx *dcn401_state_otg_master(
+static const struct pipe_ctx *dcn401_state_otg_master(const struct dc *dc,
const struct dc_state *state, bool *not_one_pipe)
{
const struct pipe_ctx *otg_master = NULL;
unsigned int i;
+ (void)dc;
*not_one_pipe = false;
for (i = 0; i < MAX_PIPES; i++) {
const struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
@@ -312,69 +286,24 @@ static const struct pipe_ctx *dcn401_state_otg_master(
return otg_master;
}
-static const struct pipe_ctx *dcn401_checked_otg_master(
+const struct pipe_ctx *dcn401_checked_otg_master(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
bool not_one_pipe = false;
- return dcn401_state_otg_master(dcn401_checked_state(current_state,
+ return dcn401_state_otg_master(dc, dcn401_checked_state(current_state,
new_state), ¬_one_pipe);
}
-/* The OTG master of one state, or NULL when that state is not one pipe. */
-static const struct pipe_ctx *dcn401_otg_of(const struct dc_state *state)
+const struct pipe_ctx *dcn401_otg_of(const struct dc *dc,
+ const struct dc_state *state)
{
bool not_one_pipe = false;
if (!state)
return NULL;
- return dcn401_state_otg_master(state, ¬_one_pipe);
-}
-
-/* The OTG master and its MPC bottom pipe, top to bottom. */
-static unsigned int dcn401_dpp_pipe_count(const struct pipe_ctx *otg_master)
-{
- unsigned int count = 0;
-
- for (; otg_master; otg_master = otg_master->bottom_pipe)
- count++;
- return count;
-}
-
-typedef bool (*dcn401_pipe_check)(const struct pipe_ctx *pipe);
-
-/* False when state has no OTG master, or check fails on one of its pipes. */
-static bool dcn401_each_dpp_pipe(const struct dc_state *state,
- dcn401_pipe_check check)
-{
- const struct pipe_ctx *pipe = dcn401_otg_of(state);
-
- if (!pipe)
- return false;
- for (; pipe; pipe = pipe->bottom_pipe) {
- if (!check(pipe))
- return false;
- }
- return true;
-}
-
-static bool check_plane_count(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- const struct dc_state *state = dcn401_checked_state(current_state,
- new_state);
- unsigned int planes = state->stream_status[0].plane_count;
-
- (void)dc;
- /*
- * The current state carries the removed planes, one per DPP pipe.
- * The new state has none.
- */
- if (!current_state)
- return !planes;
- return planes && planes == dcn401_dpp_pipe_count(dcn401_otg_of(state));
+ return dcn401_state_otg_master(dc, state, ¬_one_pipe);
}
static bool check_one_otg_pipe(const struct dc *dc,
@@ -383,9 +312,8 @@ static bool check_one_otg_pipe(const struct dc *dc,
{
bool not_one_pipe = false;
- (void)dc;
- dcn401_state_otg_master(dcn401_checked_state(current_state, new_state),
- ¬_one_pipe);
+ dcn401_state_otg_master(dc, dcn401_checked_state(current_state,
+ new_state), ¬_one_pipe);
return !not_one_pipe;
}
@@ -393,18 +321,16 @@ static bool check_otg_master_present(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
- return dcn401_checked_otg_master(current_state, new_state) != NULL;
+ return dcn401_checked_otg_master(dc, current_state, new_state) != NULL;
}
static bool check_odm_combine(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ current_state, new_state);
- (void)dc;
return pipe && !pipe->next_odm_pipe && !pipe->prev_odm_pipe;
}
@@ -412,111 +338,40 @@ static bool check_mpc_blend(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ current_state, new_state);
- const struct pipe_ctx *bottom;
+ struct dc_state_facts facts;
- (void)dc;
if (!pipe || pipe->top_pipe)
return false;
- /* At most one bottom pipe, blended on the master's OTG and OPP. */
- bottom = pipe->bottom_pipe;
- if (!bottom)
- return true;
- return bottom->top_pipe == pipe && !bottom->bottom_pipe &&
- !bottom->next_odm_pipe && !bottom->prev_odm_pipe &&
- bottom->stream == pipe->stream &&
- bottom->stream_res.tg == pipe->stream_res.tg &&
- bottom->stream_res.opp == pipe->stream_res.opp;
-}
-
-static bool dcn401_pipe_has_plane(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state != NULL;
-}
-
-static bool dcn401_pipe_has_no_plane(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state == NULL;
-}
-
-static bool check_pipe_plane(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- (void)dc;
- /*
- * The current state carries the removed planes. The new state has
- * none.
- */
- if (current_state)
- return dcn401_each_dpp_pipe(current_state,
- dcn401_pipe_has_plane);
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_has_no_plane);
+ dc_state_facts_derive(dcn401_checked_state(current_state, new_state),
+ &facts);
+ /* At most one bottom pipe, on the master's OTG and OPP, and no ODM. */
+ return facts.global.blend_tree[0].dpp_pipe_count <= 2 &&
+ facts.global.blend_tree[0].on_otg_master_blocks &&
+ !dc_state_facts_any_pipe(&facts, 0,
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_ODM, 0));
}
static bool check_otg_master_blocks(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ current_state, new_state);
- (void)dc;
return pipe && pipe->stream_res.tg && pipe->stream_res.opp &&
pipe->stream_res.stream_enc && pipe->clock_source;
}
-static bool dcn401_pipe_plane_blocks(const struct pipe_ctx *pipe)
-{
- return pipe->plane_res.hubp && pipe->plane_res.dpp &&
- pipe->plane_res.mpcc_inst < MAX_MPCC;
-}
-
-static bool check_plane_blocks(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- (void)dc;
- (void)new_state;
- if (!current_state)
- return true;
- return dcn401_each_dpp_pipe(current_state, dcn401_pipe_plane_blocks);
-}
-
-/* The OPP's tree holds exactly the removed MPCCs, in pipe order. */
-static bool check_mpcc_tree(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
- const struct mpcc *mpcc;
-
- if (!current_state)
- return true;
- if (!pipe || !dc->res_pool || !dc->res_pool->mpc)
- return false;
- mpcc = pipe->stream_res.opp->mpc_tree_params.opp_list;
- for (; pipe; pipe = pipe->bottom_pipe) {
- if (mpcc != &dc->res_pool->mpc->mpcc_array[
- pipe->plane_res.mpcc_inst] ||
- mpcc->dpp_id != pipe->plane_res.dpp->inst)
- return false;
- mpcc = mpcc->mpcc_bot;
- }
- return !mpcc;
-}
-
static bool check_pipe_audio(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ current_state, new_state);
- (void)dc;
return pipe && !pipe->stream_res.audio;
}
@@ -524,10 +379,9 @@ static bool check_pipe_dsc(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ current_state, new_state);
- (void)dc;
return pipe && !pipe->stream_res.dsc;
}
@@ -535,10 +389,9 @@ static bool check_pipe_gsl(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ current_state, new_state);
- (void)dc;
return pipe && !pipe->stream_res.gsl_group;
}
@@ -546,8 +399,8 @@ static bool check_link_enc_assignment(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(current_state,
- new_state);
+ const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ current_state, new_state);
if (!dc->config.unify_link_enc_assignment)
return true;
@@ -555,9 +408,8 @@ static bool check_link_enc_assignment(const struct dc *dc,
pipe->stream->link->link_enc;
}
-/* What both builders need of a state's stream, apart from its planes. */
-static enum bls_builder_result dcn401_check_stream_support(
- const struct dc *dc, const struct dc_state *current_state,
+enum bls_builder_result dcn401_check_stream_support(const struct dc *dc,
+ const struct dc_state *current_state,
const struct dc_state *new_state)
{
const struct dc_state *state = dcn401_checked_state(current_state,
@@ -584,7 +436,8 @@ static enum bls_builder_result dcn401_check_stream_support(
CHECK_CONDITION(odm_combine, "odm is set");
CHECK_CONDITION(mpc_blend,
"more than one bottom pipe, or it is off the otg or opp");
- CHECK_RESOURCE(otg_master_blocks, "otg master is missing a block");
+ CHECK_RESOURCE(otg_master_blocks,
+ "otg master is missing a stream resource or pixel clock");
CHECK_CONDITION(pipe_audio, "audio is set");
CHECK_CONDITION(pipe_dsc, "dsc is set");
CHECK_CONDITION(pipe_gsl, "gsl is set");
@@ -592,75 +445,6 @@ static enum bls_builder_result dcn401_check_stream_support(
return BLS_BUILD_SUCCESS;
}
-static enum bls_builder_result dcn401_check_commit_state_support(
- const struct dc *dc, const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- enum bls_builder_result result;
-
- result = dcn401_check_stream_support(dc, current_state, new_state);
- if (result != BLS_BUILD_SUCCESS ||
- !dcn401_checked_state(current_state,
- new_state)->stream_count)
- return result;
-
- CHECK_CONDITION(plane_count, "plane count does not match the state");
- CHECK_CONDITION(pipe_plane, "pipe plane does not match the state");
- CHECK_RESOURCE(plane_blocks, "plane is missing hubp or dpp");
- CHECK_CONDITION(mpcc_tree, "opp tree does not match the pipes");
- return BLS_BUILD_SUCCESS;
-}
-
-static bool check_stream_not_kept(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- (void)dc;
- /* A stream is either removed or added, never kept. */
- return !current_state->stream_count || !new_state->stream_count;
-}
-
-static bool check_link_not_active(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- (void)dc;
- (void)current_state;
- if (!new_state->stream_count)
- return true;
- return !new_state->streams[0]->link->link_status.link_active;
-}
-
-static enum bls_builder_result dcn401_check_non_seamless_commit_support(
- const struct dc *dc, const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- enum bls_builder_result result;
-
- if (!dc || !current_state || !new_state)
- return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
-
- CHECK_RESOURCE(dc_resources, "dc resources are missing");
- CHECK_CONDITION(fams2_debug, "FAMS2 debug is enabled");
- CHECK_CONDITION(cursor_offload, "cursor offload is enabled");
- CHECK_CONDITION(dc_mode_softmax, "dc mode softmax is enabled");
- CHECK_CONDITION(visual_confirm, "visual confirm, dpg, hblank borrow, "
- "or frame sync is set");
- CHECK_CONDITION(dsc_power_gate, "DSC power gate is enabled");
- CHECK_CONDITION(stream_config_update, "stream config update is set");
-
- result = dcn401_check_commit_state_support(dc, current_state, NULL);
- if (result != BLS_BUILD_SUCCESS)
- return result;
- result = dcn401_check_commit_state_support(dc, NULL, new_state);
- if (result != BLS_BUILD_SUCCESS)
- return result;
-
- CHECK_CONDITION(stream_not_kept, "stream is kept across the commit");
- CHECK_CONDITION(link_not_active, "link is already active");
- return BLS_BUILD_SUCCESS;
-}
-
/* The seamless update keeps the one stream across the update. */
static bool check_stream_kept(const struct dc *dc,
const struct dc_state *current_state,
@@ -698,11 +482,14 @@ static bool check_plane_add_or_kept(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
+ struct dc_state_facts facts;
+
(void)dc;
if (!dcn401_is_plane_add(current_state, new_state) &&
!dcn401_is_plane_kept(current_state, new_state))
return false;
- return dcn401_dpp_pipe_count(dcn401_otg_of(new_state)) ==
+ dc_state_facts_derive(new_state, &facts);
+ return facts.global.blend_tree[0].dpp_pipe_count ==
new_state->stream_status[0].plane_count;
}
@@ -710,29 +497,29 @@ static bool check_same_otg_master_blocks(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *current_pipe = dcn401_otg_of(current_state);
- const struct pipe_ctx *new_pipe = dcn401_otg_of(new_state);
+ const struct pipe_ctx *current_pipe = dcn401_otg_of(dc, current_state);
+ const struct pipe_ctx *new_pipe = dcn401_otg_of(dc, new_state);
- (void)dc;
if (!current_pipe || !new_pipe)
return false;
return current_pipe->stream_res.tg == new_pipe->stream_res.tg &&
current_pipe->stream_res.opp == new_pipe->stream_res.opp;
}
-static bool dcn401_pipe_new_plane_blocks(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state && pipe->plane_res.hubp &&
- pipe->plane_res.dpp && pipe->plane_res.dpp->funcs;
-}
-
static bool check_new_plane_blocks(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
+ struct dc_state_facts facts;
+
(void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_new_plane_blocks);
+ if (!dcn401_otg_of(dc, new_state))
+ return false;
+ dc_state_facts_derive(new_state, &facts);
+ return facts.pipe.count &&
+ dc_state_facts_all_pipe(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE |
+ DC_PIPE_TRAIT_PLANE_RESOURCES, 0));
}
static bool check_pool_funcs(const struct dc *dc,
@@ -744,27 +531,6 @@ static bool check_pool_funcs(const struct dc *dc,
return dc->res_pool->funcs && dc->res_pool->hubbub->funcs;
}
-static bool dcn401_pipe_plane_instances(const struct pipe_ctx *pipe)
-{
- int inst;
-
- if (!pipe->plane_res.hubp || !pipe->plane_res.dpp)
- return false;
- inst = pipe->plane_res.hubp->inst;
- return inst < MAX_MPCC && pipe->plane_res.mpcc_inst == inst &&
- pipe->plane_res.dpp->inst == inst;
-}
-
-/* DC1 uses the HUBP instance as the MPCC and DPP instance. */
-static bool check_plane_instances(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- (void)dc;
- (void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_plane_instances);
-}
-
/* dcn401 programs DET by segments only. */
static bool check_det_by_segments(const struct dc *dc,
const struct dc_state *current_state,
@@ -795,18 +561,17 @@ static bool check_no_sanity_checks(const struct dc *dc,
return !dc->debug.sanity_checks;
}
-static bool dcn401_pipe_no_wait(const struct pipe_ctx *pipe)
-{
- return !pipe->wait_is_required;
-}
-
static bool check_no_pipe_wait(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
+ struct dc_state_facts facts;
+
(void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_wait);
+ (void)dc;
+ dc_state_facts_derive(new_state, &facts);
+ return facts.pipe.count && !dc_state_facts_any_pipe(&facts, 0,
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_WAIT, 0));
}
/* dcn20_blank_pixel_data does nothing while a test pattern is on. */
@@ -834,18 +599,17 @@ static bool check_no_forced_dpg(const struct dc *dc,
}
/* dcn20_blank_pixel_data sets the ABM pipe on unblank when there is one. */
-static bool dcn401_pipe_no_abm(const struct pipe_ctx *pipe)
-{
- return !pipe->stream_res.abm;
-}
-
static bool check_no_abm_pipe(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
+ struct dc_state_facts facts;
+
(void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_abm);
+ (void)dc;
+ dc_state_facts_derive(new_state, &facts);
+ return facts.pipe.count && !dc_state_facts_any_pipe(&facts, 0,
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_ABM, 0));
}
/* enable_plane programs the system aperture when VM is valid. */
@@ -859,25 +623,20 @@ static bool check_no_vm_aperture(const struct dc *dc,
}
/* enable_plane sets the flip interrupt when the plane asks for one. */
-static bool dcn401_pipe_no_flip_int(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state && !pipe->plane_state->flip_int_enabled;
-}
-
static bool check_no_flip_int(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
- (void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_flip_int);
-}
+ const struct pipe_ctx *pipe;
-
-static bool dcn401_pipe_no_plane_gamut_remap(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state &&
- !pipe->plane_state->gamut_remap_matrix.enable_remap;
+ (void)current_state;
+ if (!dcn401_otg_of(dc, new_state))
+ return false;
+ DCN401_FOR_EACH_DPP_PIPE(dc, new_state, pipe) {
+ if (!pipe->plane_state || pipe->plane_state->flip_int_enabled)
+ return false;
+ }
+ return true;
}
/* The update bypasses the plane gamut remap. */
@@ -885,10 +644,17 @@ static bool check_no_plane_gamut_remap(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
+ const struct pipe_ctx *pipe;
+
(void)current_state;
- return dcn401_each_dpp_pipe(new_state,
- dcn401_pipe_no_plane_gamut_remap);
+ if (!dcn401_otg_of(dc, new_state))
+ return false;
+ DCN401_FOR_EACH_DPP_PIPE(dc, new_state, pipe) {
+ if (!pipe->plane_state ||
+ pipe->plane_state->gamut_remap_matrix.enable_remap)
+ return false;
+ }
+ return true;
}
/* The update bypasses the stream gamut remap. */
@@ -912,24 +678,21 @@ static bool check_no_output_csc(const struct dc *dc,
}
/* The update programs the surface and its address without DCC. */
-static bool dcn401_pipe_no_dcc(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state && !pipe->plane_state->dcc.enable;
-}
-
static bool check_no_dcc(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
- (void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_dcc);
-}
+ struct dc_state_facts facts;
-static bool dcn401_pipe_graphics_address(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state &&
- pipe->plane_state->address.type == PLN_ADDR_TYPE_GRAPHICS;
+ (void)current_state;
+ if (!dcn401_otg_of(dc, new_state))
+ return false;
+ dc_state_facts_derive(new_state, &facts);
+ return facts.pipe.count &&
+ dc_state_facts_all_pipe(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE, 0)) &&
+ !dc_state_facts_any_plane(&facts, 0,
+ DC_TRAIT_MATCH(DC_PLANE_TRAIT_DCC, 0));
}
/* The update programs a single graphics address, not stereo or video. */
@@ -937,14 +700,16 @@ static bool check_graphics_address(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
- (void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_graphics_address);
-}
+ struct dc_state_facts facts;
-static bool dcn401_pipe_no_immediate_flip(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state && !pipe->plane_state->flip_immediate;
+ (void)current_state;
+ (void)dc;
+ dc_state_facts_derive(new_state, &facts);
+ return facts.pipe.count &&
+ dc_state_facts_all_pipe(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE, 0)) &&
+ dc_state_facts_all_plane(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PLANE_TRAIT_GRAPHICS_ADDRESS, 0));
}
/* DC1 programs triple buffering and the flip only for a queued flip. */
@@ -952,56 +717,37 @@ static bool check_no_immediate_flip(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
+ struct dc_state_facts facts;
+
(void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_immediate_flip);
+ if (!dcn401_otg_of(dc, new_state))
+ return false;
+ dc_state_facts_derive(new_state, &facts);
+ return facts.pipe.count &&
+ dc_state_facts_all_pipe(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE, 0)) &&
+ !dc_state_facts_any_plane(&facts, 0,
+ DC_TRAIT_MATCH(DC_PLANE_TRAIT_FLIP_IMMEDIATE, 0));
}
/*
* DC1 translates a distributed-points curve into a LUT while it programs
* the plane. The update programs only the predefined and bypass curves.
*/
-static bool dcn401_pipe_no_degamma_curve(const struct pipe_ctx *pipe)
-{
- return pipe->plane_state &&
- pipe->plane_state->in_transfer_func.type !=
- TF_TYPE_DISTRIBUTED_POINTS;
-}
-
static bool check_no_degamma_curve(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
- (void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_degamma_curve);
-}
-
-static bool dcn401_pipe_no_dpp_pregam(const struct pipe_ctx *pipe)
-{
- return pipe->plane_res.dpp && pipe->plane_res.dpp->funcs &&
- !pipe->plane_res.dpp->funcs->dpp_set_pregam_state;
-}
+ struct dc_state_facts facts;
-/* dcn32_set_input_transfer_func calls dpp_set_pregam_state if present. */
-static bool check_no_dpp_pregam(const struct dc *dc,
- const struct dc_state *current_state,
- const struct dc_state *new_state)
-{
- (void)dc;
(void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_dpp_pregam);
-}
-
-static bool dcn401_pipe_no_mcm_luts(const struct pipe_ctx *pipe)
-{
- const union dc_plane_cm_flags *flags;
-
- if (!pipe->plane_state)
- return false;
- flags = &pipe->plane_state->cm.flags;
- return !flags->bits.blend_enable && !flags->bits.shaper_enable &&
- !flags->bits.lut3d_enable && !flags->bits.rmcm_enable;
+ (void)dc;
+ dc_state_facts_derive(new_state, &facts);
+ return facts.pipe.count &&
+ dc_state_facts_all_pipe(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE, 0)) &&
+ !dc_state_facts_any_plane(&facts, 0,
+ DC_TRAIT_MATCH(DC_PLANE_TRAIT_DEGAMMA_CURVE, 0));
}
/* dcn401_set_mcm_luts then programs every MCM LUT off. */
@@ -1009,9 +755,17 @@ static bool check_no_mcm_luts(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- (void)dc;
+ struct dc_state_facts facts;
+
(void)current_state;
- return dcn401_each_dpp_pipe(new_state, dcn401_pipe_no_mcm_luts);
+
+ (void)dc;
+ dc_state_facts_derive(new_state, &facts);
+ return facts.pipe.count &&
+ dc_state_facts_all_pipe(&facts, 0, DC_TRAIT_MATCH(0, 0),
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE, 0)) &&
+ !dc_state_facts_any_plane(&facts, 0,
+ DC_TRAIT_MATCH(DC_PLANE_TRAIT_MCM_LUTS, 0));
}
static bool check_no_shaper_curve(const struct dc *dc,
@@ -1077,12 +831,9 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
if (!dc || !current_state || !new_state)
return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
- CHECK_RESOURCE(dc_resources, "dc resources are missing");
- CHECK_CONDITION(fams2_debug, "FAMS2 debug is enabled");
- CHECK_CONDITION(cursor_offload, "cursor offload is enabled");
- CHECK_CONDITION(dc_mode_softmax, "dc mode softmax is enabled");
- CHECK_CONDITION(visual_confirm, "visual confirm, dpg, hblank borrow, or frame sync is set");
- CHECK_CONDITION(dsc_power_gate, "DSC power gate is enabled");
+ result = dcn401_check_dc_support(dc);
+ if (result != BLS_BUILD_SUCCESS)
+ return result;
result = dcn401_check_stream_support(dc, current_state, NULL);
if (result != BLS_BUILD_SUCCESS)
@@ -1098,7 +849,6 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
CHECK_CONDITION(same_otg_master_blocks, "otg or opp changes");
CHECK_RESOURCE(new_plane_blocks, "plane is missing hubp or dpp");
CHECK_RESOURCE(pool_funcs, "resource pool or hubbub has no funcs");
- CHECK_CONDITION(plane_instances, "mpcc or dpp is not the hubp instance");
CHECK_CONDITION(det_by_segments, "hubbub programs det size");
/* Under the lock, before the pipe is programmed. */
@@ -1123,7 +873,6 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
/* Input, MCM and output transfer functions. */
CHECK_CONDITION(no_degamma_curve, "plane degamma is distributed points");
- CHECK_CONDITION(no_dpp_pregam, "dpp has a pregam state");
CHECK_CONDITION(no_mcm_luts, "an mcm lut is enabled");
CHECK_CONDITION(no_shaper_curve, "shaper is distributed points");
CHECK_CONDITION(no_stream_3dlut, "stream 3dlut is set");
@@ -1136,14 +885,18 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
}
static struct pipe_ctx *get_otg_master_from_state_by_stream(
- struct dc_state *state, const struct dc_stream_state *stream)
+ const struct dc *dc, struct dc_state *state,
+ const struct dc_stream_state *stream)
{
unsigned int i;
+ (void)dc;
+
for (i = 0; i < MAX_PIPES; i++) {
- if (state->res_ctx.pipe_ctx[i].stream == stream &&
- !state->res_ctx.pipe_ctx[i].top_pipe)
- return &state->res_ctx.pipe_ctx[i];
+ struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
+
+ if (pipe->stream == stream && !pipe->top_pipe)
+ return pipe;
}
return NULL;
}
@@ -1152,7 +905,8 @@ static struct pipe_ctx *get_otg_master_from_state_by_stream(
* Net effect: dcn401_prepare_bandwidth without FAMS2, SubVP or DC-mode
* softmax.
*/
-static const struct dcn401_commit_step bls_head_per_state_new[] = {
+static const struct dcn401_commit_step
+bls_update_head_per_state_new_prepare_bandwidth[] = {
{ .func = CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
.meta.clk_mgr_update_clocks_for_state.safe_to_lower = false },
{ .func = HUBBUB_PROGRAM_WATERMARKS,
@@ -1163,202 +917,6 @@ static const struct dcn401_commit_step bls_head_per_state_new[] = {
.meta.hubbub_program_compbuf_segments.safe_to_lower = false },
};
-/*
- * Net effect: disable the stream of the current state.
- * - disable_dangling_plane: disconnect the MPCC under the OTG lock, then
- * dcn20_disable_plane.
- * - link_set_dpms_off for the virtual signal: dcn401_disable_stream, then
- * disable_link, which disables the output as DisplayPort.
- * - dcn401_reset_back_end_for_pipe after link_set_dpms_off, then the
- * clock source power-down from dcn401_reset_hw_ctx_wrap.
- * The front end disconnects every MPCC under the lock, then powers down
- * each DPP pipe after the unlock; the _dpp tables run once per DPP pipe.
- */
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_current_lock[] = {
- { .func = TG_LOCK, .meta.tg_lock.lock = true },
- { .func = OPP_SET_DISP_PATTERN_GENERATOR },
-};
-
-static const struct dcn401_commit_step
- bls_head_per_stream_per_dpp_pipe_current_disconnect[] = {
- { .func = HUBP_PROGRAM_DET_SEGMENTS },
- { .func = MPC_REMOVE_MPCC },
-};
-
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_current_unlock[] = {
- { .func = TG_LOCK, .meta.tg_lock.lock = false },
-};
-
-static const struct dcn401_commit_step
- bls_head_per_stream_per_dpp_pipe_current_power_down[] = {
- { .func = MPC_ASSERT_IDLE_MPCC },
- { .func = HUBP_SET_BLANK },
- { .func = HUBP_UPDATE_MALL_SEL },
- { .func = HUBP_SET_FLIP_CONTROL_GSL },
- { .func = HUBP_CLK_CNTL },
- { .func = DPP_DPPCLK_CONTROL },
- /*
- * dcn10_plane_atomic_power_down sets IP_REQUEST_EN only when it
- * reads 0. Every writer clears it again, so it always reads 0 here.
- */
- { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true },
- { .func = HUBP_PG_CONTROL },
- { .func = HUBP_RESET },
- { .func = DPP_RESET },
- { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false },
-};
-
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_current_stream_off[] = {
- { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
-
- { .func = DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
- .meta.link_enc_connect_dig_be_to_fe.connect = false },
- { .func = DCCG_DISABLE_SYMCLK_SE },
- { .func = DIO_LINK_ENC_DISABLE_OUTPUT },
-
- { .func = TG_DISABLE_CRTC },
- { .func = TG_ENABLE_OPTC_CLOCK,
- .meta.tg_enable_optc_clock.enable = false },
- { .func = OPTC_SET_ODM_BYPASS },
- { .func = TG_SET_DRR },
- /* link_set_dpms_off disabled the output already. */
- { .func = DCCG_SET_DTBCLK_P_SRC,
- .meta.dccg_set_dtbclk_p_src.src = REFCLK },
- { .func = CLK_SRC_POWER_DOWN },
-};
-
-/*
- * Net effect: enable the stream of the new state. Between the BIOS
- * critical-state calls:
- * - dcn401_enable_stream_timing plus the dce110_apply_single_controller
- * calls around it, up to the DIG connect.
- * - link_set_dpms_on for the virtual signal: enable_link_virtual, then
- * dcn401_enable_stream. The virtual stream encoder has no enable, map,
- * input-mode or FIFO funcs, and update_info_frame programs nothing.
- * DC1 skips dig_connect_to_otg for the virtual signal.
- */
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_new[] = {
- { .func = BIOS_SET_SCRATCH_CRITICAL_STATE,
- .meta.bios_set_scratch_critical_state.state = true },
-
- { .func = OPP_PROGRAM_FMT },
- { .func = OPP_SET_DYN_EXPANSION },
- { .func = DCCG_SET_PIXEL_RATE_DIV,
- .meta.dccg_set_pixel_rate_div.k1 = PIXEL_RATE_DIV_BY_4 },
- { .func = DCCG_SET_DTBCLK_P_SRC,
- .meta.dccg_set_dtbclk_p_src.src = DPREFCLK },
- { .func = TG_ENABLE_OPTC_CLOCK,
- .meta.tg_enable_optc_clock.enable = true },
- { .func = CLK_SRC_PROGRAM_PIX_CLK },
- { .func = TG_PROGRAM_TIMING },
- { .func = TG_SET_H_TIMING_DIV_MANUAL_MODE },
- { .func = OPP_PIPE_CLOCK_CONTROL },
- { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
- { .func = OPP_PIPE_CLOCK_CONTROL },
- { .func = OPP_SET_DISP_PATTERN_GENERATOR },
- { .func = TG_ENABLE_CRTC },
- { .func = OPP_WAIT_FOR_BLANK_COMPLETE },
- { .func = TG_SET_DRR, .meta.tg_set_drr.from_adjust = true,
- .meta.tg_set_drr.with_mid = true },
- { .func = TG_SET_STATIC_SCREEN_CONTROL },
- { .func = TG_SETUP_VERTICAL_INTERRUPT2 },
- { .func = TG_SET_DRR, .meta.tg_set_drr.from_adjust = true },
- { .func = TG_SET_STATIC_SCREEN_CONTROL },
-
- { .func = TG_SET_OUT_MUX },
- { .func = DIO_LINK_ENC_ENABLE_DP_OUTPUT },
- { .func = DCCG_ENABLE_SYMCLK_SE },
- /* dcn401_enable_stream divides by one unless the signal is HDMI or DVI. */
- { .func = DCCG_SET_PIXEL_RATE_DIV,
- .meta.dccg_set_pixel_rate_div.k1 = PIXEL_RATE_DIV_BY_1 },
- { .func = DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
- .meta.link_enc_connect_dig_be_to_fe.connect = true },
- { .func = TG_SET_EARLY_CONTROL },
-
- { .func = BIOS_SET_SCRATCH_CRITICAL_STATE,
- .meta.bios_set_scratch_critical_state.state = false },
-};
-
-/*
- * Net effect: finish the head for the new OTG master. program_front_end
- * for a master with no plane, then stereo, then the waits that complete
- * the head's double buffering. legacy_commit_state_hw sets up stereo for
- * a mode-changed stream, then dc_enable_stereo sets it up again for every
- * stream; dcn10_setup_stereo with no 3D format is the two STEREO calls.
- * The bandwidth lower starts only after these waits.
- */
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_new_front_end[] = {
- { .func = TG_LOCK, .meta.tg_lock.lock = true },
- { .func = OPTC_SET_ODM_BYPASS },
- { .func = OPP_PIPE_CLOCK_CONTROL },
- { .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
- { .func = OPP_SET_DYN_EXPANSION },
- { .func = OPP_PROGRAM_FMT },
- { .func = OPP_SET_DISP_PATTERN_GENERATOR },
- { .func = TG_LOCK, .meta.tg_lock.lock = false },
- { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
-
- { .func = OPP_PROGRAM_STEREO,
- .meta.skip_if.stream_mode_unchanged = true },
- { .func = TG_PROGRAM_STEREO,
- .meta.skip_if.stream_mode_unchanged = true },
- { .func = OPP_PROGRAM_STEREO },
- { .func = TG_PROGRAM_STEREO },
-
- { .func = TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR },
- { .func = OPP_WAIT_FOR_BLANK_COMPLETE },
-};
-
-/*
- * Net effect: release the forced p-state when no OTG master did, then
- * dcn401_optimize_bandwidth without FAMS2, SubVP or DC-mode softmax.
- */
-static const struct dcn401_commit_step bls_head_per_state_new_postamble[] = {
- { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
- .meta.skip_if.new_state_has_stream = true },
- { .func = HUBBUB_PROGRAM_WATERMARKS,
- .meta.hubbub_program_watermarks.safe_to_lower = true },
- { .func = HUBBUB_PROGRAM_ARBITER,
- .meta.hubbub_program_arbiter.safe_to_lower = true },
- { .func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS,
- .meta.hubbub_program_compbuf_segments.safe_to_lower = true },
- { .func = CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
- .meta.clk_mgr_update_clocks_for_state.safe_to_lower = true },
-};
-
-/* The support check allows the OTG master and one MPC bottom pipe. */
-#define DCN401_MAX_DPP_PIPES 2
-
-/*
- * Bounds on what build adds. The support check allows one stream per
- * state, and never one in both.
- */
-static_assert(ARRAY_SIZE(bls_head_per_state_new) +
- ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_current_lock) +
- DCN401_MAX_DPP_PIPES * ARRAY_SIZE(
- bls_head_per_stream_per_dpp_pipe_current_disconnect) +
- ARRAY_SIZE(
- bls_head_per_stream_per_otg_master_pipe_current_unlock) +
- DCN401_MAX_DPP_PIPES * ARRAY_SIZE(
- bls_head_per_stream_per_dpp_pipe_current_power_down) +
- ARRAY_SIZE(
- bls_head_per_stream_per_otg_master_pipe_current_stream_off) +
- ARRAY_SIZE(bls_head_per_state_new_postamble) <=
- DC2_BLS_MAX_STEPS,
- "dcn401 commit stream-off head exceeds DC2_BLS_MAX_STEPS");
-static_assert(ARRAY_SIZE(bls_head_per_state_new) +
- ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_new) +
- ARRAY_SIZE(
- bls_head_per_stream_per_otg_master_pipe_new_front_end) +
- ARRAY_SIZE(bls_head_per_state_new_postamble) <=
- DC2_BLS_MAX_STEPS,
- "dcn401 commit enable head exceeds DC2_BLS_MAX_STEPS");
-
/*
* Net effect: hwss_process_outstanding_hw_updates on the current state,
* which commit_planes_for_stream runs while optimized_required is set.
@@ -1569,7 +1127,7 @@ static_assert(
ARRAY_SIZE(
bls_update_head_per_stream_per_otg_master_pipe_preamble) +
ARRAY_SIZE(bls_update_head_global_preamble) +
- ARRAY_SIZE(bls_head_per_state_new) +
+ ARRAY_SIZE(bls_update_head_per_state_new_prepare_bandwidth) +
ARRAY_SIZE(
bls_update_head_per_stream_per_otg_master_pipe_current_pending) +
ARRAY_SIZE(bls_update_head_new_lock) +
@@ -1585,14 +1143,6 @@ static_assert(
ARRAY_SIZE(bls_update_head_new_dpp_mall)) <= DC2_BLS_MAX_STEPS,
"dcn401 seamless update head exceeds DC2_BLS_MAX_STEPS");
-/* pipe is NULL only for the bandwidth and p-state steps. */
-struct dcn401_commit_step_args {
- struct dc *dc;
- struct dc_state *current_state;
- struct dc_state *new_state;
- struct pipe_ctx *pipe;
-};
-
/* enable_stream_timing_calc: manual mode unless every span is even. */
static bool dcn401_h_timing_div_manual_mode(
const struct dc_crtc_timing *timing)
@@ -2423,7 +1973,7 @@ static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
return false;
}
-static void dcn401_add_steps(struct block_sequence_state *sequence,
+void dcn401_add_steps(struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *args,
const struct dcn401_commit_step *steps, unsigned int count)
{
@@ -2435,9 +1985,7 @@ static void dcn401_add_steps(struct block_sequence_state *sequence,
}
}
-/* The support check found an OTG master for each stream of state. */
-static void dcn401_add_per_otg_master(
- struct block_sequence_state *sequence,
+void dcn401_add_per_otg_master(struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *state_args,
struct dc_state *state,
const struct dcn401_commit_step *steps, unsigned int count)
@@ -2446,15 +1994,13 @@ static void dcn401_add_per_otg_master(
unsigned int i;
for (i = 0; i < state->stream_count; i++) {
- args.pipe = get_otg_master_from_state_by_stream(state,
- state->streams[i]);
+ args.pipe = get_otg_master_from_state_by_stream(args.dc,
+ state, state->streams[i]);
dcn401_add_steps(sequence, &args, steps, count);
}
}
-/* Each DPP pipe of each stream, the OTG master first. */
-static void dcn401_add_per_dpp_pipe(
- struct block_sequence_state *sequence,
+void dcn401_add_per_dpp_pipe(struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *state_args,
struct dc_state *state,
const struct dcn401_commit_step *steps, unsigned int count)
@@ -2463,28 +2009,14 @@ static void dcn401_add_per_dpp_pipe(
unsigned int i;
for (i = 0; i < state->stream_count; i++) {
- for (args.pipe = get_otg_master_from_state_by_stream(state,
- state->streams[i]); args.pipe;
+ for (args.pipe = get_otg_master_from_state_by_stream(args.dc,
+ state, state->streams[i]); args.pipe;
args.pipe = args.pipe->bottom_pipe)
dcn401_add_steps(sequence, &args, steps, count);
}
}
-#define DCN401_ADD_STEPS(sequence, args, steps) \
- dcn401_add_steps(sequence, args, steps, ARRAY_SIZE(steps))
-#define DCN401_ADD_PER_OTG_MASTER(sequence, args, state, steps) \
- dcn401_add_per_otg_master(sequence, args, state, steps, \
- ARRAY_SIZE(steps))
-#define DCN401_ADD_PER_DPP_PIPE(sequence, args, state, steps) \
- dcn401_add_per_dpp_pipe(sequence, args, state, steps, \
- ARRAY_SIZE(steps))
-
-/*
- * Points the step arguments at the states and the sequence at
- * head_sequence, both empty. The entries point into the states; the build
- * writes neither.
- */
-static void dcn401_init_and_unpack_params(
+void dcn401_init_and_unpack_params(
struct bls_builder_declarative_params *params,
struct dcn401_commit_step_args *state_args,
struct block_sequence_state *head)
@@ -2500,44 +2032,6 @@ static void dcn401_init_and_unpack_params(
head->num_steps = ¶ms->head_num_steps;
}
-static enum bls_builder_result dcn401_build_non_seamless_commit(
- struct bls_builder_declarative_params *params)
-{
- struct dcn401_commit_step_args state_args;
- struct block_sequence_state head;
- enum bls_builder_result result;
-
- if (!params)
- return BLS_BUILD_ERROR_UNSUPPORTED_STATE_UPDATE;
- result = dcn401_check_non_seamless_commit_support(params->dc,
- params->current_state, params->new_state);
- if (result != BLS_BUILD_SUCCESS)
- return result;
-
- dcn401_init_and_unpack_params(params, &state_args, &head);
- DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
- state_args.current_state,
- bls_head_per_stream_per_otg_master_pipe_current_lock);
- DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.current_state,
- bls_head_per_stream_per_dpp_pipe_current_disconnect);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
- state_args.current_state,
- bls_head_per_stream_per_otg_master_pipe_current_unlock);
- DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.current_state,
- bls_head_per_stream_per_dpp_pipe_current_power_down);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
- state_args.current_state,
- bls_head_per_stream_per_otg_master_pipe_current_stream_off);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_head_per_stream_per_otg_master_pipe_new);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_head_per_stream_per_otg_master_pipe_new_front_end);
- DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new_postamble);
-
- return BLS_BUILD_SUCCESS;
-}
-
static enum bls_builder_result dcn401_build_seamless_update(
struct bls_builder_declarative_params *params)
{
@@ -2558,7 +2052,8 @@ static enum bls_builder_result dcn401_build_seamless_update(
bls_update_head_per_stream_per_otg_master_pipe_preamble);
DCN401_ADD_STEPS(&head, &state_args,
bls_update_head_global_preamble);
- DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new);
+ DCN401_ADD_STEPS(&head, &state_args,
+ bls_update_head_per_state_new_prepare_bandwidth);
DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
state_args.current_state,
bls_update_head_per_stream_per_otg_master_pipe_current_pending);
@@ -2595,26 +2090,6 @@ static enum bls_builder_result dcn401_build_seamless_update(
return BLS_BUILD_SUCCESS;
}
-static bool dcn401_non_seamless_commit_check_capability(
- const struct bls_builder_check_capability_params *params)
-{
- return params && dcn401_check_non_seamless_commit_support(params->dc,
- params->current_state, params->new_state) ==
- BLS_BUILD_SUCCESS;
-}
-
-const struct bls_builder *dcn401_non_seamless_commit_builder(void)
-{
- static const struct bls_builder builder = {
- .name = DCN_DECL_NON_SEAMLESS_COMMIT,
- .kind = BLS_BUILDER_DECLARATIVE,
- .check_capability = dcn401_non_seamless_commit_check_capability,
- .build.declarative = dcn401_build_non_seamless_commit,
- };
-
- return &builder;
-}
-
static bool dcn401_seamless_update_check_capability(
const struct bls_builder_check_capability_params *params)
{
diff --git a/drivers/gpu/drm/amd/display/dc/state_facts/state_facts.c b/drivers/gpu/drm/amd/display/dc/state_facts/state_facts.c
new file mode 100644
index 000000000000..d6ac474e707a
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/state_facts/state_facts.c
@@ -0,0 +1,294 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include "dm_services.h"
+#include "core_types.h"
+#include "dc_state_priv.h"
+#include "state_facts/state_facts.h"
+
+static bool trait_match(uint32_t traits, struct dc_trait_match where)
+{
+ return (traits & where.mask) == where.value;
+}
+
+static int stream_index_of(const struct dc_state *state,
+ const struct dc_stream_state *stream)
+{
+ int i;
+
+ for (i = 0; i < state->stream_count; i++) {
+ if (state->streams[i] == stream)
+ return i;
+ }
+ return -1;
+}
+
+static uint32_t stream_traits(const struct dc_state *state, int index)
+{
+ const struct dc_stream_state *stream = state->streams[index];
+ const struct dc_link *link = stream->link;
+ uint32_t traits = 0;
+
+ if (!stream->dpms_off)
+ traits |= DC_STREAM_TRAIT_ENABLED;
+ if (state->stream_status[index].plane_count)
+ traits |= DC_STREAM_TRAIT_HAS_PLANE;
+ if (dc_state_get_stream_subvp_type(state, stream) == SUBVP_PHANTOM)
+ traits |= DC_STREAM_TRAIT_PHANTOM;
+ if (stream->signal == SIGNAL_TYPE_EDP)
+ traits |= DC_STREAM_TRAIT_EDP;
+ if (link && !link->panel_config.psr.disable_psr &&
+ (link->psr_settings.psr_version == DC_PSR_VERSION_1 ||
+ link->psr_settings.psr_version == DC_PSR_VERSION_SU_1))
+ traits |= DC_STREAM_TRAIT_PSR;
+ if (link && link->replay_settings.replay_feature_enabled)
+ traits |= DC_STREAM_TRAIT_REPLAY;
+ if (traits & (DC_STREAM_TRAIT_PSR | DC_STREAM_TRAIT_REPLAY))
+ traits |= DC_STREAM_TRAIT_SELF_REFRESH;
+ return traits;
+}
+
+static uint32_t plane_traits(const struct dc_plane_state *plane)
+{
+ const union dc_plane_cm_flags *flags = &plane->cm.flags;
+ uint32_t traits = 0;
+
+ if (plane->address.type == PLN_ADDR_TYPE_GRAPHICS)
+ traits |= DC_PLANE_TRAIT_GRAPHICS_ADDRESS;
+ if (plane->in_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS)
+ traits |= DC_PLANE_TRAIT_DEGAMMA_CURVE;
+ if (flags->bits.blend_enable || flags->bits.shaper_enable ||
+ flags->bits.lut3d_enable || flags->bits.rmcm_enable)
+ traits |= DC_PLANE_TRAIT_MCM_LUTS;
+ if (plane->dcc.enable)
+ traits |= DC_PLANE_TRAIT_DCC;
+ if (plane->flip_immediate)
+ traits |= DC_PLANE_TRAIT_FLIP_IMMEDIATE;
+ return traits;
+}
+
+static uint32_t pipe_traits(const struct pipe_ctx *pipe)
+{
+ uint32_t traits = 0;
+
+ if (pipe->plane_state)
+ traits |= DC_PIPE_TRAIT_PLANE;
+ if (!pipe->top_pipe)
+ traits |= DC_PIPE_TRAIT_MPC_TOP;
+ if (pipe->next_odm_pipe || pipe->prev_odm_pipe)
+ traits |= DC_PIPE_TRAIT_ODM;
+ if (pipe->stream_res.tg && pipe->stream_res.opp &&
+ pipe->stream_res.stream_enc)
+ traits |= DC_PIPE_TRAIT_STREAM_RESOURCES;
+ if (pipe->clock_source)
+ traits |= DC_PIPE_TRAIT_PIXEL_CLOCK;
+ if (pipe->plane_res.hubp && pipe->plane_res.dpp &&
+ pipe->plane_res.mpcc_inst < MAX_MPCC)
+ traits |= DC_PIPE_TRAIT_PLANE_RESOURCES;
+ if (pipe->wait_is_required)
+ traits |= DC_PIPE_TRAIT_WAIT;
+ if (pipe->stream_res.abm)
+ traits |= DC_PIPE_TRAIT_ABM;
+ if (pipe->stream_res.audio)
+ traits |= DC_PIPE_TRAIT_AUDIO;
+ if (pipe->stream_res.dsc)
+ traits |= DC_PIPE_TRAIT_DSC;
+ if (pipe->stream_res.gsl_group)
+ traits |= DC_PIPE_TRAIT_GSL;
+ return traits;
+}
+
+static void derive_blend_tree(const struct dc_state *state,
+ struct dc_state_facts *out, int stream_index)
+{
+ const struct pipe_ctx *master = NULL;
+ const struct pipe_ctx *pipe;
+ unsigned int count = 0;
+ bool on_blocks = true;
+ int i;
+
+ for (i = 0; i < MAX_PIPES; i++) {
+ pipe = &state->res_ctx.pipe_ctx[i];
+ if (pipe->stream != state->streams[stream_index])
+ continue;
+ if (!pipe->top_pipe && !pipe->prev_odm_pipe)
+ master = pipe;
+ }
+ if (!master)
+ return;
+ for (pipe = master; pipe; pipe = pipe->bottom_pipe) {
+ count++;
+ if (pipe != master &&
+ (pipe->stream_res.tg != master->stream_res.tg ||
+ pipe->stream_res.opp != master->stream_res.opp))
+ on_blocks = false;
+ }
+ out->global.blend_tree[stream_index].dpp_pipe_count = (uint8_t)count;
+ out->global.blend_tree[stream_index].on_otg_master_blocks = on_blocks;
+}
+
+static void derive_composite(struct dc_state_facts *out)
+{
+ struct dc_trait_match active = DC_STREAM_MATCH_ACTIVE;
+
+ out->composite.has_active_stream =
+ dc_state_facts_any_stream(out, active);
+ out->composite.has_active_edp = dc_state_facts_any_stream(out,
+ DC_TRAIT_MATCH(DC_STREAM_TRAIT_ENABLED |
+ DC_STREAM_TRAIT_HAS_PLANE | DC_STREAM_TRAIT_EDP,
+ DC_STREAM_TRAIT_PHANTOM));
+ out->composite.has_active_non_edp = dc_state_facts_any_stream(out,
+ DC_TRAIT_MATCH(DC_STREAM_TRAIT_ENABLED |
+ DC_STREAM_TRAIT_HAS_PLANE,
+ DC_STREAM_TRAIT_PHANTOM | DC_STREAM_TRAIT_EDP));
+ out->composite.has_only_active_edp =
+ out->composite.has_active_stream &&
+ !out->composite.has_active_non_edp;
+ out->composite.has_active_edp_without_self_refresh =
+ dc_state_facts_any_stream(out,
+ DC_TRAIT_MATCH(DC_STREAM_TRAIT_ENABLED |
+ DC_STREAM_TRAIT_HAS_PLANE | DC_STREAM_TRAIT_EDP,
+ DC_STREAM_TRAIT_PHANTOM | DC_STREAM_TRAIT_SELF_REFRESH));
+ out->composite.has_only_active_edp_with_self_refresh =
+ out->composite.has_active_stream &&
+ dc_state_facts_all_stream(out, active,
+ DC_TRAIT_MATCH(DC_STREAM_TRAIT_EDP |
+ DC_STREAM_TRAIT_SELF_REFRESH, 0));
+}
+
+void dc_state_facts_derive(const struct dc_state *state,
+ struct dc_state_facts *out)
+{
+ int i;
+ int s;
+
+ memset(out, 0, sizeof(*out));
+ if (state->power_source == DC_POWER_SOURCE_DC)
+ out->state |= DC_STATE_TRAIT_DC_POWER;
+
+ out->stream.count = state->stream_count;
+ for (i = 0; i < state->stream_count; i++) {
+ out->stream.entry[i].traits = stream_traits(state, i);
+ derive_blend_tree(state, out, i);
+ for (s = 0; s < state->stream_status[i].plane_count; s++) {
+ struct dc_plane_facts *plane =
+ &out->plane.entry[out->plane.count++];
+
+ plane->traits = plane_traits(
+ state->stream_status[i].plane_states[s]);
+ plane->stream_index = (uint8_t)i;
+ }
+ }
+ for (i = 0; i < MAX_PIPES; i++) {
+ const struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
+ struct dc_pipe_facts *facts;
+ int index;
+
+ if (!pipe->stream)
+ continue;
+ index = stream_index_of(state, pipe->stream);
+ if (index < 0)
+ continue;
+ facts = &out->pipe.entry[out->pipe.count++];
+ facts->traits = pipe_traits(pipe);
+ facts->pipe_index = (uint8_t)i;
+ facts->stream_index = (uint8_t)index;
+ }
+ derive_composite(out);
+}
+
+bool dc_state_facts_any_stream(const struct dc_state_facts *f,
+ struct dc_trait_match where)
+{
+ uint8_t i;
+
+ for (i = 0; i < f->stream.count; i++) {
+ if (trait_match(f->stream.entry[i].traits, where))
+ return true;
+ }
+ return false;
+}
+
+bool dc_state_facts_all_stream(const struct dc_state_facts *f,
+ struct dc_trait_match among, struct dc_trait_match where)
+{
+ uint8_t i;
+
+ for (i = 0; i < f->stream.count; i++) {
+ if (!trait_match(f->stream.entry[i].traits, among))
+ continue;
+ if (!trait_match(f->stream.entry[i].traits, where))
+ return false;
+ }
+ return true;
+}
+
+static bool in_stream(uint8_t entry_stream, uint8_t stream_index)
+{
+ return stream_index == DC_FACTS_ALL_STREAMS ||
+ entry_stream == stream_index;
+}
+
+bool dc_state_facts_any_plane(const struct dc_state_facts *f,
+ uint8_t stream_index, struct dc_trait_match where)
+{
+ uint8_t i;
+
+ for (i = 0; i < f->plane.count; i++) {
+ if (!in_stream(f->plane.entry[i].stream_index, stream_index))
+ continue;
+ if (trait_match(f->plane.entry[i].traits, where))
+ return true;
+ }
+ return false;
+}
+
+bool dc_state_facts_all_plane(const struct dc_state_facts *f,
+ uint8_t stream_index, struct dc_trait_match among,
+ struct dc_trait_match where)
+{
+ uint8_t i;
+
+ for (i = 0; i < f->plane.count; i++) {
+ if (!in_stream(f->plane.entry[i].stream_index, stream_index))
+ continue;
+ if (!trait_match(f->plane.entry[i].traits, among))
+ continue;
+ if (!trait_match(f->plane.entry[i].traits, where))
+ return false;
+ }
+ return true;
+}
+
+bool dc_state_facts_any_pipe(const struct dc_state_facts *f,
+ uint8_t stream_index, struct dc_trait_match where)
+{
+ uint8_t i;
+
+ for (i = 0; i < f->pipe.count; i++) {
+ if (!in_stream(f->pipe.entry[i].stream_index, stream_index))
+ continue;
+ if (trait_match(f->pipe.entry[i].traits, where))
+ return true;
+ }
+ return false;
+}
+
+bool dc_state_facts_all_pipe(const struct dc_state_facts *f,
+ uint8_t stream_index, struct dc_trait_match among,
+ struct dc_trait_match where)
+{
+ uint8_t i;
+
+ for (i = 0; i < f->pipe.count; i++) {
+ if (!in_stream(f->pipe.entry[i].stream_index, stream_index))
+ continue;
+ if (!trait_match(f->pipe.entry[i].traits, among))
+ continue;
+ if (!trait_match(f->pipe.entry[i].traits, where))
+ return false;
+ }
+ return true;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/state_facts/state_facts.h b/drivers/gpu/drm/amd/display/dc/state_facts/state_facts.h
new file mode 100644
index 000000000000..b8a8b8256262
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/state_facts/state_facts.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#pragma once
+
+#include "dc.h"
+
+struct dc_state;
+
+/* Matches a trait mask t when (t & mask) == value. */
+struct dc_trait_match {
+ uint32_t mask;
+ uint32_t value;
+};
+
+/* Traits in set present and traits in clear absent. */
+#define DC_TRAIT_MATCH(set, clear) \
+ ((struct dc_trait_match){ .mask = (set) | (clear), .value = (set) })
+
+/* Plane and pipe accessors: no stream filter. */
+#define DC_FACTS_ALL_STREAMS 0xFFu
+
+enum dc_state_trait {
+ DC_STATE_TRAIT_DC_POWER = 1u << 0,
+};
+
+enum dc_stream_trait {
+ DC_STREAM_TRAIT_ENABLED = 1u << 0,
+ DC_STREAM_TRAIT_HAS_PLANE = 1u << 1,
+ DC_STREAM_TRAIT_PHANTOM = 1u << 2,
+ DC_STREAM_TRAIT_EDP = 1u << 3,
+ DC_STREAM_TRAIT_PSR = 1u << 4,
+ DC_STREAM_TRAIT_REPLAY = 1u << 5,
+ DC_STREAM_TRAIT_SELF_REFRESH = 1u << 6,
+};
+
+#define DC_STREAM_MATCH_ACTIVE \
+ DC_TRAIT_MATCH(DC_STREAM_TRAIT_ENABLED | DC_STREAM_TRAIT_HAS_PLANE, \
+ DC_STREAM_TRAIT_PHANTOM)
+
+enum dc_plane_trait {
+ DC_PLANE_TRAIT_GRAPHICS_ADDRESS = 1u << 0,
+ DC_PLANE_TRAIT_DEGAMMA_CURVE = 1u << 1,
+ DC_PLANE_TRAIT_MCM_LUTS = 1u << 2,
+ DC_PLANE_TRAIT_DCC = 1u << 3,
+ DC_PLANE_TRAIT_FLIP_IMMEDIATE = 1u << 4,
+};
+
+enum dc_pipe_trait {
+ DC_PIPE_TRAIT_PLANE = 1u << 0,
+ DC_PIPE_TRAIT_MPC_TOP = 1u << 1,
+ DC_PIPE_TRAIT_ODM = 1u << 2,
+ DC_PIPE_TRAIT_STREAM_RESOURCES = 1u << 3,
+ DC_PIPE_TRAIT_PIXEL_CLOCK = 1u << 4,
+ DC_PIPE_TRAIT_PLANE_RESOURCES = 1u << 5,
+ DC_PIPE_TRAIT_WAIT = 1u << 6,
+ DC_PIPE_TRAIT_ABM = 1u << 7,
+ DC_PIPE_TRAIT_AUDIO = 1u << 8,
+ DC_PIPE_TRAIT_DSC = 1u << 9,
+ DC_PIPE_TRAIT_GSL = 1u << 10,
+};
+
+struct dc_stream_facts {
+ uint32_t traits;
+};
+
+struct dc_plane_facts {
+ uint32_t traits;
+ uint8_t stream_index;
+};
+
+struct dc_pipe_facts {
+ uint32_t traits;
+ uint8_t pipe_index;
+ uint8_t stream_index;
+};
+
+struct dc_state_composite_facts {
+ bool has_active_stream;
+ bool has_active_edp;
+ bool has_active_non_edp;
+ bool has_only_active_edp;
+ bool has_active_edp_without_self_refresh;
+ bool has_only_active_edp_with_self_refresh;
+};
+
+struct dc_blend_tree_facts {
+ uint8_t dpp_pipe_count;
+ bool on_otg_master_blocks;
+};
+
+struct dc_state_global_facts {
+ struct dc_blend_tree_facts blend_tree[MAX_PIPES];
+};
+
+struct dc_state_facts {
+ uint32_t state;
+ struct {
+ uint8_t count;
+ struct dc_stream_facts entry[MAX_PIPES];
+ } stream;
+ struct {
+ uint8_t count;
+ struct dc_plane_facts entry[MAX_PIPES * MAX_SURFACES];
+ } plane;
+ struct {
+ uint8_t count;
+ struct dc_pipe_facts entry[MAX_PIPES];
+ } pipe;
+ struct dc_state_composite_facts composite;
+ struct dc_state_global_facts global;
+};
+
+void dc_state_facts_derive(const struct dc_state *state,
+ struct dc_state_facts *out);
+
+bool dc_state_facts_any_stream(const struct dc_state_facts *f,
+ struct dc_trait_match where);
+bool dc_state_facts_all_stream(const struct dc_state_facts *f,
+ struct dc_trait_match among, struct dc_trait_match where);
+bool dc_state_facts_any_plane(const struct dc_state_facts *f,
+ uint8_t stream_index, struct dc_trait_match where);
+bool dc_state_facts_all_plane(const struct dc_state_facts *f,
+ uint8_t stream_index, struct dc_trait_match among,
+ struct dc_trait_match where);
+bool dc_state_facts_any_pipe(const struct dc_state_facts *f,
+ uint8_t stream_index, struct dc_trait_match where);
+bool dc_state_facts_all_pipe(const struct dc_state_facts *f,
+ uint8_t stream_index, struct dc_trait_match among,
+ struct dc_trait_match where);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 54/74] drm/amd/display: Program DC2 commit stereo once
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (52 preceding siblings ...)
2026-11-11 1:28 ` [PATCH 53/74] drm/amd/display: Add DC2 state facts and split the builders Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 55/74] drm/amd/display: Read DCN6 SOP constraints from the idle row Wayne Lin
` (20 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
The DCN4 DC2 commit programs OPP and TG stereo twice in a row for a
stream whose mode changed. Both times use the same values.
[How]
Remove the first, mode-changed-only stereo steps. Remove the skip flag
that only these steps used.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dc/bls_builder/bls_builder_private_types.h | 2 --
.../dc/bls_builder/dcn401_non_seamless_builder.c | 11 ++++-------
.../display/dc/bls_builder/dcn401_seamless_builder.c | 3 ---
3 files changed, 4 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
index 0e6733eba83d..784a3cb4d311 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
@@ -107,8 +107,6 @@ struct dcn401_commit_step_meta {
} dc_ip_request_cntl;
};
struct {
- /* The OTG master's stream has mode_changed clear. */
- bool stream_mode_unchanged;
/* The new state has a stream. */
bool new_state_has_stream;
/* dc->optimized_required is clear. */
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
index e379793a8efd..d28e162a6632 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
@@ -318,9 +318,10 @@ static const struct dcn401_commit_step
/*
* Net effect: finish the head for the new OTG master. program_front_end
* for a master with no plane, then stereo, then the waits that complete
- * the head's double buffering. legacy_commit_state_hw sets up stereo for
- * a mode-changed stream, then dc_enable_stereo sets it up again for every
- * stream; dcn10_setup_stereo with no 3D format is the two STEREO calls.
+ * the head's double buffering. DC1 sets up stereo twice for a
+ * mode-changed stream, in legacy_commit_state_hw and dc_enable_stereo,
+ * with the same result; one dcn10_setup_stereo with no 3D format, the two
+ * STEREO calls, matches it.
* The bandwidth lower starts only after these waits.
*/
static const struct dcn401_commit_step
@@ -335,10 +336,6 @@ static const struct dcn401_commit_step
{ .func = TG_LOCK, .meta.tg_lock.lock = false },
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
- { .func = OPP_PROGRAM_STEREO,
- .meta.skip_if.stream_mode_unchanged = true },
- { .func = TG_PROGRAM_STEREO,
- .meta.skip_if.stream_mode_unchanged = true },
{ .func = OPP_PROGRAM_STEREO },
{ .func = TG_PROGRAM_STEREO },
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
index b7f6909d69e0..23951ef4fe04 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
@@ -1944,9 +1944,6 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
const struct dcn401_commit_step *step)
{
- if (step->meta.skip_if.stream_mode_unchanged &&
- !args->pipe->stream->mode_changed)
- return true;
if (step->meta.skip_if.new_state_has_stream &&
args->new_state->stream_count)
return true;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 55/74] drm/amd/display: Read DCN6 SOP constraints from the idle row
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (53 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 54/74] drm/amd/display: Program DC2 commit stereo once Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 56/74] drm/amd/display: Use soc_bb hostvm fraction instead of hardcoded value Wayne Lin
` (19 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Austin Zheng, Dillon Varone
From: Austin Zheng <Austin.Zheng@amd.com>
[Why]
The n-1 UTM model has 8 SOC operating points. It sets
sop_count to 8 and fills sops[0] through sops[7]. Mode
support copies latency and available bandwidth from the v3
constraint for one point. That constraint was taken from the
alternate-pstate row, so its latency and urgent bandwidth
did not match the idle row.
[How]
This change does not write sop_count. The v3 table length
stays UTM_QOS_MODEL_V3_MAX_SOP_COUNT, which is 5.
dcn6_v3_sop_table_get_sop_constraint_at_index() reads latency
from sops[IDLE][index]. dml2_utm_soc_bb_dcn6_v3_build_sop_table()
stores that row's urgent_bandwidth_KBps and
nominal_bandwidth_KBps as the minimum urgent and non-urgent
bandwidth. dcn6_v3_sop_table_is_bandwidth_supported_at_index()
still compares the highest alternate-pstate row as well.
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Austin Zheng <Austin.Zheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c
index 586618d7dd58..7a1b2492969e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c
@@ -157,8 +157,9 @@ static void dcn6_v3_sop_table_get_sop_constraint_at_index(const struct dml2_sop_
unsigned int index, struct dml2_sop_constraint *constraint)
{
const struct utm_qos_model_dchub_v3 *dchub = table->model->dchub_v3;
+ // The mode support check is done based on the idle SOPs, not the active SOPs
const struct utm_qos_model_dchub_v3_sop_entry *entry =
- &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE_ALTERNATE_PSTATE][index];
+ &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_IDLE][index];
DML_ASSERT_MSG(index < dchub->sop_count, "unsupported sop index\n");
constraint->dcn5.clocks.fclk_khz = 0;
@@ -174,6 +175,8 @@ static void dcn6_v3_sop_table_get_sop_constraint_at_index(const struct dml2_sop_
constraint->dcn5.latency.dcn5.avg_req_latency_non_urg = entry->avg_req_latency_non_urg_ps / 1000000.0;
constraint->dcn5.latency.dcn5.df_response_time_us = entry->df_response_time_ps / 1000000.0;
constraint->dcn5.min_available_urgent_bandwidth_KBps = table->sop_min_available_urgent_bandwidths_KBps[index];
+ // non urgent bandwidth needs to be plumbed for presentation in the spreadsheet
+ constraint->dcn5.min_available_non_urgent_bandwidth_KBps = table->sop_min_available_non_urgent_bandwidths_KBps[index];
constraint->dcn5.min_sop_index = index;
}
@@ -267,14 +270,18 @@ static void dml2_utm_soc_bb_dcn6_v3_build_sop_table(struct dml2_sop_table *table
for (i = 0; i < dchub->sop_count; i++) {
const struct utm_qos_model_dchub_v3_sop_entry *idle_entry =
- &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_IDLE][i];
- const struct utm_qos_model_dchub_v3_sop_entry *active_entry =
- &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_ACTIVE_ALTERNATE_PSTATE][i];
+ &dchub->sops[UTM_QOS_MODEL_V3_LOAD_LEVEL_IDLE][i];
table->sop_optimal_dcfclks_khz[i] =
- dcn6_v3_sop_table_calculate_optimal_dcfclk_khz(utm_soc_bb, idle_entry);
+ dcn6_v3_sop_table_calculate_optimal_dcfclk_khz(utm_soc_bb, idle_entry);
+ // The mode support check is done based on the idle SOPs, not the active SOPs
table->sop_min_available_urgent_bandwidths_KBps[i] =
- active_entry->urgent_bandwidth_KBps;
+ idle_entry->urgent_bandwidth_KBps;
+ // non urgent bandwidth needs to be plumbed for presentation in the spreadsheet
+ table->sop_min_available_non_urgent_bandwidths_KBps[i] =
+ idle_entry->nominal_bandwidth_KBps;
+ // TODO - remove min_available_urgent_bandwidth_KBps and move stutter efficiency calculation to populate informative.
+
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 56/74] drm/amd/display: Use soc_bb hostvm fraction instead of hardcoded value
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (54 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 55/74] drm/amd/display: Read DCN6 SOP constraints from the idle row Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 57/74] drm/amd/display: Increase state if dcfclk deep sleep needed exceeds dcfclk Wayne Lin
` (18 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Austin Zheng, Dillon Varone
From: Austin Zheng <Austin.Zheng@amd.com>
[Why/How]
soc_bb->hostvm_inefficiency_fraction value should be used instead of
a fixed value
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Austin Zheng <Austin.Zheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
index 2ca3a603615e..157b579073b6 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
@@ -2664,7 +2664,7 @@ void dcn6_ms_calculate_hostvm_inefficiency_factor(
ip->remote_iommu_outstanding_translations,
soc_bb->max_outstanding_reqs,
1.0,
- 0.5);
+ soc_bb->hostvm_inefficiency_fraction);
DML_LOG_DEBUG_DOUBLE(outputs->HostVMInefficiencyFactorPrefetch);
DML_LOG_DEBUG_DOUBLE(outputs->HostVMInefficiencyFactor);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 57/74] drm/amd/display: Increase state if dcfclk deep sleep needed exceeds dcfclk
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (55 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 56/74] drm/amd/display: Use soc_bb hostvm fraction instead of hardcoded value Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 58/74] drm/amd/display: Flip dml2_core_utils_get_segment_horizontal_contiguous() Inputs Wayne Lin
` (17 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Gabriel Abarca, Dillon Varone
From: Gabriel Abarca <Gabriel.Abarca@amd.com>
For dcn6, take out dcfclk_deepsleep path to line transfer time.
Increase the state if dcfclk deep sleep needed is more than dcfclk.
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Gabriel Abarca <Gabriel.Abarca@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/dc/dml2_0/dml21/inc/dml_top_types.h | 1 +
.../dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c | 2 ++
.../dml2_core/dml2_core_dcn6_funcs_mode_programming.c | 4 +++-
.../src/dml2_core/dml2_core_dcn6_funcs_mode_support.c | 10 +++++++++-
.../dml21/src/dml2_core/dml2_core_shared_types.h | 1 +
.../dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c | 4 ++++
6 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h
index fd7ccefc7fb7..4f4bfa58284a 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h
@@ -382,6 +382,7 @@ struct dml2_mode_support_info {
bool temp_read_or_ppt_support;
bool qos_bandwidth_support;
bool dcfclk_support;
+ bool dcfclk_deep_sleep_support;
bool alternate_channel_size_support;
bool EnoughUrgentLatencyHidingSupport;
bool DSCSlicesODMModeSupported;
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 c84cbad7fde1..a497a75ff9ac 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
@@ -13249,6 +13249,8 @@ void dml2_core_calcs_get_informative(const struct dml2_core_internal_display_mod
out->informative.mode_support_info.ViewportSizeSupport = mode_lib->ms.support.ViewportSizeSupport;
out->informative.mode_support_info.qos_bandwidth_support = mode_lib->ms.support.qos_bandwidth_support;
out->informative.mode_support_info.dcfclk_support = mode_lib->ms.support.dcfclk_support;
+ out->informative.mode_support_info.dcfclk_deep_sleep_support = mode_lib->ms.support.dcfclk_deep_sleep_support;
+
for (k = 0; k < out->display_config.num_planes; k++) {
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
index b46b396f45d4..464b7b00e8c2 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
@@ -110,7 +110,9 @@ static void dcn6_mp_calculate_pixel_delivery_times(struct dml2_core_calculate_mp
inputs->PSCL_THROUGHPUT,
inputs->PSCL_THROUGHPUT_CHROMA,
inputs->Dppclk,
- inputs->DCFCLKDeepSleep,
+ // It is unnecessary and circular to use dcfclk_deepsleep here because dcfclk_deepsleep is derived from the line transferr time
+ // dcfclk is a better value here
+ inputs->Dcfclk,
inputs->BytePerPixelY,
inputs->BytePerPixelC,
inputs->req_per_swath_ub_l,
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
index 157b579073b6..c1cb43386473 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
@@ -3434,6 +3434,9 @@ bool dcn6_ms_check_mode_support(
goto fail;
if (inputs->support.dcfclk_support == false)
goto fail;
+ // it is better to increase the state than to clamp dcfclk deep sleep to dcfclk, so we check if the dcfclk deep sleep is less than or equal to dcfclk
+ if (inputs->support.dcfclk_deep_sleep_support == false)
+ goto fail;
if (inputs->support.qos_bandwidth_support == false)
goto fail;
if (inputs->support.global_dram_clock_change_supported == false)
@@ -3679,12 +3682,17 @@ bool dcn6_ms_check_dcfclk_support(
&& inputs->DCFCLK <= soc_bb->max_dcfclk_khz / 1000.0
&& inputs->DCFCLK >= soc_bb->min_dcfclk_khz / 1000.0;
+ // it is better to increase the state than to clamp dcfclk deep sleep to dcfclk, so we check if the dcfclk deep sleep is less than or equal to dcfclk
+ outputs->support.dcfclk_deep_sleep_support = inputs->dcfclk_deepsleep <= inputs->DCFCLK;
+
DML_LOG_VERBOSE("outputs->support.dcfclk_support = %s\n", outputs->support.dcfclk_support ? "true" : "false");
+ DML_LOG_VERBOSE("outputs->support.dcfclk_deepsleep_support = %s\n", outputs->support.dcfclk_deep_sleep_support ? "true" : "false");
DML_LOG_DEBUG_BOOL(outputs->support.dcfclk_support);
+ DML_LOG_DEBUG_BOOL(outputs->support.dcfclk_deep_sleep_support);
DML_LOG_FUNC_EXIT();
- return outputs->support.dcfclk_support;
+ return outputs->support.dcfclk_support && outputs->support.dcfclk_deep_sleep_support;
}
void dcn6_ms_check_alternate_channel_size_support(
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 9eabee1fc001..4848dbffc184 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
@@ -356,6 +356,7 @@ struct dml2_core_internal_mode_support_info {
struct dml2_core_internal_watermarks watermarks;
bool dcfclk_support;
+ bool dcfclk_deep_sleep_support;
bool qos_bandwidth_support;
bool alternate_channel_size_support;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c
index 4f5533dc0430..7cc3a4b4b3b7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c
@@ -306,6 +306,10 @@ void dml2_core_utils_print_mode_support_info(const struct dml2_core_internal_mod
DML_LOG_VERBOSE("DML: support: ExceededMALLSize = %d\n", support->ExceededMALLSize);
if (!fail_only || support->g6_temp_read_support == 0)
DML_LOG_VERBOSE("DML: support: g6_temp_read_support = %d\n", support->g6_temp_read_support);
+ if (!fail_only || support->dcfclk_support == 0)
+ DML_LOG_VERBOSE("DML: support: dcfclk_support = %d\n", support->dcfclk_support);
+ if (!fail_only || support->dcfclk_deep_sleep_support == 0)
+ DML_LOG_VERBOSE("DML: support: dcfclk_deepsleep_support = %d\n", support->dcfclk_deep_sleep_support);
if (!fail_only || (support->global_dram_clock_change_supported == 0 && support->global_dram_clock_change_support_required))
DML_LOG_VERBOSE("DML: support: dram_clock_change_support = %d\n", support->global_dram_clock_change_supported);
if (!fail_only || support->ImmediateFlipSupport == 0)
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 58/74] drm/amd/display: Flip dml2_core_utils_get_segment_horizontal_contiguous() Inputs
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (56 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 57/74] drm/amd/display: Increase state if dcfclk deep sleep needed exceeds dcfclk Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 59/74] drm/amd/display: Update DCN6 Calcs Wayne Lin
` (16 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Austin Zheng, Dillon Varone,
Mario Limonciello, Alex Deucher, stable, Wayne Lin
From: Austin Zheng <Austin.Zheng@amd.com>
[Why/How]
Parameters for dml2_core_utils_get_segment_horizontal_contiguous() should be flipped.
Should be swizzle mode then bytes per pixel.
Cc: Dillon Varone <dillon.varone@amd.com>
Cc: Mario Limonciello <mario.limonciello@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
Signed-off-by: Austin Zheng <Austin.Zheng@amd.com>
Signed-off-by: Wayne Lin <Wayne.Lin@amd.com>
---
.../dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c
index 09cfb5046c81..569ab02ea080 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c
@@ -836,7 +836,7 @@ void dcn5_calculate_swath_and_det_configuration(struct dml2_core_internal_scratc
p->SwathHeightC[k] = MaximumSwathHeightC[k];
RoundedUpSwathSizeBytesY[k] = p->full_swath_bytes_l[k] / 2;
RoundedUpSwathSizeBytesC[k] = p->full_swath_bytes_c[k];
- p->request_size_bytes_luma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->BytePerPixY[k], p->display_cfg->plane_descriptors[k].surface.tiling)
+ p->request_size_bytes_luma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->display_cfg->plane_descriptors[k].surface.tiling, p->BytePerPixY[k])
== dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) ? 64 : 128;
p->request_size_bytes_chroma[k] = 256;
@@ -846,7 +846,7 @@ void dcn5_calculate_swath_and_det_configuration(struct dml2_core_internal_scratc
RoundedUpSwathSizeBytesY[k] = p->full_swath_bytes_l[k];
RoundedUpSwathSizeBytesC[k] = p->full_swath_bytes_c[k] / 2;
p->request_size_bytes_luma[k] = 256;
- p->request_size_bytes_chroma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->BytePerPixC[k], p->display_cfg->plane_descriptors[k].surface.tiling)
+ p->request_size_bytes_chroma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->display_cfg->plane_descriptors[k].surface.tiling, p->BytePerPixC[k])
== dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) ? 64 : 128;
} else {
@@ -854,9 +854,9 @@ void dcn5_calculate_swath_and_det_configuration(struct dml2_core_internal_scratc
p->SwathHeightC[k] = MaximumSwathHeightC[k] / 2;
RoundedUpSwathSizeBytesY[k] = p->full_swath_bytes_l[k] / 2;
RoundedUpSwathSizeBytesC[k] = p->full_swath_bytes_c[k] / 2;
- p->request_size_bytes_luma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->BytePerPixY[k], p->display_cfg->plane_descriptors[k].surface.tiling)
+ p->request_size_bytes_luma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->display_cfg->plane_descriptors[k].surface.tiling, p->BytePerPixY[k])
== dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) ? 64 : 128;
- p->request_size_bytes_chroma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->BytePerPixC[k], p->display_cfg->plane_descriptors[k].surface.tiling)
+ p->request_size_bytes_chroma[k] = dml2_core_utils_get_segment_horizontal_contiguous(p->display_cfg->plane_descriptors[k].surface.tiling, p->BytePerPixC[k])
== dml2_core_utils_is_vertical_rotation(p->display_cfg->plane_descriptors[k].composition.rotation_angle) ? 64 : 128;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 59/74] drm/amd/display: Update DCN6 Calcs
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (57 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 58/74] drm/amd/display: Flip dml2_core_utils_get_segment_horizontal_contiguous() Inputs Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 60/74] drm/amd/display: Add effective_number_of_dpp parameter Wayne Lin
` (15 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Gabriel Abarca, Dillon Varone,
Austin Zheng
From: Gabriel Abarca <Gabriel.Abarca@amd.com>
Update dcfclk deep sleep calcs and register size limit accounting
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Gabriel Abarca <Gabriel.Abarca@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
Signed-off-by: Austin Zheng <austin.zheng@amd.com>
---
.../dml2_core/dml2_core_dcn6_calcs_dchub.c | 4 +--
.../dml2_core_dcn6_funcs_mode_programming.c | 25 +++----------------
2 files changed, 6 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c
index b3a2858281ec..a61a1037bb73 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c
@@ -1041,9 +1041,9 @@ static void dcn6_rq_dlg_get_dlg_reg(
if (disp_dlg_regs->refcyc_per_pte_group_nom_c >= (unsigned int)math_pow(2, 23))
disp_dlg_regs->refcyc_per_pte_group_nom_c = (unsigned int)(math_pow(2, 23) - 1);
}
- DML_ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_l < (unsigned int)math_pow(2, 13));
+ DML_ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_l < (unsigned int)math_pow(2, 23));
if (l->dual_plane) {
- DML_ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_c < (unsigned int)math_pow(2, 13));
+ DML_ASSERT(disp_dlg_regs->refcyc_per_pte_group_vblank_c < (unsigned int)math_pow(2, 23));
}
DML_ASSERT(disp_dlg_regs->refcyc_per_line_delivery_pre_l < (unsigned int)math_pow(2, 13));
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
index 464b7b00e8c2..372691a263d5 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
@@ -519,26 +519,10 @@ static void dcn6_mp_calculate_dcfclk_deep_sleep_hysteresis(struct dml2_core_calc
struct dml2_core_internal_mode_program *states)
{
const struct dml2_core_ip_params *ip = ctx->ip;
- const struct dml2_utm_soc_bb *soc_bb = ctx->soc_bb;
- struct dml2_core_internal_mode_program *inputs = states;
struct dml2_core_internal_mode_program *outputs = states;
- const struct dml2_sop_table *sop_table = &soc_bb->sop_table;
- struct dml2_soc_operating_point max_sop;
- double min_return_latency_in_DCFCLK_cycles = 0;
- const long min_return_uclk_cycles = 83;
- const long min_return_fclk_cycles = 75;
-
- sop_table->get_max_sop(sop_table, &max_sop);
- min_return_latency_in_DCFCLK_cycles =
- (min_return_uclk_cycles / (max_sop.uclk_khz / 1000.0)
- + min_return_fclk_cycles / (max_sop.fclk_khz / 1000.0))
- * inputs->Dcfclk;
-
- outputs->min_return_latency_in_dcfclk = (unsigned int)min_return_latency_in_DCFCLK_cycles;
- outputs->dcfclk_deep_sleep_hysteresis = (unsigned int)math_max2(
- 32,
- (double)ip->pixel_chunk_size_kbytes * 1024 * 3 / 4 / 64
- - min_return_latency_in_DCFCLK_cycles);
+
+ outputs->min_return_latency_in_dcfclk = 0;
+ outputs->dcfclk_deep_sleep_hysteresis = (unsigned int)math_max2(32, (double)ip->pixel_chunk_size_kbytes * 1024 * 3 / 4 / 64);
}
void dcn6_calculate_mode_programming(struct dml2_core_calculate_mp_context *ctx,
@@ -558,8 +542,6 @@ void dcn6_calculate_mode_programming(struct dml2_core_calculate_mp_context *ctx,
dcn6_mp_calculate_vm_group_and_request_times(ctx, states);
- dcn6_mp_calculate_vstartup_adjustment(ctx, states);
-
dcn6_mp_calculate_max_bandwidth_used(ctx, states);
dcn6_mp_calculate_stutter_efficiency(ctx, states);
@@ -1394,6 +1376,7 @@ enum dml2_status dml2_core_dcn6_funcs_populate_programming(struct dml2_core_inst
dcn6_mp_initialize_from_solution(&mode_lib->mp, solution, core->utm_soc_bb);
dcn6_mp_build_calculate_mp_context(calc_mp_ctx, core, solution);
dcn6_calculate_mode_programming(calc_mp_ctx, &mode_lib->mp);
+ dcn6_mp_calculate_vstartup_adjustment(calc_mp_ctx, &mode_lib->mp);
dcn6_populate_mode_programming(calc_mp_ctx, programming, &mode_lib->scratch, mode_lib, solution, core->utm_soc_bb);
DML_LOG_DEBUG("%s exit\n", __func__);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 60/74] drm/amd/display: Add effective_number_of_dpp parameter
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (58 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 59/74] drm/amd/display: Update DCN6 Calcs Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 61/74] drm/amd/display: Read DRAM channel width from SMU init table Wayne Lin
` (14 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Austin Zheng, Dillon Varone
From: Austin Zheng <Austin.Zheng@amd.com>
[Why/How]
New parameter to track DPPs effectively in-use per-plane
vs total number of DPPs assigned to a plane
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Austin Zheng <Austin.Zheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c | 5 +++--
.../src/dml2_core/dml2_core_dcn6_funcs_mode_support.c | 7 ++++---
.../dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h | 2 ++
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
index 372691a263d5..0bec9baf809e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_programming.c
@@ -101,7 +101,7 @@ static void dcn6_mp_calculate_pixel_delivery_times(struct dml2_core_calculate_mp
//Display Pipeline Delivery Time in Prefetch, Groups
get_calcs(ctx)->calculate_pixel_delivery_times(
display_cfg,
- inputs->NoOfDPP,
+ inputs->effective_number_of_dpp,
display_cfg->num_planes,
inputs->VRatioPrefetchY,
inputs->VRatioPrefetchC,
@@ -403,7 +403,7 @@ static void dcn6_mp_calculate_stutter_efficiency(struct dml2_core_calculate_mp_c
p->Z8StutterEnterPlusExitWatermark = inputs->Watermark.Z8StutterEnterPlusExitWatermark;
p->ProgressiveToInterlaceUnitInOPP = ip->ptoi_supported;
p->MinTTUVBlank = inputs->MinTTUVBlank;
- p->DPPPerSurface = inputs->NoOfDPP;
+ p->DPPPerSurface = inputs->effective_number_of_dpp;
p->DETBufferSizeY = inputs->DETBufferSizeY;
p->BytePerPixelY = inputs->BytePerPixelY;
p->BytePerPixelDETY = inputs->BytePerPixelInDETY;
@@ -1079,6 +1079,7 @@ void dcn6_mp_initialize_from_solution(struct dml2_core_internal_mode_program *ou
outputs->immediate_flip_required = outputs->immediate_flip_required
|| plane->immediate_flip;
outputs->NoOfDPP[k] = plane_support_info->dpps_used;
+ outputs->effective_number_of_dpp[k] = outputs->NoOfDPP[k];
outputs->dsc_enable[k] = stream_support_info->dsc_enable;
outputs->num_dsc_slices[k] = stream_support_info->num_dsc_slices;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
index c1cb43386473..ef56428d373e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_funcs_mode_support.c
@@ -1018,6 +1018,7 @@ static void dcn6_ms_calculate_num_of_dpp_required(
outputs->NoOfDPP[k] = 2;
}
}
+ outputs->effective_number_of_dpp[k] = outputs->NoOfDPP[k];
DML_LOG_VERBOSE("DML::%s: k=%d, NoOfDPP = %d\n", __func__, k,
outputs->NoOfDPP[k]);
}
@@ -2205,7 +2206,7 @@ void dcn6_ms_calculate_min_dcfclk_deepsleep_clock(
inputs->BytePerPixelC,
inputs->SwathWidthY,
inputs->SwathWidthC,
- inputs->NoOfDPP,
+ inputs->effective_number_of_dpp,
inputs->PSCL_FACTOR,
inputs->PSCL_FACTOR_CHROMA,
inputs->RequiredDPPCLK,
@@ -2742,7 +2743,7 @@ void dcn6_ms_calculate_urgent_latency(
ip->pixel_chunk_size_kbytes,
inputs->min_available_urgent_bandwidth_MBps,
display_cfg->num_planes,
- inputs->NoOfDPP,
+ inputs->effective_number_of_dpp,
inputs->dpte_group_bytes,
inputs->tdlut_bytes_per_group,
inputs->HostVMInefficiencyFactor,
@@ -3781,7 +3782,7 @@ void dcn6_ms_calculate_watermarks(
//CalculateWatermarks_params->LBBitPerPixel = 57; // FIXME_STAGE2, need a new ip param?
p->SwathWidthY = inputs->SwathWidthY;
p->SwathWidthC = inputs->SwathWidthC;
- p->DPPPerSurface = inputs->NoOfDPP;
+ p->DPPPerSurface = inputs->effective_number_of_dpp;
p->BytePerPixelDETY = inputs->BytePerPixelInDETY;
p->BytePerPixelDETC = inputs->BytePerPixelInDETC;
p->DSTXAfterScaler = inputs->DSTXAfterScaler;
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 4848dbffc184..dbc59c308762 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
@@ -586,6 +586,7 @@ struct dml2_core_internal_mode_support {
double surface_avg_vactive_required_bw[dml2_core_internal_soc_state_max][dml2_core_internal_bw_max][DML2_MAX_PLANES];
double surface_peak_required_bw[dml2_core_internal_soc_state_max][dml2_core_internal_bw_max][DML2_MAX_PLANES];
+ unsigned int effective_number_of_dpp[DML2_MAX_PLANES];
// Something that should be feedback to caller
enum dml2_odm_mode ODMMode[DML2_MAX_PLANES];
unsigned int SurfaceSizeInMALL[DML2_MAX_PLANES];
@@ -779,6 +780,7 @@ struct dml2_core_internal_mode_program {
double prefetch_vmrow_bw[DML2_MAX_PLANES];
double cursor_bw[DML2_MAX_PLANES];
double prefetch_cursor_bw[DML2_MAX_PLANES];
+ unsigned int effective_number_of_dpp[DML2_MAX_PLANES];
double WritebackDelay[DML2_MAX_PLANES];
unsigned int dpte_row_height[DML2_MAX_PLANES];
unsigned int dpte_row_height_linear[DML2_MAX_PLANES];
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 61/74] drm/amd/display: Read DRAM channel width from SMU init table
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (59 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 60/74] drm/amd/display: Add effective_number_of_dpp parameter Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 62/74] drm/amd/display: Update DCN42B stutter latency to match DCN42 Wayne Lin
` (13 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Wenjing Liu, Dillon Varone,
Mario Limonciello, Alex Deucher, stable, Wayne Lin
From: Wenjing Liu <wenjing.liu@amd.com>
[Why]
The channel count came from the SMU init table but the channel
width came from VBIOS vram info. Both values need to describe the
same memory type.
[How]
Add DramChannelWidthBytes to MemoryConfig_t, using a reserved slot
so the layout is unchanged. Populate the channel width in bw_params
from the init table instead of VBIOS vram info.
Cc: Dillon Varone <dillon.varone@amd.com>
Cc: Mario Limonciello <mario.limonciello@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
Signed-off-by: Wenjing Liu <wenjing.liu@amd.com>
Signed-off-by: Wayne Lin <Wayne.Lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h | 3 ++-
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 3 +--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h
index 234777bdd183..ea3927223117 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h
@@ -77,7 +77,8 @@ typedef struct {
typedef struct {
uint32_t NumUmcChannels;
- uint32_t Reserved[3];
+ uint32_t DramChannelWidthBytes;
+ uint32_t Reserved[2];
} MemoryConfig_t;
/**
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
index 8eee54c7d5d5..c5d56757eafd 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
@@ -747,8 +747,7 @@ static bool dcn60_fetch_dal_init_table(struct clk_mgr_internal *clk_mgr)
//dcn60_populate_dc_mode_limit(&bw_params->dc_mode_limit, init_table);
bw_params->num_channels = init_table->MemoryConfig.NumUmcChannels;
- bw_params->dram_channel_width_bytes =
- clk_mgr->base.ctx->dc_bios->vram_info.dram_channel_width_bytes;
+ bw_params->dram_channel_width_bytes = init_table->MemoryConfig.DramChannelWidthBytes;
dcn60_populate_utm_qos_model(clk_mgr, &bw_params->utm_qos_model, init_table);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 62/74] drm/amd/display: Update DCN42B stutter latency to match DCN42
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (60 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 61/74] drm/amd/display: Read DRAM channel width from SMU init table Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 63/74] drm/amd/display: Expect VRAM|GTT for the DMUB framebuffer Wayne Lin
` (12 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Allen Pan, Dillon Varone,
Charlene Liu, Mario Limonciello, Alex Deucher, stable, Wayne Lin
From: Allen Pan <Allen.Pan@amd.com>
[Why]
The DCN42B stutter latencies do not match DCN42, which can lead to
underflow and stutter issues.
[How]
Update the DCN42B LPDDR5/LPCAMM2 stutter latencies to the same
values DCN42 already uses.
Cc: Dillon Varone <dillon.varone@amd.com>
Cc: Charlene Liu <charlene.liu@amd.com>
Cc: Mario Limonciello <mario.limonciello@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
Signed-off-by: Allen Pan <Allen.Pan@amd.com>
Signed-off-by: Wayne Lin <Wayne.Lin@amd.com>
---
.../dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 e83a9ac018db..07f1bffc75ca 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
@@ -188,8 +188,8 @@ static const struct dml2_soc_bb dml2_socbb_dcn42b = {
.dram_clk_change_blackout_us = 36,
.fclk_change_blackout_us = 0,
.g7_ppt_blackout_us = 0,
- .stutter_enter_plus_exit_latency_us = 14,
- .stutter_exit_latency_us = 12,
+ .stutter_enter_plus_exit_latency_us = 15.5,
+ .stutter_exit_latency_us = 13.5,
.z8_stutter_enter_plus_exit_latency_us = 300,
.z8_stutter_exit_latency_us = 200,
},
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 63/74] drm/amd/display: Expect VRAM|GTT for the DMUB framebuffer
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (61 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 62/74] drm/amd/display: Update DCN42B stutter latency to match DCN42 Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-12 7:17 ` Eren Özen
2026-11-11 1:29 ` [PATCH 64/74] drm/amd/display: Report QoS latency bounds and request Wayne Lin
` (11 subsequent siblings)
74 siblings, 1 reply; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Wayne Lin
The DCN32 GTT-only framebuffer allocation was reverted, so
dm_dmub_sw_init() passes VRAM and GTT for every ASIC. The KUnit
case still required GTT alone and failed with domain 0x6.
Cc: ChiaHsuan Chung <chiahsuan.chung@amd.com>
Signed-off-by: Wayne Lin <Wayne.Lin@amd.com>
---
.../display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
index b48c96ec4250..595a7dcb57ad 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c
@@ -1816,13 +1816,14 @@ static void dm_test_dmub_sw_init_asic_mapping(struct kunit *test)
}
/**
- * dm_test_dmub_sw_init_gtt_only_asic - Test the GTT-only memory domain
+ * dm_test_dmub_sw_init_framebuffer_domain - Test the framebuffer memory domain
* @test: The KUnit test context
*
- * DCN32 and DCN321 keep the DMUB framebuffer in GTT; every other ASIC also
- * allows VRAM.
+ * dm_dmub_sw_init() allows both VRAM and GTT for every ASIC, including
+ * DCN32. The allocation is forced to fail so the test records the domain
+ * and stops.
*/
-static void dm_test_dmub_sw_init_gtt_only_asic(struct kunit *test)
+static void dm_test_dmub_sw_init_framebuffer_domain(struct kunit *test)
{
struct amdgpu_device *adev = dm_test_alloc_adev_for_sw_init(test);
@@ -1830,7 +1831,8 @@ static void dm_test_dmub_sw_init_gtt_only_asic(struct kunit *test)
dm_test_bo.create_ret = -ENOMEM;
KUNIT_EXPECT_EQ(test, dm_dmub_sw_init(adev), -ENOMEM);
- KUNIT_EXPECT_EQ(test, dm_test_bo.create_domain, (u32)AMDGPU_GEM_DOMAIN_GTT);
+ KUNIT_EXPECT_EQ(test, dm_test_bo.create_domain,
+ (u32)(AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM));
dm_test_free_sw_init(adev);
@@ -2000,7 +2002,7 @@ static struct kunit_case amdgpu_dm_dmub_hw_access_tests[] = {
KUNIT_CASE(dm_test_init_microcode_request_fails),
/* dm_dmub_sw_init() */
KUNIT_CASE(dm_test_dmub_sw_init_asic_mapping),
- KUNIT_CASE(dm_test_dmub_sw_init_gtt_only_asic),
+ KUNIT_CASE(dm_test_dmub_sw_init_framebuffer_domain),
KUNIT_CASE(dm_test_dmub_sw_init_success),
KUNIT_CASE(dm_test_dmub_sw_init_bss_data),
KUNIT_CASE(dm_test_dmub_sw_init_psp_load),
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 64/74] drm/amd/display: Report QoS latency bounds and request
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (62 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 63/74] drm/amd/display: Expect VRAM|GTT for the DMUB framebuffer Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 65/74] drm/amd/display: Bypass chroma upsampling without the DPP cap Wayne Lin
` (10 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Wenjing Liu, Alvin Lee,
Mario Limonciello, Alex Deucher, stable, Wayne Lin
From: Wenjing Liu <wenjing.liu@amd.com>
[Why]
The requested QoS values returned by the QoS query read zero. The
latency upper bounds and the calculated average bandwidth were never
filled, and clearing the memory bandwidth override zeroed the
bandwidth request instead of restoring the request of the committed
mode.
[How]
Fill the avg and max nominal latency upper bounds from the DML
qos_bound, converting from microseconds to nanoseconds. Fill the
average active bandwidth from the DML informative qos output,
converting from MBps to KBps. Report these from the committed state
in dc_get_qos_info(). Restore the bandwidth lower bounds from the
committed state when the override is cleared.
Cc: Alvin Lee <alvin.lee2@amd.com>
Cc: Mario Limonciello <mario.limonciello@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
Signed-off-by: Wenjing Liu <wenjing.liu@amd.com>
Signed-off-by: Wayne Lin <Wayne.Lin@amd.com>
---
.../drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 7 +++++--
drivers/gpu/drm/amd/display/dc/core/dc.c | 8 +++++---
.../dml21_wrapper/dml21_translation_helper.c | 11 +++++++++++
3 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
index c5d56757eafd..f801f3337e5d 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c
@@ -1619,8 +1619,11 @@ static unsigned int dcn60_override_memory_bandwidth_request(
memcpy(&new_clocks, &clk_mgr_base->clks, sizeof(struct dc_clocks));
if (bw_kbps == 0) {
- new_clocks.utm_nominal_bandwidth_lb_KBps = 0;
- new_clocks.utm_urgent_bandwidth_lb_KBps = 0;
+ /* Clearing the override restores the request of the committed mode. */
+ new_clocks.utm_nominal_bandwidth_lb_KBps =
+ dc->current_state->bw_ctx.bw.dcn.clk.utm_nominal_bandwidth_lb_KBps;
+ new_clocks.utm_urgent_bandwidth_lb_KBps =
+ dc->current_state->bw_ctx.bw.dcn.clk.utm_urgent_bandwidth_lb_KBps;
} else {
max_bw_kbps = dcn60_get_max_active_utm_bandwidth_kbps(clk_mgr_base);
capped_bw_kbps = (bw_kbps > max_bw_kbps) ? max_bw_kbps : bw_kbps;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 656a941b1ba0..8a02bcbb9649 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -8105,12 +8105,14 @@ bool dc_get_qos_info(struct dc *dc, struct dc_qos_info *info)
if (dc->clk_mgr && dc->clk_mgr->funcs->get_requested_memory_qos) {
dc->clk_mgr->funcs->get_requested_memory_qos(dc->clk_mgr, &requested);
info->qos_bandwidth_lb_in_mbps = requested.bandwidth_lb_in_mbps;
- info->calculated_avg_bw_in_mbps = requested.calculated_avg_bw_in_mbps;
- info->qos_max_latency_ub_in_ns = requested.max_latency_ub_in_ns;
- info->qos_avg_latency_ub_in_ns = requested.avg_latency_ub_in_ns;
info->qos_max_bw_budget_in_mbps = requested.max_bw_budget_in_mbps;
}
+ /* The clk_mgr does not track these, so report the ones of the committed state. */
+ info->calculated_avg_bw_in_mbps = clk->required_avg_active_bandwidth_KBps / 1000;
+ info->qos_max_latency_ub_in_ns = clk->utm_nominal_max_latency_ub_ns;
+ info->qos_avg_latency_ub_in_ns = clk->utm_nominal_avg_latency_ub_ns;
+
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
index a7d6ff59870a..31292a70a84e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_wrapper/dml21_wrapper/dml21_translation_helper.c
@@ -1036,6 +1036,14 @@ void dml21_copy_clocks_to_dc_state(struct dml2_context *in_ctx, struct dc_state
context->bw_ctx.bw.dcn.clk.utm_latency_ub_index = 0; /* TODO - derive from qos model */
context->bw_ctx.bw.dcn.clk.utm_nominal_bandwidth_lb_KBps = (unsigned int) in_ctx->v21.mode_programming.programming->qos_bound.bandwidth_lb.dcn5.non_urgent_bandwidth_kbps;
context->bw_ctx.bw.dcn.clk.utm_urgent_bandwidth_lb_KBps = (unsigned int) in_ctx->v21.mode_programming.programming->qos_bound.bandwidth_lb.dcn5.urgent_bandwidth_kbps;
+ /* DML latency upper bounds are in microseconds, the dc clocks fields are in nanoseconds. */
+ context->bw_ctx.bw.dcn.clk.utm_nominal_avg_latency_ub_ns = (unsigned int)(
+ in_ctx->v21.mode_programming.programming->qos_bound.latency_ub.dcn5.avg_req_latency_non_urg * 1000);
+ context->bw_ctx.bw.dcn.clk.utm_nominal_max_latency_ub_ns = (unsigned int)(
+ in_ctx->v21.mode_programming.programming->qos_bound.latency_ub.dcn5.max_req_latency_non_urg * 1000);
+ /* The average bandwidth DML calculated for the active state, converted from MBps to KBps. */
+ context->bw_ctx.bw.dcn.clk.required_avg_active_bandwidth_KBps = (unsigned int)(
+ in_ctx->v21.mode_programming.programming->informative.qos.avg_bw_required.sys_active.sdp_bw_mbps * 1000);
context->bw_ctx.bw.dcn.clk.stutter_efficiency.base_efficiency = (uint8_t)in_ctx->v21.mode_programming.programming->stutter.base_percent_efficiency;
context->bw_ctx.bw.dcn.clk.stutter_efficiency.low_power_efficiency = (uint8_t)in_ctx->v21.mode_programming.programming->stutter.low_power_percent_efficiency;
context->bw_ctx.bw.dcn.clk.stutter_efficiency.z8_stutter_efficiency = (uint8_t)in_ctx->v21.mode_programming.programming->informative.power_management.z8.stutter_efficiency;
@@ -1177,6 +1185,9 @@ void dml21_init_min_clocks_for_dc_state(struct dml2_context *in_ctx, struct dc_s
min_clocks->utm_latency_ub_index = 0; /* TODO - derive from qos model */
min_clocks->utm_nominal_bandwidth_lb_KBps = 0;
min_clocks->utm_urgent_bandwidth_lb_KBps = 0;
+ min_clocks->utm_nominal_max_latency_ub_ns = 0;
+ min_clocks->utm_nominal_avg_latency_ub_ns = 0;
+ min_clocks->required_avg_active_bandwidth_KBps = 0;
min_clocks->stutter_efficiency.base_efficiency = 1;
min_clocks->stutter_efficiency.low_power_efficiency = 1;
min_clocks->stutter_efficiency.z8_stutter_efficiency = 1;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 65/74] drm/amd/display: Bypass chroma upsampling without the DPP cap
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (63 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 64/74] drm/amd/display: Report QoS latency bounds and request Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 66/74] drm/amd/display: Limit VRAM aperture retrieval to DCN6 Wayne Lin
` (9 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Sridevi Arvindekar, Alvin Lee
From: Sridevi Arvindekar <sridevi.arvindekar@amd.com>
[Why]
420 and 422 planes scaled in linear light asked for chroma
upsampling even when the DPP has no pre-scaler for it. The
scaler input forced bypass only for DCN 4.01, 4.2 and 4.2B.
[How]
resource_is_upsp_required() returns UPSP_BYPASS when
caps.color.dpp.upsp_pre_scaler is 0. Only DCN6 sets that
cap. translate_SPL_in_params_from_pipe_ctx() copies
scl_data.upsp for every ASIC.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Sridevi Arvindekar <sridevi.arvindekar@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/dc/bls_builder/dcn401_seamless_builder.c | 2 +-
drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c | 2 +-
drivers/gpu/drm/amd/display/dc/core/dc_resource.c | 2 +-
drivers/gpu/drm/amd/display/dc/core/dc_resource_helper.c | 7 ++++++-
drivers/gpu/drm/amd/display/dc/dc_spl_translate.c | 8 ++++----
drivers/gpu/drm/amd/display/dc/inc/resource.h | 3 ++-
6 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
index 23951ef4fe04..8a563386d725 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
@@ -1789,7 +1789,7 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
plane->input_csc_color_matrix;
p->setup_dpp_params.color_space = plane->color_space;
p->setup_dpp_params.upsp_mode = resource_is_upsp_required(
- plane->format, plane->scaling_linearity);
+ (bool) plane->ctx->dc->caps.color.dpp.upsp_pre_scaler, plane->format, plane->scaling_linearity);
break;
case DPP_SET_CURSOR_MATRIX:
p->dpp_set_cursor_matrix_params.dpp = dpp;
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 c5e8a88d051d..82ac4ca4a13b 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
@@ -5793,7 +5793,7 @@ void hwss_add_dpp_setup_dpp(struct block_sequence_state *seq_state,
p->format = plane_state->format;
p->input_csc_color_matrix = plane_state->input_csc_color_matrix;
p->color_space = plane_state->color_space;
- p->upsp_mode = resource_is_upsp_required(plane_state->format, plane_state->scaling_linearity);
+ p->upsp_mode = resource_is_upsp_required((bool)plane_state->ctx->dc->caps.color.dpp.upsp_pre_scaler, plane_state->format, plane_state->scaling_linearity);
(*seq_state->num_steps)++;
}
}
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 3e87ec8ad6a5..2240afde4089 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -1608,7 +1608,7 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx)
pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_30BPP;
pipe_ctx->plane_res.scl_data.lb_params.alpha_en = plane_state->per_pixel_alpha;
- pipe_ctx->plane_res.scl_data.upsp = resource_is_upsp_required(plane_state->format, plane_state->scaling_linearity);
+ pipe_ctx->plane_res.scl_data.upsp = resource_is_upsp_required((bool)plane_state->ctx->dc->caps.color.dpp.upsp_pre_scaler, plane_state->format, plane_state->scaling_linearity);
// Convert pipe_ctx to respective input params for SPL
translate_SPL_in_params_from_pipe_ctx(pipe_ctx, spl_in);
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource_helper.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource_helper.c
index 55dfa465163f..d3987be3ce39 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource_helper.c
@@ -26,9 +26,14 @@
#include "sspl/dc_spl_types.h"
#include "resource.h"
-enum upsp_mode resource_is_upsp_required(enum surface_pixel_format format,
+enum upsp_mode resource_is_upsp_required(bool upsp_pre_scaler,
+ enum surface_pixel_format format,
enum dc_scaling_linearity scaling_linearity)
{
+ /* UPSP is not supported on all ASICs */
+ if (!upsp_pre_scaler)
+ return UPSP_BYPASS;
+
bool scaling_in_linear = (scaling_linearity == DC_SCALING_LINEARITY_LINEAR);
bool is_420_format = (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN &&
format <= SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb);
diff --git a/drivers/gpu/drm/amd/display/dc/dc_spl_translate.c b/drivers/gpu/drm/amd/display/dc/dc_spl_translate.c
index 00e121bb8ef7..1b7f0ee6ba97 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_spl_translate.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_spl_translate.c
@@ -211,19 +211,19 @@ void translate_SPL_in_params_from_pipe_ctx(struct pipe_ctx *pipe_ctx, struct spl
// Translate linear light scaling preference
if (pipe_ctx->stream->ctx->dc->debug.force_lls > 0) {
spl_in->lls_pref = pipe_ctx->stream->ctx->dc->debug.force_lls;
- spl_in->upsp_mode = pipe_ctx->plane_res.scl_data.upsp;
} else {
if ((plane_state->ctx->dce_version == DCN_VERSION_4_01) ||
(plane_state->ctx->dce_version == DCN_VERSION_4_2) ||
(plane_state->ctx->dce_version == DCN_VERSION_4_2B)) {
spl_in->lls_pref = LLS_PREF_DONT_CARE;
- spl_in->upsp_mode = UPSP_BYPASS;
} else {
set_linear_light_scaling_preference(plane_state, spl_in);
- /* Translate upsampling mode*/
- spl_in->upsp_mode = pipe_ctx->plane_res.scl_data.upsp;
}
}
+
+ /* Translate upsampling mode*/
+ spl_in->upsp_mode = pipe_ctx->plane_res.scl_data.upsp;
+
/* Translate chroma subsampling offset ( cositing ) */
if (pipe_ctx->stream->ctx->dc->debug.force_cositing) {
spl_in->basic_in.cositing = pipe_ctx->stream->ctx->dc->debug.force_cositing - 1;
diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h
index 0d9c9771ea7c..fbd874476aaf 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/resource.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h
@@ -123,7 +123,8 @@ void resource_build_test_pattern_params(
struct resource_context *res_ctx,
struct pipe_ctx *pipe_ctx);
-enum upsp_mode resource_is_upsp_required(enum surface_pixel_format format,
+enum upsp_mode resource_is_upsp_required(bool upsp_pre_scaler,
+ enum surface_pixel_format format,
enum dc_scaling_linearity scaling_linearity);
bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 66/74] drm/amd/display: Limit VRAM aperture retrieval to DCN6
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (64 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 65/74] drm/amd/display: Bypass chroma upsampling without the DPP cap Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 67/74] drm/amd/display: Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant Wayne Lin
` (8 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
From: Ivan Lipski <ivan.lipski@amd.com>
[Why&How]
Only DCN6 currently supports alt channel pstate switch, which requires VRAM
aperture configuration.
Return only dm_get_alt_channel_pstate_switch_aperture on other ASICs.
Cc: Sun peng Li <sunpeng.li@amd.com>
Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
index d655ae8bbb52..ebd8130cb04d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c
@@ -168,7 +168,7 @@ EXPORT_IF_KUNIT(dm_register_dmub_notify_callback);
*
* Reads both apertures before populating the DMUB parameters and DML2 config.
* Translates their base addresses into MC space for DML2. If either aperture
- * is unavailable, the feature remains disabled.
+ * is unavailable, the feature remains disabled. Only DCN 6.0 supports the feature.
*/
static void
dm_get_alt_channel_pstate_switch_aperture(struct amdgpu_device *adev,
@@ -183,6 +183,13 @@ dm_get_alt_channel_pstate_switch_aperture(struct amdgpu_device *adev,
u32 i;
int r;
+ switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
+ case IP_VERSION(6, 0, 0):
+ break;
+ default:
+ return;
+ }
+
/* Region IDs 5 and 6 identify the p-state switch apertures 1 and 2.
* Do not publish partial aperture configuration if either lookup fails.
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 67/74] drm/amd/display: Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (65 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 66/74] drm/amd/display: Limit VRAM aperture retrieval to DCN6 Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 68/74] drm/amd/display: Seed boot clocks to avoid lowering pre-OS display clocks Wayne Lin
` (7 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Rafal Ostrowski, Alvin Lee
From: Rafal Ostrowski <rafal.ostrowski@amd.com>
[Why]
- The DCN401 builders read IP_REQUEST_EN from HW to decide whether to
emit DC_IP_REQUEST_CNTL, so the sequence depended on HW state at build
time.
- The PG hooks read the PG policy from dc->debug at execute time.
- The DCN20 and DCN314 cursor workarounds found the DPP by indexing
res_pool->dpps[] with dpp_inst instead of using the DPP being gated.
[How]
- Add DC_IP_REQUEST_STATUS. The DCN401 status hook reads IP_REQUEST_EN
at execute time and keeps it in hws->ip_request_already_enabled. When
it was already 1, dcn401_dc_ip_request_cntl writes nothing for the
pair, as in the direct path, and the closing call clears the flag.
Both executors only dispatch.
- Emit the status step and the pair only when
dcn401_pg_needs_ip_request() is true: both hooks exist and a PG hook
can gate (DPP PG enabled, or HUBP PG enabled and ignore_pg clear).
- The DC2 commit builder asks the same check through the new
hws->funcs.pg_needs_ip_request hook, bound on DCN401. It is built
without CONFIG_DRM_AMD_DC_FP, so it must not call dcn401_hwseq.c, and
its include of dcn401_hwseq.h is dropped.
- Pass disable_power_gate and ignore_pg to the PG hooks, and the DPP
handle to dpp_pg_control instead of dpp_inst.
- Adders validate their arguments and hooks; executors only program.
Cc: Alvin Lee <alvin.lee2@amd.com>
Signed-off-by: Rafal Ostrowski <rafal.ostrowski@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../bls_builder/bls_builder_private_types.h | 2 +
.../bls_builder/dcn401_non_seamless_builder.c | 11 +--
.../dc/bls_builder/dcn401_seamless_builder.c | 20 +++-
.../display/dc/bls_executor/bls_executor.c | 12 ++-
.../drm/amd/display/dc/core/dc_hw_sequencer.c | 62 +++++++++++--
.../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 44 ++++++---
.../amd/display/dc/hwss/dcn10/dcn10_hwseq.h | 9 +-
.../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 31 ++++---
.../amd/display/dc/hwss/dcn20/dcn20_hwseq.h | 9 +-
.../amd/display/dc/hwss/dcn302/dcn302_hwseq.c | 15 ++-
.../amd/display/dc/hwss/dcn302/dcn302_hwseq.h | 6 +-
.../amd/display/dc/hwss/dcn303/dcn303_hwseq.c | 11 ++-
.../amd/display/dc/hwss/dcn303/dcn303_hwseq.h | 6 +-
.../amd/display/dc/hwss/dcn31/dcn31_hwseq.c | 8 +-
.../amd/display/dc/hwss/dcn31/dcn31_hwseq.h | 3 +-
.../amd/display/dc/hwss/dcn314/dcn314_hwseq.c | 15 +--
.../amd/display/dc/hwss/dcn314/dcn314_hwseq.h | 3 +-
.../amd/display/dc/hwss/dcn32/dcn32_hwseq.c | 7 +-
.../amd/display/dc/hwss/dcn32/dcn32_hwseq.h | 3 +-
.../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 92 ++++++++++++++-----
.../amd/display/dc/hwss/dcn401/dcn401_hwseq.h | 2 +
.../amd/display/dc/hwss/dcn401/dcn401_init.c | 2 +
.../drm/amd/display/dc/hwss/hw_sequencer.h | 25 ++++-
.../display/dc/hwss/hw_sequencer_private.h | 14 ++-
24 files changed, 303 insertions(+), 109 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
index 784a3cb4d311..cc301a02c494 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
@@ -126,6 +126,8 @@ struct dcn401_commit_step_meta {
* so a plane add always programs the scaler and viewport.
*/
bool scaling_unchanged;
+ /* funcs.pg_needs_ip_request() is false: no PG hook can gate. */
+ bool no_ip_request;
} skip_if;
};
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
index d28e162a6632..965004f6e49b 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
@@ -231,15 +231,14 @@ static const struct dcn401_commit_step
{ .func = HUBP_SET_FLIP_CONTROL_GSL },
{ .func = HUBP_CLK_CNTL },
{ .func = DPP_DPPCLK_CONTROL },
- /*
- * dcn10_plane_atomic_power_down sets IP_REQUEST_EN only when it
- * reads 0. Every writer clears it again, so it always reads 0 here.
- */
- { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true },
+ { .func = DC_IP_REQUEST_STATUS, .meta.skip_if.no_ip_request = true },
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true,
+ .meta.skip_if.no_ip_request = true },
{ .func = HUBP_PG_CONTROL },
{ .func = HUBP_RESET },
{ .func = DPP_RESET },
- { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false },
+ { .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false,
+ .meta.skip_if.no_ip_request = true },
};
static const struct dcn401_commit_step
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
index 8a563386d725..863919e0967e 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
@@ -14,6 +14,7 @@
#include "dc_dmub_srv.h"
#include "custom_float.h"
#include "dcn32/dcn32_resource.h"
+#include "dce/dce_hwseq.h"
#include "bls_builder/bls_builder_internal.h"
#include "bls_builder/dcn401_init_builder.h"
#include "bls_builder/bls_builder_private_types.h"
@@ -1001,12 +1002,17 @@ static const struct dcn401_commit_step bls_update_head_new_stream[] = {
/* enable_plane, then the watermark propagate and the DET segments. */
static const struct dcn401_commit_step bls_update_head_new_enable_plane[] = {
+ { .func = DC_IP_REQUEST_STATUS,
+ .meta.skip_if.hubp_dpp_already_enabled = true,
+ .meta.skip_if.no_ip_request = true },
{ .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = true,
- .meta.skip_if.hubp_dpp_already_enabled = true },
+ .meta.skip_if.hubp_dpp_already_enabled = true,
+ .meta.skip_if.no_ip_request = true },
{ .func = HUBP_PG_CONTROL, .meta.hubp_pg_control.power_on = true,
.meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = DC_IP_REQUEST_CNTL, .meta.dc_ip_request_cntl.enable = false,
- .meta.skip_if.hubp_dpp_already_enabled = true },
+ .meta.skip_if.hubp_dpp_already_enabled = true,
+ .meta.skip_if.no_ip_request = true },
{ .func = HUBP_CLK_CNTL, .meta.hubp_clk_cntl.enable = true,
.meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = HUBP_INIT,
@@ -1690,6 +1696,9 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
p->dpp_dppclk_control_params.enable =
m->dpp_dppclk_control.enable;
break;
+ case DC_IP_REQUEST_STATUS:
+ p->dc_ip_request_status_params.hws = dc->hwseq;
+ break;
case DC_IP_REQUEST_CNTL:
p->dc_ip_request_cntl_params.hws = dc->hwseq;
p->dc_ip_request_cntl_params.enable =
@@ -1701,6 +1710,9 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
pipe->plane_res.hubp->inst;
p->hubp_pg_control_params.power_on =
m->hubp_pg_control.power_on;
+ p->hubp_pg_control_params.disable_power_gate =
+ dc->debug.disable_hubp_power_gate;
+ p->hubp_pg_control_params.ignore_pg = dc->debug.ignore_pg;
break;
case HUBP_RESET:
p->hubp_reset_params.hubp = pipe->plane_res.hubp;
@@ -1967,6 +1979,10 @@ static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
!args->pipe->plane_state->update_bits.position_change &&
!args->pipe->stream->update_flags.bits.scaling)
return true;
+ if (step->meta.skip_if.no_ip_request &&
+ (!args->dc->hwseq->funcs.pg_needs_ip_request ||
+ !args->dc->hwseq->funcs.pg_needs_ip_request(args->dc)))
+ return true;
return false;
}
diff --git a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
index 6a875add72e2..47c7137eeeaf 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
@@ -453,6 +453,9 @@ static noinline_for_stack bool dc2_bls_step_supported(const struct dc2 *dc2,
case HUBP_PG_CONTROL:
hws = step->params.hubp_pg_control_params.hws;
return hws && hws->funcs.hubp_pg_control;
+ case DC_IP_REQUEST_STATUS:
+ hws = step->params.dc_ip_request_status_params.hws;
+ return hws && hws->funcs.dc_ip_request_status;
case DC_IP_REQUEST_CNTL:
hws = step->params.dc_ip_request_cntl_params.hws;
return hws && hws->funcs.dc_ip_request_cntl;
@@ -1415,7 +1418,14 @@ enum dc2_bls_exec_result dc2_bls_execute(
&entry->params.hubp_pg_control_params;
p->hws->funcs.hubp_pg_control(p->hws, p->hubp_inst,
- p->power_on);
+ p->power_on, p->disable_power_gate, p->ignore_pg);
+ break;
+ }
+ case DC_IP_REQUEST_STATUS: {
+ struct dce_hwseq *hws =
+ entry->params.dc_ip_request_status_params.hws;
+
+ hws->funcs.dc_ip_request_status(hws);
break;
}
case DC_IP_REQUEST_CNTL: {
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 82ac4ca4a13b..ecf90e3a770c 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
@@ -2094,6 +2094,9 @@ void hwss_execute_sequence(struct dc *dc,
case DPP_ROOT_CLOCK_CONTROL:
hwss_dpp_root_clock_control(params);
break;
+ case DC_IP_REQUEST_STATUS:
+ hwss_dc_ip_request_status(params);
+ break;
case DC_IP_REQUEST_CNTL:
hwss_dc_ip_request_cntl(params);
break;
@@ -4066,11 +4069,11 @@ void hwss_dccg_set_ref_dscclk(union block_sequence_params *params)
void hwss_dpp_pg_control(union block_sequence_params *params)
{
struct dce_hwseq *hws = params->dpp_pg_control_params.hws;
- unsigned int dpp_inst = params->dpp_pg_control_params.dpp_inst;
+ struct dpp *dpp = params->dpp_pg_control_params.dpp;
bool power_on = params->dpp_pg_control_params.power_on;
+ bool disable_power_gate = params->dpp_pg_control_params.disable_power_gate;
- if (hws->funcs.dpp_pg_control)
- hws->funcs.dpp_pg_control(hws, dpp_inst, power_on);
+ hws->funcs.dpp_pg_control(hws, dpp, power_on, disable_power_gate);
}
void hwss_hubp_pg_control(union block_sequence_params *params)
@@ -4078,9 +4081,10 @@ void hwss_hubp_pg_control(union block_sequence_params *params)
struct dce_hwseq *hws = params->hubp_pg_control_params.hws;
unsigned int hubp_inst = params->hubp_pg_control_params.hubp_inst;
bool power_on = params->hubp_pg_control_params.power_on;
+ bool disable_power_gate = params->hubp_pg_control_params.disable_power_gate;
+ bool ignore_pg = params->hubp_pg_control_params.ignore_pg;
- if (hws->funcs.hubp_pg_control)
- hws->funcs.hubp_pg_control(hws, hubp_inst, power_on);
+ hws->funcs.hubp_pg_control(hws, hubp_inst, power_on, disable_power_gate, ignore_pg);
}
void hwss_hubp_reset(union block_sequence_params *params)
@@ -4109,6 +4113,13 @@ void hwss_dpp_root_clock_control(union block_sequence_params *params)
dccg->funcs->dpp_root_clock_control(dccg, dpp_inst, clock_on, root_clock_optimization_dpp);
}
+void hwss_dc_ip_request_status(union block_sequence_params *params)
+{
+ struct dce_hwseq *hws = params->dc_ip_request_status_params.hws;
+
+ hws->funcs.dc_ip_request_status(hws);
+}
+
void hwss_dc_ip_request_cntl(union block_sequence_params *params)
{
struct dce_hwseq *hws = params->dc_ip_request_cntl_params.hws;
@@ -5018,6 +5029,23 @@ void hwss_add_dpp_program_cm_hist(struct block_sequence_state *seq_state,
}
}
+bool hwss_add_dc_ip_request_status(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws)
+{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps ||
+ !hws || !hws->funcs.dc_ip_request_status)
+ return false;
+
+ if (*seq_state->num_steps >= MAX_HWSS_BLOCK_SEQUENCE_SIZE)
+ return false;
+
+ seq_state->steps[*seq_state->num_steps].func = DC_IP_REQUEST_STATUS;
+ seq_state->steps[*seq_state->num_steps].params.dc_ip_request_status_params.hws = hws;
+ (*seq_state->num_steps)++;
+
+ return true;
+}
+
void hwss_add_dc_ip_request_cntl(struct block_sequence_state *seq_state,
struct dc *dc,
bool enable)
@@ -5561,14 +5589,21 @@ void hwss_add_dpp_root_clock_control(struct block_sequence_state *seq_state,
void hwss_add_dpp_pg_control(struct block_sequence_state *seq_state,
struct dce_hwseq *hws,
- unsigned int dpp_inst,
- bool power_on)
+ struct dpp *dpp,
+ bool power_on,
+ bool disable_power_gate)
{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps || !hws ||
+ !hws->funcs.dpp_pg_control || !dpp)
+ return;
+
if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
seq_state->steps[*seq_state->num_steps].func = DPP_PG_CONTROL;
seq_state->steps[*seq_state->num_steps].params.dpp_pg_control_params.hws = hws;
- seq_state->steps[*seq_state->num_steps].params.dpp_pg_control_params.dpp_inst = dpp_inst;
+ seq_state->steps[*seq_state->num_steps].params.dpp_pg_control_params.dpp = dpp;
seq_state->steps[*seq_state->num_steps].params.dpp_pg_control_params.power_on = power_on;
+ seq_state->steps[*seq_state->num_steps].params.dpp_pg_control_params.disable_power_gate =
+ disable_power_gate;
(*seq_state->num_steps)++;
}
}
@@ -5576,13 +5611,22 @@ void hwss_add_dpp_pg_control(struct block_sequence_state *seq_state,
void hwss_add_hubp_pg_control(struct block_sequence_state *seq_state,
struct dce_hwseq *hws,
unsigned int hubp_inst,
- bool power_on)
+ bool power_on,
+ bool disable_power_gate,
+ bool ignore_pg)
{
+ if (!seq_state || !seq_state->steps || !seq_state->num_steps || !hws ||
+ !hws->funcs.hubp_pg_control)
+ return;
+
if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
seq_state->steps[*seq_state->num_steps].func = HUBP_PG_CONTROL;
seq_state->steps[*seq_state->num_steps].params.hubp_pg_control_params.hws = hws;
seq_state->steps[*seq_state->num_steps].params.hubp_pg_control_params.hubp_inst = hubp_inst;
seq_state->steps[*seq_state->num_steps].params.hubp_pg_control_params.power_on = power_on;
+ seq_state->steps[*seq_state->num_steps].params.hubp_pg_control_params.disable_power_gate =
+ disable_power_gate;
+ seq_state->steps[*seq_state->num_steps].params.hubp_pg_control_params.ignore_pg = ignore_pg;
(*seq_state->num_steps)++;
}
}
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 79c0f55d11e8..08fff8d42cbd 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
@@ -935,25 +935,27 @@ void dcn10_disable_vga(
* dcn10_dpp_pg_control - DPP power gate control.
*
* @hws: dce_hwseq reference.
- * @dpp_inst: DPP instance reference.
+ * @dpp: DPP instance reference.
* @power_on: true if we want to enable power gate, false otherwise.
+ * @disable_power_gate: true if DPP power gating is disabled by debug policy.
*
* Enable or disable power gate in the specific DPP instance.
*/
void dcn10_dpp_pg_control(
struct dce_hwseq *hws,
- unsigned int dpp_inst,
- bool power_on)
+ struct dpp *dpp,
+ bool power_on,
+ bool disable_power_gate)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? PGFSM_POWER_ON : PGFSM_POWER_OFF;
- if (hws->ctx->dc->debug.disable_dpp_power_gate)
+ if (disable_power_gate)
return;
if (REG(DOMAIN1_PG_CONFIG) == 0)
return;
- switch (dpp_inst) {
+ switch (dpp->inst) {
case 0: /* DPP0 */
REG_UPDATE(DOMAIN1_PG_CONFIG,
DOMAIN1_POWER_GATE, power_gate);
@@ -998,18 +1000,24 @@ void dcn10_dpp_pg_control(
* @hws: dce_hwseq reference.
* @hubp_inst: DPP instance reference.
* @power_on: true if we want to enable power gate, false otherwise.
+ * @disable_power_gate: true if HUBP power gating is disabled by debug policy.
+ * @ignore_pg: unused on DCN10.
*
* Enable or disable power gate in the specific HUBP instance.
*/
void dcn10_hubp_pg_control(
struct dce_hwseq *hws,
unsigned int hubp_inst,
- bool power_on)
+ bool power_on,
+ bool disable_power_gate,
+ bool ignore_pg)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? PGFSM_POWER_ON : PGFSM_POWER_OFF;
- if (hws->ctx->dc->debug.disable_hubp_power_gate)
+ (void)ignore_pg;
+
+ if (disable_power_gate)
return;
if (REG(DOMAIN0_PG_CONFIG) == 0)
return;
@@ -1055,8 +1063,11 @@ void dcn10_hubp_pg_control(
static void power_on_plane_resources(
struct dce_hwseq *hws,
+ struct dpp *dpp,
int plane_id)
{
+ struct dc *dc = hws->ctx->dc;
+
DC_LOGGER_INIT(hws->ctx);
if (hws->funcs.dpp_root_clock_control &&
@@ -1068,10 +1079,12 @@ static void power_on_plane_resources(
IP_REQUEST_EN, 1);
if (hws->funcs.dpp_pg_control)
- hws->funcs.dpp_pg_control(hws, plane_id, true);
+ hws->funcs.dpp_pg_control(hws, dpp, true,
+ dc->debug.disable_dpp_power_gate);
if (hws->funcs.hubp_pg_control)
- hws->funcs.hubp_pg_control(hws, plane_id, true);
+ hws->funcs.hubp_pg_control(hws, plane_id, true,
+ dc->debug.disable_hubp_power_gate, dc->debug.ignore_pg);
REG_SET(DC_IP_REQUEST_CNTL, 0,
IP_REQUEST_EN, 0);
@@ -1093,7 +1106,8 @@ static void undo_DEGVIDCN10_253_wa(struct dc *dc)
REG_SET(DC_IP_REQUEST_CNTL, 0,
IP_REQUEST_EN, 1);
- hws->funcs.hubp_pg_control(hws, 0, false);
+ hws->funcs.hubp_pg_control(hws, 0, false, dc->debug.disable_hubp_power_gate,
+ dc->debug.ignore_pg);
REG_SET(DC_IP_REQUEST_CNTL, 0,
IP_REQUEST_EN, 0);
@@ -1122,7 +1136,8 @@ static void apply_DEGVIDCN10_253_wa(struct dc *dc)
REG_SET(DC_IP_REQUEST_CNTL, 0,
IP_REQUEST_EN, 1);
- hws->funcs.hubp_pg_control(hws, 0, true);
+ hws->funcs.hubp_pg_control(hws, 0, true, dc->debug.disable_hubp_power_gate,
+ dc->debug.ignore_pg);
REG_SET(DC_IP_REQUEST_CNTL, 0,
IP_REQUEST_EN, 0);
@@ -1546,10 +1561,12 @@ void dcn10_plane_atomic_power_down(struct dc *dc,
IP_REQUEST_EN, 1);
if (hws->funcs.dpp_pg_control)
- hws->funcs.dpp_pg_control(hws, dpp->inst, false);
+ hws->funcs.dpp_pg_control(hws, dpp, false,
+ dc->debug.disable_dpp_power_gate);
if (hws->funcs.hubp_pg_control)
- hws->funcs.hubp_pg_control(hws, hubp->inst, false);
+ hws->funcs.hubp_pg_control(hws, hubp->inst, false,
+ dc->debug.disable_hubp_power_gate, dc->debug.ignore_pg);
hubp->funcs->hubp_reset(hubp);
dpp->funcs->dpp_reset(dpp);
@@ -2806,6 +2823,7 @@ static void dcn10_enable_plane(
undo_DEGVIDCN10_253_wa(dc);
power_on_plane_resources(dc->hwseq,
+ pipe_ctx->plane_res.dpp,
pipe_ctx->plane_res.hubp->inst);
/* enable DCFCLK current DCHUB */
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h
index 383998531503..c62a32115e11 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.h
@@ -90,11 +90,14 @@ void dcn10_post_unlock_program_front_end(
void dcn10_hubp_pg_control(
struct dce_hwseq *hws,
unsigned int hubp_inst,
- bool power_on);
+ bool power_on,
+ bool disable_power_gate,
+ bool ignore_pg);
void dcn10_dpp_pg_control(
struct dce_hwseq *hws,
- unsigned int dpp_inst,
- bool power_on);
+ struct dpp *dpp,
+ bool power_on,
+ bool disable_power_gate);
void dcn10_enable_power_gating_plane(
struct dce_hwseq *hws,
bool enable);
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 696c0d5b8132..356ca90bf9bb 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
@@ -556,18 +556,19 @@ void dcn20_dsc_pg_control(
void dcn20_dpp_pg_control(
struct dce_hwseq *hws,
- unsigned int dpp_inst,
- bool power_on)
+ struct dpp *dpp,
+ bool power_on,
+ bool disable_power_gate)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
- if (hws->ctx->dc->debug.disable_dpp_power_gate)
+ if (disable_power_gate)
return;
if (REG(DOMAIN1_PG_CONFIG) == 0)
return;
- switch (dpp_inst) {
+ switch (dpp->inst) {
case 0: /* DPP0 */
REG_UPDATE(DOMAIN1_PG_CONFIG,
DOMAIN1_POWER_GATE, power_gate);
@@ -622,11 +623,8 @@ void dcn20_dpp_pg_control(
*/
/* Force disable cursor on plane powerdown on DPP 5 using dpp_force_disable_cursor */
- if (!power_on) {
- struct dpp *dpp5 = hws->ctx->dc->res_pool->dpps[dpp_inst];
- if (dpp5 && dpp5->funcs->dpp_force_disable_cursor)
- dpp5->funcs->dpp_force_disable_cursor(dpp5);
- }
+ if (!power_on && dpp->funcs->dpp_force_disable_cursor)
+ dpp->funcs->dpp_force_disable_cursor(dpp);
break;
default:
@@ -639,12 +637,16 @@ void dcn20_dpp_pg_control(
void dcn20_hubp_pg_control(
struct dce_hwseq *hws,
unsigned int hubp_inst,
- bool power_on)
+ bool power_on,
+ bool disable_power_gate,
+ bool ignore_pg)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
- if (hws->ctx->dc->debug.disable_hubp_power_gate)
+ (void)ignore_pg;
+
+ if (disable_power_gate)
return;
if (REG(DOMAIN0_PG_CONFIG) == 0)
return;
@@ -1321,10 +1323,13 @@ static void dcn20_power_on_plane_resources(
IP_REQUEST_EN, 1);
if (hws->funcs.dpp_pg_control)
- hws->funcs.dpp_pg_control(hws, pipe_ctx->plane_res.dpp->inst, true);
+ hws->funcs.dpp_pg_control(hws, pipe_ctx->plane_res.dpp, true,
+ hws->ctx->dc->debug.disable_dpp_power_gate);
if (hws->funcs.hubp_pg_control)
- hws->funcs.hubp_pg_control(hws, pipe_ctx->plane_res.hubp->inst, true);
+ hws->funcs.hubp_pg_control(hws, pipe_ctx->plane_res.hubp->inst, true,
+ hws->ctx->dc->debug.disable_hubp_power_gate,
+ hws->ctx->dc->debug.ignore_pg);
if (org_ip_request_cntl == 0)
REG_SET(DC_IP_REQUEST_CNTL, 0,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h
index 9a5addaa9a03..e2f65c3748de 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.h
@@ -107,12 +107,15 @@ void dcn20_enable_power_gating_plane(
bool enable);
void dcn20_dpp_pg_control(
struct dce_hwseq *hws,
- unsigned int dpp_inst,
- bool power_on);
+ struct dpp *dpp,
+ bool power_on,
+ bool disable_power_gate);
void dcn20_hubp_pg_control(
struct dce_hwseq *hws,
unsigned int hubp_inst,
- bool power_on);
+ bool power_on,
+ bool disable_power_gate,
+ bool ignore_pg);
void dcn20_program_triple_buffer(
struct hubp *hubp,
bool enable_triple_buffer);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.c
index 0a6d58dd8f6d..ba4b1492825f 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.c
@@ -29,6 +29,7 @@
#include "reg_helper.h"
#include "dc.h"
+#include "core_types.h"
#define DC_LOGGER_INIT(logger)
@@ -42,17 +43,18 @@
hws->shifts->field_name, hws->masks->field_name
-void dcn302_dpp_pg_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool power_on)
+void dcn302_dpp_pg_control(struct dce_hwseq *hws, struct dpp *dpp, bool power_on,
+ bool disable_power_gate)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
- if (hws->ctx->dc->debug.disable_dpp_power_gate)
+ if (disable_power_gate)
return;
if (REG(DOMAIN1_PG_CONFIG) == 0)
return;
- switch (dpp_inst) {
+ switch (dpp->inst) {
case 0: /* DPP0 */
REG_UPDATE(DOMAIN1_PG_CONFIG,
DOMAIN1_POWER_GATE, power_gate);
@@ -99,12 +101,15 @@ void dcn302_dpp_pg_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool po
}
}
-void dcn302_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on)
+void dcn302_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on,
+ bool disable_power_gate, bool ignore_pg)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
- if (hws->ctx->dc->debug.disable_hubp_power_gate)
+ (void)ignore_pg;
+
+ if (disable_power_gate)
return;
if (REG(DOMAIN0_PG_CONFIG) == 0)
return;
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.h
index 1e5126a0e695..2c4268970a2d 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn302/dcn302_hwseq.h
@@ -28,8 +28,10 @@
#include "hw_sequencer_private.h"
-void dcn302_dpp_pg_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool power_on);
-void dcn302_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on);
+void dcn302_dpp_pg_control(struct dce_hwseq *hws, struct dpp *dpp, bool power_on,
+ bool disable_power_gate);
+void dcn302_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on,
+ bool disable_power_gate, bool ignore_pg);
void dcn302_dsc_pg_control(struct dce_hwseq *hws, unsigned int dsc_inst, bool power_on);
#endif /* __DC_HWSS_DCN302_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c
index 6e1877a8682d..ecc70952aabb 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.c
@@ -43,19 +43,24 @@
hws->shifts->field_name, hws->masks->field_name
-void dcn303_dpp_pg_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool power_on)
+void dcn303_dpp_pg_control(struct dce_hwseq *hws, struct dpp *dpp, bool power_on,
+ bool disable_power_gate)
{
- (void)dpp_inst;
+ (void)dpp;
(void)hws;
(void)power_on;
+ (void)disable_power_gate;
/*DCN303 removes PG registers*/
}
-void dcn303_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on)
+void dcn303_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on,
+ bool disable_power_gate, bool ignore_pg)
{
(void)hubp_inst;
(void)hws;
(void)power_on;
+ (void)disable_power_gate;
+ (void)ignore_pg;
/*DCN303 removes PG registers*/
}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.h
index 7fdfc4175f80..494ad66f68d6 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn303/dcn303_hwseq.h
@@ -29,8 +29,10 @@
#include "hw_sequencer_private.h"
-void dcn303_dpp_pg_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool power_on);
-void dcn303_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on);
+void dcn303_dpp_pg_control(struct dce_hwseq *hws, struct dpp *dpp, bool power_on,
+ bool disable_power_gate);
+void dcn303_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on,
+ bool disable_power_gate, bool ignore_pg);
void dcn303_dsc_pg_control(struct dce_hwseq *hws, unsigned int dsc_inst, bool power_on);
void dcn303_enable_power_gating_plane(struct dce_hwseq *hws, bool enable);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c
index b6f3c0480ab6..7acf6af7e472 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c
@@ -459,12 +459,16 @@ void dcn31_z10_restore(const struct dc *dc)
dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
}
-void dcn31_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on)
+void dcn31_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on,
+ bool disable_power_gate, bool ignore_pg)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
uint32_t org_ip_request_cntl;
- if (hws->ctx->dc->debug.disable_hubp_power_gate)
+
+ (void)ignore_pg;
+
+ if (disable_power_gate)
return;
if (REG(DOMAIN0_PG_CONFIG) == 0)
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.h
index 0d09aa8cfb65..5aebb31fe2ec 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.h
@@ -46,7 +46,8 @@ void dcn31_update_info_frame(struct pipe_ctx *pipe_ctx);
void dcn31_z10_restore(const struct dc *dc);
void dcn31_z10_save_init(struct dc *dc);
-void dcn31_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on);
+void dcn31_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on,
+ bool disable_power_gate, bool ignore_pg);
int dcn31_init_sys_ctx(struct dce_hwseq *hws, struct dc *dc, struct dc_phy_addr_space_config *pa_config);
void dcn31_reset_hw_ctx_wrap(
struct dc *dc,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c
index de7dde2b036b..8af325a21267 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c
@@ -550,28 +550,29 @@ void dcn314_disable_link_output(struct dc_link *link,
* dcn314_dpp_pg_control - DPP power gate control.
*
* @hws: dce_hwseq reference.
- * @dpp_inst: DPP instance reference.
+ * @dpp: DPP instance reference.
* @power_on: true if we want to enable power gate, false otherwise.
+ * @disable_power_gate: true if DPP power gating is disabled by debug policy.
*
* Enable or disable power gate in the specific DPP instance.
* If power gating is disabled, will force disable cursor in the DPP instance.
*/
void dcn314_dpp_pg_control(
struct dce_hwseq *hws,
- unsigned int dpp_inst,
- bool power_on)
+ struct dpp *dpp,
+ bool power_on,
+ bool disable_power_gate)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
- if (hws->ctx->dc->debug.disable_dpp_power_gate) {
+ if (disable_power_gate) {
/* Workaround for DCN314 with disabled power gating */
if (!power_on) {
/* Force disable cursor if power gating is disabled */
- struct dpp *dpp = hws->ctx->dc->res_pool->dpps[dpp_inst];
- if (dpp && dpp->funcs->dpp_force_disable_cursor)
+ if (dpp->funcs->dpp_force_disable_cursor)
dpp->funcs->dpp_force_disable_cursor(dpp);
}
return;
@@ -579,7 +580,7 @@ void dcn314_dpp_pg_control(
if (REG(DOMAIN1_PG_CONFIG) == 0)
return;
- switch (dpp_inst) {
+ switch (dpp->inst) {
case 0: /* DPP0 */
REG_UPDATE(DOMAIN1_PG_CONFIG,
DOMAIN1_POWER_GATE, power_gate);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h
index e33237fb74be..a631aa7c56d5 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.h
@@ -49,6 +49,7 @@ bool dcn314_is_dpp_root_clock_control_supported(bool root_clock_optimization_dpp
void dcn314_disable_link_output(struct dc_link *link, const struct link_resource *link_res, enum signal_type signal);
-void dcn314_dpp_pg_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool power_on);
+void dcn314_dpp_pg_control(struct dce_hwseq *hws, struct dpp *dpp, bool power_on,
+ bool disable_power_gate);
#endif /* __DC_HWSS_DCN314_H__ */
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 9135b01fe388..b78041ba94e9 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
@@ -164,15 +164,16 @@ void dcn32_enable_power_gating_plane(
REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0);
}
-void dcn32_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on)
+void dcn32_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on,
+ bool disable_power_gate, bool ignore_pg)
{
uint32_t power_gate = power_on ? 0 : 1;
uint32_t pwr_status = power_on ? 0 : 2;
- if (hws->ctx->dc->debug.disable_hubp_power_gate)
+ if (disable_power_gate)
return;
- if (hws->ctx->dc->debug.ignore_pg)
+ if (ignore_pg)
return;
if (REG(DOMAIN0_PG_CONFIG) == 0)
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h
index 734727015b3f..3db34bff3847 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.h
@@ -39,7 +39,8 @@ void dcn32_enable_power_gating_plane(
struct dce_hwseq *hws,
bool enable);
-void dcn32_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on);
+void dcn32_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on,
+ bool disable_power_gate, bool ignore_pg);
bool dcn32_apply_idle_power_optimizations(struct dc *dc, bool enable);
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 bac9c5e18e6a..c39189deb244 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
@@ -3191,10 +3191,11 @@ void dcn401_plane_atomic_power_down(struct dc *dc,
}
if (hws->funcs.dpp_pg_control)
- hws->funcs.dpp_pg_control(hws, dpp->inst, false);
+ hws->funcs.dpp_pg_control(hws, dpp, false, dc->debug.disable_dpp_power_gate);
if (hws->funcs.hubp_pg_control)
- hws->funcs.hubp_pg_control(hws, hubp->inst, false);
+ hws->funcs.hubp_pg_control(hws, hubp->inst, false,
+ dc->debug.disable_hubp_power_gate, dc->debug.ignore_pg);
hubp->funcs->hubp_reset(hubp);
dpp->funcs->dpp_reset(dpp);
@@ -3261,26 +3262,41 @@ void dcn401_update_cursor_offload_pipe(struct dmub_srv *dmub, uint32_t stream_id
cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe_idx);
}
+bool dcn401_pg_needs_ip_request(const struct dc *dc)
+{
+ const struct dce_hwseq *hws = dc->hwseq;
+
+ /* The status step and the pair need both hooks. */
+ ASSERT(!hws->funcs.dc_ip_request_cntl == !hws->funcs.dc_ip_request_status);
+ if (!hws->funcs.dc_ip_request_cntl || !hws->funcs.dc_ip_request_status)
+ return false;
+
+ /* dcn32_hubp_pg_control, bound on DCN401, also honors ignore_pg. */
+ return (hws->funcs.dpp_pg_control && !dc->debug.disable_dpp_power_gate) ||
+ (hws->funcs.hubp_pg_control && !dc->debug.disable_hubp_power_gate &&
+ !dc->debug.ignore_pg);
+}
+
void dcn401_plane_atomic_power_down_sequence(struct dc *dc,
struct dpp *dpp,
struct hubp *hubp,
struct block_sequence_state *seq_state)
{
struct dce_hwseq *hws = dc->hwseq;
- uint32_t org_ip_request_cntl = 0;
+ bool ip_request = false;
- /* Check and set DC_IP_REQUEST_CNTL if needed */
- if (REG(DC_IP_REQUEST_CNTL)) {
- REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
- if (org_ip_request_cntl == 0)
- hwss_add_dc_ip_request_cntl(seq_state, dc, true);
- }
+ if (dcn401_pg_needs_ip_request(dc))
+ ip_request = hwss_add_dc_ip_request_status(seq_state, hws);
+
+ if (ip_request)
+ hwss_add_dc_ip_request_cntl(seq_state, dc, true);
/* DPP power gating control */
- hwss_add_dpp_pg_control(seq_state, hws, dpp->inst, false);
+ hwss_add_dpp_pg_control(seq_state, hws, dpp, false, dc->debug.disable_dpp_power_gate);
/* HUBP power gating control */
- hwss_add_hubp_pg_control(seq_state, hws, hubp->inst, false);
+ hwss_add_hubp_pg_control(seq_state, hws, hubp->inst, false,
+ dc->debug.disable_hubp_power_gate, dc->debug.ignore_pg);
/* HUBP reset */
hwss_add_hubp_reset(seq_state, hubp);
@@ -3288,8 +3304,7 @@ void dcn401_plane_atomic_power_down_sequence(struct dc *dc,
/* DPP reset */
hwss_add_dpp_reset(seq_state, dpp);
- /* Restore DC_IP_REQUEST_CNTL if it was originally 0 */
- if (org_ip_request_cntl == 0 && REG(DC_IP_REQUEST_CNTL))
+ if (ip_request)
hwss_add_dc_ip_request_cntl(seq_state, dc, false);
DC_LOG_DEBUG("Power gated front end %d\n", hubp->inst);
@@ -3672,12 +3687,39 @@ void dcn401_post_unlock_reset_opp_sequence(
}
}
+/*
+ * Called in pairs after dcn401_dc_ip_request_status(). Writes nothing when
+ * IP_REQUEST_EN was already 1; the disable call clears that status.
+ */
void dcn401_dc_ip_request_cntl(struct dce_hwseq *hws, bool enable)
{
+ bool already_enabled;
+
if (!hws || !hws->ctx || !hws->regs || !hws->shifts || !hws->masks)
return;
+
+ already_enabled = hws->ip_request_already_enabled;
+
+ if (!enable)
+ hws->ip_request_already_enabled = false;
+
+ if (already_enabled || !REG(DC_IP_REQUEST_CNTL))
+ return;
+
+ REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, enable ? 1 : 0);
+}
+
+void dcn401_dc_ip_request_status(struct dce_hwseq *hws)
+{
+ uint32_t ip_request_en = 0;
+
+ if (!hws || !hws->ctx || !hws->regs || !hws->shifts || !hws->masks)
+ return;
+
if (REG(DC_IP_REQUEST_CNTL))
- REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, enable ? 1 : 0);
+ REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &ip_request_en);
+
+ hws->ip_request_already_enabled = ip_request_en != 0;
}
void dcn401_enable_plane_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
@@ -3686,32 +3728,32 @@ void dcn401_enable_plane_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
{
(void)context;
struct dce_hwseq *hws = dc->hwseq;
- uint32_t org_ip_request_cntl = 0;
+ bool ip_request = false;
if (!pipe_ctx->plane_res.dpp || !pipe_ctx->plane_res.hubp || !pipe_ctx->stream_res.opp)
return;
- if (REG(DC_IP_REQUEST_CNTL))
- REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
-
/* Step 1: DPP root clock control - enable clock */
if (hws->funcs.dpp_root_clock_control)
hwss_add_dpp_root_clock_control(seq_state, hws, pipe_ctx->plane_res.dpp->inst, true);
/* Step 2: Enable DC IP request (if needed) */
- if (hws->funcs.dc_ip_request_cntl)
+ if (dcn401_pg_needs_ip_request(dc))
+ ip_request = hwss_add_dc_ip_request_status(seq_state, hws);
+
+ if (ip_request)
hwss_add_dc_ip_request_cntl(seq_state, dc, true);
/* Step 3: DPP power gating control - power on */
- if (REG(DC_IP_REQUEST_CNTL) && hws->funcs.dpp_pg_control)
- hwss_add_dpp_pg_control(seq_state, hws, pipe_ctx->plane_res.dpp->inst, true);
+ hwss_add_dpp_pg_control(seq_state, hws, pipe_ctx->plane_res.dpp, true,
+ dc->debug.disable_dpp_power_gate);
/* Step 4: HUBP power gating control - power on */
- if (REG(DC_IP_REQUEST_CNTL) && hws->funcs.hubp_pg_control)
- hwss_add_hubp_pg_control(seq_state, hws, pipe_ctx->plane_res.hubp->inst, true);
+ hwss_add_hubp_pg_control(seq_state, hws, pipe_ctx->plane_res.hubp->inst, true,
+ dc->debug.disable_hubp_power_gate, dc->debug.ignore_pg);
- /* Step 5: Disable DC IP request (restore state) */
- if (org_ip_request_cntl == 0 && hws->funcs.dc_ip_request_cntl)
+ /* Step 5: Disable DC IP request */
+ if (ip_request)
hwss_add_dc_ip_request_cntl(seq_state, dc, false);
/* Step 6: HUBP clock control - enable DCFCLK */
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 fef0dd59a81f..c95d929b453c 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
@@ -191,6 +191,8 @@ void dcn401_post_unlock_reset_opp_sequence(
struct block_sequence_state *seq_state);
void dcn401_dc_ip_request_cntl(struct dce_hwseq *hws, bool enable);
+void dcn401_dc_ip_request_status(struct dce_hwseq *hws);
+bool dcn401_pg_needs_ip_request(const struct dc *dc);
void dcn401_enable_plane_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
struct dc_state *context,
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 1e79b4945801..09737dddce3e 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
@@ -176,6 +176,8 @@ static const struct hwseq_private_funcs dcn401_private_funcs = {
.perform_3dlut_wa_unlock = dcn401_perform_3dlut_wa_unlock,
.program_pipe_sequence = dcn401_program_pipe_sequence,
.dc_ip_request_cntl = dcn401_dc_ip_request_cntl,
+ .dc_ip_request_status = dcn401_dc_ip_request_status,
+ .pg_needs_ip_request = dcn401_pg_needs_ip_request,
.wait_for_pipe_update_if_needed = dcn10_wait_for_pipe_update_if_needed,
.set_wait_for_update_needed_for_pipe = dcn10_set_wait_for_update_needed_for_pipe,
};
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 c4f3ec532572..bfe9afb434af 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -1005,14 +1005,17 @@ struct disable_phantom_crtc_params {
struct dpp_pg_control_params {
struct dce_hwseq *hws;
- unsigned int dpp_inst;
+ struct dpp *dpp;
bool power_on;
+ bool disable_power_gate;
};
struct hubp_pg_control_params {
struct dce_hwseq *hws;
unsigned int hubp_inst;
bool power_on;
+ bool disable_power_gate;
+ bool ignore_pg;
};
struct hubp_reset_params {
@@ -1030,6 +1033,10 @@ struct dpp_root_clock_control_params {
bool root_clock_optimization_dpp;
};
+struct dc_ip_request_status_params {
+ struct dce_hwseq *hws;
+};
+
struct dc_ip_request_cntl_params {
struct dce_hwseq *hws;
bool enable;
@@ -1461,6 +1468,7 @@ union block_sequence_params {
struct hubp_reset_params hubp_reset_params;
struct dpp_reset_params dpp_reset_params;
struct dpp_root_clock_control_params dpp_root_clock_control_params;
+ struct dc_ip_request_status_params dc_ip_request_status_params;
struct dc_ip_request_cntl_params dc_ip_request_cntl_params;
struct dsc_pg_status_params dsc_pg_status_params;
struct dsc_wait_disconnect_pending_clear_params dsc_wait_disconnect_pending_clear_params;
@@ -1699,6 +1707,7 @@ enum block_sequence_func {
HUBP_RESET,
DPP_RESET,
DPP_ROOT_CLOCK_CONTROL,
+ DC_IP_REQUEST_STATUS,
DC_IP_REQUEST_CNTL,
DCCG_UPDATE_DPP_DTO,
HUBP_VTG_SEL,
@@ -2547,6 +2556,8 @@ void hwss_dpp_reset(union block_sequence_params *params);
void hwss_dpp_root_clock_control(union block_sequence_params *params);
+void hwss_dc_ip_request_status(union block_sequence_params *params);
+
void hwss_dc_ip_request_cntl(union block_sequence_params *params);
void hwss_dccg_update_dpp_dto(union block_sequence_params *params);
@@ -2786,6 +2797,9 @@ void hwss_add_dpp_program_cm_hist(struct block_sequence_state *seq_state,
struct cm_hist_control cm_hist_control,
enum dc_color_space color_space);
+bool hwss_add_dc_ip_request_status(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws);
+
void hwss_add_dc_ip_request_cntl(struct block_sequence_state *seq_state,
struct dc *dc,
bool enable);
@@ -2938,13 +2952,16 @@ void hwss_add_dpp_root_clock_control(struct block_sequence_state *seq_state,
void hwss_add_dpp_pg_control(struct block_sequence_state *seq_state,
struct dce_hwseq *hws,
- unsigned int dpp_inst,
- bool power_on);
+ struct dpp *dpp,
+ bool power_on,
+ bool disable_power_gate);
void hwss_add_hubp_pg_control(struct block_sequence_state *seq_state,
struct dce_hwseq *hws,
unsigned int hubp_inst,
- bool power_on);
+ bool power_on,
+ bool disable_power_gate,
+ bool ignore_pg);
void hwss_add_hubp_set_blank(struct block_sequence_state *seq_state,
struct hubp *hubp,
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 91a84fb50b59..e4001656a330 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
@@ -150,11 +150,14 @@ struct hwseq_private_funcs {
unsigned int phy_inst,
bool clock_on);
void (*dpp_pg_control)(struct dce_hwseq *hws,
- unsigned int dpp_inst,
- bool power_on);
+ struct dpp *dpp,
+ bool power_on,
+ bool disable_power_gate);
void (*hubp_pg_control)(struct dce_hwseq *hws,
unsigned int hubp_inst,
- bool power_on);
+ bool power_on,
+ bool disable_power_gate,
+ bool ignore_pg);
void (*dsc_pg_control)(struct dce_hwseq *hws,
unsigned int dsc_inst,
bool power_on);
@@ -237,6 +240,8 @@ struct hwseq_private_funcs {
void (*wait_for_pipe_update_if_needed)(struct dc *dc, struct pipe_ctx *pipe_ctx, bool is_surface_update_only);
void (*set_wait_for_update_needed_for_pipe)(struct dc *dc, struct pipe_ctx *pipe_ctx);
void (*dc_ip_request_cntl)(struct dce_hwseq *hws, bool enable);
+ void (*dc_ip_request_status)(struct dce_hwseq *hws);
+ bool (*pg_needs_ip_request)(const struct dc *dc);
void (*program_cm_hist)(struct dc *dc,
struct pipe_ctx *pipe_ctx,
const struct dc_plane_state *plane_state);
@@ -254,6 +259,9 @@ struct dce_hwseq {
struct hwseq_wa_state wa_state;
struct hwseq_private_funcs funcs;
+ /* Set by dc_ip_request_status(), cleared by the closing dc_ip_request_cntl(). */
+ bool ip_request_already_enabled;
+
PHYSICAL_ADDRESS_LOC fb_base;
PHYSICAL_ADDRESS_LOC fb_top;
PHYSICAL_ADDRESS_LOC fb_offset;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 68/74] drm/amd/display: Seed boot clocks to avoid lowering pre-OS display clocks
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (66 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 67/74] drm/amd/display: Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 69/74] drm/amd/display: Account for split pipe overlap in alt channel copy size Wayne Lin
` (6 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Jing Zhou, Nicholas Kazlauskas,
Charlene Liu, Mario Limonciello, Alex Deucher, stable, Wayne Lin
From: Jing Zhou <Jing.Zhou@amd.com>
[WHY]
USB4 DPIA HPD can arrive early in boot, before the first stream commit.
Its link detection runs prepare_phy_clocks(), which goes through
clk_mgr_exit_optimized_pwr_state() and clk_mgr_optimize_pwr_state()
and calls update_clocks(dc->current_state).
At that point both dc->current_state->bw_ctx.bw.dcn.clk and
clk_mgr->clks are still zero, because init_clocks() memsets them. On the
safe_to_lower path, dcn42_update_clocks() clamps the requested DPPCLK to
the 100MHz floor and programs it. This drops DPPCLK below what the
GOP-lit eDP pipe needs, and the panel flashes white.
[HOW]
1. In dcn42_init_clocks() and dcn42b_init_clocks(), seed clk_mgr->clks
dispclk/dppclk from the boot snapshot(10kHz units, x10).
2. Add a DC core helper, sync_current_state_clocks_from_clk_mgr().
After every hwss.init_hw() call (legacy_hardware_init, D0 entry in
dc_set_power_state, and dc_reinitialize_hardware), it copies
clk_mgr->clks into dc->current_state->bw_ctx.bw.dcn.clk for DCN.
A later update_clocks() before the first commit then sees the real
pre-OS clocks and does not lower them. Raising clocks is still
allowed and does not cause a flash.
Cc: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Cc: Charlene Liu <charlene.liu@amd.com>
Cc: Mario Limonciello <mario.limonciello@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: stable@vger.kernel.org
Signed-off-by: Jing Zhou <Jing.Zhou@amd.com>
Signed-off-by: Wayne Lin <Wayne.Lin@amd.com>
---
.../amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c | 3 +++
.../display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c | 4 ++++
drivers/gpu/drm/amd/display/dc/core/dc.c | 16 +++++++++++++++-
3 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
index b1fbadd0d885..054eab1e0cb9 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c
@@ -643,6 +643,9 @@ DC_LOGGER_INIT(clk_mgr_base->ctx->logger);
DC_LOG_SMU("dp_dto_source_clock %d, dprefclk %d\n", clk_mgr_base->dp_dto_source_clock_in_khz, clk_mgr_base->dprefclk_khz);
dcn42_dump_clk_registers(&clk_mgr_base->boot_snapshot, clk_mgr);
+ /* Snapshot is in 10kHz units; seed clks so update_clocks() does not lower the pre-OS clocks */
+ clk_mgr_base->clks.dispclk_khz = clk_mgr_base->boot_snapshot.dispclk * 10;
+ clk_mgr_base->clks.dppclk_khz = clk_mgr_base->boot_snapshot.dppclk * 10;
clk_mgr_base->clks.ref_dtbclk_khz = clk_mgr_base->boot_snapshot.dtbclk * 10;
clk_mgr_base->clks.dtbclk_en = clk_mgr_base->boot_snapshot.dtbclk > 59000;
}
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
index 57291c10c917..5e8a65f1ee68 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c
@@ -251,6 +251,10 @@ DC_LOGGER_INIT(clk_mgr_base->ctx->logger);
DC_LOG_SMU("dp_dto_source_clock %d, dprefclk %d\n", clk_mgr_base->dp_dto_source_clock_in_khz, clk_mgr_base->dprefclk_khz);
dcn42b_dump_clk_registers(&clk_mgr_base->boot_snapshot, clk_mgr);
+
+ /* Snapshot is in 10kHz units; seed clks so update_clocks() does not lower the pre-OS clocks */
+ clk_mgr_base->clks.dispclk_khz = clk_mgr_base->boot_snapshot.dispclk * 10;
+ clk_mgr_base->clks.dppclk_khz = clk_mgr_base->boot_snapshot.dppclk * 10;
}
static struct clk_bw_params dcn42b_bw_params = {
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 8a02bcbb9649..463a3fc2d8c2 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -1728,12 +1728,25 @@ static void detect_edp_presence(struct dc *dc)
}
}
+static void sync_current_state_clocks_from_clk_mgr(struct dc *dc)
+{
+ if (!dc->current_state || !dc->clk_mgr)
+ return;
+
+ if (!(dc->ctx->dce_version == DCN_VERSION_4_2 || dc->ctx->dce_version == DCN_VERSION_4_2B))
+ return;
+
+ dc->current_state->bw_ctx.bw.dcn.clk = dc->clk_mgr->clks;
+}
+
void legacy_hardware_init(struct dc *dc)
{
detect_edp_presence(dc);
- if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW)
+ if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW) {
dc->hwss.init_hw(dc);
+ sync_current_state_clocks_from_clk_mgr(dc);
+ }
dc_dmub_srv_notify_fw_dc_power_state(dc->ctx->dmub_srv, DC_ACPI_CM_POWER_STATE_D0);
}
@@ -6435,6 +6448,7 @@ void dc_set_power_state(struct dc *dc, enum dc_acpi_cm_power_state power_state)
dc_dmub_srv_notify_fw_dc_power_state(dc->ctx->dmub_srv, power_state);
dc->hwss.init_hw(dc);
+ sync_current_state_clocks_from_clk_mgr(dc);
invalidate_drr_adjust(dc);
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 69/74] drm/amd/display: Account for split pipe overlap in alt channel copy size
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (67 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 68/74] drm/amd/display: Seed boot clocks to avoid lowering pre-OS display clocks Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 70/74] drm/amd/display: Get OPP heads and DPP pipes by stream in dc_resource Wayne Lin
` (5 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Ashwin Santhosh, Dillon Varone
From: Ashwin Santhosh <Ashwin.Santhosh@amd.com>
[Why]
When a plane is split across pipes (ODM or MPC), each pipe's viewport
extends past its even share: scaler taps at the split, the leftover of
an uneven split, and the 4:2:x left edge pixel under ODM. DML sized the
per-pipe swath width and the alt channel copy region without these, so
with 8K VSR on a 4K240 panel (ODM 2:1, 6 taps) each pipe's copy was one
tile column too small and overran the next pipe's region, showing as
blocks of corruption during MCLK switches.
[How]
Add calculate_per_pipe_swath_width to pad the ODM and MPC swath widths
by these extra pixels, and size the alt channel copy from the swath
width for all pipe configs.
Cc: Dillon Varone <dillon.varone@amd.com>
Signed-off-by: Ashwin Santhosh <Ashwin.Santhosh@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../dml2_core/dml2_core_dcn5_calcs_dchub.c | 67 ++++++++++++++-----
.../dml2_core/dml2_core_dcn6_calcs_dchub.c | 26 ++-----
2 files changed, 57 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c
index 569ab02ea080..9c1f66c18a19 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_calcs_dchub.c
@@ -242,6 +242,51 @@ void dcn5_calculate_byte_per_pixel_and_block_sizes(
DML_LOG_VERBOSE("DML::%s: MacroTileHeightC = %u\n", __func__, *MacroTileHeightC);
}
+/*
+ * calculate_per_pipe_swath_width: Swath width of one pipe, accounting for the plane being split across pipes
+ * (ODM or MPC combine)
+ *
+ * Each pipe's viewport extends past its even share of the split:
+ * - max_recout_pixels: the widest recout (screen / dst pixels) any pipe of the split can get
+ * - h_taps: the scaler taps sampling past the split, already counted in surface (src) pixels
+ * h_ratio converts the recout into src pixels, since the swath is fetched from the surface.
+ */
+static unsigned int calculate_per_pipe_swath_width(const struct dml2_plane_parameters *plane,
+ const struct dml2_stream_parameters *stream, enum dml2_odm_mode odm_mode, unsigned int dpp_per_surface,
+ unsigned int single_dpp_swath_width, bool chroma)
+{
+ double h_ratio = chroma ? plane->composition.scaler_info.plane1.h_ratio : plane->composition.scaler_info.plane0.h_ratio;
+ unsigned int h_taps = chroma ? plane->composition.scaler_info.plane1.h_taps : plane->composition.scaler_info.plane0.h_taps;
+ unsigned int odm_factor;
+ double max_recout_pixels;
+ unsigned int swath_width;
+
+ if (odm_mode == dml2_odm_mode_combine_4to1)
+ odm_factor = 4;
+ else if (odm_mode == dml2_odm_mode_combine_3to1)
+ odm_factor = 3;
+ else if (odm_mode == dml2_odm_mode_combine_2to1)
+ odm_factor = 2;
+ else
+ odm_factor = 1;
+
+ if (odm_factor > 1) {
+ // h_active may not split evenly across ODM pipes. The last pipe takes the leftover, which is at most odm - 1 pixels
+ max_recout_pixels = (double)stream->timing.h_active / odm_factor + (odm_factor - 1);
+ // 4:2:0 / 4:2:2 outputs add a left edge pixel for the chroma subsampling filter
+ if (stream->output.output_format == dml2_420 || stream->output.output_format == dml2_s422 || stream->output.output_format == dml2_n422)
+ max_recout_pixels += 1;
+ swath_width = (unsigned int)math_ceil(max_recout_pixels * h_ratio) + h_taps;
+ } else if (dpp_per_surface == 2) {
+ // MPC splits the plane in half, with the leftover pixel of an odd width going to one pipe
+ swath_width = (single_dpp_swath_width + 1) / 2 + h_taps;
+ } else {
+ swath_width = single_dpp_swath_width;
+ }
+
+ return (unsigned int)math_min2((double)single_dpp_swath_width, (double)swath_width);
+}
+
void dcn5_calculate_swath_width(
const struct dml2_display_cfg *display_cfg,
bool ForceSingleDPP,
@@ -273,7 +318,6 @@ void dcn5_calculate_swath_width(
{
(void)BytePerPixY;
enum dml2_odm_mode MainSurfaceODMMode;
- double odm_hactive_factor = 1.0;
unsigned int req_width_horz_y;
unsigned int req_width_horz_c;
unsigned int surface_width_ub_l;
@@ -285,6 +329,9 @@ void dcn5_calculate_swath_width(
DML_LOG_VERBOSE("DML::%s: NumberOfActiveSurfaces = %u\n", __func__, NumberOfActiveSurfaces);
for (unsigned int k = 0; k < NumberOfActiveSurfaces; ++k) {
+ const struct dml2_plane_parameters *plane = &display_cfg->plane_descriptors[k];
+ const struct dml2_stream_parameters *stream = &display_cfg->stream_descriptors[plane->stream_index];
+
if (!dml2_core_utils_is_vertical_rotation(display_cfg->plane_descriptors[k].composition.rotation_angle)) {
SwathWidthSingleDPPY[k] = (unsigned int)display_cfg->plane_descriptors[k].composition.viewport.plane0.width;
} else {
@@ -300,20 +347,7 @@ void dcn5_calculate_swath_width(
if (ForceSingleDPP) {
SwathWidthY[k] = SwathWidthSingleDPPY[k];
} else {
- if (MainSurfaceODMMode == dml2_odm_mode_combine_4to1)
- odm_hactive_factor = 4.0;
- else if (MainSurfaceODMMode == dml2_odm_mode_combine_3to1)
- odm_hactive_factor = 3.0;
- else if (MainSurfaceODMMode == dml2_odm_mode_combine_2to1)
- odm_hactive_factor = 2.0;
-
- if (MainSurfaceODMMode == dml2_odm_mode_combine_4to1 || MainSurfaceODMMode == dml2_odm_mode_combine_3to1 || MainSurfaceODMMode == dml2_odm_mode_combine_2to1) {
- SwathWidthY[k] = (unsigned int)(math_min2((double)SwathWidthSingleDPPY[k], math_round((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_active / odm_hactive_factor * display_cfg->plane_descriptors[k].composition.scaler_info.plane0.h_ratio)));
- } else if (DPPPerSurface[k] == 2) {
- SwathWidthY[k] = SwathWidthSingleDPPY[k] / 2;
- } else {
- SwathWidthY[k] = SwathWidthSingleDPPY[k];
- }
+ SwathWidthY[k] = calculate_per_pipe_swath_width(plane, stream, MainSurfaceODMMode, DPPPerSurface[k], SwathWidthSingleDPPY[k], false);
}
DML_LOG_VERBOSE("DML::%s: k=%u HActive=%lu\n", __func__, k, display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.h_active);
@@ -323,8 +357,9 @@ void dcn5_calculate_swath_width(
DML_LOG_VERBOSE("DML::%s: k=%u SwathWidthY=%u\n", __func__, k, SwathWidthY[k]);
if (dml2_core_utils_is_420(display_cfg->plane_descriptors[k].pixel_format) || dml2_core_utils_is_422_planar(display_cfg->plane_descriptors[k].pixel_format)) {
- SwathWidthC[k] = SwathWidthY[k] / 2;
SwathWidthSingleDPPC[k] = SwathWidthSingleDPPY[k] / 2;
+ // Chroma taps apply at the chroma ratio, so the padded luma width can't simply be halved
+ SwathWidthC[k] = calculate_per_pipe_swath_width(plane, stream, MainSurfaceODMMode, DPPPerSurface[k], SwathWidthSingleDPPC[k], true);
} else {
SwathWidthC[k] = SwathWidthY[k];
SwathWidthSingleDPPC[k] = SwathWidthSingleDPPY[k];
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c
index a61a1037bb73..2ee50f8cf2f5 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn6_calcs_dchub.c
@@ -227,43 +227,29 @@ static unsigned int calculate_tile_aligned_size(unsigned int copy_width, unsigne
* *******************************************************************************************************************************************************
* calculate_max_mem_size_per_plane_per_dpp: Calculate (loose) upper bound for total number of bytes reserved in memory for the copy given number of swaths
*
- * - The copy width / height is the min of the vp_width/height and maximum number of pixels that can fit across an ODM slice
- * - This is to account for recout positions that cross the ODM seam but are "mostly" within the same ODM slice
+ * - The copy width / height is the min of the vp_width/height and the per-pipe swath width
+ * - This is to account for recout positions that cross the ODM / MPC split but are "mostly" within the same pipe
*
* *******************************************************************************************************************************************************
*/
static unsigned int calculate_max_mem_size_per_plane_per_dpp(const struct dml2_core_calcs_calculate_alternate_params *p, unsigned int plane_idx, unsigned int copy_swaths, bool chroma)
{
bool vertical_access = p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_90 || p->display_cfg->plane_descriptors[plane_idx].composition.rotation_angle == dml2_rotation_270;
- unsigned int h_active = p->display_cfg->stream_descriptors[p->display_cfg->plane_descriptors[plane_idx].stream_index].timing.h_active;
- double h_ratio = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.scaler_info.plane1.h_ratio : p->display_cfg->plane_descriptors[plane_idx].composition.scaler_info.plane0.h_ratio;
unsigned int copy_src_lines = copy_swaths * (chroma ? p->SwathHeightC[plane_idx] : p->SwathHeightY[plane_idx]);
unsigned int vp_width = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.width : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.width;
unsigned int vp_height = chroma ? p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane1.height : p->display_cfg->plane_descriptors[plane_idx].composition.viewport.plane0.height;
unsigned int tile_width = chroma ? p->MacroTileWidthC[plane_idx] : p->MacroTileWidthY[plane_idx];
unsigned int tile_height = chroma ? p->MacroTileHeightC[plane_idx] : p->MacroTileHeightY[plane_idx];
unsigned int byte_per_pixel = chroma ? p->BytePerPixelC[plane_idx] : p->BytePerPixelY[plane_idx];
+ unsigned int swath_width = chroma ? p->SwathWidthC[plane_idx] : p->SwathWidthY[plane_idx];
unsigned int copy_width;
unsigned int copy_height;
- unsigned int odm_combine_factor;
- double odm_slice_pixels;
-
- if (p->ODMMode[plane_idx] == dml2_odm_mode_combine_4to1)
- odm_combine_factor = 4;
- else if (p->ODMMode[plane_idx] == dml2_odm_mode_combine_3to1)
- odm_combine_factor = 3;
- else if (p->ODMMode[plane_idx] == dml2_odm_mode_combine_2to1)
- odm_combine_factor = 2;
- else
- odm_combine_factor = 1;
-
- odm_slice_pixels = (double)h_active / odm_combine_factor * h_ratio + (odm_combine_factor == 3 ? 2 : 0);
/* Unaligned copy region dimensions (in pixels / lines) prior to tile alignment.
- * No need to divide vp width/height by NoOfDPP. The entire viewport could be within a single ODM slice if less than the odm slice pixels
+ * No need to divide vp width/height by NoOfDPP. The entire viewport could be within a single pipe if less than the swath width
* (e.g., multi-plane scenario with ODM combine). */
- copy_width = vertical_access ? copy_src_lines : (unsigned int)math_ceil(math_min2(odm_slice_pixels, vp_width));
- copy_height = vertical_access ? (unsigned int)math_ceil(math_min2(odm_slice_pixels, vp_height)) : copy_src_lines;
+ copy_width = vertical_access ? copy_src_lines : (unsigned int)math_min2((double)swath_width, (double)vp_width);
+ copy_height = vertical_access ? (unsigned int)math_min2((double)swath_width, (double)vp_height) : copy_src_lines;
return calculate_tile_aligned_size(copy_width, copy_height, tile_width, tile_height, byte_per_pixel);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 70/74] drm/amd/display: Get OPP heads and DPP pipes by stream in dc_resource
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (68 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 69/74] drm/amd/display: Account for split pipe overlap in alt channel copy size Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 71/74] drm/amd/display: Program ODM 2:1 and 3:1 on one stream in DC2 Wayne Lin
` (4 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
The DC2 builders on dcn401 need every OPP head and DPP pipe of a stream,
across all of its ODM slices. Core2 walked a stream's DPP pipes with
its own helper. Pipe traversal belongs to dc_resource.
[How]
Add resource_get_opp_heads_for_stream and
resource_get_dpp_pipes_for_stream. Both find the OTG master of the
stream; the DPP pipes come slice by slice, in OPP head order. Core2
walks a stream's DPP pipes through dc_resource and drops
core2_next_dpp_pipe.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../gpu/drm/amd/display/dc/core/dc_resource.c | 32 +++++++++++++++++++
.../gpu/drm/amd/display/dc/core2/dc_core2.c | 27 +++++++++++-----
drivers/gpu/drm/amd/display/dc/inc/resource.h | 18 +++++++++++
3 files changed, 69 insertions(+), 8 deletions(-)
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 2240afde4089..7f770a43e1fe 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -2174,6 +2174,38 @@ int resource_get_dpp_pipes_for_opp_head(const struct pipe_ctx *opp_head,
return i;
}
+int resource_get_opp_heads_for_stream(const struct dc_stream_state *stream,
+ struct resource_context *res_ctx,
+ struct pipe_ctx *opp_heads[MAX_PIPES])
+{
+ struct pipe_ctx *otg_master = resource_get_otg_master_for_stream(
+ res_ctx, stream);
+
+ if (!otg_master)
+ return 0;
+ return resource_get_opp_heads_for_otg_master(otg_master, res_ctx,
+ opp_heads);
+}
+
+int resource_get_dpp_pipes_for_stream(const struct dc_stream_state *stream,
+ struct resource_context *res_ctx,
+ struct pipe_ctx *dpp_pipes[MAX_PIPES])
+{
+ struct pipe_ctx *opp_heads[MAX_PIPES];
+ struct pipe_ctx *slice_pipes[MAX_PIPES];
+ int opp_head_count = resource_get_opp_heads_for_stream(stream, res_ctx,
+ opp_heads);
+ int i, j, slice_count, count = 0;
+
+ for (i = 0; i < opp_head_count; i++) {
+ slice_count = resource_get_dpp_pipes_for_opp_head(opp_heads[i],
+ res_ctx, slice_pipes);
+ for (j = 0; j < slice_count && count < MAX_PIPES; j++)
+ dpp_pipes[count++] = slice_pipes[j];
+ }
+ return count;
+}
+
int resource_get_dpp_pipes_for_plane(const struct dc_plane_state *plane,
struct resource_context *res_ctx,
struct pipe_ctx *dpp_pipes[MAX_PIPES])
diff --git a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
index 33c52ef9b728..0e92c2e7a4e6 100644
--- a/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
+++ b/drivers/gpu/drm/amd/display/dc/core2/dc_core2.c
@@ -61,7 +61,8 @@ static struct pipe_ctx *core2_otg_master(struct dc_state *state)
return NULL;
for (i = 0; i < MAX_PIPES; i++) {
if (state->res_ctx.pipe_ctx[i].stream == state->streams[0] &&
- !state->res_ctx.pipe_ctx[i].top_pipe)
+ !state->res_ctx.pipe_ctx[i].top_pipe &&
+ !state->res_ctx.pipe_ctx[i].prev_odm_pipe)
return &state->res_ctx.pipe_ctx[i];
}
return NULL;
@@ -160,10 +161,12 @@ static void core2_commit_enable_done(struct pipe_ctx *pipe)
static void core2_commit_disable_done(struct dc *dc, struct pipe_ctx *pipe)
{
struct dc_link *link = pipe->stream->link;
- struct pipe_ctx *dpp_pipe;
+ struct pipe_ctx *dpp_pipes[MAX_PIPES];
+ int i, n = resource_get_dpp_pipes_for_stream(pipe->stream,
+ &dc->current_state->res_ctx, dpp_pipes);
- for (dpp_pipe = pipe; dpp_pipe; dpp_pipe = dpp_pipe->bottom_pipe)
- dpp_pipe->plane_res.hubp->power_gated = true;
+ for (i = 0; i < n; i++)
+ dpp_pipes[i]->plane_res.hubp->power_gated = true;
link->link_status.link_active = false;
link->phy_state.symclk_state = SYMCLK_OFF_TX_OFF;
pipe->stream->timing_adjust_pending = true;
@@ -314,11 +317,15 @@ static bool core2_update_state_supported(
*/
static void core2_update_done(struct dc *dc, struct dc_state *context)
{
+ struct pipe_ctx *dpp_pipes[MAX_PIPES];
struct pipe_ctx *pipe;
struct dc_plane_state *plane;
struct hubp *hubp;
+ int i, n = context->stream_count ? resource_get_dpp_pipes_for_stream(
+ context->streams[0], &context->res_ctx, dpp_pipes) : 0;
- for (pipe = core2_otg_master(context); pipe; pipe = pipe->bottom_pipe) {
+ for (i = 0; i < n; i++) {
+ pipe = dpp_pipes[i];
if (!pipe->plane_state || !pipe->plane_res.hubp ||
!pipe->stream_res.opp)
return;
@@ -341,6 +348,8 @@ static void core2_update_state_execute(
struct dc *dc = scratch->dc;
struct dc_state *context = scratch->new_context;
struct pipe_ctx *pipe = core2_otg_master(context);
+ struct pipe_ctx *dpp_pipes[MAX_PIPES];
+ int i, n;
/*
* The values DC1 computes into the context before it programs the
@@ -350,9 +359,11 @@ static void core2_update_state_execute(
return;
if (dc->res_pool->funcs->prepare_mcache_programming)
dc->res_pool->funcs->prepare_mcache_programming(dc, context);
- for (; pipe; pipe = pipe->bottom_pipe)
- pipe->plane_res.scl_data.lb_params.alpha_en =
- pipe->plane_state->per_pixel_alpha;
+ n = resource_get_dpp_pipes_for_stream(pipe->stream, &context->res_ctx,
+ dpp_pipes);
+ for (i = 0; i < n; i++)
+ dpp_pipes[i]->plane_res.scl_data.lb_params.alpha_en =
+ dpp_pipes[i]->plane_state->per_pixel_alpha;
if (core2_build_and_run(dc, DCN_DECL_SEAMLESS_UPDATE, context) != DC_OK) {
ASSERT(false);
diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h
index fbd874476aaf..5dc6173b3948 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/resource.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h
@@ -427,6 +427,24 @@ int resource_get_dpp_pipes_for_opp_head(const struct pipe_ctx *opp_head,
struct resource_context *res_ctx,
struct pipe_ctx *dpp_pipes[MAX_PIPES]);
+/*
+ * Get an array of OPP heads in opp_heads ordered with index low to high for
+ * the OTG master of stream in res_ctx.
+ * return - number of OPP heads in the array, 0 if stream has no OTG master.
+ */
+int resource_get_opp_heads_for_stream(const struct dc_stream_state *stream,
+ struct resource_context *res_ctx,
+ struct pipe_ctx *opp_heads[MAX_PIPES]);
+
+/*
+ * Get an array of DPP pipes in dpp_pipes for stream in res_ctx, ODM slice by
+ * slice, each slice top pipe first.
+ * return - number of DPP pipes in the array, 0 if stream has no OTG master.
+ */
+int resource_get_dpp_pipes_for_stream(const struct dc_stream_state *stream,
+ struct resource_context *res_ctx,
+ struct pipe_ctx *dpp_pipes[MAX_PIPES]);
+
/*
* Get an array of DPP pipes in dpp_pipes ordered with index low to high for
* plane in res_ctx.
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 71/74] drm/amd/display: Program ODM 2:1 and 3:1 on one stream in DC2
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (69 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 70/74] drm/amd/display: Get OPP heads and DPP pipes by stream in dc_resource Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 72/74] drm/amd/display: Enable Replay timing sync by default Wayne Lin
` (3 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Cheng, Jun Lei
From: Tony Cheng <tony.cheng@amd.com>
[Why]
The DC2 commit on DCN4 rejects all streams with ODM.
[How]
Accept one stream with two or three ODM slices. Declare each builder's
steps as a table of sub-sequences that names the state each reads and
how it expands. Always program ODM: combine for more than one slice,
else bypass. Program the OPP steps on each slice, and the plane on each
slice with its own viewport. Make the step list large enough for ODM
3:1. Keep ODM 4:1, ODM with MPO and ODM changes on a kept stream
rejected.
Cc: Jun Lei <jun.lei@amd.com>
Signed-off-by: Tony Cheng <tony.cheng@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
.../amd/display/dc/bls_builder/bls_builder.h | 2 +-
.../dc/bls_builder/dcn401_builder_internal.h | 55 +--
.../bls_builder/dcn401_non_seamless_builder.c | 198 ++++++----
.../dc/bls_builder/dcn401_seamless_builder.c | 373 ++++++++++++------
.../display/dc/bls_executor/bls_executor.c | 15 +
5 files changed, 415 insertions(+), 228 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
index 9a3907bf950a..ad0348b3cae5 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder.h
@@ -12,7 +12,7 @@
#include "bls_builder/dmub_abi.h"
#include "bls_builder/bls_builder_legacy_init.h"
-#define DC2_BLS_MAX_STEPS 128
+#define DC2_BLS_MAX_STEPS 192
enum bls_builder_result {
BLS_BUILD_SUCCESS,
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h
index cc531a7eadf7..41a31202162b 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h
@@ -14,8 +14,12 @@
* table into BLS entries.
*/
-/* The support check allows the OTG master and one MPC bottom pipe. */
-#define DCN401_MAX_DPP_PIPES 2
+/*
+ * The support check allows the OTG master and one MPC bottom pipe, or up to
+ * three ODM slices with one DPP pipe each.
+ */
+#define DCN401_MAX_OPP_HEADS 3
+#define DCN401_MAX_DPP_PIPES DCN401_MAX_OPP_HEADS
void dcn401_commit_check_failed(const struct dc *dc, const char *msg);
@@ -54,10 +58,6 @@ const struct pipe_ctx *dcn401_checked_otg_master(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state);
-/* Each DPP pipe under the OTG master of state, top pipe first. */
-#define DCN401_FOR_EACH_DPP_PIPE(dc, state, pipe) \
- for ((pipe) = dcn401_otg_of(dc, state); (pipe); \
- (pipe) = (pipe)->bottom_pipe)
/* What both builders need of the dc, before either state. */
enum bls_builder_result dcn401_check_dc_support(const struct dc *dc);
@@ -74,27 +74,30 @@ struct dcn401_commit_step_args {
struct pipe_ctx *pipe;
};
-void dcn401_add_steps(struct block_sequence_state *sequence,
- const struct dcn401_commit_step_args *args,
- const struct dcn401_commit_step *steps, unsigned int count);
-/* The support check found an OTG master for each stream of state. */
-void dcn401_add_per_otg_master(struct block_sequence_state *sequence,
- const struct dcn401_commit_step_args *state_args,
- struct dc_state *state,
- const struct dcn401_commit_step *steps, unsigned int count);
-/* Each DPP pipe of each stream, the OTG master first. */
-void dcn401_add_per_dpp_pipe(struct block_sequence_state *sequence,
+/*
+ * A sub-sequence names the state its steps read and how they expand: once,
+ * or once per OTG master, OPP head (ODM slice) or DPP pipe of each stream
+ * of that state, the OTG master first. A builder lists its sub-sequences
+ * in emission order.
+ */
+#define DCN401_CURRENT_STATE (1u << 0)
+#define DCN401_NEW_STATE (1u << 1)
+#define DCN401_PER_STREAM_PER_OTG_MASTER (1u << 2)
+#define DCN401_PER_STREAM_PER_OPP_HEAD (1u << 3)
+#define DCN401_PER_STREAM_PER_DPP_PIPE (1u << 4)
+
+struct dcn401_sub_sequence {
+ uint32_t flags;
+ const struct dcn401_commit_step *steps;
+ unsigned int count;
+};
+
+/* The support check found an OTG master for each stream it expands. */
+void dcn401_add_sub_sequences(struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *state_args,
- struct dc_state *state,
- const struct dcn401_commit_step *steps, unsigned int count);
-#define DCN401_ADD_STEPS(sequence, args, steps) \
- dcn401_add_steps(sequence, args, steps, ARRAY_SIZE(steps))
-#define DCN401_ADD_PER_OTG_MASTER(sequence, args, state, steps) \
- dcn401_add_per_otg_master(sequence, args, state, steps, \
- ARRAY_SIZE(steps))
-#define DCN401_ADD_PER_DPP_PIPE(sequence, args, state, steps) \
- dcn401_add_per_dpp_pipe(sequence, args, state, steps, \
- ARRAY_SIZE(steps))
+ const struct dcn401_sub_sequence *subs, unsigned int count);
+#define DCN401_ADD_SUB_SEQUENCES(sequence, args, subs) \
+ dcn401_add_sub_sequences(sequence, args, subs, ARRAY_SIZE(subs))
/*
* Points the step arguments at the states and the sequence at
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
index 965004f6e49b..f9aa8f0b9b1e 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
@@ -14,7 +14,8 @@
* The new state has no stream, or one stream with no plane; the current
* state has no stream, or one stream with one or two planes, one per DPP
* pipe. Planes are added later by DCN_DECL_SEAMLESS_UPDATE, not by this
- * commit.
+ * commit. The stream is in ODM bypass, 2:1 or 3:1, with one OPP head
+ * and at most one DPP pipe per slice.
*
* The sequence follows legacy_commit_state_hw on dcn401: the same leaf
* calls in the same order, including the calls DC1 repeats. Everything is
@@ -90,28 +91,33 @@ static bool check_plane_blocks(const struct dc *dc,
DC_TRAIT_MATCH(DC_PIPE_TRAIT_PLANE_RESOURCES, 0));
}
-/* The OPP's tree holds exactly the removed MPCCs, in pipe order. */
+/* Each OPP's tree holds exactly the removed MPCCs of its slice, in order. */
static bool check_mpcc_tree(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ const struct pipe_ctx *opp_head = dcn401_checked_otg_master(dc,
current_state, new_state);
+ const struct pipe_ctx *pipe;
const struct mpcc *mpcc;
if (!current_state)
return true;
- if (!pipe || !dc->res_pool || !dc->res_pool->mpc)
+ if (!opp_head || !dc->res_pool || !dc->res_pool->mpc)
return false;
- mpcc = pipe->stream_res.opp->mpc_tree_params.opp_list;
- for (; pipe; pipe = pipe->bottom_pipe) {
- if (mpcc != &dc->res_pool->mpc->mpcc_array[
- pipe->plane_res.mpcc_inst] ||
- mpcc->dpp_id != pipe->plane_res.dpp->inst)
+ for (; opp_head; opp_head = opp_head->next_odm_pipe) {
+ mpcc = opp_head->stream_res.opp->mpc_tree_params.opp_list;
+ for (pipe = opp_head; pipe; pipe = pipe->bottom_pipe) {
+ if (mpcc != &dc->res_pool->mpc->mpcc_array[
+ pipe->plane_res.mpcc_inst] ||
+ mpcc->dpp_id != pipe->plane_res.dpp->inst)
+ return false;
+ mpcc = mpcc->mpcc_bot;
+ }
+ if (mpcc)
return false;
- mpcc = mpcc->mpcc_bot;
}
- return !mpcc;
+ return true;
}
static enum bls_builder_result dcn401_check_commit_state_support(
@@ -184,7 +190,7 @@ static enum bls_builder_result dcn401_check_non_seamless_commit_support(
* softmax.
*/
static const struct dcn401_commit_step
-bls_head_per_state_new_prepare_bandwidth[] = {
+prepare_bandwidth[] = {
{ .func = CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
.meta.clk_mgr_update_clocks_for_state.safe_to_lower = false },
{ .func = HUBBUB_PROGRAM_WATERMARKS,
@@ -203,28 +209,28 @@ bls_head_per_state_new_prepare_bandwidth[] = {
* disable_link, which disables the output as DisplayPort.
* - dcn401_reset_back_end_for_pipe after link_set_dpms_off, then the
* clock source power-down from dcn401_reset_hw_ctx_wrap.
- * The front end disconnects every MPCC under the lock, then powers down
- * each DPP pipe after the unlock; the _dpp tables run once per DPP pipe.
+ * The front end blanks each ODM slice and disconnects every MPCC under
+ * the lock, then powers down each DPP pipe after the unlock; the _dpp
+ * tables run once per DPP pipe and the _opp tables once per OPP head.
*/
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_current_lock[] = {
+static const struct dcn401_commit_step lock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = true },
+};
+
+static const struct dcn401_commit_step blank[] = {
{ .func = OPP_SET_DISP_PATTERN_GENERATOR },
};
-static const struct dcn401_commit_step
- bls_head_per_stream_per_dpp_pipe_current_disconnect[] = {
+static const struct dcn401_commit_step disconnect[] = {
{ .func = HUBP_PROGRAM_DET_SEGMENTS },
{ .func = MPC_REMOVE_MPCC },
};
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_current_unlock[] = {
+static const struct dcn401_commit_step unlock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = false },
};
-static const struct dcn401_commit_step
- bls_head_per_stream_per_dpp_pipe_current_power_down[] = {
+static const struct dcn401_commit_step power_down[] = {
{ .func = MPC_ASSERT_IDLE_MPCC },
{ .func = HUBP_SET_BLANK },
{ .func = HUBP_UPDATE_MALL_SEL },
@@ -241,8 +247,7 @@ static const struct dcn401_commit_step
.meta.skip_if.no_ip_request = true },
};
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_current_stream_off[] = {
+static const struct dcn401_commit_step stream_off[] = {
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
{ .func = DIO_LINK_ENC_CONNECT_DIG_BE_TO_FE,
@@ -270,9 +275,9 @@ static const struct dcn401_commit_step
* dcn401_enable_stream. The virtual stream encoder has no enable, map,
* input-mode or FIFO funcs, and update_info_frame programs nothing.
* DC1 skips dig_connect_to_otg for the virtual signal.
+ * The OPP clock, left edge pixel and blank run on each ODM slice.
*/
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_new[] = {
+static const struct dcn401_commit_step stream_timing[] = {
{ .func = BIOS_SET_SCRATCH_CRITICAL_STATE,
.meta.bios_set_scratch_critical_state.state = true },
@@ -280,6 +285,7 @@ static const struct dcn401_commit_step
{ .func = OPP_SET_DYN_EXPANSION },
{ .func = DCCG_SET_PIXEL_RATE_DIV,
.meta.dccg_set_pixel_rate_div.k1 = PIXEL_RATE_DIV_BY_4 },
+ { .func = OPTC_SET_ODM_COMBINE },
{ .func = DCCG_SET_DTBCLK_P_SRC,
.meta.dccg_set_dtbclk_p_src.src = DPREFCLK },
{ .func = TG_ENABLE_OPTC_CLOCK,
@@ -287,10 +293,18 @@ static const struct dcn401_commit_step
{ .func = CLK_SRC_PROGRAM_PIX_CLK },
{ .func = TG_PROGRAM_TIMING },
{ .func = TG_SET_H_TIMING_DIV_MANUAL_MODE },
+};
+
+static const struct dcn401_commit_step odm[] = {
{ .func = OPP_PIPE_CLOCK_CONTROL },
{ .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
+};
+
+static const struct dcn401_commit_step opp_clock[] = {
{ .func = OPP_PIPE_CLOCK_CONTROL },
- { .func = OPP_SET_DISP_PATTERN_GENERATOR },
+};
+
+static const struct dcn401_commit_step crtc[] = {
{ .func = TG_ENABLE_CRTC },
{ .func = OPP_WAIT_FOR_BLANK_COMPLETE },
{ .func = TG_SET_DRR, .meta.tg_set_drr.from_adjust = true,
@@ -316,22 +330,27 @@ static const struct dcn401_commit_step
/*
* Net effect: finish the head for the new OTG master. program_front_end
- * for a master with no plane, then stereo, then the waits that complete
- * the head's double buffering. DC1 sets up stereo twice for a
+ * for a master with no plane: update_odm, which programs each plane-less
+ * OPP head and blanks each ODM slice. Then stereo, then the waits that
+ * complete the head's double buffering. DC1 sets up stereo twice for a
* mode-changed stream, in legacy_commit_state_hw and dc_enable_stereo,
* with the same result; one dcn10_setup_stereo with no 3D format, the two
* STEREO calls, matches it.
* The bandwidth lower starts only after these waits.
*/
-static const struct dcn401_commit_step
- bls_head_per_stream_per_otg_master_pipe_new_front_end[] = {
+static const struct dcn401_commit_step front_end_lock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = true },
- { .func = OPTC_SET_ODM_BYPASS },
+ { .func = OPTC_SET_ODM_COMBINE },
+};
+
+static const struct dcn401_commit_step front_end_odm[] = {
{ .func = OPP_PIPE_CLOCK_CONTROL },
{ .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL },
{ .func = OPP_SET_DYN_EXPANSION },
{ .func = OPP_PROGRAM_FMT },
- { .func = OPP_SET_DISP_PATTERN_GENERATOR },
+};
+
+static const struct dcn401_commit_step front_end[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = false },
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
@@ -346,7 +365,8 @@ static const struct dcn401_commit_step
* Net effect: release the forced p-state when no OTG master did, then
* dcn401_optimize_bandwidth without FAMS2, SubVP or DC-mode softmax.
*/
-static const struct dcn401_commit_step bls_head_per_state_new_postamble[] = { { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
+static const struct dcn401_commit_step postamble[] = {
+ { .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
.meta.skip_if.new_state_has_stream = true },
{ .func = HUBBUB_PROGRAM_WATERMARKS,
.meta.hubbub_program_watermarks.safe_to_lower = true },
@@ -362,27 +382,84 @@ static const struct dcn401_commit_step bls_head_per_state_new_postamble[] = { {
* Bounds on what build adds. The support check allows one stream per
* state, and never one in both.
*/
-static_assert(ARRAY_SIZE(bls_head_per_state_new_prepare_bandwidth) +
- ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_current_lock) +
- DCN401_MAX_DPP_PIPES * ARRAY_SIZE(
- bls_head_per_stream_per_dpp_pipe_current_disconnect) +
- ARRAY_SIZE(
- bls_head_per_stream_per_otg_master_pipe_current_unlock) +
- DCN401_MAX_DPP_PIPES * ARRAY_SIZE(
- bls_head_per_stream_per_dpp_pipe_current_power_down) +
- ARRAY_SIZE(
- bls_head_per_stream_per_otg_master_pipe_current_stream_off) +
- ARRAY_SIZE(bls_head_per_state_new_postamble) <=
+static_assert(ARRAY_SIZE(prepare_bandwidth) +
+ ARRAY_SIZE(lock) +
+ DCN401_MAX_OPP_HEADS * ARRAY_SIZE(blank) +
+ DCN401_MAX_DPP_PIPES * ARRAY_SIZE(disconnect) +
+ ARRAY_SIZE(unlock) +
+ DCN401_MAX_DPP_PIPES * ARRAY_SIZE(power_down) +
+ ARRAY_SIZE(stream_off) +
+ ARRAY_SIZE(postamble) <=
DC2_BLS_MAX_STEPS,
"dcn401 commit stream-off head exceeds DC2_BLS_MAX_STEPS");
-static_assert(ARRAY_SIZE(bls_head_per_state_new_prepare_bandwidth) +
- ARRAY_SIZE(bls_head_per_stream_per_otg_master_pipe_new) +
- ARRAY_SIZE(
- bls_head_per_stream_per_otg_master_pipe_new_front_end) +
- ARRAY_SIZE(bls_head_per_state_new_postamble) <=
+static_assert(ARRAY_SIZE(prepare_bandwidth) +
+ ARRAY_SIZE(stream_timing) +
+ ARRAY_SIZE(opp_clock) +
+ ARRAY_SIZE(crtc) +
+ ARRAY_SIZE(front_end_lock) +
+ ARRAY_SIZE(front_end) +
+ DCN401_MAX_OPP_HEADS * (
+ ARRAY_SIZE(odm) +
+ 2 * ARRAY_SIZE(blank) +
+ ARRAY_SIZE(front_end_odm)) +
+ ARRAY_SIZE(postamble) <=
DC2_BLS_MAX_STEPS,
"dcn401 commit enable head exceeds DC2_BLS_MAX_STEPS");
+static const struct dcn401_sub_sequence bls_head[] = {
+ { .flags = DCN401_NEW_STATE,
+ .steps = prepare_bandwidth,
+ .count = ARRAY_SIZE(prepare_bandwidth) },
+ { .flags = DCN401_CURRENT_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = lock,
+ .count = ARRAY_SIZE(lock) },
+ { .flags = DCN401_CURRENT_STATE | DCN401_PER_STREAM_PER_OPP_HEAD,
+ .steps = blank,
+ .count = ARRAY_SIZE(blank) },
+ { .flags = DCN401_CURRENT_STATE | DCN401_PER_STREAM_PER_DPP_PIPE,
+ .steps = disconnect,
+ .count = ARRAY_SIZE(disconnect) },
+ { .flags = DCN401_CURRENT_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = unlock,
+ .count = ARRAY_SIZE(unlock) },
+ { .flags = DCN401_CURRENT_STATE | DCN401_PER_STREAM_PER_DPP_PIPE,
+ .steps = power_down,
+ .count = ARRAY_SIZE(power_down) },
+ { .flags = DCN401_CURRENT_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = stream_off,
+ .count = ARRAY_SIZE(stream_off) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = stream_timing,
+ .count = ARRAY_SIZE(stream_timing) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OPP_HEAD,
+ .steps = odm,
+ .count = ARRAY_SIZE(odm) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = opp_clock,
+ .count = ARRAY_SIZE(opp_clock) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OPP_HEAD,
+ .steps = blank,
+ .count = ARRAY_SIZE(blank) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = crtc,
+ .count = ARRAY_SIZE(crtc) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = front_end_lock,
+ .count = ARRAY_SIZE(front_end_lock) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OPP_HEAD,
+ .steps = front_end_odm,
+ .count = ARRAY_SIZE(front_end_odm) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OPP_HEAD,
+ .steps = blank,
+ .count = ARRAY_SIZE(blank) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = front_end,
+ .count = ARRAY_SIZE(front_end) },
+ { .flags = DCN401_NEW_STATE,
+ .steps = postamble,
+ .count = ARRAY_SIZE(postamble) },
+};
+
static enum bls_builder_result dcn401_build_non_seamless_commit(
struct bls_builder_declarative_params *params)
{
@@ -398,26 +475,7 @@ static enum bls_builder_result dcn401_build_non_seamless_commit(
return result;
dcn401_init_and_unpack_params(params, &state_args, &head);
- DCN401_ADD_STEPS(&head, &state_args,
- bls_head_per_state_new_prepare_bandwidth);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
- state_args.current_state,
- bls_head_per_stream_per_otg_master_pipe_current_lock);
- DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.current_state,
- bls_head_per_stream_per_dpp_pipe_current_disconnect);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
- state_args.current_state,
- bls_head_per_stream_per_otg_master_pipe_current_unlock);
- DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.current_state,
- bls_head_per_stream_per_dpp_pipe_current_power_down);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
- state_args.current_state,
- bls_head_per_stream_per_otg_master_pipe_current_stream_off);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_head_per_stream_per_otg_master_pipe_new);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_head_per_stream_per_otg_master_pipe_new_front_end);
- DCN401_ADD_STEPS(&head, &state_args, bls_head_per_state_new_postamble);
+ DCN401_ADD_SUB_SEQUENCES(&head, &state_args, bls_head);
return BLS_BUILD_SUCCESS;
}
diff --git a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
index 863919e0967e..a9884eefdd9f 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
@@ -260,8 +260,8 @@ static bool check_abm(const struct dc *dc,
/*
* The OTG master for this state's one stream, or NULL when the stream has
- * more than one. An MPC bottom pipe of the stream is not an OTG master;
- * check_mpc_blend bounds it.
+ * more than one. An MPC bottom pipe or an ODM slice of the stream is not
+ * an OTG master; check_mpc_blend and check_odm_combine bound them.
*/
static const struct pipe_ctx *dcn401_state_otg_master(const struct dc *dc,
const struct dc_state *state, bool *not_one_pipe)
@@ -276,7 +276,8 @@ static const struct pipe_ctx *dcn401_state_otg_master(const struct dc *dc,
if (!pipe->stream)
continue;
- if (pipe->stream == state->streams[0] && pipe->top_pipe)
+ if (pipe->stream == state->streams[0] &&
+ (pipe->top_pipe || pipe->prev_odm_pipe))
continue;
if (pipe->stream != state->streams[0] || otg_master) {
*not_one_pipe = true;
@@ -325,14 +326,19 @@ static bool check_otg_master_present(const struct dc *dc,
return dcn401_checked_otg_master(dc, current_state, new_state) != NULL;
}
+/* At most DCN401_MAX_OPP_HEADS ODM slices, each with an OPP. */
static bool check_odm_combine(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe = dcn401_checked_otg_master(dc,
+ const struct pipe_ctx *odm = dcn401_checked_otg_master(dc,
current_state, new_state);
+ unsigned int count = 0;
- return pipe && !pipe->next_odm_pipe && !pipe->prev_odm_pipe;
+ for (; odm; odm = odm->next_odm_pipe)
+ if (!odm->stream_res.opp || ++count > DCN401_MAX_OPP_HEADS)
+ return false;
+ return count;
}
static bool check_mpc_blend(const struct dc *dc,
@@ -348,11 +354,15 @@ static bool check_mpc_blend(const struct dc *dc,
return false;
dc_state_facts_derive(dcn401_checked_state(current_state, new_state),
&facts);
- /* At most one bottom pipe, on the master's OTG and OPP, and no ODM. */
+ /*
+ * At most one bottom pipe, on the master's OTG and OPP. A bottom pipe
+ * and ODM do not go together.
+ */
return facts.global.blend_tree[0].dpp_pipe_count <= 2 &&
facts.global.blend_tree[0].on_otg_master_blocks &&
+ (facts.global.blend_tree[0].dpp_pipe_count == 1 ||
!dc_state_facts_any_pipe(&facts, 0,
- DC_TRAIT_MATCH(DC_PIPE_TRAIT_ODM, 0));
+ DC_TRAIT_MATCH(DC_PIPE_TRAIT_ODM, 0)));
}
static bool check_otg_master_blocks(const struct dc *dc,
@@ -434,9 +444,9 @@ enum bls_builder_result dcn401_check_stream_support(const struct dc *dc,
CHECK_CONDITION(abm, "ABM level is set");
CHECK_CONDITION(one_otg_pipe, "stream is not on one pipe");
CHECK_RESOURCE(otg_master_present, "stream has no otg master");
- CHECK_CONDITION(odm_combine, "odm is set");
+ CHECK_CONDITION(odm_combine, "odm is not bypass, 2:1 or 3:1");
CHECK_CONDITION(mpc_blend,
- "more than one bottom pipe, or it is off the otg or opp");
+ "more than one bottom pipe, off the otg or opp, or with odm");
CHECK_RESOURCE(otg_master_blocks,
"otg master is missing a stream resource or pixel clock");
CHECK_CONDITION(pipe_audio, "audio is set");
@@ -501,10 +511,16 @@ static bool check_same_otg_master_blocks(const struct dc *dc,
const struct pipe_ctx *current_pipe = dcn401_otg_of(dc, current_state);
const struct pipe_ctx *new_pipe = dcn401_otg_of(dc, new_state);
- if (!current_pipe || !new_pipe)
+ if (!current_pipe || !new_pipe ||
+ current_pipe->stream_res.tg != new_pipe->stream_res.tg)
return false;
- return current_pipe->stream_res.tg == new_pipe->stream_res.tg &&
- current_pipe->stream_res.opp == new_pipe->stream_res.opp;
+ /* The same OPP for each ODM slice. */
+ for (; current_pipe && new_pipe;
+ current_pipe = current_pipe->next_odm_pipe,
+ new_pipe = new_pipe->next_odm_pipe)
+ if (current_pipe->stream_res.opp != new_pipe->stream_res.opp)
+ return false;
+ return !current_pipe && !new_pipe;
}
static bool check_new_plane_blocks(const struct dc *dc,
@@ -628,15 +644,19 @@ static bool check_no_flip_int(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe;
+ struct pipe_ctx *dpp_pipes[MAX_PIPES];
+ int i, n = new_state && new_state->stream_count ?
+ resource_get_dpp_pipes_for_stream(new_state->streams[0],
+ (struct resource_context *)&new_state->res_ctx,
+ dpp_pipes) : 0;
+ (void)dc;
(void)current_state;
- if (!dcn401_otg_of(dc, new_state))
+ if (!n)
return false;
- DCN401_FOR_EACH_DPP_PIPE(dc, new_state, pipe) {
- if (!pipe->plane_state || pipe->plane_state->flip_int_enabled)
+ for (i = 0; i < n; i++)
+ if (dpp_pipes[i]->plane_state->flip_int_enabled)
return false;
- }
return true;
}
@@ -645,16 +665,19 @@ static bool check_no_plane_gamut_remap(const struct dc *dc,
const struct dc_state *current_state,
const struct dc_state *new_state)
{
- const struct pipe_ctx *pipe;
+ struct pipe_ctx *dpp_pipes[MAX_PIPES];
+ int i, n = new_state && new_state->stream_count ?
+ resource_get_dpp_pipes_for_stream(new_state->streams[0],
+ (struct resource_context *)&new_state->res_ctx,
+ dpp_pipes) : 0;
+ (void)dc;
(void)current_state;
- if (!dcn401_otg_of(dc, new_state))
+ if (!n)
return false;
- DCN401_FOR_EACH_DPP_PIPE(dc, new_state, pipe) {
- if (!pipe->plane_state ||
- pipe->plane_state->gamut_remap_matrix.enable_remap)
+ for (i = 0; i < n; i++)
+ if (dpp_pipes[i]->plane_state->gamut_remap_matrix.enable_remap)
return false;
- }
return true;
}
@@ -847,7 +870,7 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
CHECK_CONDITION(stream_kept, "stream is not kept across the update");
CHECK_CONDITION(plane_add_or_kept,
"update is not a plane add or a kept-plane surface update");
- CHECK_CONDITION(same_otg_master_blocks, "otg or opp changes");
+ CHECK_CONDITION(same_otg_master_blocks, "otg, opp or odm changes");
CHECK_RESOURCE(new_plane_blocks, "plane is missing hubp or dpp");
CHECK_RESOURCE(pool_funcs, "resource pool or hubbub has no funcs");
CHECK_CONDITION(det_by_segments, "hubbub programs det size");
@@ -885,29 +908,12 @@ static enum bls_builder_result dcn401_check_seamless_update_support(
return BLS_BUILD_SUCCESS;
}
-static struct pipe_ctx *get_otg_master_from_state_by_stream(
- const struct dc *dc, struct dc_state *state,
- const struct dc_stream_state *stream)
-{
- unsigned int i;
-
- (void)dc;
-
- for (i = 0; i < MAX_PIPES; i++) {
- struct pipe_ctx *pipe = &state->res_ctx.pipe_ctx[i];
-
- if (pipe->stream == stream && !pipe->top_pipe)
- return pipe;
- }
- return NULL;
-}
-
/*
* Net effect: dcn401_prepare_bandwidth without FAMS2, SubVP or DC-mode
* softmax.
*/
static const struct dcn401_commit_step
-bls_update_head_per_state_new_prepare_bandwidth[] = {
+prepare_bandwidth[] = {
{ .func = CLK_MGR_UPDATE_CLOCKS_FOR_STATE,
.meta.clk_mgr_update_clocks_for_state.safe_to_lower = false },
{ .func = HUBBUB_PROGRAM_WATERMARKS,
@@ -923,8 +929,7 @@ bls_update_head_per_state_new_prepare_bandwidth[] = {
* which commit_planes_for_stream runs while optimized_required is set.
* The OTG master has no plane yet, so no MPCC disconnect is pending.
*/
-static const struct dcn401_commit_step
- bls_update_head_per_stream_per_otg_master_pipe_preamble[] = {
+static const struct dcn401_commit_step preamble[] = {
{ .func = TG_WAIT_FOR_ALL_PENDING_UPDATES,
.meta.skip_if.optimized_not_required = true },
{ .func = TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR,
@@ -934,7 +939,7 @@ static const struct dcn401_commit_step
};
/* Net effect: dcn401_program_outstanding_updates on the current state. */
-static const struct dcn401_commit_step bls_update_head_global_preamble[] = {
+static const struct dcn401_commit_step global_preamble[] = {
{ .func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS,
.meta.hubbub_program_compbuf_segments.safe_to_lower = true,
.meta.hubbub_program_compbuf_segments.use_current_state_size = true,
@@ -945,8 +950,7 @@ static const struct dcn401_commit_step bls_update_head_global_preamble[] = {
* Net effect: hwss_wait_for_outstanding_hw_updates on the current state,
* which a full update runs before it takes the pipe lock.
*/
-static const struct dcn401_commit_step
- bls_update_head_per_stream_per_otg_master_pipe_current_pending[] = {
+static const struct dcn401_commit_step pending[] = {
{ .func = TG_WAIT_FOR_ALL_PENDING_UPDATES },
{ .func = TG_WAIT_ODM_DOUBLEBUFFER_PENDING_CLEAR },
{ .func = OPP_WAIT_FOR_BLANK_COMPLETE },
@@ -961,29 +965,32 @@ static const struct dcn401_commit_step
* dcn401_program_front_end_for_ctx, then the forced p-state and delay for
* the first HUBP.
*/
-static const struct dcn401_commit_step bls_update_head_new_lock[] = {
+static const struct dcn401_commit_step lock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = true },
};
-static const struct dcn401_commit_step bls_update_head_new_dpp_lock[] = {
+static const struct dcn401_commit_step dpp_lock[] = {
{ .func = HUBP_PROGRAM_TRIPLEBUFFER },
{ .func = HUBP_PROGRAM_TRIPLEBUFFER },
};
-static const struct dcn401_commit_step bls_update_head_new_first_hubp[] = {
+static const struct dcn401_commit_step first_hubp[] = {
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
.meta.hubbub_force_pstate_change_control.enable = true },
{ .func = DC_UDELAY },
};
/*
- * blank_pixel_data (unblank), dcn401_program_tg and update_odm.
- * First enable of the HUBP/DPP only.
+ * blank_pixel_data (unblank) on each ODM slice, dcn401_program_tg and
+ * update_odm. First enable of the HUBP/DPP only.
*/
-static const struct dcn401_commit_step bls_update_head_new_stream[] = {
+static const struct dcn401_commit_step opp_unblank[] = {
{ .func = OPP_SET_DISP_PATTERN_GENERATOR,
.meta.opp_set_disp_pattern_generator.unblank = true,
.meta.skip_if.hubp_dpp_already_enabled = true },
+};
+
+static const struct dcn401_commit_step global_sync[] = {
{ .func = TG_PROGRAM_GLOBAL_SYNC,
.meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = TG_WAIT_FOR_STATE,
@@ -992,8 +999,11 @@ static const struct dcn401_commit_step bls_update_head_new_stream[] = {
.meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = TG_SETUP_VERTICAL_INTERRUPT2,
.meta.skip_if.hubp_dpp_already_enabled = true },
- { .func = OPTC_SET_ODM_BYPASS,
+ { .func = OPTC_SET_ODM_COMBINE,
.meta.skip_if.hubp_dpp_already_enabled = true },
+};
+
+static const struct dcn401_commit_step opp_odm[] = {
{ .func = OPP_PIPE_CLOCK_CONTROL,
.meta.skip_if.hubp_dpp_already_enabled = true },
{ .func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL,
@@ -1001,7 +1011,7 @@ static const struct dcn401_commit_step bls_update_head_new_stream[] = {
};
/* enable_plane, then the watermark propagate and the DET segments. */
-static const struct dcn401_commit_step bls_update_head_new_enable_plane[] = {
+static const struct dcn401_commit_step enable_plane[] = {
{ .func = DC_IP_REQUEST_STATUS,
.meta.skip_if.hubp_dpp_already_enabled = true,
.meta.skip_if.no_ip_request = true },
@@ -1106,47 +1116,48 @@ static const struct dcn401_commit_step bls_update_head_new_enable_plane[] = {
* flip-pending wait, the p-state release and the MALL selection. Last,
* the manual trigger.
*/
-static const struct dcn401_commit_step bls_update_head_new_unlock[] = {
+static const struct dcn401_commit_step unlock[] = {
{ .func = TG_LOCK, .meta.tg_lock.lock = false },
};
-static const struct dcn401_commit_step bls_update_head_new_dpp_flip[] = {
+static const struct dcn401_commit_step dpp_flip[] = {
{ .func = HUBP_WAIT_FLIP_PENDING },
};
-static const struct dcn401_commit_step bls_update_head_new_pstate[] = {
+static const struct dcn401_commit_step pstate[] = {
{ .func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL },
};
-static const struct dcn401_commit_step bls_update_head_new_dpp_mall[] = {
+static const struct dcn401_commit_step dpp_mall[] = {
{ .func = HUBP_UPDATE_MALL_SEL,
.meta.hubp_update_mall_sel.from_new_state = true },
};
-static const struct dcn401_commit_step bls_update_head_new_trigger[] = {
+static const struct dcn401_commit_step trigger[] = {
{ .func = OPTC_PROGRAM_MANUAL_TRIGGER,
.meta.skip_if.no_manual_trigger = true },
};
/* The support check allows one stream, kept across the update. */
static_assert(
- ARRAY_SIZE(
- bls_update_head_per_stream_per_otg_master_pipe_preamble) +
- ARRAY_SIZE(bls_update_head_global_preamble) +
- ARRAY_SIZE(bls_update_head_per_state_new_prepare_bandwidth) +
- ARRAY_SIZE(
- bls_update_head_per_stream_per_otg_master_pipe_current_pending) +
- ARRAY_SIZE(bls_update_head_new_lock) +
- ARRAY_SIZE(bls_update_head_new_first_hubp) +
- ARRAY_SIZE(bls_update_head_new_stream) +
- ARRAY_SIZE(bls_update_head_new_unlock) +
- ARRAY_SIZE(bls_update_head_new_pstate) +
- ARRAY_SIZE(bls_update_head_new_trigger) +
+ ARRAY_SIZE(preamble) +
+ ARRAY_SIZE(global_preamble) +
+ ARRAY_SIZE(prepare_bandwidth) +
+ ARRAY_SIZE(pending) +
+ ARRAY_SIZE(lock) +
+ ARRAY_SIZE(first_hubp) +
+ ARRAY_SIZE(global_sync) +
+ DCN401_MAX_OPP_HEADS * (
+ ARRAY_SIZE(opp_unblank) +
+ ARRAY_SIZE(opp_odm)) +
+ ARRAY_SIZE(unlock) +
+ ARRAY_SIZE(pstate) +
+ ARRAY_SIZE(trigger) +
DCN401_MAX_DPP_PIPES * (
- ARRAY_SIZE(bls_update_head_new_dpp_lock) +
- ARRAY_SIZE(bls_update_head_new_enable_plane) +
- ARRAY_SIZE(bls_update_head_new_dpp_flip) +
- ARRAY_SIZE(bls_update_head_new_dpp_mall)) <= DC2_BLS_MAX_STEPS,
+ ARRAY_SIZE(dpp_lock) +
+ ARRAY_SIZE(enable_plane) +
+ ARRAY_SIZE(dpp_flip) +
+ ARRAY_SIZE(dpp_mall)) <= DC2_BLS_MAX_STEPS,
"dcn401 seamless update head exceeds DC2_BLS_MAX_STEPS");
/* enable_stream_timing_calc: manual mode unless every span is even. */
@@ -1190,14 +1201,15 @@ static uint32_t dcn401_early_cntl(const struct pipe_ctx *pipe)
}
/*
- * dcn20_blank_pixel_data: sRGB black over the whole OTG, or the video
- * on unblank.
+ * dcn20_blank_pixel_data: sRGB black over the ODM slice of this OPP head,
+ * or the video on unblank.
*/
static void dcn401_opp_blank_params(const struct dc *dc,
- const struct pipe_ctx *pipe, bool unblank,
+ struct pipe_ctx *pipe, bool unblank,
struct opp_set_disp_pattern_generator_params *p)
{
const struct dc_crtc_timing *timing = &pipe->stream->timing;
+ struct rect slice = resource_get_odm_slice_src_rect(pipe);
p->opp = pipe->stream_res.opp;
p->test_pattern = unblank ? CONTROLLER_DP_TEST_PATTERN_VIDEOMODE :
@@ -1205,14 +1217,32 @@ static void dcn401_opp_blank_params(const struct dc *dc,
p->color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED;
p->color_depth = timing->display_color_depth;
p->use_solid_color = true;
- p->width = timing->h_addressable + timing->h_border_left +
- timing->h_border_right;
- p->height = timing->v_addressable + timing->v_border_top +
- timing->v_border_bottom;
+ p->width = slice.width;
+ p->height = slice.height;
+ p->offset = slice.x;
p->disable_dyn_exp_for_test_pattern =
dc->debug.disable_dynamic_expansion_for_test_pattern;
}
+/*
+ * dcn401_update_odm: one OPP per ODM slice, in slice order. A table's
+ * OPTC_SET_ODM_COMBINE programs the ODM unconditionally: N:1 combine for
+ * N > 1 slices, else bypass.
+ */
+static void dcn401_odm_combine_params(struct pipe_ctx *otg_master,
+ struct set_odm_combine_params *p)
+{
+ struct pipe_ctx *odm;
+
+ p->tg = otg_master->stream_res.tg;
+ for (odm = otg_master; odm; odm = odm->next_odm_pipe)
+ p->opp_inst[p->opp_head_count++] = odm->stream_res.opp->inst;
+ p->odm_slice_width = resource_get_odm_slice_dst_width(otg_master,
+ false);
+ p->last_odm_slice_width = resource_get_odm_slice_dst_width(otg_master,
+ true);
+}
+
static void dcn401_program_timing_params(const struct pipe_ctx *pipe,
struct tg_program_timing_params *p)
{
@@ -1534,6 +1564,16 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
p->tg_program_stereo_params.tg = pipe->stream_res.tg;
p->tg_program_stereo_params.timing = &pipe->stream->timing;
break;
+ case OPTC_SET_ODM_COMBINE:
+ if (pipe->next_odm_pipe) {
+ dcn401_odm_combine_params(pipe,
+ &p->set_odm_combine_params);
+ break;
+ }
+ entry->func = OPTC_SET_ODM_BYPASS;
+ p->set_odm_bypass_params.tg = pipe->stream_res.tg;
+ p->set_odm_bypass_params.timing = &pipe->stream->timing;
+ break;
case OPTC_SET_ODM_BYPASS:
p->set_odm_bypass_params.tg = pipe->stream_res.tg;
p->set_odm_bypass_params.timing = &pipe->stream->timing;
@@ -1548,7 +1588,7 @@ static void dcn401_add_step(struct block_sequence_state *sequence,
p->opp_program_left_edge_extra_pixel_params.pixel_encoding =
pipe->stream->timing.pixel_encoding;
p->opp_program_left_edge_extra_pixel_params.is_otg_master =
- true;
+ !pipe->prev_odm_pipe;
break;
case OPP_PROGRAM_FMT:
p->opp_program_fmt_params.opp = pipe->stream_res.opp;
@@ -1986,7 +2026,7 @@ static bool dcn401_skip_step(const struct dcn401_commit_step_args *args,
return false;
}
-void dcn401_add_steps(struct block_sequence_state *sequence,
+static void dcn401_add_steps(struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *args,
const struct dcn401_commit_step *steps, unsigned int count)
{
@@ -1998,7 +2038,7 @@ void dcn401_add_steps(struct block_sequence_state *sequence,
}
}
-void dcn401_add_per_otg_master(struct block_sequence_state *sequence,
+static void dcn401_add_per_otg_master(struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *state_args,
struct dc_state *state,
const struct dcn401_commit_step *steps, unsigned int count)
@@ -2007,25 +2047,77 @@ void dcn401_add_per_otg_master(struct block_sequence_state *sequence,
unsigned int i;
for (i = 0; i < state->stream_count; i++) {
- args.pipe = get_otg_master_from_state_by_stream(args.dc,
- state, state->streams[i]);
+ args.pipe = resource_get_otg_master_for_stream(
+ &state->res_ctx, state->streams[i]);
dcn401_add_steps(sequence, &args, steps, count);
}
}
-void dcn401_add_per_dpp_pipe(struct block_sequence_state *sequence,
+static void dcn401_add_per_opp_head(struct block_sequence_state *sequence,
const struct dcn401_commit_step_args *state_args,
struct dc_state *state,
const struct dcn401_commit_step *steps, unsigned int count)
{
struct dcn401_commit_step_args args = *state_args;
+ struct pipe_ctx *opp_heads[MAX_PIPES];
unsigned int i;
+ int j, n;
for (i = 0; i < state->stream_count; i++) {
- for (args.pipe = get_otg_master_from_state_by_stream(args.dc,
- state, state->streams[i]); args.pipe;
- args.pipe = args.pipe->bottom_pipe)
+ n = resource_get_opp_heads_for_stream(state->streams[i],
+ &state->res_ctx, opp_heads);
+ for (j = 0; j < n; j++) {
+ args.pipe = opp_heads[j];
dcn401_add_steps(sequence, &args, steps, count);
+ }
+ }
+}
+
+static void dcn401_add_per_dpp_pipe(struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *state_args,
+ struct dc_state *state,
+ const struct dcn401_commit_step *steps, unsigned int count)
+{
+ struct dcn401_commit_step_args args = *state_args;
+ struct pipe_ctx *dpp_pipes[MAX_PIPES];
+ unsigned int i;
+ int j, n;
+
+ for (i = 0; i < state->stream_count; i++) {
+ n = resource_get_dpp_pipes_for_stream(state->streams[i],
+ &state->res_ctx, dpp_pipes);
+ for (j = 0; j < n; j++) {
+ args.pipe = dpp_pipes[j];
+ dcn401_add_steps(sequence, &args, steps, count);
+ }
+ }
+}
+
+void dcn401_add_sub_sequences(struct block_sequence_state *sequence,
+ const struct dcn401_commit_step_args *state_args,
+ const struct dcn401_sub_sequence *subs, unsigned int count)
+{
+ const struct dcn401_sub_sequence *sub;
+ struct dc_state *state;
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ sub = &subs[i];
+ state = sub->flags & DCN401_CURRENT_STATE ?
+ state_args->current_state :
+ state_args->new_state;
+ if (sub->flags & DCN401_PER_STREAM_PER_OTG_MASTER)
+ dcn401_add_per_otg_master(sequence, state_args, state,
+ sub->steps, sub->count);
+ else if (sub->flags & DCN401_PER_STREAM_PER_OPP_HEAD)
+ dcn401_add_per_opp_head(sequence, state_args, state,
+ sub->steps, sub->count);
+ else if (sub->flags & DCN401_PER_STREAM_PER_DPP_PIPE)
+ dcn401_add_per_dpp_pipe(sequence, state_args, state,
+ sub->steps, sub->count);
+ else
+ dcn401_add_steps(sequence, state_args, sub->steps,
+ sub->count);
}
}
@@ -2045,6 +2137,63 @@ void dcn401_init_and_unpack_params(
head->num_steps = ¶ms->head_num_steps;
}
+/*
+ * One stream. skip_if drops first-enable steps when the HUBP/DPP is
+ * already on, so a kept-plane surface update is the same table. The
+ * enable_plane list runs per DPP pipe, top pipe first, so the top MPCC is
+ * inserted before the bottom one.
+ */
+static const struct dcn401_sub_sequence bls_update_head[] = {
+ { .flags = DCN401_CURRENT_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = preamble,
+ .count = ARRAY_SIZE(preamble) },
+ { .flags = DCN401_NEW_STATE,
+ .steps = global_preamble,
+ .count = ARRAY_SIZE(global_preamble) },
+ { .flags = DCN401_NEW_STATE,
+ .steps = prepare_bandwidth,
+ .count = ARRAY_SIZE(prepare_bandwidth) },
+ { .flags = DCN401_CURRENT_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = pending,
+ .count = ARRAY_SIZE(pending) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = lock,
+ .count = ARRAY_SIZE(lock) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_DPP_PIPE,
+ .steps = dpp_lock,
+ .count = ARRAY_SIZE(dpp_lock) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = first_hubp,
+ .count = ARRAY_SIZE(first_hubp) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OPP_HEAD,
+ .steps = opp_unblank,
+ .count = ARRAY_SIZE(opp_unblank) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = global_sync,
+ .count = ARRAY_SIZE(global_sync) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OPP_HEAD,
+ .steps = opp_odm,
+ .count = ARRAY_SIZE(opp_odm) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_DPP_PIPE,
+ .steps = enable_plane,
+ .count = ARRAY_SIZE(enable_plane) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = unlock,
+ .count = ARRAY_SIZE(unlock) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_DPP_PIPE,
+ .steps = dpp_flip,
+ .count = ARRAY_SIZE(dpp_flip) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = pstate,
+ .count = ARRAY_SIZE(pstate) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_DPP_PIPE,
+ .steps = dpp_mall,
+ .count = ARRAY_SIZE(dpp_mall) },
+ { .flags = DCN401_NEW_STATE | DCN401_PER_STREAM_PER_OTG_MASTER,
+ .steps = trigger,
+ .count = ARRAY_SIZE(trigger) },
+};
+
static enum bls_builder_result dcn401_build_seamless_update(
struct bls_builder_declarative_params *params)
{
@@ -2060,45 +2209,7 @@ static enum bls_builder_result dcn401_build_seamless_update(
return result;
dcn401_init_and_unpack_params(params, &state_args, &head);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
- state_args.current_state,
- bls_update_head_per_stream_per_otg_master_pipe_preamble);
- DCN401_ADD_STEPS(&head, &state_args,
- bls_update_head_global_preamble);
- DCN401_ADD_STEPS(&head, &state_args,
- bls_update_head_per_state_new_prepare_bandwidth);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args,
- state_args.current_state,
- bls_update_head_per_stream_per_otg_master_pipe_current_pending);
-
- /*
- * One stream. skip_if drops first-enable steps when the HUBP/DPP
- * is already on, so a kept-plane surface update is the same table.
- */
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_lock);
- DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.new_state,
- bls_update_head_new_dpp_lock);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_first_hubp);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_stream);
- /*
- * One step list per DPP pipe, top pipe first, so the top MPCC is
- * inserted before the bottom one.
- */
- DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.new_state,
- bls_update_head_new_enable_plane);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_unlock);
- DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.new_state,
- bls_update_head_new_dpp_flip);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_pstate);
- DCN401_ADD_PER_DPP_PIPE(&head, &state_args, state_args.new_state,
- bls_update_head_new_dpp_mall);
- DCN401_ADD_PER_OTG_MASTER(&head, &state_args, state_args.new_state,
- bls_update_head_new_trigger);
+ DCN401_ADD_SUB_SEQUENCES(&head, &state_args, bls_update_head);
return BLS_BUILD_SUCCESS;
}
diff --git a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
index 47c7137eeeaf..297ddbed2eac 100644
--- a/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
+++ b/drivers/gpu/drm/amd/display/dc/bls_executor/bls_executor.c
@@ -353,6 +353,10 @@ static noinline_for_stack bool dc2_bls_step_supported(const struct dc2 *dc2,
case TG_SETUP_VERTICAL_INTERRUPT2:
tg = step->params.tg_setup_vertical_interrupt2_params.tg;
return tg && tg->funcs && tg->funcs->setup_vertical_interrupt2;
+ case OPTC_SET_ODM_COMBINE:
+ tg = step->params.set_odm_combine_params.tg;
+ return tg && tg->funcs && tg->funcs->set_odm_combine &&
+ step->params.set_odm_combine_params.opp_head_count > 1;
case OPTC_SET_ODM_BYPASS:
tg = step->params.set_odm_bypass_params.tg;
return tg && tg->funcs && tg->funcs->set_odm_bypass &&
@@ -1230,6 +1234,17 @@ enum dc2_bls_exec_result dc2_bls_execute(
tg->funcs->setup_vertical_interrupt2(tg, entry->params
.tg_setup_vertical_interrupt2_params.start_line);
break;
+ case OPTC_SET_ODM_COMBINE: {
+ const struct set_odm_combine_params *p =
+ &entry->params.set_odm_combine_params;
+ int opp_inst[MAX_PIPES];
+
+ memcpy(opp_inst, p->opp_inst, sizeof(opp_inst));
+ p->tg->funcs->set_odm_combine(p->tg, opp_inst,
+ p->opp_head_count, p->odm_slice_width,
+ p->last_odm_slice_width);
+ break;
+ }
case OPTC_SET_ODM_BYPASS:
tg = entry->params.set_odm_bypass_params.tg;
tg->funcs->set_odm_bypass(tg,
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 72/74] drm/amd/display: Enable Replay timing sync by default
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (70 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 71/74] drm/amd/display: Program ODM 2:1 and 3:1 on one stream in DC2 Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 73/74] drm/amd/display: [FW Promotion] Release 0.1.84.0 Wayne Lin
` (2 subsequent siblings)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Ray Wu
From: Ray Wu <ray.wu@amd.com>
[Why & How]
Enable Replay timing sync by default to allow DMUB to resync
during active Replay. Remove the platform-specific conditional;
scenario-specific power optimizations can still disable timing sync
when needed.
Cc: Wayne Lin <wayne.lin@amd.com>
Signed-off-by: Ray Wu <ray.wu@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/modules/power/power_replay.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
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 666585b7e00c..fcfb6066066c 100644
--- a/drivers/gpu/drm/amd/display/modules/power/power_replay.c
+++ b/drivers/gpu/drm/amd/display/modules/power/power_replay.c
@@ -162,7 +162,7 @@ static bool mod_power_update_replay_active_status(unsigned int active_replay_eve
ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM] <= 0xFFFF);
*frame_skip_number = (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_NOM];
- link->replay_settings.config.replay_timing_sync_supported = false;
+ link->replay_settings.config.replay_timing_sync_supported = true;
*is_full_screen_video = false;
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 73/74] drm/amd/display: [FW Promotion] Release 0.1.84.0
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (71 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 72/74] drm/amd/display: Enable Replay timing sync by default Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-11 1:29 ` [PATCH 74/74] drm/amd/display: Promote DC to 3.2.407 Wayne Lin
2026-11-13 10:20 ` [PATCH 00/74] DC Patches November 16, 2026 Chung, ChiaHsuan (Tom)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Taimur Hassan
From: Taimur Hassan <Syed.Hassan@amd.com>
Updated display microcontroller (DMUB) interface
- Add ramless_lobf_state: disabled 0x00, link on 0x10,
Advanced Link Power Management sleep 0x20, invalid
0xFF.
Signed-off-by: Taimur Hassan <Syed.Hassan@amd.com>
Signed-off-by: Wayne Lin <wayne.lin@amd.com>
---
drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 10 ++++++++++
1 file changed, 10 insertions(+)
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 c3aa0d0a74be..e9eee64fd834 100644
--- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
+++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
@@ -7781,6 +7781,16 @@ enum ramless_lobf_enable_cmd {
LOBF_EXIT_IDLE_SCREEN = 4, /**< Exit idle screen */
};
+/**
+ * enum ramless_lobf_state - Ramless LOBF FSM states visible in firmware state.
+ */
+enum ramless_lobf_state {
+ LOBF_STATE_DISABLED = 0x00, /**< Feature completely disabled */
+ LOBF_STATE_LINK_ON = 0x10, /**< Link is ON; Vline interrupt registered for Vblank start */
+ LOBF_STATE_ALPM_SLEEP_ENGAGED = 0x20, /**< ALPM Sleep pattern sent; transitioning to PHY Off */
+ LOBF_STATE_INVALID = 0xFF, /**< Invalid / uninitialized */
+};
+
/**
* enum ramless_lobf_phy_mode - PHY power-state mode for Ramless LOBF.
*/
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH 74/74] drm/amd/display: Promote DC to 3.2.407
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (72 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 73/74] drm/amd/display: [FW Promotion] Release 0.1.84.0 Wayne Lin
@ 2026-11-11 1:29 ` Wayne Lin
2026-11-13 10:20 ` [PATCH 00/74] DC Patches November 16, 2026 Chung, ChiaHsuan (Tom)
74 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-11 1:29 UTC (permalink / raw)
To: amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Wayne Lin,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Taimur Hassan, Wayne Lin
From: Taimur Hassan <Syed.Hassan@amd.com>
This version brings along the following updates:
- Fix KUnit tiled EDID size
- fix hubbub read state logging
- skip_lcdvdd_off for eDP link lost retrain
- Don't shrink zero-area recout in full viewport visual confirm
- panel specific wa not fall back to tmds
- Use expanded DMUB-provided SoC BB and IP caps
- Only force P-State on first plane if TTU is below WM
- Program the kept-plane scaler in DC2
- Add and remove two MPO planes in DC2
- Add DC2 state facts and split the builders
- Program DC2 commit stereo once
- Read DCN6 SOP constraints from the idle row
- Use soc_bb hostvm fraction instead of hardcoded value
- Increase state if dcfclk deep sleep needed exceeds dcfclk
- Flip dml2_core_utils_get_segment_horizontal_contiguous() Inputs
- Update DCN6 Calcs
- Add effective_number_of_dpp parameter
- Read DRAM channel width from SMU init table
- Update DCN42B stutter latency to match DCN42
- Expect VRAM|GTT for the DMUB framebuffer
- Report QoS latency bounds and request
- upsp fixes
- Limit VRAM aperture retrieval to DCN6
- Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant
- Seed boot clocks to avoid lowering pre-OS display clocks
- Account for split pipe overlap in alt channel copy size
- Get OPP heads and DPP pipes by stream in dc_resource
- Program ODM 2:1 and 3:1 on one stream in DC2
- Enable Replay timing sync by default
Signed-off-by: Taimur Hassan <Syed.Hassan@amd.com>
Signed-off-by: Wayne Lin <Wayne.Lin@amd.com>
---
drivers/gpu/drm/amd/display/dc/dc.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 2dff1b271416..b71a9a5cd424 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -69,7 +69,7 @@ struct dcn_dsc_reg_state;
struct dcn_optc_reg_state;
struct dcn_dccg_reg_state;
-#define DC_VER "3.2.406"
+#define DC_VER "3.2.407"
/**
* MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC
--
2.43.0
^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH 63/74] drm/amd/display: Expect VRAM|GTT for the DMUB framebuffer
2026-11-11 1:29 ` [PATCH 63/74] drm/amd/display: Expect VRAM|GTT for the DMUB framebuffer Wayne Lin
@ 2026-11-12 7:17 ` Eren Özen
0 siblings, 0 replies; 80+ messages in thread
From: Eren Özen @ 2026-11-12 7:17 UTC (permalink / raw)
To: Wayne Lin
Cc: amd-gfx, Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
On Wed, Nov 11, 2026 at 09:29:09AM +0800, Wayne Lin wrote:
> The DCN32 GTT-only framebuffer allocation was reverted, so
> dm_dmub_sw_init() passes VRAM and GTT for every ASIC. The KUnit
> case still required GTT alone and failed with domain 0x6.
I hit this failure too. Ran the amdgpu_dm KUnit suite on UML
(kunit.py, amdgpu_dm/tests .kunitconfig, GCC 16, CONFIG_GCOV=n) on
amd-staging-drm-next 47df73aa49:
before: 1910/1912 passed (create_domain 0x6, expected 0x2)
after this patch and 44/74: 1912/1912 passed
Tested-by: Eren Özen <erenozen@protonmail.com>
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 44/74] drm/amd/display: Fix KUnit tiled EDID size
2026-11-11 1:28 ` [PATCH 44/74] drm/amd/display: Fix KUnit tiled EDID size Wayne Lin
@ 2026-11-12 7:17 ` Eren Özen
0 siblings, 0 replies; 80+ messages in thread
From: Eren Özen @ 2026-11-12 7:17 UTC (permalink / raw)
To: Wayne Lin
Cc: amd-gfx, Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen, Tony Qi
On Wed, Nov 11, 2026 at 09:28:50AM +0800, Wayne Lin wrote:
> dm_test_detect_hides_secondary_tile fails because its synthesized
> DisplayID tiled display block is 21 bytes, built around an 8-byte
> topology ID.
I hit this failure too. Ran the amdgpu_dm KUnit suite on UML
(kunit.py, amdgpu_dm/tests .kunitconfig, GCC 16, CONFIG_GCOV=n) on
amd-staging-drm-next 47df73aa49:
before: 1910/1912 passed (detect returned connected, expected
disconnected)
after this patch and 63/74: 1912/1912 passed
Tested-by: Eren Özen <erenozen@protonmail.com>
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 10/74] drm/amd/display: Stop HDMI FRL status polling on fini
2026-11-11 1:28 ` [PATCH 10/74] drm/amd/display: Stop HDMI FRL status polling on fini Wayne Lin
@ 2026-11-12 8:51 ` Eren Özen
2026-11-13 3:19 ` Wayne Lin
0 siblings, 1 reply; 80+ messages in thread
From: Eren Özen @ 2026-11-12 8:51 UTC (permalink / raw)
To: Wayne Lin
Cc: amd-gfx, Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li,
Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu, Ivan Lipski,
Alex Hung, James Lin, Chenyu Chen
On Wed, 11 Nov 2026 09:28:16 +0800, Wayne Lin wrote:
> + /* The poll re-queues itself, so stop it before the wq and dc go away. */
> + if (adev->dm.hdmi_frl_status_polling_wq) {
> + cancel_delayed_work_sync(&adev->dm.hdmi_frl_status_polling_work);
This is the same change as patch 2/3 of a series I posted on Nov 10:
https://lore.kernel.org/all/20261110-frl-polling-wq-teardown-v1-0-0ff028d5c35b@protonmail.com/
I'm fine with either version going in, but checking the wq pointer
isn't enough on its own. amdgpu_dm_init() creates the wq before
amdgpu_dm_initialize_drm_device(), and hdmi_frl_status_polling_work is
only initialized later, in amdgpu_dm_connector_init_helper(). If init
fails between those two points (the dmub_notify or delayed_hpd_wq
allocation, for example), the error path calls amdgpu_dm_fini(), which
runs cancel_delayed_work_sync() on a work item that was never
initialized. __flush_work() then hits WARN_ON(!work->func).
Patch 1/3 of my series fixes that by moving INIT_DELAYED_WORK() into
amdgpu_dm_init(), before the wq is created. Patch 3/3 (don't queue the
work on a NULL wq when the wq allocation failed) isn't covered by this
batch either.
If this patch is applied, I'll rebase 1/3 and 3/3 on top of it and drop
2/3 in v2. Should I wait for this batch to land, or send v2 against
amd-staging-drm-next now?
One small request: my series was on the list the day before this batch.
Would you consider taking my 2/3 in place of this patch, or, if you'd
rather keep this version, adding a Reported-by: and a Link: to my
posting? This is my first kernel series, so the credit would mean a lot
to me.
Thanks,
Eren
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 10/74] drm/amd/display: Stop HDMI FRL status polling on fini
2026-11-12 8:51 ` Eren Özen
@ 2026-11-13 3:19 ` Wayne Lin
0 siblings, 0 replies; 80+ messages in thread
From: Wayne Lin @ 2026-11-13 3:19 UTC (permalink / raw)
To: amd-gfx
Cc: Eren Özen, Harry Wentland, Leo Li, Aurabindo Pillai,
Roman Li, Wayne Lin, Tom Chung, Fangzhi Zuo, Dan Wheeler, Ray Wu,
Ivan Lipski, Alex Hung, James Lin, Chenyu Chen
On Thu, 12 Nov 2026 at 08:51, Eren Özen wrote:
> If this patch is applied, I'll rebase 1/3 and 3/3 on top of it and drop
> 2/3 in v2. Should I wait for this batch to land, or send v2 against
> amd-staging-drm-next now?
>
> One small request: my series was on the list the day before this batch.
> Would you consider taking my 2/3 in place of this patch, or, if you'd
> rather keep this version, adding a Reported-by: and a Link: to my
> posting? This is my first kernel series, so the credit would mean a lot
> to me.
Hi,
Thanks for your patch! This change came in through our promotion flow, so
we missed that the same fix was already on the list.
We would like this patch to go in first. Please send v2 for the parts
that are not in this batch, and drop the patch that matches this one.
This batch is about to land on amd-staging-drm-next. Please rebase v2
on top of it and then send.
We will add a Reported-by and a Link to your posting on this commit.
Thanks!
Regards,
Wayne Lin
^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 00/74] DC Patches November 16, 2026
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
` (73 preceding siblings ...)
2026-11-11 1:29 ` [PATCH 74/74] drm/amd/display: Promote DC to 3.2.407 Wayne Lin
@ 2026-11-13 10:20 ` Chung, ChiaHsuan (Tom)
74 siblings, 0 replies; 80+ messages in thread
From: Chung, ChiaHsuan (Tom) @ 2026-11-13 10:20 UTC (permalink / raw)
To: Wayne Lin, amd-gfx
Cc: Harry Wentland, Leo Li, Aurabindo Pillai, Roman Li, Fangzhi Zuo,
Dan Wheeler, Ray Wu, Ivan Lipski, Alex Hung, James Lin,
Chenyu Chen
On 11/11/2026 9:28 AM, Wayne Lin wrote:
> This DC patchset brings improvements in multiple areas. In summary, we highlight:
>
> - Build DCN401 virtual commits and seamless plane updates in DC2, and
> dispatch the hardware span of a commit through dc2_funcs.
> - Add the leaf block-sequence entries a virtual commit needs, and read
> the accelerated-mode bit through a query builder.
> - Resolve watermark policy and the DPP, DTBCLK and DCN401 DENTIST
> targets when the sequence is built.
> - Fix DSC teardown gating in the post-unlock sequence.
> - Move HDMI FRL status polling outside dc_lock and stop it on fini.
> - Relax stutter constraints for DRR streams, including ramless idle,
> track static-screen idle, and enable the relaxation on DCN42. Raise
> the DCN42 DDR5 stutter latency.
> - Let DC_PSR_MASK enable PSR without a DMUB PSR cap.
> - Enable HDCP on DCN6 and the DF C-state vote on DCN42B.
> - Enable SST PCON FRL DSC passthrough and program the correct DP HPO
> APG instance.
> - Exit IPS before checking flip pending, and keep the HPD IRQ reset
> path from taking dc_lock.
> - Skip UPSP programming when the registers are absent, and skip
> visual confirm color programming when firmware owns it.
> - Align DCN42B with DCN42 and extend the IOMMU mismatch workaround to
> DCN42B. Restore the missing DCN6 vtotal cap and vstartup assignment.
> - Update the DMUB interface to 0.1.83.0 with an extended SoC bounding
> box, RAMLESS_LOBF commands, and a seamless-boot eDP request.
> - Initialize the DMUB LSDMA ring, accept a 1932-byte PSP footer, and
> update the default VCO frequency.
> - Fix the FFE link-rate change path and the p-state enable sequence.
> Commit the validated target when a minimal transition cannot be
> built.
> - Fix KUnit tiled EDID size
> - fix hubbub read state logging
> - skip_lcdvdd_off for eDP link lost retrain
> - Don't shrink zero-area recout in full viewport visual confirm
> - panel specific wa not fall back to tmds
> - Use expanded DMUB-provided SoC BB and IP caps
> - Only force P-State on first plane if TTU is below WM
> - Program the kept-plane scaler in DC2
> - Add and remove two MPO planes in DC2
> - Add DC2 state facts and split the builders
> - Program DC2 commit stereo once
> - Maximum SOP states is 8
> - Use soc_bb hostvm fraction instead of hardcoded value
> - Increase state if dcfclk deep sleep needed exceeds dcfclk
> - Flip dml2_core_utils_get_segment_horizontal_contiguous() Inputs
> - Update DCN6 Calcs
> - Add effective_number_of_dpp parameter
> - Read DRAM channel width from SMU init table
> - Update DCN42B stutter latency to match DCN42
> - Expect VRAM|GTT for the DMUB framebuffer
> - Report QoS latency bounds and request
> - upsp fixes
> - Limit VRAM aperture retrieval to DCN6
> - Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant
> - Seed boot clocks to avoid lowering pre-OS display clocks
> - Account for split pipe overlap in alt channel copy size
> - Get OPP heads and DPP pipes by stream in dc_resource
> - Program ODM 2:1 and 3:1 on one stream in DC2
> - Enable Replay timing sync by default
> - Various cleanups.
Series is
Reviewed-by: Tom Chung <chiahsuan.chung@amd.com>
Tom
>
> ---
>
> Alex Hung (2):
> drm/amd/display: Fix elapsed time indentation
> drm/amd/display: Fix KUnit tiled EDID size
>
> Allen Pan (1):
> drm/amd/display: Update DCN42B stutter latency to match DCN42
>
> Alvin Lee (4):
> drm/amd/display: Update indicate pstate enable sequence
> drm/amd/display: Update DP HPO to use correct APG instance
> drm/amd/display: Updating missing assignments for DCN6
> drm/amd/display: Don't shrink zero-area recout in full viewport visual
> confirm
>
> Aric Cyr (1):
> drm/amd/display: Ensure exit IPS before checking flip status
>
> Ashwin Santhosh (2):
> drm/amd/display: Skip visual confirm color programming when FW owns it
> drm/amd/display: Account for split pipe overlap in alt channel copy
> size
>
> Austin Zheng (5):
> drm/amd/display: Fix Stage Optimizer Parameter Naming
> drm/amd/display: Read DCN6 SOP constraints from the idle row
> drm/amd/display: Use soc_bb hostvm fraction instead of hardcoded value
> drm/amd/display: Flip
> dml2_core_utils_get_segment_horizontal_contiguous() Inputs
> drm/amd/display: Add effective_number_of_dpp parameter
>
> Charlene Liu (6):
> drm/amd/display: Extend the IOMMU mismatch workaround to DCN42B
> drm/amd/display: update dcn42b based on dcn42
> drm/amd/display: enable DF C-state vote on DCN42B
> drm/amd/display: Increase the DCN42 DDR5 stutter latency
> drm/amd/display: Keep panel power across link-loss retrain
> drm/amd/display: Keep patched HDMI panels on FRL across detect
>
> Dillon Varone (3):
> drm/amd/display: Relax stutter constraints for DRR streams
> drm/amd/display: cleanup tiling and address formats
> drm/amd/display: Enable DRR stutter constraint relaxation on DCN42
>
> Eric Bernstein (1):
> drm/amd/display: Skip UPSP programming when UPSP regs are absent
>
> Fangzhi Zuo (4):
> drm/amd/display: Correct the forced YCbCr420 comment
> drm/amd/display: Move HDMI FRL Polling Outside dc_lock
> drm/amd/display: Stop HDMI FRL status polling on fini
> drm/amd/display: Enable SST PCON FRL DSC passthrough
>
> Gabriel Abarca (2):
> drm/amd/display: Increase state if dcfclk deep sleep needed exceeds
> dcfclk
> drm/amd/display: Update DCN6 Calcs
>
> George Zhang (1):
> drm/amd/display: Don't let HPD IRQ handler take dc_lock in reset
>
> Ilya Bakoulin (1):
> drm/amd/display: Fix FFE test automation link rate change
>
> Ivan Lipski (4):
> drm/amd/display: Initialize DMUB LSDMA ring buffer
> drm/amd/display: Enable HDCP on DCN6
> drm/amd/display: Use expanded DMUB-provided SoC BB and IP caps
> drm/amd/display: Limit VRAM aperture retrieval to DCN6
>
> James Lin (1):
> drm/amd/display: commit validated target when minimal transition
> cannot be built
>
> Jing Zhou (1):
> drm/amd/display: Seed boot clocks to avoid lowering pre-OS display
> clocks
>
> Karen Chen (1):
> drm/amd/display: fix hubbub read state logging
>
> Nevenko Stupar (1):
> drm/amd/display: Update default VCO frequency
>
> Ovidiu Bunea (1):
> drm/amd/display: Only force P-State on first plane if TTU is below WM
>
> Piotr Maziarz (3):
> drm/amd/display: Resolve clk_mgr DPP DTO targets when the sequence is
> built
> drm/amd/display: Resolve clk_mgr DTBCLK DTO targets when the sequence
> is built
> drm/amd/display: Resolve the DCN401 DENTIST divider when the sequence
> is built
>
> Rafal Ostrowski (4):
> drm/amd/display: Remove duplicate block sequence leftovers
> drm/amd/display: HWSS Refactor - Watermarks
> drm/amd/display: Fix DSC teardown gating in post-unlock BLS sequence
> drm/amd/display: Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant
>
> Ray Wu (2):
> drm/amd/display: Allow DC_PSR_MASK to enable PSR without DMUB PSR cap
> drm/amd/display: Enable Replay timing sync by default
>
> Sreeja Golui (2):
> drm/amd/display: Include Ramless Idle in stutter relaxation
> drm/amd/display: detect static screen state and propagate
>
> Sridevi Arvindekar (1):
> drm/amd/display: Bypass chroma upsampling without the DPP cap
>
> Taimur Hassan (4):
> drm/amd/display: [FW Promotion] Release 0.1.83.0
> drm/amd/display: Promote DC to 3.2.406
> drm/amd/display: [FW Promotion] Release 0.1.84.0
> drm/amd/display: Promote DC to 3.2.407
>
> Tony Cheng (13):
> drm/amd/display: Dispatch commit_state_hw through dc2_funcs
> drm/amd/display: Add leaf BLS entries for a virtual commit
> drm/amd/display: Build the dcn401 virtual commit natively
> drm/amd/display: Build the dcn401 seamless update in DC2
> drm/amd/display: Read accelerated mode through a query builder
> drm/amd/display: Build the kept-plane surface update in DC2
> drm/amd/display: Skip first-enable steps on a kept plane
> drm/amd/display: Program the kept-plane scaler in DC2
> drm/amd/display: Add and remove two MPO planes in DC2
> drm/amd/display: Add DC2 state facts and split the builders
> drm/amd/display: Program DC2 commit stereo once
> drm/amd/display: Get OPP heads and DPP pipes by stream in dc_resource
> drm/amd/display: Program ODM 2:1 and 3:1 on one stream in DC2
>
> Wayne Lin (1):
> drm/amd/display: Expect VRAM|GTT for the DMUB framebuffer
>
> Wenjing Liu (2):
> drm/amd/display: Read DRAM channel width from SMU init table
> drm/amd/display: Report QoS latency bounds and request
>
> .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 17 +-
> .../display/amdgpu_dm/amdgpu_dm_connector.c | 16 +-
> .../amd/display/amdgpu_dm/amdgpu_dm_dmub.c | 15 +-
> .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 3 +-
> .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 9 +-
> .../drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c | 10 +-
> .../drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c | 3 +-
> .../tests/amdgpu_dm_connector_test.c | 9 +-
> .../amdgpu_dm/tests/amdgpu_dm_dmub_test.c | 45 +-
> .../amdgpu_dm/tests/amdgpu_dm_hdcp_test.c | 27 +
> .../amdgpu_dm/tests/amdgpu_dm_irq_test.c | 6 +
> .../amdgpu_dm/tests/amdgpu_dm_psr_test.c | 35 +-
> drivers/gpu/drm/amd/display/dc/Makefile | 3 +
> .../amd/display/dc/bls_builder/bls_builder.h | 6 +-
> .../dc/bls_builder/bls_builder_internal.h | 2 +
> .../bls_builder/bls_builder_private_types.h | 137 +
> .../dc/bls_builder/dcn401_builder_internal.h | 110 +
> .../dc/bls_builder/dcn401_init_builder.c | 36 +
> .../bls_builder/dcn401_non_seamless_builder.c | 501 ++++
> .../dc/bls_builder/dcn401_seamless_builder.c | 2235 +++++++++++++++++
> .../display/dc/bls_executor/bls_executor.c | 1076 ++++++++
> .../gpu/drm/amd/display/dc/clk_mgr/clk_mgr.c | 83 +
> .../dc/clk_mgr/dcn401/dcn401_clk_mgr.c | 170 +-
> .../dc/clk_mgr/dcn401/dcn401_clk_mgr.h | 15 +-
> .../display/dc/clk_mgr/dcn42/dcn42_clk_mgr.c | 31 +-
> .../dc/clk_mgr/dcn42b/dcn42b_clk_mgr.c | 25 +-
> .../dc/clk_mgr/dcn42b/dcn42b_clk_mgr.h | 1 +
> .../drm/amd/display/dc/clk_mgr/dcn60/dalsmc.h | 3 +-
> .../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.c | 157 +-
> .../display/dc/clk_mgr/dcn60/dcn60_clk_mgr.h | 14 +-
> drivers/gpu/drm/amd/display/dc/core/dc.c | 115 +-
> .../drm/amd/display/dc/core/dc_hw_sequencer.c | 317 ++-
> .../gpu/drm/amd/display/dc/core/dc_resource.c | 48 +-
> .../amd/display/dc/core/dc_resource_helper.c | 7 +-
> .../gpu/drm/amd/display/dc/core2/dc_core2.c | 341 ++-
> .../gpu/drm/amd/display/dc/core2/dc_core2.h | 1 +
> .../drm/amd/display/dc/core2/dc_core2_init.c | 2 +-
> .../display/dc/core2/dc_core2_legacy_init.c | 2 +-
> drivers/gpu/drm/amd/display/dc/dc.h | 2 +-
> .../amd/display/dc/dc_api_dispatch/api_shim.c | 25 +
> .../amd/display/dc/dc_api_dispatch/api_shim.h | 9 +-
> drivers/gpu/drm/amd/display/dc/dc_dsc.h | 1 +
> .../gpu/drm/amd/display/dc/dc_hdmi_types.h | 1 +
> drivers/gpu/drm/amd/display/dc/dc_hw_types.h | 16 +-
> .../gpu/drm/amd/display/dc/dc_spl_translate.c | 8 +-
> drivers/gpu/drm/amd/display/dc/dc_stream.h | 3 +
> .../dml21/inc/bounding_boxes/dcn42_soc_bb.h | 4 +-
> .../dml21/inc/bounding_boxes/dcn42b_soc_bb.h | 4 +-
> .../dml21/inc/dml_top_display_cfg_types.h | 1 +
> .../dc/dml2_0/dml21/inc/dml_top_types.h | 2 +
> .../src/dml2_core/dml2_core_dcn4_calcs.c | 2 +
> .../dml2_core/dml2_core_dcn5_calcs_dchub.c | 75 +-
> .../dml2_core/dml2_core_dcn6_calcs_dchub.c | 30 +-
> .../dml2_core_dcn6_funcs_mode_programming.c | 34 +-
> .../dml2_core_dcn6_funcs_mode_support.c | 19 +-
> .../src/dml2_core/dml2_core_shared_types.h | 3 +
> .../dml21/src/dml2_core/dml2_core_utils.c | 4 +
> .../dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c | 20 +-
> .../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn5.h | 4 +-
> .../dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6.h | 8 +-
> .../dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.c | 19 +-
> .../src/inc/dml2_internal_shared_types.h | 4 +-
> .../dml21_wrapper/dml21_translation_helper.c | 17 +-
> .../dc/dml2_wrapper/dml2_mall_phantom.c | 1 -
> .../display/dc/dml2_wrapper/dml2_wrapper.h | 1 +
> .../drm/amd/display/dc/dpp/dcn60/dcn60_dpp.c | 2 +-
> .../amd/display/dc/dpp/dcn60/dcn60_dpp_dscl.c | 4 +
> .../display/dc/hubbub/dcn10/dcn10_hubbub.c | 3 +-
> .../display/dc/hubbub/dcn10/dcn10_hubbub.h | 1 +
> .../display/dc/hubbub/dcn20/dcn20_hubbub.c | 8 +-
> .../display/dc/hubbub/dcn201/dcn201_hubbub.c | 3 +-
> .../display/dc/hubbub/dcn21/dcn21_hubbub.c | 3 +-
> .../display/dc/hubbub/dcn21/dcn21_hubbub.h | 1 +
> .../display/dc/hubbub/dcn30/dcn30_hubbub.c | 5 +-
> .../display/dc/hubbub/dcn30/dcn30_hubbub.h | 1 +
> .../display/dc/hubbub/dcn31/dcn31_hubbub.c | 5 +-
> .../display/dc/hubbub/dcn32/dcn32_hubbub.c | 23 +-
> .../display/dc/hubbub/dcn35/dcn35_hubbub.c | 7 +-
> .../display/dc/hubbub/dcn35/dcn35_hubbub.h | 1 +
> .../display/dc/hubbub/dcn401/dcn401_hubbub.c | 12 +-
> .../display/dc/hubbub/dcn401/dcn401_hubbub.h | 3 +-
> .../display/dc/hubbub/dcn42/dcn42_hubbub.c | 20 +-
> .../display/dc/hubbub/dcn60/dcn60_hubbub.c | 3 +-
> .../amd/display/dc/hubp/dcn50/dcn50_hubp.c | 52 +-
> .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c | 61 +-
> .../amd/display/dc/hwss/dcn10/dcn10_hwseq.h | 9 +-
> .../amd/display/dc/hwss/dcn20/dcn20_hwseq.c | 48 +-
> .../amd/display/dc/hwss/dcn20/dcn20_hwseq.h | 9 +-
> .../amd/display/dc/hwss/dcn302/dcn302_hwseq.c | 15 +-
> .../amd/display/dc/hwss/dcn302/dcn302_hwseq.h | 6 +-
> .../amd/display/dc/hwss/dcn303/dcn303_hwseq.c | 11 +-
> .../amd/display/dc/hwss/dcn303/dcn303_hwseq.h | 6 +-
> .../amd/display/dc/hwss/dcn31/dcn31_hwseq.c | 8 +-
> .../amd/display/dc/hwss/dcn31/dcn31_hwseq.h | 3 +-
> .../amd/display/dc/hwss/dcn314/dcn314_hwseq.c | 15 +-
> .../amd/display/dc/hwss/dcn314/dcn314_hwseq.h | 3 +-
> .../amd/display/dc/hwss/dcn32/dcn32_hwseq.c | 7 +-
> .../amd/display/dc/hwss/dcn32/dcn32_hwseq.h | 3 +-
> .../amd/display/dc/hwss/dcn401/dcn401_hwseq.c | 180 +-
> .../amd/display/dc/hwss/dcn401/dcn401_hwseq.h | 2 +
> .../amd/display/dc/hwss/dcn401/dcn401_init.c | 2 +
> .../amd/display/dc/hwss/dcn60/dcn60_hwseq.c | 9 +
> .../amd/display/dc/hwss/dcn60/dcn60_hwseq.h | 1 +
> .../amd/display/dc/hwss/dcn60/dcn60_init.c | 2 +-
> .../drm/amd/display/dc/hwss/hw_sequencer.h | 422 +++-
> .../display/dc/hwss/hw_sequencer_private.h | 14 +-
> .../amd/display/dc/inc/dc_core_interface.h | 12 +
> .../amd/display/dc/inc/hw/clk_mgr_internal.h | 38 +
> .../gpu/drm/amd/display/dc/inc/hw/dchubbub.h | 22 +-
> drivers/gpu/drm/amd/display/dc/inc/resource.h | 21 +-
> .../display/dc/inc/soc_and_ip_translator.h | 4 +-
> .../drm/amd/display/dc/link/link_detection.c | 26 +
> .../dc/link/protocols/link_dp_irq_handler.c | 8 +-
> .../display/dc/link/protocols/link_hdmi_frl.c | 30 +-
> .../dc/resource/dcn42/dcn42_resource.c | 2 +
> .../dc/resource/dcn42b/dcn42b_resource.c | 5 +-
> .../dc/resource/dcn60/dcn60_resource.c | 30 +-
> .../dcn401/dcn401_soc_and_ip_translator.c | 4 +-
> .../dcn42/dcn42_soc_and_ip_translator.c | 4 +-
> .../dcn42b/dcn42b_soc_and_ip_translator.c | 4 +-
> .../dcn60/dcn60_soc_and_ip_translator.c | 96 +-
> .../amd/display/dc/state_facts/state_facts.c | 294 +++
> .../amd/display/dc/state_facts/state_facts.h | 132 +
> .../gpu/drm/amd/display/dmub/inc/dmub_cmd.h | 213 +-
> .../amd/display/modules/power/power_replay.c | 2 +-
> 125 files changed, 6985 insertions(+), 905 deletions(-)
> create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/bls_builder_private_types.h
> create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_builder_internal.h
> create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_non_seamless_builder.c
> create mode 100644 drivers/gpu/drm/amd/display/dc/bls_builder/dcn401_seamless_builder.c
> create mode 100644 drivers/gpu/drm/amd/display/dc/state_facts/state_facts.c
> create mode 100644 drivers/gpu/drm/amd/display/dc/state_facts/state_facts.h
>
^ permalink raw reply [flat|nested] 80+ messages in thread
end of thread, other threads:[~2026-11-13 10:25 UTC | newest]
Thread overview: 80+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-11 1:28 [PATCH 00/74] DC Patches November 16, 2026 Wayne Lin
2026-11-11 1:28 ` [PATCH 01/74] drm/amd/display: Fix elapsed time indentation Wayne Lin
2026-11-11 1:28 ` [PATCH 02/74] drm/amd/display: Correct the forced YCbCr420 comment Wayne Lin
2026-11-11 1:28 ` [PATCH 03/74] drm/amd/display: Remove duplicate block sequence leftovers Wayne Lin
2026-11-11 1:28 ` [PATCH 04/74] drm/amd/display: Skip UPSP programming when UPSP regs are absent Wayne Lin
2026-11-11 1:28 ` [PATCH 05/74] drm/amd/display: Dispatch commit_state_hw through dc2_funcs Wayne Lin
2026-11-11 1:28 ` [PATCH 06/74] drm/amd/display: Add leaf BLS entries for a virtual commit Wayne Lin
2026-11-11 1:28 ` [PATCH 07/74] drm/amd/display: Build the dcn401 virtual commit natively Wayne Lin
2026-11-11 1:28 ` [PATCH 08/74] drm/amd/display: Move HDMI FRL Polling Outside dc_lock Wayne Lin
2026-11-11 1:28 ` [PATCH 09/74] drm/amd/display: Extend the IOMMU mismatch workaround to DCN42B Wayne Lin
2026-11-11 1:28 ` [PATCH 10/74] drm/amd/display: Stop HDMI FRL status polling on fini Wayne Lin
2026-11-12 8:51 ` Eren Özen
2026-11-13 3:19 ` Wayne Lin
2026-11-11 1:28 ` [PATCH 11/74] drm/amd/display: Relax stutter constraints for DRR streams Wayne Lin
2026-11-11 1:28 ` [PATCH 12/74] drm/amd/display: Include Ramless Idle in stutter relaxation Wayne Lin
2026-11-11 1:28 ` [PATCH 13/74] drm/amd/display: Initialize DMUB LSDMA ring buffer Wayne Lin
2026-11-11 1:28 ` [PATCH 14/74] drm/amd/display: Fix FFE test automation link rate change Wayne Lin
2026-11-11 1:28 ` [PATCH 15/74] drm/amd/display: Update indicate pstate enable sequence Wayne Lin
2026-11-11 1:28 ` [PATCH 16/74] drm/amd/display: HWSS Refactor - Watermarks Wayne Lin
2026-11-11 1:28 ` [PATCH 17/74] drm/amd/display: Resolve clk_mgr DPP DTO targets when the sequence is built Wayne Lin
2026-11-11 1:28 ` [PATCH 18/74] drm/amd/display: Resolve clk_mgr DTBCLK " Wayne Lin
2026-11-11 1:28 ` [PATCH 19/74] drm/amd/display: Resolve the DCN401 DENTIST divider " Wayne Lin
2026-11-11 1:28 ` [PATCH 20/74] drm/amd/display: Update default VCO frequency Wayne Lin
2026-11-11 1:28 ` [PATCH 21/74] drm/amd/display: commit validated target when minimal transition cannot be built Wayne Lin
2026-11-11 1:28 ` [PATCH 22/74] drm/amd/display: Update DP HPO to use correct APG instance Wayne Lin
2026-11-11 1:28 ` [PATCH 23/74] drm/amd/display: cleanup tiling and address formats Wayne Lin
2026-11-11 1:28 ` [PATCH 24/74] drm/amd/display: Enable SST PCON FRL DSC passthrough Wayne Lin
2026-11-11 1:28 ` [PATCH 25/74] drm/amd/display: Don't let HPD IRQ handler take dc_lock in reset Wayne Lin
2026-11-11 1:28 ` [PATCH 26/74] drm/amd/display: Skip visual confirm color programming when FW owns it Wayne Lin
2026-11-11 1:28 ` [PATCH 27/74] drm/amd/display: Fix Stage Optimizer Parameter Naming Wayne Lin
2026-11-11 1:28 ` [PATCH 28/74] drm/amd/display: Build the dcn401 seamless update in DC2 Wayne Lin
2026-11-11 1:28 ` [PATCH 29/74] drm/amd/display: Read accelerated mode through a query builder Wayne Lin
2026-11-11 1:28 ` [PATCH 30/74] drm/amd/display: Ensure exit IPS before checking flip status Wayne Lin
2026-11-11 1:28 ` [PATCH 31/74] drm/amd/display: Updating missing assignments for DCN6 Wayne Lin
2026-11-11 1:28 ` [PATCH 32/74] drm/amd/display: update dcn42b based on dcn42 Wayne Lin
2026-11-11 1:28 ` [PATCH 33/74] drm/amd/display: Build the kept-plane surface update in DC2 Wayne Lin
2026-11-11 1:28 ` [PATCH 34/74] drm/amd/display: Skip first-enable steps on a kept plane Wayne Lin
2026-11-11 1:28 ` [PATCH 35/74] drm/amd/display: Enable DRR stutter constraint relaxation on DCN42 Wayne Lin
2026-11-11 1:28 ` [PATCH 36/74] drm/amd/display: detect static screen state and propagate Wayne Lin
2026-11-11 1:28 ` [PATCH 37/74] drm/amd/display: Allow DC_PSR_MASK to enable PSR without DMUB PSR cap Wayne Lin
2026-11-11 1:28 ` [PATCH 38/74] drm/amd/display: Fix DSC teardown gating in post-unlock BLS sequence Wayne Lin
2026-11-11 1:28 ` [PATCH 39/74] drm/amd/display: Enable HDCP on DCN6 Wayne Lin
2026-11-11 1:28 ` [PATCH 40/74] drm/amd/display: enable DF C-state vote on DCN42B Wayne Lin
2026-11-11 1:28 ` [PATCH 41/74] drm/amd/display: Increase the DCN42 DDR5 stutter latency Wayne Lin
2026-11-11 1:28 ` [PATCH 42/74] drm/amd/display: [FW Promotion] Release 0.1.83.0 Wayne Lin
2026-11-11 1:28 ` [PATCH 43/74] drm/amd/display: Promote DC to 3.2.406 Wayne Lin
2026-11-11 1:28 ` [PATCH 44/74] drm/amd/display: Fix KUnit tiled EDID size Wayne Lin
2026-11-12 7:17 ` Eren Özen
2026-11-11 1:28 ` [PATCH 45/74] drm/amd/display: fix hubbub read state logging Wayne Lin
2026-11-11 1:28 ` [PATCH 46/74] drm/amd/display: Keep panel power across link-loss retrain Wayne Lin
2026-11-11 1:28 ` [PATCH 47/74] drm/amd/display: Don't shrink zero-area recout in full viewport visual confirm Wayne Lin
2026-11-11 1:28 ` [PATCH 48/74] drm/amd/display: Keep patched HDMI panels on FRL across detect Wayne Lin
2026-11-11 1:28 ` [PATCH 49/74] drm/amd/display: Use expanded DMUB-provided SoC BB and IP caps Wayne Lin
2026-11-11 1:28 ` [PATCH 50/74] drm/amd/display: Only force P-State on first plane if TTU is below WM Wayne Lin
2026-11-11 1:28 ` [PATCH 51/74] drm/amd/display: Program the kept-plane scaler in DC2 Wayne Lin
2026-11-11 1:28 ` [PATCH 52/74] drm/amd/display: Add and remove two MPO planes " Wayne Lin
2026-11-11 1:28 ` [PATCH 53/74] drm/amd/display: Add DC2 state facts and split the builders Wayne Lin
2026-11-11 1:29 ` [PATCH 54/74] drm/amd/display: Program DC2 commit stereo once Wayne Lin
2026-11-11 1:29 ` [PATCH 55/74] drm/amd/display: Read DCN6 SOP constraints from the idle row Wayne Lin
2026-11-11 1:29 ` [PATCH 56/74] drm/amd/display: Use soc_bb hostvm fraction instead of hardcoded value Wayne Lin
2026-11-11 1:29 ` [PATCH 57/74] drm/amd/display: Increase state if dcfclk deep sleep needed exceeds dcfclk Wayne Lin
2026-11-11 1:29 ` [PATCH 58/74] drm/amd/display: Flip dml2_core_utils_get_segment_horizontal_contiguous() Inputs Wayne Lin
2026-11-11 1:29 ` [PATCH 59/74] drm/amd/display: Update DCN6 Calcs Wayne Lin
2026-11-11 1:29 ` [PATCH 60/74] drm/amd/display: Add effective_number_of_dpp parameter Wayne Lin
2026-11-11 1:29 ` [PATCH 61/74] drm/amd/display: Read DRAM channel width from SMU init table Wayne Lin
2026-11-11 1:29 ` [PATCH 62/74] drm/amd/display: Update DCN42B stutter latency to match DCN42 Wayne Lin
2026-11-11 1:29 ` [PATCH 63/74] drm/amd/display: Expect VRAM|GTT for the DMUB framebuffer Wayne Lin
2026-11-12 7:17 ` Eren Özen
2026-11-11 1:29 ` [PATCH 64/74] drm/amd/display: Report QoS latency bounds and request Wayne Lin
2026-11-11 1:29 ` [PATCH 65/74] drm/amd/display: Bypass chroma upsampling without the DPP cap Wayne Lin
2026-11-11 1:29 ` [PATCH 66/74] drm/amd/display: Limit VRAM aperture retrieval to DCN6 Wayne Lin
2026-11-11 1:29 ` [PATCH 67/74] drm/amd/display: Make DPP_PG_CONTROL and HUBP_PG_CONTROL BLS compliant Wayne Lin
2026-11-11 1:29 ` [PATCH 68/74] drm/amd/display: Seed boot clocks to avoid lowering pre-OS display clocks Wayne Lin
2026-11-11 1:29 ` [PATCH 69/74] drm/amd/display: Account for split pipe overlap in alt channel copy size Wayne Lin
2026-11-11 1:29 ` [PATCH 70/74] drm/amd/display: Get OPP heads and DPP pipes by stream in dc_resource Wayne Lin
2026-11-11 1:29 ` [PATCH 71/74] drm/amd/display: Program ODM 2:1 and 3:1 on one stream in DC2 Wayne Lin
2026-11-11 1:29 ` [PATCH 72/74] drm/amd/display: Enable Replay timing sync by default Wayne Lin
2026-11-11 1:29 ` [PATCH 73/74] drm/amd/display: [FW Promotion] Release 0.1.84.0 Wayne Lin
2026-11-11 1:29 ` [PATCH 74/74] drm/amd/display: Promote DC to 3.2.407 Wayne Lin
2026-11-13 10:20 ` [PATCH 00/74] DC Patches November 16, 2026 Chung, ChiaHsuan (Tom)
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox