[PATCH 14/34] drm/amd/display: eDP panel polarity control and test support

Tom Chung <[email protected]> Wed, 5 Aug 2026 14:36:26 +0800
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Harry VanZyllDeJong <[email protected]>

eDP panels can require explicit polarity control during power transitions
to ensure correct signal behavior. Without this, panels that depend on
polarity state may behave incorrectly when combined with refresh rate drop
logic, since the two features previously had no coordination point.

To address this, a set/reset/query API is introduced and integrated into
the resource layer so polarity is managed as a first-class concern within
the existing power optimization flow. RamlessPowerOptimization is updated
to coordinate polarity state alongside refresh rate drop decisions,
preventing the two paths from conflicting during transitions.

Reviewed-by: Sreeja Golui <[email protected]>
Signed-off-by: Harry VanZyllDeJong <[email protected]>
Signed-off-by: Tom Chung <[email protected]>
---
 .../drm/amd/display/dc/core/dc_link_exports.c | 22 ++++++++
 drivers/gpu/drm/amd/display/dc/dc.h           |  7 +++
 drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c  | 52 +++++++++++++++++++
 drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h  | 27 ++++++++++
 .../gpu/drm/amd/display/dc/inc/link_service.h |  5 +-
 .../drm/amd/display/dc/link/link_factory.c    |  4 ++
 .../link/protocols/link_edp_panel_control.c   | 30 +++++++++++
 .../link/protocols/link_edp_panel_control.h   |  4 ++
 8 files changed, 150 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c
