[PATCH v3 3/8] drm: Add bus timings helper

From: Fabrizio Castro
Date: Wed Aug 28 2019 - 14:37:10 EST


Helper to provide bus timing information.

Signed-off-by: Fabrizio Castro <fabrizio.castro@xxxxxxxxxxxxxx>

---
v2->v3:
* new patch
---
drivers/gpu/drm/Makefile | 3 +-
drivers/gpu/drm/drm_bus_timings.c | 97 +++++++++++++++++++++++++++++++++++++++
include/drm/drm_bus_timings.h | 21 +++++++++
3 files changed, 120 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/drm_bus_timings.c
create mode 100644 include/drm/drm_bus_timings.h

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__ */
--
2.7.4