mirror of https://lore.kernel.org/linux-amlogic/
 help / color / mirror / Atom feed
From: Xilin Wu <sophon@radxa.com>
To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
	 Maxime Ripard <mripard@kernel.org>,
	Thomas Zimmermann <tzimmermann@suse.de>,
	 David Airlie <airlied@gmail.com>,
	Simona Vetter <simona@ffwll.ch>,
	 Rob Clark <robin.clark@oss.qualcomm.com>,
	 Dmitry Baryshkov <lumag@kernel.org>,
	 Abhinav Kumar <abhinav.kumar@linux.dev>,
	 Jessica Zhang <jesszhan0024@gmail.com>,
	Sean Paul <sean@poorly.run>,
	 Marijn Suijten <marijn.suijten@somainline.org>,
	 Andrzej Hajda <andrzej.hajda@intel.com>,
	 Neil Armstrong <neil.armstrong@linaro.org>,
	Robert Foss <rfoss@kernel.org>,
	 Laurent Pinchart <Laurent.pinchart@ideasonboard.com>,
	 Jonas Karlman <jonas@kwiboo.se>,
	Jernej Skrabec <jernej.skrabec@gmail.com>,
	 Luca Ceresoli <luca.ceresoli@bootlin.com>,
	 Kevin Hilman <khilman@baylibre.com>,
	Jerome Brunet <jbrunet@baylibre.com>,
	 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	 linux-arm-msm@vger.kernel.org, freedreno@lists.freedesktop.org,
	 dragon@radxa.com, Xilin Wu <sophon@radxa.com>,
	 linux-amlogic@lists.infradead.org,
	linux-arm-kernel@lists.infradead.org
Subject: [PATCH v2 13/20] drm/msm/dp: Track output bit depth in bridge atomic state
Date: Fri, 09 Oct 2026 11:16:05 +0800	[thread overview]
Message-ID: <20261009-msm-dp-hdr10-v2-13-1835d4966da3@radxa.com> (raw)
In-Reply-To: <20261009-msm-dp-hdr10-v2-0-1835d4966da3@radxa.com>

Advertise the 10 bpc DP and eDP capability through bridge->max_bpc so
the bridge connector helper attaches the max bpc property. Select a
supported component depth from the new connector state. Keep the result
in a private bridge state so TEST_ONLY atomic commits do not modify the
running stream. Force a modeset when max bpc changes to apply the new
depth to the stream.

Require valid cached bandwidth limits during atomic check. The preceding
eDP discovery change supplies them before the first modeset. Reject
unknown limits instead of accepting a configuration for validation during
enable. Retain support for 6 bpc SDR panels and use an 8 bpc ceiling when
the bridge chain does not expose max bpc.

Keep the selected depth throughout enable. Filter link training fallback
candidates against the checked stream bandwidth, including YUV420, and
retain a final bandwidth check before starting video. A lower-rate link
may be used only if it carries the unchanged stream. Otherwise unwind
and report the link failure rather than silently lowering the depth.
PHY and video compliance tests retain their specified parameters.

Continue searching when a candidate lacks bandwidth: reducing the lane
count restores the maximum rate and can yield a viable combination.
Only successful link training completes the search; successful PHY
reinitialization on the final retry must not masquerade as training
success. Keep sink training cleanup on the failure path.

Retain the generic bridge helper's missing-state guard in the custom
duplicate callback. Initial state allocation can fail at bridge attach;
return NULL in that case so atomic state acquisition reports -ENOMEM
instead of copying from a NULL pointer.

Assisted-by: LLM
Signed-off-by: Xilin Wu <sophon@radxa.com>
---
 drivers/gpu/drm/msm/dp/dp_ctrl.c    | 117 ++++++++++++++++++++++--------------
 drivers/gpu/drm/msm/dp/dp_display.c |  39 +++++++++++-
 drivers/gpu/drm/msm/dp/dp_display.h |   6 ++
 drivers/gpu/drm/msm/dp/dp_drm.c     |  82 ++++++++++++++++++++++---
 drivers/gpu/drm/msm/dp/dp_drm.h     |   7 +++
 drivers/gpu/drm/msm/dp/dp_panel.c   |   5 --
 drivers/gpu/drm/msm/dp/dp_utils.c   |  33 ++++++++++
 drivers/gpu/drm/msm/dp/dp_utils.h   |   6 ++
 8 files changed, 232 insertions(+), 63 deletions(-)

diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index f633f6cf5764..791bb3af2cad 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -23,6 +23,7 @@
 
 #include "dp_reg.h"
 #include "dp_ctrl.h"
+#include "dp_utils.h"
 #include "dp_link.h"
 
 #define POLLING_SLEEP_US			1000
@@ -2365,10 +2366,56 @@ static bool msm_dp_ctrl_channel_eq_ok(struct msm_dp_ctrl_private *ctrl)
 	return drm_dp_channel_eq_ok(link_status, num_lanes);
 }
 
+static bool msm_dp_ctrl_link_fits(struct msm_dp_ctrl_private *ctrl,
+				  const struct msm_dp_panel *panel)
+{
+	return msm_dp_utils_link_fits(panel->msm_dp_mode.drm_mode.clock,
+				      panel->msm_dp_mode.bpp,
+				      ctrl->link->link_params.rate,
+				      ctrl->link->link_params.num_lanes,
+				      panel->msm_dp_mode.out_fmt_is_yuv_420);
+}
+
+static int msm_dp_ctrl_next_link_params(struct msm_dp_ctrl_private *ctrl,
+					struct msm_dp_panel *panel,
+					unsigned int training_step,
+					const u8 *link_status, bool check_bw)
+{
+	unsigned int lanes;
+	int ret;
+
+	do {
+		lanes = ctrl->link->link_params.num_lanes;
+		if (training_step == DP_TRAINING_1) {
+			ret = msm_dp_ctrl_link_rate_down_shift(ctrl);
+			if (ret) {
+				if (!msm_dp_ctrl_clock_recovery_any_ok(link_status, lanes))
+					return ret;
+				ret = msm_dp_ctrl_link_lane_down_shift(ctrl, panel);
+			}
+		} else if (training_step == DP_TRAINING_2) {
+			if (!drm_dp_clock_recovery_ok(link_status, lanes))
+				ret = msm_dp_ctrl_link_rate_down_shift(ctrl);
+			else
+				ret = msm_dp_ctrl_link_lane_down_shift(ctrl, panel);
+		} else {
+			return -EIO;
+		}
+		if (ret)
+			return ret;
+
+		/* Reducing lanes restores the maximum rate; keep looking. */
+	} while (check_bw && !msm_dp_ctrl_link_fits(ctrl, panel));
+
+	return 0;
+}
+
 int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl,
 			struct msm_dp_panel *panel)
 {
 	int rc = 0;
+	bool trained = false;
+	bool check_bw;
 	struct msm_dp_ctrl_private *ctrl;
 	u32 rate;
 	int link_train_max_retries = 5;
@@ -2382,6 +2429,8 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl,
 
 	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
 
+	check_bw = !(ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) &&
+		   !panel->video_test;
 	rate = panel->link_info.rate;
 	pixel_rate = panel->msm_dp_mode.drm_mode.clock;
 
@@ -2404,6 +2453,9 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl,
 		ctrl->link->link_params.rate, ctrl->link->link_params.num_lanes,
 		pixel_rate);
 
+	if (check_bw && !msm_dp_ctrl_link_fits(ctrl, panel))
+		return -ENOSPC;
+
 	rc = msm_dp_ctrl_enable_mainlink_clocks(ctrl, panel);
 	if (rc)
 		return rc;
