@@ -344,6 +344,14 @@
#define COLOR_BAR_MODE_BARS 2
#define PLL_DBG 0x0a04
+enum tc_mode {
+ mode_dpi_to_edp = BIT(1) | BIT(2),
+ mode_dpi_to_dp = BIT(1),
+ mode_dsi_to_edp = BIT(0) | BIT(2),
+ mode_dsi_to_dp = BIT(0),
+ mode_dsi_to_dpi = BIT(0) | BIT(1),
+};
+
static bool tc_test_pattern;
module_param_named(test, tc_test_pattern, bool, 0644);
@@ -2327,7 +2335,6 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc)
if (bridge) {
tc->panel_bridge = bridge;
tc->bridge.type = DRM_MODE_CONNECTOR_DPI;
- tc->bridge.funcs = &tc_dpi_bridge_funcs;
return 0;
}
@@ -2360,7 +2367,6 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort;
}
- tc->bridge.funcs = &tc_edp_bridge_funcs;
if (tc->hpd_pin >= 0)
tc->bridge.ops |= DRM_BRIDGE_OP_DETECT;
tc->bridge.ops |= DRM_BRIDGE_OP_EDID;
@@ -2368,17 +2374,11 @@ static int tc_probe_edp_bridge_endpoint(struct tc_data *tc)
return 0;
}
-static int tc_probe_bridge_endpoint(struct tc_data *tc)
+static enum tc_mode tc_probe_get_mode(struct device *dev)
{
- struct device *dev = tc->dev;
struct of_endpoint endpoint;
struct device_node *node = NULL;
- const u8 mode_dpi_to_edp = BIT(1) | BIT(2);
- const u8 mode_dpi_to_dp = BIT(1);
- const u8 mode_dsi_to_edp = BIT(0) | BIT(2);
- const u8 mode_dsi_to_dp = BIT(0);
- const u8 mode_dsi_to_dpi = BIT(0) | BIT(1);
- u8 mode = 0;
+ enum tc_mode mode = 0;
/*
* Determine bridge configuration.
@@ -2401,7 +2401,27 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc)
return -EINVAL;
}
mode |= BIT(endpoint.port);
+ }
+
+ if (mode != mode_dpi_to_edp &&
+ mode != mode_dpi_to_dp &&
+ mode != mode_dsi_to_dpi &&
+ mode != mode_dsi_to_edp &&
+ mode != mode_dsi_to_dp) {
+ dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode);
+ return -EINVAL;
+ }
+
+ return mode;
+}
+static int tc_probe_bridge_endpoint(struct tc_data *tc, enum tc_mode mode)
+{
+ struct device *dev = tc->dev;
+ struct of_endpoint endpoint;
+ struct device_node *node = NULL;
+
+ for_each_endpoint_of_node(dev->of_node, node) {
if (endpoint.port == 2) {
of_property_read_u8_array(node, "toshiba,pre-emphasis",
tc->pre_emphasis,
@@ -2427,24 +2447,28 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc)
return tc_probe_edp_bridge_endpoint(tc);
}
- dev_warn(dev, "Invalid mode (0x%x) is not supported!\n", mode);
-
+ /* Should never happen, mode was validated by tc_probe_get_mode() */
return -EINVAL;
}
static int tc_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
+ const struct drm_bridge_funcs *funcs;
struct tc_data *tc;
+ int mode;
int ret;
- tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL);
- if (!tc)
- return -ENOMEM;
+ mode = tc_probe_get_mode(dev);
+ funcs = (mode == mode_dsi_to_dpi) ? &tc_dpi_bridge_funcs : &tc_edp_bridge_funcs;
+
+ tc = devm_drm_bridge_alloc(dev, struct tc_data, bridge, funcs);
+ if (IS_ERR(tc))
+ return PTR_ERR(tc);
tc->dev = dev;
- ret = tc_probe_bridge_endpoint(tc);
+ ret = tc_probe_bridge_endpoint(tc, mode);
if (ret)
return ret;
This is the new API for allocating DRM bridges. Converting this driver is a bit complex because the drm_bridge funcs pointer differs based on the bridge mode. So the current code does: * tc_probe() * devm_kzalloc() private struct embedding drm_bridge * call tc_probe_bridge_endpoint() which * parses DT description into struct fields * computes the mode * calls different bridge init functions based on the mode * each sets a different bridge.funcs pointer The new API expects the funcs pointer to be known at alloc time, which does not fit in the current code structure. Solve this by splitting tc_probe_bridge_endpoint() in two functions: * tc_probe_get_mode(), computing the mode without needing the private driver structure * tc_probe_bridge_endpoint(), only initializing the endpoints So now the mode is known before allocation and so is the funcs pointer, while all other operations are still happening after allocation, directly into the private struct data, as they used to. The new code flow is: * tc_probe() * tc_probe_get_mode() * parses DT description * computes and returns the mode * based onf the mode, pick the funcs pointer * devm_drm_bridfge_alloc(..., funcs) * call tc_probe_bridge_endpoint() which * calls different bridge init functions based on the mode * these don't set the funcs pointer, it was done by _alloc This solution is chosen to minimize the changes in the driver logical code flow. The drawback is we now iterate twice over the endpoints during probe. Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com> --- devm_drm_bridge_alloc() [0] is the new API to allocate and initialize a DRM bridge, and the only one supported from now on. It is the first milestone towards removal of bridges from a still existing DRM pipeline without use-after-free. The steps in the grand plan [1] are: 1. ➜ add refcounting to DRM bridges (struct drm_bridge) 2. handle gracefully atomic updates during bridge removal 3. avoid DSI host drivers to have dangling pointers to DSI devices 4. finish the hotplug bridge work, removing the "always-disconnected" connector, moving code to the core and potentially removing the hotplug-bridge itself (this needs to be clarified as points 1-3 are developed) This series is part of step 1 of the grand plan. Current tasks in step 1 of the grand plan: A. ✔ add new alloc API and refcounting -> (now in drm-misc-next) B. ➜ convert all bridge drivers to new API (this series) C. … documentation, kunit tests (v1 under discussion) D. after (B), add get/put to drm_bridge_add/remove() + attach/detech() E. after (B), convert accessors; this is a large work and can be done in chunks F. debugfs improvements More info about this series in the v2 cover [2]. Luca [0] https://gitlab.freedesktop.org/drm/misc/kernel/-/commit/0cc6aadd7fc1e629b715ea3d1ba537ef2da95eec [1] https://lore.kernel.org/lkml/20250206-hotplug-drm-bridge-v6-0-9d6f2c9c3058@bootlin.com/t/#u [2] https://lore.kernel.org/lkml/20250424-drm-bridge-convert-to-alloc-api-v2-0-8f91a404d86b@bootlin.com/ --- Changes in v4: - Removed patches already in drm-misc-next -> only 1 left - Improve commit message of patch 1 - Link to v3: https://lore.kernel.org/all/20250509-drm-bridge-convert-to-alloc-api-v3-0-b8bc1f16d7aa@bootlin.com/ Changes in v3: - Fixed issues reported for some patches - Added review tags - Removed patches that have been applied - Added revert for the exynos patch, applied by mistake - Update cover with grand plan info and trim some of it - Updated bouncing e-mail address in Cc list - Link to v2: https://lore.kernel.org/lkml/20250424-drm-bridge-convert-to-alloc-api-v2-0-8f91a404d86b@bootlin.com/ Changes in v2: - Improved cover letter with link to commit adding devm_drm_bridge_alloc() - add review tags - fix bugs in zynqmp, vc4 patches - fix patch 1 error code checking - Link to v1: https://lore.kernel.org/r/20250407-drm-bridge-convert-to-alloc-api-v1-0-42113ff8d9c0@bootlin.com --- changes in v4: - improved commit message --- drivers/gpu/drm/bridge/tc358767.c | 56 ++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 16 deletions(-) --- base-commit: 18fb864d3afccf7ecdf13d0435464465d31ccf1d change-id: 20250404-drm-bridge-convert-to-alloc-api-614becf62294 Best regards,