diff mbox series

[3/6] drm/bridge: ti-sn65dsi83: add support to swap the LVDS data lanes

Message ID 20220530150548.1236307-4-m.felsch@pengutronix.de
State New
Headers show
Series None | expand

Commit Message

Marco Felsch May 30, 2022, 3:05 p.m. UTC
The chip can swap the LVDS channel A/B data lanes e.g. to improve the
layout characteristic. This commit adds the feature so the system
integrator can specify it within the device-tree.

Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
---
 drivers/gpu/drm/bridge/ti-sn65dsi83.c | 64 +++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)
diff mbox series

Patch

diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
index 112fea004c8e..baf94b2b78a1 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
@@ -32,6 +32,7 @@ 
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of_graph.h>
+#include <linux/property.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 
@@ -148,6 +149,8 @@  struct sn65dsi83 {
 	int				dsi_lanes;
 	bool				lvds_dual_link;
 	bool				lvds_dual_link_even_odd_swap;
+	bool				lvds_reverse_cha;
+	bool				lvds_reverse_chb;
 };
 
 static const struct regmap_range sn65dsi83_readable_ranges[] = {
@@ -441,6 +444,10 @@  static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
 	val = REG_LVDS_LANE_CHA_LVDS_TERM | REG_LVDS_LANE_CHB_LVDS_TERM;
 	if (ctx->lvds_dual_link_even_odd_swap)
 		val |= REG_LVDS_LANE_EVEN_ODD_SWAP;
+	if (ctx->lvds_reverse_cha)
+		val |= REG_LVDS_LANE_CHA_REVERSE_LVDS;
+	if (ctx->lvds_reverse_chb)
+		val |= REG_LVDS_LANE_CHB_REVERSE_LVDS;
 
 	regmap_write(ctx->regmap, REG_LVDS_LANE, val);
 	regmap_write(ctx->regmap, REG_LVDS_CM, 0x00);
@@ -566,6 +573,47 @@  static const struct drm_bridge_funcs sn65dsi83_funcs = {
 	.atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts,
 };
 
+static int sn65dsi83_parse_lvds_lane_order(struct sn65dsi83 *ctx, unsigned char port)
+{
+	struct device *dev = ctx->dev;
+	struct device_node *ep;
+	int lvds_lanes;
+	int ret = 0;
+
+	if (port < 2 || port > 3)
+		return -EINVAL;
+
+	ep = of_graph_get_endpoint_by_regs(dev->of_node, port, 0);
+	lvds_lanes = of_property_count_u32_elems(ep, "data-lanes");
+	if (lvds_lanes == 4) {
+		u32 lane_assignments[] = { 1, 2, 3, 4 };
+
+		of_property_read_u32_array(ep, "data-lanes", lane_assignments,
+					   lvds_lanes);
+		if (lane_assignments[0] == 4 &&
+		    lane_assignments[1] == 3 &&
+		    lane_assignments[2] == 2 &&
+		    lane_assignments[3] == 1) {
+			if (port == 2)
+				ctx->lvds_reverse_cha = true;
+			else
+				ctx->lvds_reverse_chb = true;
+		} else if (lane_assignments[0] != 1 ||
+			   lane_assignments[1] != 2 ||
+			   lane_assignments[2] != 3 ||
+			   lane_assignments[3] != 4) {
+			dev_err(dev, "Unsupported LVDS lane order\n");
+			ret = -EINVAL;
+		}
+	} else if (lvds_lanes > 0) {
+		dev_err(dev, "All 4 LVDS data-lanes must be specified\n");
+		ret = -EINVAL;
+	}
+	of_node_put(ep);
+
+	return ret;
+}
+
 static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
 {
 	struct drm_bridge *panel_bridge;
@@ -610,6 +658,22 @@  static int sn65dsi83_parse_dt(struct sn65dsi83 *ctx, enum sn65dsi83_model model)
 		}
 	}
 
+	/*
+	 * Todo:
+	 * Check if the reverse lane setup is working in dual-link as well.
+	 */
+	if (!ctx->lvds_dual_link) {
+		ret = sn65dsi83_parse_lvds_lane_order(ctx, 2);
+		if (ret)
+			return ret;
+
+		if (model != MODEL_SN65DSI83) {
+			ret = sn65dsi83_parse_lvds_lane_order(ctx, 3);
+			if (ret)
+				return ret;
+		}
+	}
+
 	panel_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 2, 0);
 	if (IS_ERR(panel_bridge)) {
 		ret = PTR_ERR(panel_bridge);