@@ -2411,55 +2463,22 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl,
 	while (--link_train_max_retries) {
 		training_step = DP_TRAINING_NONE;
 		rc = msm_dp_ctrl_setup_main_link(ctrl, panel, &training_step);
-		if (rc == 0) {
-			/* training completed successfully */
+		if (!rc) {
+			trained = true;
 			break;
-		} else if (training_step == DP_TRAINING_1) {
-			/* link train_1 failed */
-			if (!msm_dp_aux_is_link_connected(ctrl->aux))
-				break;
-
-			drm_dp_dpcd_read_link_status(ctrl->aux, link_status);
-
-			rc = msm_dp_ctrl_link_rate_down_shift(ctrl);
-			if (rc < 0) { /* already in RBR = 1.6G */
-				if (msm_dp_ctrl_clock_recovery_any_ok(link_status,
-					ctrl->link->link_params.num_lanes)) {
-					/*
-					 * some lanes are ready,
-					 * reduce lane number
-					 */
-					rc = msm_dp_ctrl_link_lane_down_shift(ctrl, panel);
-					if (rc < 0) { /* lane == 1 already */
-						/* end with failure */
-						break;
-					}
-				} else {
-					/* end with failure */
-					break; /* lane == 1 already */
-				}
-			}
-		} else if (training_step == DP_TRAINING_2) {
-			/* link train_2 failed */
-			if (!msm_dp_aux_is_link_connected(ctrl->aux))
-				break;
-
-			drm_dp_dpcd_read_link_status(ctrl->aux, link_status);
+		}
 
-			if (!drm_dp_clock_recovery_ok(link_status,
-					ctrl->link->link_params.num_lanes))
-				rc = msm_dp_ctrl_link_rate_down_shift(ctrl);
-			else
-				rc = msm_dp_ctrl_link_lane_down_shift(ctrl, panel);
+		if (!msm_dp_aux_is_link_connected(ctrl->aux))
+			break;
 
-			if (rc < 0) {
-				/* end with failure */
-				break; /* lane == 1 already */
-			}
+		if (drm_dp_dpcd_read_link_status(ctrl->aux, link_status) < 0)
+			break;
 
-			/* stop link training before start re training  */
-			msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX);
-		}
+		msm_dp_ctrl_clear_training_pattern(ctrl, panel, DP_PHY_DPRX);
+		rc = msm_dp_ctrl_next_link_params(ctrl, panel, training_step,
+						  link_status, check_bw);
+		if (rc)
+			break;
 
 		rc = msm_dp_ctrl_reinitialize_mainlink(ctrl, panel);
 		if (rc) {
@@ -2468,6 +2487,10 @@ int msm_dp_ctrl_on_link(struct msm_dp_ctrl *msm_dp_ctrl,
 		}
 	}
 
+	/* Reinitializing the PHY is not a successful training attempt. */
+	if (!trained && !rc)
+		rc = -EIO;
+
 	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
 		return rc;
 
