1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * (C) Copyright 2023 Rockchip Electronics Co., Ltd
4 *
5 */
6 #include <dm/device.h>
7 #include <dm/read.h>
8
9 #include "drm_of.h"
10
11 enum drm_of_lvds_pixels {
12 DRM_OF_LVDS_EVEN = BIT(0),
13 DRM_OF_LVDS_ODD = BIT(1),
14 DRM_OF_LVDS_LEFT = BIT(2),
15 DRM_OF_LVDS_RIGHT = BIT(3),
16 };
17
18 static int
drm_of_lvds_get_port_pixels_type(const struct device_node * port_node)19 drm_of_lvds_get_port_pixels_type(const struct device_node *port_node)
20 {
21 ofnode node = np_to_ofnode(port_node);
22
23 bool even_pixels =
24 ofnode_read_bool(node, "dual-lvds-even-pixels");
25 bool odd_pixels =
26 ofnode_read_bool(node, "dual-lvds-odd-pixels");
27 bool left_pixels =
28 ofnode_read_bool(node, "dual-lvds-left-pixels");
29 bool right_pixels =
30 ofnode_read_bool(node, "dual-lvds-right-pixels");
31
32 return (even_pixels ? DRM_OF_LVDS_EVEN : 0) |
33 (odd_pixels ? DRM_OF_LVDS_ODD : 0) |
34 (left_pixels ? DRM_OF_LVDS_LEFT : 0) |
35 (right_pixels ? DRM_OF_LVDS_RIGHT : 0);
36 }
37
38 static int
drm_of_lvds_get_remote_pixels_type(const struct device_node * port_node)39 drm_of_lvds_get_remote_pixels_type(const struct device_node *port_node)
40 {
41 ofnode node = np_to_ofnode(port_node);
42 ofnode endpoint;
43 uint phandle;
44 int pixels_type = -EPIPE;
45
46 ofnode_for_each_subnode(endpoint, node) {
47 int current_pt;
48 const char *name;
49
50 if (!ofnode_is_available(endpoint))
51 continue;
52
53 name = ofnode_get_name(endpoint);
54 if (strncmp(name, "endpoint", 8) != 0)
55 continue;
56
57 if (ofnode_read_u32(endpoint, "remote-endpoint", &phandle))
58 continue;
59
60 endpoint = ofnode_get_by_phandle(phandle);
61 if (!ofnode_valid(endpoint) || !ofnode_is_available(endpoint))
62 continue;
63
64 endpoint = ofnode_get_parent(endpoint);
65 if (!ofnode_valid(endpoint))
66 continue;
67
68 current_pt =
69 drm_of_lvds_get_port_pixels_type(ofnode_to_np(endpoint
70 ));
71 if (pixels_type < 0)
72 pixels_type = current_pt;
73
74 /*
75 * Sanity check, ensure that all remote endpoints have the same
76 * pixel type. We may lift this restriction later if we need to
77 * support multiple sinks with different dual-link
78 * configurations by passing the endpoints explicitly to
79 * drm_of_lvds_get_dual_link_pixel_order().
80 */
81 if (!current_pt || pixels_type != current_pt)
82 return -EINVAL;
83 }
84
85 return pixels_type;
86 }
87
88 /**
89 * drm_of_lvds_get_dual_link_pixel_order - Get LVDS dual-link pixel order
90 * @port1: First DT port node of the Dual-link LVDS source
91 * @port2: Second DT port node of the Dual-link LVDS source
92 *
93 * An LVDS dual-link connection is made of two links, the two link can transmit
94 * odd pixels and even pixels independently, or the two link can also transmit
95 * left pixels and right pixels independently. This function returns for two
96 * ports of an LVDS dual-link source, based on the requirements of the connected
97 * sink.
98 *
99 * The pixel order is determined from the dual-lvds-even-pixels +
100 * dual-lvds-odd-pixels or dual-lvds-left-pixels + dual-lvds-right-pixels
101 * properties in the sink's DT port nodes. If those
102 * properties are not present, or if their usage is not valid, this function
103 * returns -EINVAL.
104 *
105 * If either port is not connected, this function returns -EPIPE.
106 *
107 * @port1 and @port2 are typically DT sibling nodes, but may have different
108 * parents when, for instance, two separate LVDS encoders carry the even and
109 * odd pixels.
110 *
111 * Return:
112 * * DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS - @port1 carries even pixels and @port2
113 * carries odd pixels
114 * * DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS - @port1 carries odd pixels and @port2
115 * carries even pixels
116 * * DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS - @port1 carries left pixels and
117 * @port2 carries right pixels
118 * * DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS - @port1 carries right pixels and
119 * @port2 carries left pixels
120
121 * * -EINVAL - @port1 and @port2 are not connected to a dual-link LVDS sink, or
122 * the sink configuration is invalid
123 * * -EPIPE - when @port1 or @port2 are not connected
124 */
drm_of_lvds_get_dual_link_pixel_order(const struct device_node * port1,const struct device_node * port2)125 int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
126 const struct device_node *port2)
127 {
128 int remote_p1_pt, remote_p2_pt;
129
130 if (!port1 || !port2)
131 return -EINVAL;
132
133 remote_p1_pt = drm_of_lvds_get_remote_pixels_type(port1);
134 if (remote_p1_pt < 0)
135 return remote_p1_pt;
136
137 remote_p2_pt = drm_of_lvds_get_remote_pixels_type(port2);
138 if (remote_p2_pt < 0)
139 return remote_p2_pt;
140
141 /*
142 * A valid dual-lVDS bus is found when one remote port is marked with
143 * "dual-lvds-even-pixels" or "dual-lvds-left-pixels", and the other
144 * remote port is marked with "dual-lvds-odd-pixels"or
145 * "dual-lvds-right-pixels", bail out if the markers are not right.
146 */
147 if ((remote_p1_pt + remote_p2_pt !=
148 DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD) &&
149 (remote_p1_pt + remote_p2_pt !=
150 DRM_OF_LVDS_LEFT + DRM_OF_LVDS_RIGHT))
151 return -EINVAL;
152
153 if (remote_p1_pt == DRM_OF_LVDS_EVEN)
154 return DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
155 else if (remote_p1_pt == DRM_OF_LVDS_ODD)
156 return DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
157 else if (remote_p1_pt == DRM_OF_LVDS_LEFT)
158 return DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS;
159 else
160 return DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS;
161 }
162