diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 9f0d2ee..a270063 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -17,7 +17,8 @@ drm-y       :=	drm_auth.o drm_cache.o \
 		drm_plane.o drm_color_mgmt.o drm_print.o \
 		drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \
 		drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \
-		drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o
+		drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \
+		drm_bus_timings.o
 
 drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o
 drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
diff --git a/drivers/gpu/drm/drm_bus_timings.c b/drivers/gpu/drm/drm_bus_timings.c
new file mode 100644
index 0000000..e2ecd22
--- /dev/null
+++ b/drivers/gpu/drm/drm_bus_timings.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <drm/drm_bus_timings.h>
+#include <linux/errno.h>
+#include <linux/of_graph.h>
+#include <linux/of.h>
+#include <linux/types.h>
+
+#define DRM_OF_LVDS_ODD		1
+#define DRM_OF_LVDS_EVEN	2
+
+static int drm_of_lvds_get_port_pixels_type(struct device_node *port_node)
+{
+	bool even_pixels, odd_pixels;
+
+	even_pixels = of_property_read_bool(port_node, "dual-lvds-even-pixels");
+	odd_pixels = of_property_read_bool(port_node, "dual-lvds-odd-pixels");
+	return  even_pixels * DRM_OF_LVDS_EVEN + odd_pixels * DRM_OF_LVDS_ODD;
+}
+
+/**
+ * drm_of_lvds_get_dual_link_configuration - get the dual-LVDS configuration
+ * @p1: device tree node corresponding to the first port of the source
+ * @p2: device tree node corresponding to the second port of the source
+ *
+ * An LVDS dual-link bus is made of two connections, even pixels transit on one
+ * connection, and odd pixels transit on the other connection.
+ * This function walks the DT (from the source ports to the sink ports) looking
+ * for a dual-LVDS bus. A dual-LVDS bus is identfied by markers found on the DT
+ * ports of the sink device(s). If such a bus is found, this function returns
+ * its configuration (either p1 connected to the even pixels port and p2
+ * connected to the odd pixels port, or p1 connected to the odd pixels port and
+ * p2 connected to the even pixels port).
+ *
+ * Return: A code describing the bus configuration when a valid dual-LVDS bus is
+ * found, or an error code when no valid dual-LVDS bus is found
+ *
+ * Possible codes for the bus configuration are:
+ *
+ * - DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS: when p1 is connected to the even pixels
+ *   port and p2 is connected to the odd pixels port
+ * - DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS: when p1 is connected to the odd pixels
+ *   port and p2 is connected to the even pixels port
+ *
+ */
+int drm_of_lvds_get_dual_link_configuration(const struct device_node *p1,
+					    const struct device_node *p2)
+{
+	struct device_node *remote_p1 = NULL, *remote_p2 = NULL;
+	struct device_node *parent_p1 = NULL, *parent_p2 = NULL;
+	struct device_node *ep1 = NULL, *ep2 = NULL;
+	u32 reg_p1, reg_p2;
+	int ret = -EINVAL, remote_p1_pt, remote_p2_pt;
+
+	if (!p1 || !p2)
+		return ret;
+	if (of_property_read_u32(p1, "reg", &reg_p1) ||
+	    of_property_read_u32(p2, "reg", &reg_p2))
+		return ret;
+	parent_p1 = of_get_parent(p1);
+	parent_p2 = of_get_parent(p2);
+	if (!parent_p1 || !parent_p2)
+		goto done;
+	ep1 = of_graph_get_endpoint_by_regs(parent_p1, reg_p1, 0);
+	ep2 = of_graph_get_endpoint_by_regs(parent_p2, reg_p2, 0);
+	if (!ep1 || !ep2)
+		goto done;
+	remote_p1 = of_graph_get_remote_port(ep1);
+	remote_p2 = of_graph_get_remote_port(ep2);
+	if (!remote_p1 || !remote_p2)
+		goto done;
+	remote_p1_pt = drm_of_lvds_get_port_pixels_type(remote_p1);
+	remote_p2_pt = drm_of_lvds_get_port_pixels_type(remote_p2);
+	/*
+	 * A valid dual-lVDS bus is found when one remote port is marked with
+	 * "dual-lvds-even-pixels", and the other remote port is marked with
+	 * "dual-lvds-odd-pixels", bail out if the markers are not right.
+	 */
+	if (!remote_p1_pt || !remote_p2_pt ||
+	    remote_p1_pt + remote_p2_pt != DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD)
+		goto done;
+	if (remote_p1_pt == DRM_OF_LVDS_EVEN)
+		/* The sink expects even pixels through the first port */
+		ret = DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
+	else
+		/* The sink expects odd pixels through the first port */
+		ret = DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+
+done:
+	of_node_put(ep1);
+	of_node_put(ep2);
+	of_node_put(parent_p1);
+	of_node_put(parent_p2);
+	of_node_put(remote_p1);
+	of_node_put(remote_p2);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_configuration);
diff --git a/include/drm/drm_bus_timings.h b/include/drm/drm_bus_timings.h
new file mode 100644
index 0000000..db8a385
--- /dev/null
+++ b/include/drm/drm_bus_timings.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DRM_BUS_TIMINGS__
+#define __DRM_BUS_TIMINGS__
+
+struct device_node;
+
+#define DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS	0
+#define DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS	1
+
+#ifdef CONFIG_OF
+int drm_of_lvds_get_dual_link_configuration(const struct device_node *p1,
+					    const struct device_node *p2);
+#else
+int drm_of_lvds_get_dual_link_configuration(const struct device_node *p1,
+					    const struct device_node *p2)
+{
+	return -EINVAL;
+}
+#endif
+
+#endif /* __DRM_BUS_TIMINGS__ */