@@ -2617,6 +2640,10 @@ int msm_dp_ctrl_on_stream(struct msm_dp_ctrl *msm_dp_ctrl, struct msm_dp_panel *
 
 	ctrl = container_of(msm_dp_ctrl, struct msm_dp_ctrl_private, msm_dp_ctrl);
 
+	/* Never change the stream depth selected by atomic check. */
+	if (!panel->video_test && !msm_dp_ctrl_link_fits(ctrl, panel))
+		return -ENOSPC;
+
 	pixel_rate_orig = panel->msm_dp_mode.drm_mode.clock;
 	pixel_rate = pixel_rate_orig;
 
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 9b7a1d18dfe0..1f9595ff5fd9 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -28,6 +28,7 @@
 #include "dp_panel.h"
 #include "dp_display.h"
 #include "dp_drm.h"
+#include "dp_utils.h"
 #include "dp_audio.h"
 #include "dp_debug.h"
 
@@ -677,7 +678,8 @@ static int msm_dp_init_sub_modules(struct msm_dp_display_private *dp)
 
 static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
 				   const struct drm_display_mode *adjusted_mode,
-				   struct msm_dp_panel *msm_dp_panel)
+				   struct msm_dp_panel *msm_dp_panel,
+				   const struct msm_dp_bridge_state *state)
 {
 	struct msm_dp_display_private *dp;
 	u32 bpp;
@@ -687,7 +689,7 @@ static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
 	if (msm_dp_display_check_video_test(msm_dp_display))
 		bpp = msm_dp_display_get_test_bpp(msm_dp_display);
 	else
-		bpp = msm_dp_panel->connector->display_info.bpc * 3;
+		bpp = state->bpp;
 
 	msm_dp_panel_init_panel_info(msm_dp_panel, adjusted_mode, bpp ? bpp : 24);
 
@@ -697,6 +699,34 @@ static int msm_dp_display_set_mode(struct msm_dp *msm_dp_display,
 	return 0;
 }
 
+int msm_dp_display_atomic_check(struct msm_dp *msm_dp_display,
+				struct msm_dp_bridge_state *state,
+				const struct drm_display_mode *mode,
+				const struct drm_connector_state *conn_state)
+{
+	const struct drm_display_info *info = &conn_state->connector->display_info;
+	struct msm_dp_display_private *dp;
+	unsigned int clock = mode->clock;
+	int bpp;
+
+	dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
+
+	guard(mutex)(&dp->plugged_lock);
+
+	if (!dp->panel->link_caps_valid)
+		return -EINVAL;
+
+	bpp = msm_dp_utils_select_bpp(info->bpc, conn_state->max_requested_bpc ?: 8,
+				      clock, dp->panel->link_info.rate,
+				      dp->panel->link_info.num_lanes,
+				      drm_mode_is_420_only(info, mode));
+	if (bpp < 0)
+		return bpp;
+
+	state->bpp = bpp;
+	return 0;
+}
+
 static int msm_dp_display_prepare(struct msm_dp_display_private *dp)
 {
 	struct msm_dp *msm_dp_display = &dp->msm_dp_display;
@@ -1593,6 +1623,7 @@ void msm_dp_display_atomic_pre_enable(struct msm_dp *msm_dp_display,
 	struct msm_dp_display_private *dp;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *crtc_state;
+	struct drm_bridge_state *bridge_state;
 
 	dp = container_of(msm_dp_display, struct msm_dp_display_private, msm_dp_display);
 
@@ -1600,13 +1631,15 @@ void msm_dp_display_atomic_pre_enable(struct msm_dp *msm_dp_display,
 	if (!crtc)
 		return;
 	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	bridge_state = drm_atomic_get_new_bridge_state(state, msm_dp_display->bridge);
 
 	/*
 	 * The DPU encoder's .atomic_enable() reads the mode's YUV420 / wide bus
 	 * state and runs before the bridge's .atomic_enable(), so the mode must
 	 * be programmed here, in .atomic_pre_enable().
 	 */
-	msm_dp_display_set_mode(msm_dp_display, &crtc_state->adjusted_mode, dp->panel);
+	msm_dp_display_set_mode(msm_dp_display, &crtc_state->adjusted_mode, dp->panel,
+				to_dp_bridge_state(bridge_state));
 }
 
 static void msm_dp_display_abort_enable_locked(struct msm_dp_display_private *dp)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 83117e7619f3..6a77e5952530 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -11,6 +11,8 @@
 
 #define DP_MAX_PIXEL_CLK_KHZ	675000
 
+struct msm_dp_bridge_state;
+
 struct msm_dp {
 	struct drm_device *drm_dev;
 	struct platform_device *pdev;
@@ -43,4 +45,8 @@ enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
 					       const struct drm_display_info *info,
 					       const struct drm_display_mode *mode);
 
+int msm_dp_display_atomic_check(struct msm_dp *dp, struct msm_dp_bridge_state *state,
+				const struct drm_display_mode *mode,
+				const struct drm_connector_state *conn_state);
+
 #endif /* _DP_DISPLAY_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index df224ae46d9d..9f1aa87a3687 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -4,6 +4,7 @@
  */
 
 #include <linux/string_choices.h>
+#include <linux/slab.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_bridge.h>
@@ -116,10 +117,71 @@ static enum drm_mode_status msm_dp_bridge_mode_valid(struct drm_bridge *drm_brid
 	return msm_dp_display_mode_valid(dp, info, mode);
 }
 
+static struct drm_bridge_state *msm_dp_bridge_create_state(struct drm_bridge *bridge)
+{
+	struct msm_dp_bridge_state *state;
+
+	state = kzalloc_obj(*state);
+	if (!state)
+		return ERR_PTR(-ENOMEM);
+
+	__drm_atomic_helper_bridge_state_init(&state->base, bridge);
+	return &state->base;
+}
+
+static struct drm_bridge_state *msm_dp_bridge_duplicate_state(struct drm_bridge *bridge)
+{
+	struct msm_dp_bridge_state *state;
+	struct drm_bridge_state *old_state;
+
+	if (WARN_ON(!bridge->base.state))
+		return NULL;
+
+	old_state = drm_priv_to_bridge_state(bridge->base.state);
+	state = kmemdup(to_dp_bridge_state(old_state), sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_bridge_duplicate_state(bridge, &state->base);
+	return &state->base;
+}
+
+static void msm_dp_bridge_destroy_state(struct drm_bridge *bridge,
+					struct drm_bridge_state *state)
+{
+	kfree(to_dp_bridge_state(state));
+}
+
+static int msm_dp_bridge_atomic_check(struct drm_bridge *bridge,
+				      struct drm_bridge_state *bridge_state,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	struct drm_connector_state *old_conn_state;
+	int ret;
+
+	if (!conn_state->crtc)
+		return 0;
+
+	ret = msm_dp_display_atomic_check(to_dp_bridge(bridge)->msm_dp_display,
+					  to_dp_bridge_state(bridge_state),
+					  &crtc_state->adjusted_mode, conn_state);
+	if (ret)
+		return ret;
+
+	old_conn_state = drm_atomic_get_old_connector_state(conn_state->state,
+							    conn_state->connector);
+	if (old_conn_state->max_requested_bpc != conn_state->max_requested_bpc)
+		crtc_state->mode_changed = true;
+
+	return 0;
+}
+
 static const struct drm_bridge_funcs msm_dp_bridge_ops = {
-	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
-	.atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
-	.atomic_create_state           = drm_atomic_helper_bridge_create_state,
+	.atomic_check           = msm_dp_bridge_atomic_check,
+	.atomic_duplicate_state = msm_dp_bridge_duplicate_state,
+	.atomic_destroy_state   = msm_dp_bridge_destroy_state,
+	.atomic_create_state           = msm_dp_bridge_create_state,
 	.atomic_pre_enable      = msm_dp_bridge_atomic_pre_enable,
 	.atomic_enable          = msm_dp_bridge_atomic_enable,
 	.atomic_disable         = msm_dp_bridge_atomic_disable,
@@ -154,7 +216,7 @@ static int msm_edp_bridge_atomic_check(struct drm_bridge *drm_bridge,
 	if (crtc_state->self_refresh_active && !dp->psr_supported)
 		return -EINVAL;
 
-	return 0;
+	return msm_dp_bridge_atomic_check(drm_bridge, bridge_state, crtc_state, conn_state);
 }
 
 static void msm_edp_bridge_atomic_enable(struct drm_bridge *drm_bridge,
@@ -283,9 +345,8 @@ static enum drm_mode_status msm_edp_bridge_mode_valid(struct drm_bridge *bridge,
 		return MODE_CLOCK_HIGH;
 
 	/*
-	 * The eDP controller currently does not have a reliable way of
-	 * enabling panel power to read sink capabilities. So, we rely
-	 * on the panel driver to populate only supported modes for now.
+	 * Atomic check validates bandwidth for the selected component depth
+	 * against the capabilities cached during connector detection.
 	 */
 	return MODE_OK;
 }
@@ -304,9 +365,9 @@ static const struct drm_bridge_funcs msm_edp_bridge_ops = {
 	.atomic_disable = msm_edp_bridge_atomic_disable,
 	.atomic_post_disable = msm_edp_bridge_atomic_post_disable,
 	.mode_valid = msm_edp_bridge_mode_valid,
-	.atomic_create_state = drm_atomic_helper_bridge_create_state,
-	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_create_state = msm_dp_bridge_create_state,
+	.atomic_duplicate_state = msm_dp_bridge_duplicate_state,
+	.atomic_destroy_state = msm_dp_bridge_destroy_state,
 	.atomic_check = msm_edp_bridge_atomic_check,
 	.debugfs_init = msm_edp_bridge_debugfs_init,
 };
@@ -328,6 +389,7 @@ int msm_dp_bridge_init(struct msm_dp *msm_dp_display, struct drm_device *dev,
 
 	bridge = &msm_dp_bridge->bridge;
 	bridge->type = msm_dp_display->connector_type;
+	bridge->max_bpc = 10;
 	bridge->ycbcr_420_allowed = yuv_supported;
 
 	/*
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index d37e2ff95f4b..4c651cc40f4d 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -17,6 +17,13 @@ struct msm_dp_bridge {
 	struct msm_dp *msm_dp_display;
 };
 
+struct msm_dp_bridge_state {
+	struct drm_bridge_state base;
+	u32 bpp;
+};
+
+#define to_dp_bridge_state(x) container_of((x), struct msm_dp_bridge_state, base)
+
 #define to_dp_bridge(x)     container_of((x), struct msm_dp_bridge, bridge)
 
 struct drm_connector *msm_dp_drm_connector_init(struct msm_dp *msm_dp_display,
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index d144af2fe008..af05441bc32a 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -734,11 +734,6 @@ int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel,
 				drm_mode->clock);
 	drm_dbg_dp(panel->drm_dev, "bpp = %d\n", msm_dp_panel->msm_dp_mode.bpp);
 
-	msm_dp_panel->msm_dp_mode.bpp = msm_dp_panel_get_mode_bpp(msm_dp_panel, msm_dp_panel->msm_dp_mode.bpp,
-						      msm_dp_panel->msm_dp_mode.drm_mode.clock);
-
-	drm_dbg_dp(panel->drm_dev, "updated bpp = %d\n",
-				msm_dp_panel->msm_dp_mode.bpp);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_utils.c b/drivers/gpu/drm/msm/dp/dp_utils.c
index 72bae48a1fe0..8b6ef2f6d7f4 100644
--- a/drivers/gpu/drm/msm/dp/dp_utils.c
+++ b/drivers/gpu/drm/msm/dp/dp_utils.c
@@ -89,6 +89,39 @@ void msm_dp_utils_pack_sdp_header(const struct dp_sdp_header *sdp_header,
 		FIELD_PREP(PARITY_3_MASK, msm_dp_utils_calculate_parity(sdp_header->HB3));
 }
 
+bool msm_dp_utils_link_fits(unsigned int clock, unsigned int bpp,
+			    unsigned int rate, unsigned int lanes, bool yuv420)
+{
+	u64 available = (u64)rate * lanes * 8;
+
+	if (!clock || !bpp || !rate || !lanes)
+		return false;
+
+	if (yuv420)
+		available *= 2;
+
+	return (u64)clock * bpp <= available;
+}
+
+int msm_dp_utils_select_bpp(unsigned int bpc, unsigned int max_bpc,
+			    unsigned int clock, unsigned int rate,
+			    unsigned int lanes, bool yuv420)
+{
+	unsigned int bpp;
+
+	bpc = min3(bpc ? bpc : 8, max_bpc, 10U);
+	bpc = round_down(bpc, 2);
+	if (bpc < 6)
+		return -EINVAL;
+
+	for (bpp = bpc * 3; bpp >= 18; bpp -= 6) {
+		if (msm_dp_utils_link_fits(clock, bpp, rate, lanes, yuv420))
+			return bpp;
+	}
+
+	return -EINVAL;
+}
+
 bool msm_dp_utils_psr_needs_modeset(const struct drm_crtc_state *state)
 {
 	/* active_changed alone also describes a normal self-refresh exit. */
diff --git a/drivers/gpu/drm/msm/dp/dp_utils.h b/drivers/gpu/drm/msm/dp/dp_utils.h
index 5d86c978dffd..c6b62e3900bb 100644
--- a/drivers/gpu/drm/msm/dp/dp_utils.h
+++ b/drivers/gpu/drm/msm/dp/dp_utils.h
@@ -36,6 +36,12 @@ u8 msm_dp_utils_calculate_parity(u32 data);
 void msm_dp_utils_pack_sdp_header(const struct dp_sdp_header *sdp_header,
 				  u32 header_buff[2]);
 
+bool msm_dp_utils_link_fits(unsigned int clock, unsigned int bpp,
+			    unsigned int rate, unsigned int lanes, bool yuv420);
+int msm_dp_utils_select_bpp(unsigned int bpc, unsigned int max_bpc,
+			    unsigned int clock, unsigned int rate,
+			    unsigned int lanes, bool yuv420);
+
 bool msm_dp_utils_psr_needs_modeset(const struct drm_crtc_state *state);
 
 #endif /* _DP_UTILS_H_ */

-- 
2.56.0


_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic

  parent reply	other threads:[~2026-10-09  3:19 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-09  3:15 [PATCH v2 00/20] drm/msm/dp: Add static HDR support for DP and eDP Xilin Wu
2026-10-09  3:15 ` [PATCH v2 01/20] drm/atomic: Handle max bpc properties before connector state allocation Xilin Wu
2026-10-10  2:48   ` Chaoyi Chen
2026-10-12 12:33   ` Dmitry Baryshkov
2026-10-09  3:15 ` [PATCH v2 02/20] drm/connector: Drop early state allocation for max bpc registration Xilin Wu
2026-10-10 22:44   ` Martin Blumenstingl
2026-10-09  3:15 ` [PATCH v2 03/20] drm/bridge-connector: Attach max bpc for non-HDMI bridges Xilin Wu
2026-10-10  2:39   ` Chaoyi Chen
2026-10-12 12:40   ` Dmitry Baryshkov
2026-10-09  3:15 ` [PATCH v2 04/20] drm/msm/dp: Accept a const SDP header when packing Xilin Wu
2026-10-09  3:15 ` [PATCH v2 05/20] drm/msm/dp: Support multiple generic SDP slots Xilin Wu
2026-10-09  3:15 ` [PATCH v2 06/20] drm/msm/dp: Keep runtime PM calls outside the connection lock Xilin Wu
2026-10-09  3:37   ` sashiko-bot
2026-10-09  3:15 ` [PATCH v2 07/20] drm/msm/dp: Serialize stream operations with HPD processing Xilin Wu
2026-10-09  3:30   ` sashiko-bot
2026-10-12 12:48   ` Dmitry Baryshkov
2026-10-09  3:16 ` [PATCH v2 08/20] drm/msm/dp: Track PHY power ownership Xilin Wu
2026-10-09  3:16 ` [PATCH v2 09/20] drm/msm/dp: Unwind resources when enabling a stream fails Xilin Wu
2026-10-09  3:35   ` sashiko-bot
2026-10-09  3:16 ` [PATCH v2 10/20] drm/msm/dp: Report stream enable failures through link status Xilin Wu
2026-10-09  3:28   ` sashiko-bot
2026-10-09  3:16 ` [PATCH v2 11/20] drm/msm/dp: Cache eDP link capabilities after successful discovery Xilin Wu
2026-10-09  3:34   ` sashiko-bot
2026-10-30  4:29   ` Burke Cates
2026-10-09  3:16 ` [PATCH v2 12/20] drm/msm/dp: Rebuild the eDP stream on modesets leaving self refresh Xilin Wu
2026-10-09  3:16 ` Xilin Wu [this message]
2026-10-09  3:16 ` [PATCH v2 14/20] drm/msm/dp: Clear stale MSA colorimetry bits Xilin Wu
2026-10-12  8:00   ` Konrad Dybcio
2026-10-09  3:16 ` [PATCH v2 15/20] drm/msm/dp: Flush VSC updates when leaving YUV420 Xilin Wu
2026-10-12 14:09   ` Dmitry Baryshkov
2026-10-09  3:16 ` [PATCH v2 16/20] drm/msm/dp: Disable PSR for YUV420 streams Xilin Wu
2026-10-09  3:34   ` sashiko-bot
2026-10-12 14:36   ` Dmitry Baryshkov
2026-10-09  3:16 ` [PATCH v2 17/20] drm/msm/dp: Track VSC packets in bridge atomic state Xilin Wu
2026-10-09  3:34   ` sashiko-bot
2026-10-09  3:16 ` [PATCH v2 18/20] drm/msm/dp: Signal BT.2020 RGB on DP and eDP Xilin Wu
2026-10-12 14:48   ` Dmitry Baryshkov
2026-10-09  3:16 ` [PATCH v2 19/20] drm/msm/dp: Serialize video test state changes Xilin Wu
2026-10-09  3:44   ` sashiko-bot
2026-10-12 21:52   ` Dmitry Baryshkov
2026-10-09  3:16 ` [PATCH v2 20/20] drm/msm/dp: Add static HDR metadata support for DP and eDP Xilin Wu
2026-10-09  3:40   ` sashiko-bot

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=20261009-msm-dp-hdr10-v2-13-1835d4966da3@radxa.com \
    --to=sophon@radxa.com \
    --cc=Laurent.pinchart@ideasonboard.com \
    --cc=abhinav.kumar@linux.dev \
    --cc=airlied@gmail.com \
    --cc=andrzej.hajda@intel.com \
    --cc=dragon@radxa.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=freedreno@lists.freedesktop.org \
    --cc=jbrunet@baylibre.com \
    --cc=jernej.skrabec@gmail.com \
    --cc=jesszhan0024@gmail.com \
    --cc=jonas@kwiboo.se \
    --cc=khilman@baylibre.com \
    --cc=linux-amlogic@lists.infradead.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luca.ceresoli@bootlin.com \
    --cc=lumag@kernel.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=marijn.suijten@somainline.org \
    --cc=martin.blumenstingl@googlemail.com \
    --cc=mripard@kernel.org \
    --cc=neil.armstrong@linaro.org \
    --cc=rfoss@kernel.org \
    --cc=robin.clark@oss.qualcomm.com \
    --cc=sean@poorly.run \
    --cc=simona@ffwll.ch \
    --cc=tzimmermann@suse.de \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®