All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wayne Lin <Wayne.Lin@amd.com>
To: <amd-gfx@lists.freedesktop.org>
Cc: Harry Wentland <harry.wentland@amd.com>,
	Leo Li <sunpeng.li@amd.com>,
	Aurabindo Pillai <aurabindo.pillai@amd.com>,
	Roman Li <roman.li@amd.com>, Wayne Lin <wayne.lin@amd.com>,
	Tom Chung <chiahsuan.chung@amd.com>,
	"Fangzhi Zuo" <jerry.zuo@amd.com>,
	Dan Wheeler <daniel.wheeler@amd.com>, Ray Wu <Ray.Wu@amd.com>,
	Ivan Lipski <ivan.lipski@amd.com>, Alex Hung <alex.hung@amd.com>,
	James Lin <PingLei.Lin@amd.com>,
	Chenyu Chen <Chen-Yu.Chen@amd.com>,
	Tony Cheng <tony.cheng@amd.com>, Jun Lei <jun.lei@amd.com>
Subject: [PATCH 28/74] drm/amd/display: Build the dcn401 seamless update in DC2
Date: Wed, 11 Nov 2026 09:28:34 +0800	[thread overview]
Message-ID: <20261111014100.3596072-29-Wayne.Lin@amd.com> (raw)
In-Reply-To: <20261111014100.3596072-1-Wayne.Lin@amd.com>

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), &not_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, &not_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 = &params->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 = &params->head_num_steps;
-	tail.steps = params->tail_sequence;
-	tail.num_steps = &params->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, &params->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, &params->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, &params->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


  parent reply	other threads:[~2026-11-11  1:42 UTC|newest]

Thread overview: 78+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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-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 ` Wayne Lin [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261111014100.3596072-29-Wayne.Lin@amd.com \
    --to=wayne.lin@amd.com \
    --cc=Chen-Yu.Chen@amd.com \
    --cc=PingLei.Lin@amd.com \
    --cc=Ray.Wu@amd.com \
    --cc=alex.hung@amd.com \
    --cc=amd-gfx@lists.freedesktop.org \
    --cc=aurabindo.pillai@amd.com \
    --cc=chiahsuan.chung@amd.com \
    --cc=daniel.wheeler@amd.com \
    --cc=harry.wentland@amd.com \
    --cc=ivan.lipski@amd.com \
    --cc=jerry.zuo@amd.com \
    --cc=jun.lei@amd.com \
    --cc=roman.li@amd.com \
    --cc=sunpeng.li@amd.com \
    --cc=tony.cheng@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.