Commit | Line | Data |
---|---|---|
457c8996 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
df785aa8 | 2 | #include <linux/component.h> |
7e435aad RK |
3 | #include <linux/export.h> |
4 | #include <linux/list.h> | |
5 | #include <linux/of_graph.h> | |
0500c04e | 6 | |
1f2db303 | 7 | #include <drm/drm_bridge.h> |
7e435aad | 8 | #include <drm/drm_crtc.h> |
0500c04e | 9 | #include <drm/drm_device.h> |
9338203c | 10 | #include <drm/drm_encoder.h> |
7e435aad | 11 | #include <drm/drm_of.h> |
0500c04e | 12 | #include <drm/drm_panel.h> |
7e435aad | 13 | |
7f9e7ec9 DV |
14 | /** |
15 | * DOC: overview | |
16 | * | |
17 | * A set of helper functions to aid DRM drivers in parsing standard DT | |
18 | * properties. | |
19 | */ | |
20 | ||
97ac0e47 RK |
21 | static void drm_release_of(struct device *dev, void *data) |
22 | { | |
23 | of_node_put(data); | |
24 | } | |
25 | ||
7e435aad | 26 | /** |
8b5f7a62 | 27 | * drm_of_crtc_port_mask - find the mask of a registered CRTC by port OF node |
7e435aad RK |
28 | * @dev: DRM device |
29 | * @port: port OF node | |
30 | * | |
31 | * Given a port OF node, return the possible mask of the corresponding | |
32 | * CRTC within a device's list of CRTCs. Returns zero if not found. | |
33 | */ | |
8b5f7a62 JS |
34 | uint32_t drm_of_crtc_port_mask(struct drm_device *dev, |
35 | struct device_node *port) | |
7e435aad RK |
36 | { |
37 | unsigned int index = 0; | |
38 | struct drm_crtc *tmp; | |
39 | ||
6295d607 | 40 | drm_for_each_crtc(tmp, dev) { |
7e435aad RK |
41 | if (tmp->port == port) |
42 | return 1 << index; | |
43 | ||
44 | index++; | |
45 | } | |
46 | ||
47 | return 0; | |
48 | } | |
8b5f7a62 | 49 | EXPORT_SYMBOL(drm_of_crtc_port_mask); |
7e435aad RK |
50 | |
51 | /** | |
52 | * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port | |
53 | * @dev: DRM device | |
54 | * @port: encoder port to scan for endpoints | |
55 | * | |
56 | * Scan all endpoints attached to a port, locate their attached CRTCs, | |
57 | * and generate the DRM mask of CRTCs which may be attached to this | |
58 | * encoder. | |
59 | * | |
60 | * See Documentation/devicetree/bindings/graph.txt for the bindings. | |
61 | */ | |
62 | uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, | |
63 | struct device_node *port) | |
64 | { | |
7416f4e3 | 65 | struct device_node *remote_port, *ep; |
7e435aad RK |
66 | uint32_t possible_crtcs = 0; |
67 | ||
7416f4e3 | 68 | for_each_endpoint_of_node(port, ep) { |
7e435aad RK |
69 | remote_port = of_graph_get_remote_port(ep); |
70 | if (!remote_port) { | |
71 | of_node_put(ep); | |
72 | return 0; | |
73 | } | |
74 | ||
8b5f7a62 | 75 | possible_crtcs |= drm_of_crtc_port_mask(dev, remote_port); |
7e435aad RK |
76 | |
77 | of_node_put(remote_port); | |
7416f4e3 | 78 | } |
7e435aad RK |
79 | |
80 | return possible_crtcs; | |
81 | } | |
82 | EXPORT_SYMBOL(drm_of_find_possible_crtcs); | |
df785aa8 | 83 | |
97ac0e47 RK |
84 | /** |
85 | * drm_of_component_match_add - Add a component helper OF node match rule | |
86 | * @master: master device | |
87 | * @matchptr: component match pointer | |
88 | * @compare: compare function used for matching component | |
89 | * @node: of_node | |
90 | */ | |
91 | void drm_of_component_match_add(struct device *master, | |
92 | struct component_match **matchptr, | |
93 | int (*compare)(struct device *, void *), | |
94 | struct device_node *node) | |
95 | { | |
96 | of_node_get(node); | |
97 | component_match_add_release(master, matchptr, drm_release_of, | |
98 | compare, node); | |
99 | } | |
100 | EXPORT_SYMBOL_GPL(drm_of_component_match_add); | |
101 | ||
df785aa8 LD |
102 | /** |
103 | * drm_of_component_probe - Generic probe function for a component based master | |
104 | * @dev: master device containing the OF node | |
105 | * @compare_of: compare function used for matching components | |
7f9e7ec9 | 106 | * @m_ops: component master ops to be used |
df785aa8 LD |
107 | * |
108 | * Parse the platform device OF node and bind all the components associated | |
109 | * with the master. Interface ports are added before the encoders in order to | |
110 | * satisfy their .bind requirements | |
111 | * See Documentation/devicetree/bindings/graph.txt for the bindings. | |
112 | * | |
113 | * Returns zero if successful, or one of the standard error codes if it fails. | |
114 | */ | |
115 | int drm_of_component_probe(struct device *dev, | |
116 | int (*compare_of)(struct device *, void *), | |
117 | const struct component_master_ops *m_ops) | |
118 | { | |
119 | struct device_node *ep, *port, *remote; | |
120 | struct component_match *match = NULL; | |
121 | int i; | |
122 | ||
123 | if (!dev->of_node) | |
124 | return -EINVAL; | |
125 | ||
126 | /* | |
127 | * Bind the crtc's ports first, so that drm_of_find_possible_crtcs() | |
128 | * called from encoder's .bind callbacks works as expected | |
129 | */ | |
130 | for (i = 0; ; i++) { | |
131 | port = of_parse_phandle(dev->of_node, "ports", i); | |
132 | if (!port) | |
133 | break; | |
134 | ||
2efa8392 BS |
135 | if (of_device_is_available(port->parent)) |
136 | drm_of_component_match_add(dev, &match, compare_of, | |
137 | port); | |
df785aa8 | 138 | |
df785aa8 LD |
139 | of_node_put(port); |
140 | } | |
141 | ||
142 | if (i == 0) { | |
143 | dev_err(dev, "missing 'ports' property\n"); | |
144 | return -ENODEV; | |
145 | } | |
146 | ||
147 | if (!match) { | |
148 | dev_err(dev, "no available port\n"); | |
149 | return -ENODEV; | |
150 | } | |
151 | ||
152 | /* | |
153 | * For bound crtcs, bind the encoders attached to their remote endpoint | |
154 | */ | |
155 | for (i = 0; ; i++) { | |
156 | port = of_parse_phandle(dev->of_node, "ports", i); | |
157 | if (!port) | |
158 | break; | |
159 | ||
160 | if (!of_device_is_available(port->parent)) { | |
161 | of_node_put(port); | |
162 | continue; | |
163 | } | |
164 | ||
165 | for_each_child_of_node(port, ep) { | |
166 | remote = of_graph_get_remote_port_parent(ep); | |
167 | if (!remote || !of_device_is_available(remote)) { | |
168 | of_node_put(remote); | |
169 | continue; | |
170 | } else if (!of_device_is_available(remote->parent)) { | |
4bf99144 RH |
171 | dev_warn(dev, "parent device of %pOF is not available\n", |
172 | remote); | |
df785aa8 LD |
173 | of_node_put(remote); |
174 | continue; | |
175 | } | |
176 | ||
97ac0e47 RK |
177 | drm_of_component_match_add(dev, &match, compare_of, |
178 | remote); | |
df785aa8 LD |
179 | of_node_put(remote); |
180 | } | |
181 | of_node_put(port); | |
182 | } | |
183 | ||
184 | return component_master_add_with_match(dev, m_ops, match); | |
185 | } | |
186 | EXPORT_SYMBOL(drm_of_component_probe); | |
4cacf91f PZ |
187 | |
188 | /* | |
189 | * drm_of_encoder_active_endpoint - return the active encoder endpoint | |
190 | * @node: device tree node containing encoder input ports | |
191 | * @encoder: drm_encoder | |
192 | * | |
193 | * Given an encoder device node and a drm_encoder with a connected crtc, | |
194 | * parse the encoder endpoint connecting to the crtc port. | |
195 | */ | |
196 | int drm_of_encoder_active_endpoint(struct device_node *node, | |
197 | struct drm_encoder *encoder, | |
198 | struct of_endpoint *endpoint) | |
199 | { | |
200 | struct device_node *ep; | |
201 | struct drm_crtc *crtc = encoder->crtc; | |
202 | struct device_node *port; | |
203 | int ret; | |
204 | ||
205 | if (!node || !crtc) | |
206 | return -EINVAL; | |
207 | ||
208 | for_each_endpoint_of_node(node, ep) { | |
209 | port = of_graph_get_remote_port(ep); | |
210 | of_node_put(port); | |
211 | if (port == crtc->port) { | |
212 | ret = of_graph_parse_endpoint(ep, endpoint); | |
213 | of_node_put(ep); | |
214 | return ret; | |
215 | } | |
216 | } | |
217 | ||
218 | return -EINVAL; | |
219 | } | |
220 | EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); | |
1f2db303 | 221 | |
3fbdfe99 | 222 | /** |
1f2db303 RH |
223 | * drm_of_find_panel_or_bridge - return connected panel or bridge device |
224 | * @np: device tree node containing encoder output ports | |
3fbdfe99 DV |
225 | * @port: port in the device tree node |
226 | * @endpoint: endpoint in the device tree node | |
1f2db303 RH |
227 | * @panel: pointer to hold returned drm_panel |
228 | * @bridge: pointer to hold returned drm_bridge | |
229 | * | |
230 | * Given a DT node's port and endpoint number, find the connected node and | |
231 | * return either the associated struct drm_panel or drm_bridge device. Either | |
232 | * @panel or @bridge must not be NULL. | |
233 | * | |
87ea9580 MR |
234 | * This function is deprecated and should not be used in new drivers. Use |
235 | * devm_drm_of_get_bridge() instead. | |
236 | * | |
1f2db303 RH |
237 | * Returns zero if successful, or one of the standard error codes if it fails. |
238 | */ | |
239 | int drm_of_find_panel_or_bridge(const struct device_node *np, | |
240 | int port, int endpoint, | |
241 | struct drm_panel **panel, | |
242 | struct drm_bridge **bridge) | |
243 | { | |
244 | int ret = -EPROBE_DEFER; | |
245 | struct device_node *remote; | |
246 | ||
247 | if (!panel && !bridge) | |
248 | return -EINVAL; | |
320e421e DC |
249 | if (panel) |
250 | *panel = NULL; | |
1f2db303 | 251 | |
ea5bc3b1 DO |
252 | /* |
253 | * of_graph_get_remote_node() produces a noisy error message if port | |
254 | * node isn't found and the absence of the port is a legit case here, | |
255 | * so at first we silently check whether graph presents in the | |
256 | * device-tree node. | |
257 | */ | |
258 | if (!of_graph_is_present(np)) | |
259 | return -ENODEV; | |
260 | ||
1f2db303 RH |
261 | remote = of_graph_get_remote_node(np, port, endpoint); |
262 | if (!remote) | |
263 | return -ENODEV; | |
264 | ||
265 | if (panel) { | |
266 | *panel = of_drm_find_panel(remote); | |
5fa8e4a2 | 267 | if (!IS_ERR(*panel)) |
1f2db303 | 268 | ret = 0; |
5fa8e4a2 BB |
269 | else |
270 | *panel = NULL; | |
1f2db303 RH |
271 | } |
272 | ||
273 | /* No panel found yet, check for a bridge next. */ | |
274 | if (bridge) { | |
275 | if (ret) { | |
276 | *bridge = of_drm_find_bridge(remote); | |
277 | if (*bridge) | |
278 | ret = 0; | |
279 | } else { | |
280 | *bridge = NULL; | |
281 | } | |
282 | ||
283 | } | |
284 | ||
285 | of_node_put(remote); | |
286 | return ret; | |
287 | } | |
288 | EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); | |
65290075 FC |
289 | |
290 | enum drm_of_lvds_pixels { | |
291 | DRM_OF_LVDS_EVEN = BIT(0), | |
292 | DRM_OF_LVDS_ODD = BIT(1), | |
293 | }; | |
294 | ||
295 | static int drm_of_lvds_get_port_pixels_type(struct device_node *port_node) | |
296 | { | |
297 | bool even_pixels = | |
298 | of_property_read_bool(port_node, "dual-lvds-even-pixels"); | |
299 | bool odd_pixels = | |
300 | of_property_read_bool(port_node, "dual-lvds-odd-pixels"); | |
301 | ||
302 | return (even_pixels ? DRM_OF_LVDS_EVEN : 0) | | |
303 | (odd_pixels ? DRM_OF_LVDS_ODD : 0); | |
304 | } | |
305 | ||
306 | static int drm_of_lvds_get_remote_pixels_type( | |
307 | const struct device_node *port_node) | |
308 | { | |
309 | struct device_node *endpoint = NULL; | |
310 | int pixels_type = -EPIPE; | |
311 | ||
312 | for_each_child_of_node(port_node, endpoint) { | |
313 | struct device_node *remote_port; | |
314 | int current_pt; | |
315 | ||
316 | if (!of_node_name_eq(endpoint, "endpoint")) | |
317 | continue; | |
318 | ||
319 | remote_port = of_graph_get_remote_port(endpoint); | |
320 | if (!remote_port) { | |
b557a5f8 | 321 | of_node_put(endpoint); |
65290075 FC |
322 | return -EPIPE; |
323 | } | |
324 | ||
325 | current_pt = drm_of_lvds_get_port_pixels_type(remote_port); | |
326 | of_node_put(remote_port); | |
327 | if (pixels_type < 0) | |
328 | pixels_type = current_pt; | |
329 | ||
330 | /* | |
331 | * Sanity check, ensure that all remote endpoints have the same | |
332 | * pixel type. We may lift this restriction later if we need to | |
333 | * support multiple sinks with different dual-link | |
334 | * configurations by passing the endpoints explicitly to | |
335 | * drm_of_lvds_get_dual_link_pixel_order(). | |
336 | */ | |
6f9223a5 SP |
337 | if (!current_pt || pixels_type != current_pt) { |
338 | of_node_put(endpoint); | |
65290075 | 339 | return -EINVAL; |
6f9223a5 | 340 | } |
65290075 FC |
341 | } |
342 | ||
343 | return pixels_type; | |
344 | } | |
345 | ||
346 | /** | |
347 | * drm_of_lvds_get_dual_link_pixel_order - Get LVDS dual-link pixel order | |
348 | * @port1: First DT port node of the Dual-link LVDS source | |
349 | * @port2: Second DT port node of the Dual-link LVDS source | |
350 | * | |
351 | * An LVDS dual-link connection is made of two links, with even pixels | |
352 | * transitting on one link, and odd pixels on the other link. This function | |
353 | * returns, for two ports of an LVDS dual-link source, which port shall transmit | |
354 | * the even and odd pixels, based on the requirements of the connected sink. | |
355 | * | |
356 | * The pixel order is determined from the dual-lvds-even-pixels and | |
357 | * dual-lvds-odd-pixels properties in the sink's DT port nodes. If those | |
358 | * properties are not present, or if their usage is not valid, this function | |
359 | * returns -EINVAL. | |
360 | * | |
361 | * If either port is not connected, this function returns -EPIPE. | |
362 | * | |
363 | * @port1 and @port2 are typically DT sibling nodes, but may have different | |
364 | * parents when, for instance, two separate LVDS encoders carry the even and odd | |
365 | * pixels. | |
366 | * | |
367 | * Return: | |
368 | * * DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS - @port1 carries even pixels and @port2 | |
369 | * carries odd pixels | |
370 | * * DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS - @port1 carries odd pixels and @port2 | |
371 | * carries even pixels | |
372 | * * -EINVAL - @port1 and @port2 are not connected to a dual-link LVDS sink, or | |
373 | * the sink configuration is invalid | |
374 | * * -EPIPE - when @port1 or @port2 are not connected | |
375 | */ | |
376 | int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1, | |
377 | const struct device_node *port2) | |
378 | { | |
379 | int remote_p1_pt, remote_p2_pt; | |
380 | ||
381 | if (!port1 || !port2) | |
382 | return -EINVAL; | |
383 | ||
384 | remote_p1_pt = drm_of_lvds_get_remote_pixels_type(port1); | |
385 | if (remote_p1_pt < 0) | |
386 | return remote_p1_pt; | |
387 | ||
388 | remote_p2_pt = drm_of_lvds_get_remote_pixels_type(port2); | |
389 | if (remote_p2_pt < 0) | |
390 | return remote_p2_pt; | |
391 | ||
392 | /* | |
393 | * A valid dual-lVDS bus is found when one remote port is marked with | |
394 | * "dual-lvds-even-pixels", and the other remote port is marked with | |
395 | * "dual-lvds-odd-pixels", bail out if the markers are not right. | |
396 | */ | |
397 | if (remote_p1_pt + remote_p2_pt != DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD) | |
398 | return -EINVAL; | |
399 | ||
400 | return remote_p1_pt == DRM_OF_LVDS_EVEN ? | |
401 | DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS : | |
402 | DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS; | |
403 | } | |
404 | EXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_pixel_order); | |
7c4dd0a2 MV |
405 | |
406 | /** | |
407 | * drm_of_lvds_get_data_mapping - Get LVDS data mapping | |
408 | * @port: DT port node of the LVDS source or sink | |
409 | * | |
410 | * Convert DT "data-mapping" property string value into media bus format value. | |
411 | * | |
412 | * Return: | |
413 | * * MEDIA_BUS_FMT_RGB666_1X7X3_SPWG - data-mapping is "jeida-18" | |
414 | * * MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA - data-mapping is "jeida-24" | |
415 | * * MEDIA_BUS_FMT_RGB888_1X7X4_SPWG - data-mapping is "vesa-24" | |
416 | * * -EINVAL - the "data-mapping" property is unsupported | |
417 | * * -ENODEV - the "data-mapping" property is missing | |
418 | */ | |
419 | int drm_of_lvds_get_data_mapping(const struct device_node *port) | |
420 | { | |
421 | const char *mapping; | |
422 | int ret; | |
423 | ||
424 | ret = of_property_read_string(port, "data-mapping", &mapping); | |
425 | if (ret < 0) | |
426 | return -ENODEV; | |
427 | ||
428 | if (!strcmp(mapping, "jeida-18")) | |
429 | return MEDIA_BUS_FMT_RGB666_1X7X3_SPWG; | |
430 | if (!strcmp(mapping, "jeida-24")) | |
431 | return MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA; | |
432 | if (!strcmp(mapping, "vesa-24")) | |
433 | return MEDIA_BUS_FMT_RGB888_1X7X4_SPWG; | |
434 | ||
435 | return -EINVAL; | |
436 | } | |
437 | EXPORT_SYMBOL_GPL(drm_of_lvds_get_data_mapping); |