index 14fc4ef482f1..ba1b46d3a13e 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_exports.c
@@ -601,3 +601,25 @@ void dc_link_get_alpm_support(struct dc_link *link,
 {
 	link->dc->link_srv->edp_get_alpm_support(link, auxless_support, auxwake_support);
 }
+
+void dc_link_set_panel_polarity_enable(struct dc_link *link, bool enable)
+{
+	if (link->dc->link_srv->edp_set_panel_polarity_enabled)
+		link->dc->link_srv->edp_set_panel_polarity_enabled(link, enable);
+}
+
+void dc_link_panel_polarity_reset(struct dc_link *link)
+{
+	if (link->dc->link_srv->edp_panel_polarity_reset)
+		link->dc->link_srv->edp_panel_polarity_reset(link);
+}
+
+bool dc_link_get_panel_polarity(struct dc_link *link, int32_t *polarity)
+{
+	bool ret = false;
+
+	if (link->dc->link_srv->edp_get_panel_polarity)
+		ret = link->dc->link_srv->edp_get_panel_polarity(link, polarity);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 6e45a484a9ad..34d4ede12e79 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -3897,4 +3897,11 @@ unsigned int dc_override_memory_bandwidth_request(
 		struct dc *dc,
 		unsigned int bw_mbps);
 
+/**
+ * Panel Polarity Control
+ */
+void dc_link_set_panel_polarity_enable(struct dc_link *link, bool enable);
+void dc_link_panel_polarity_reset(struct dc_link *link);
+bool dc_link_get_panel_polarity(struct dc_link *link, int32_t *polarity);
+
 #endif /* DC_INTERFACE_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
index 47fa3d4265e0..b9fd375a3997 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
@@ -2475,3 +2475,55 @@ void dc_dmub_srv_get_fams2_debug_meta(struct dc_dmub_srv *dc_dmub_srv)
 	dm_execute_dmub_cmd_list(dc_dmub_srv->ctx, 1, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
 
 }
+
+void dc_dmub_srv_panel_polarity_set_enable(struct dc_dmub_srv *dc_dmub_srv, uint8_t panel_inst, bool enable)
+{
+	union dmub_rb_cmd cmd;
+	struct dc_context *ctx = dc_dmub_srv->ctx;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.panel_polarity_enable.header.type = DMUB_CMD__PANEL_POLARITY;
+	cmd.panel_polarity_enable.header.sub_type = DMUB_CMD__PANEL_POLARITY_ENABLE;
+	cmd.panel_polarity_enable.header.payload_bytes = sizeof(cmd.panel_polarity_enable.data);
+	cmd.panel_polarity_enable.data.enable = enable ? 1 : 0;
+	cmd.panel_polarity_enable.data.otg_inst = panel_inst;
+
+	dc_wake_and_execute_dmub_cmd(ctx, &cmd, DM_DMUB_WAIT_TYPE_NO_WAIT);
+}
+
+void dc_dmub_srv_panel_polarity_reset(struct dc_dmub_srv *dc_dmub_srv, uint8_t panel_inst)
+{
+	union dmub_rb_cmd cmd;
+	struct dc_context *ctx = dc_dmub_srv->ctx;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.panel_polarity_enable.header.type = DMUB_CMD__PANEL_POLARITY;
+	cmd.panel_polarity_enable.header.sub_type = DMUB_CMD__PANEL_POLARITY_RESET;
+	cmd.panel_polarity_enable.header.payload_bytes = sizeof(cmd.panel_polarity_enable.data);
+	cmd.panel_polarity_enable.data.otg_inst = panel_inst;
+
+	dc_wake_and_execute_dmub_cmd(ctx, &cmd, DM_DMUB_WAIT_TYPE_NO_WAIT);
+}
+
+bool dc_dmub_srv_panel_polarity_get_polarity(struct dc_dmub_srv *dc_dmub_srv, uint8_t panel_inst, int32_t *polarity)
+{
+	bool ret = false;
+	uint32_t raw_polarity = 0;
+	uint32_t retry_count = 0;
+	struct dc_context *ctx = dc_dmub_srv->ctx;
+
+	*polarity = 0;
+
+	do {
+		/* Send gpint command and wait for ack */
+		if (dc_wake_and_execute_gpint(ctx, DMUB_GPINT__PANEL_POLARITY_GET_BIAS, panel_inst, &raw_polarity,
+			DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)) {
+			*polarity = (int32_t)raw_polarity;
+			ret = true;
+		}
+	} while (++retry_count <= 1000 && ret == false);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
index 6d15ed77c17d..ec5387aedf46 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
@@ -424,4 +424,31 @@ bool dc_dmub_srv_ihc_set_dig_hdcp_interrupt_dest(
  * @dc_dmub_srv - pointer to DMUB service object
  */
 void dc_dmub_srv_get_fams2_debug_meta(struct dc_dmub_srv *dc_dmub_srv);
+
+/**
+ * dc_dmub_srv_panel_polarity_set_enable() - Enables or disables panel polarity.
+ *
+ * @dc_dmub_srv: DMUB service handle
+ * @panel_inst: Panel instance
+ * @enable: true to enable, false to disable
+ */
+void dc_dmub_srv_panel_polarity_set_enable(struct dc_dmub_srv *dc_dmub_srv, uint8_t panel_inst, bool enable);
+
+/**
+ * dc_dmub_srv_panel_polarity_reset() - Resets panel polarity.
+ *
+ * @dc_dmub_srv: DMUB service handle
+ * @panel_inst: Panel instance
+ */
+void dc_dmub_srv_panel_polarity_reset(struct dc_dmub_srv *dc_dmub_srv, uint8_t panel_inst);
+
+/**
+ * dc_dmub_srv_panel_polarity_get_polarity() - Gets the current panel polarity.
+ *
+ * @dc_dmub_srv: DMUB service handle
+ * @panel_inst: Panel instance
+ * @polarity: Pointer to store the current polarity
+ */
+bool dc_dmub_srv_panel_polarity_get_polarity(struct dc_dmub_srv *dc_dmub_srv, uint8_t panel_inst, int32_t *polarity);
+
 #endif /* _DMUB_DC_SRV_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_service.h b/drivers/gpu/drm/amd/display/dc/inc/link_service.h
index 026d28046eea..f87dfe63227d 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/link_service.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/link_service.h
@@ -315,7 +315,9 @@ struct link_service {
 	bool (*dp_pr_set_general_cmd)(struct dc_link *link, struct dmub_cmd_pr_general_cmd_data *general_cmd_data);
 	bool (*dp_pr_get_state)(const struct dc_link *link, uint64_t *state);
 	void (*edp_set_panel_power)(struct dc_link *link, bool powerOn);
-
+	void (*edp_set_panel_polarity_enabled)(const struct dc_link *link, bool enable);
+	void (*edp_panel_polarity_reset)(struct dc_link *link);
+	bool (*edp_get_panel_polarity)(struct dc_link *link, int32_t *polarity);
 
 	/*************************** HDMI FRL *********************************/
 	bool (*hdmi_frl_poll_status_flag)(struct dc_link *link);
@@ -363,5 +365,6 @@ struct link_service {
 	uint64_t (*dp_trace_get_edp_poweroff_timestamp)(struct dc_link *link);
 	void (*dp_trace_source_sequence)(
 			struct dc_link *link, uint8_t dp_test_mode);
+
 };
 #endif /* __DC_LINK_HPD_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c
index 89265b083935..8560ee727bfa 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c
@@ -234,6 +234,10 @@ static void construct_link_service_edp_panel_control(struct link_service *link_s
 	link_srv->edp_receiver_ready_T7 = edp_receiver_ready_T7;
 	link_srv->edp_power_alpm_dpcd_enable = edp_power_alpm_dpcd_enable;
 	link_srv->edp_set_panel_power = edp_set_panel_power;
+
+	link_srv->edp_set_panel_polarity_enabled = edp_set_panel_polarity_enabled;
+	link_srv->edp_panel_polarity_reset = edp_panel_polarity_reset;
+	link_srv->edp_get_panel_polarity = edp_get_panel_polarity;
 }
 
 /* link dp panel replay implements DP panel replay functionality.
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
index 16951a9550f2..e3527b14686b 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
@@ -1301,3 +1301,33 @@ void edp_set_panel_assr(struct dc_link *link, struct pipe_ctx *pipe_ctx,
 			*panel_mode = DP_PANEL_MODE_DEFAULT;
 	}
 }
+
+void edp_set_panel_polarity_enabled(const struct dc_link *link, bool enable)
+{
+	struct dc *dc = link->ctx->dc;
+	struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv;
+
+	if (dc_dmub_srv)
+		dc_dmub_srv_panel_polarity_set_enable(dc_dmub_srv, 0, enable);
+}
+
+void edp_panel_polarity_reset(struct dc_link *link)
+{
+	struct dc *dc = link->ctx->dc;
+	struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv;
+
+	if (dc_dmub_srv)
+		dc_dmub_srv_panel_polarity_reset(dc_dmub_srv, 0);
+}
+
+bool edp_get_panel_polarity(struct dc_link *link, int32_t *polarity)
+{
+	struct dc *dc = link->ctx->dc;
+	struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv;
+	bool ret = false;
+
+	if (dc_dmub_srv)
+		ret = dc_dmub_srv_panel_polarity_get_polarity(dc_dmub_srv, 0, polarity);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h
index 8fdb76d9953e..51de13e2387e 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h
@@ -77,4 +77,8 @@ bool edp_setup_freesync_replay(struct dc_link *link, const struct dc_stream_stat
 void edp_set_panel_power(struct dc_link *link, bool powerOn);
 void edp_set_panel_assr(struct dc_link *link, struct pipe_ctx *pipe_ctx,
 		enum dp_panel_mode *panel_mode, bool enable);
+
+void edp_set_panel_polarity_enabled(const struct dc_link *link, bool enable);
+void edp_panel_polarity_reset(struct dc_link *link);
+bool edp_get_panel_polarity(struct dc_link *link, int32_t *polarity);
 #endif /* __DC_LINK_EDP_POWER_CONTROL_H__ */
-- 
2.43.0