xref: /openbmc/linux/drivers/gpu/drm/drm_of.c (revision a06c488d)
1 #include <linux/component.h>
2 #include <linux/export.h>
3 #include <linux/list.h>
4 #include <linux/of_graph.h>
5 #include <drm/drmP.h>
6 #include <drm/drm_crtc.h>
7 #include <drm/drm_of.h>
8 
9 /**
10  * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node
11  * @dev: DRM device
12  * @port: port OF node
13  *
14  * Given a port OF node, return the possible mask of the corresponding
15  * CRTC within a device's list of CRTCs.  Returns zero if not found.
16  */
17 static uint32_t drm_crtc_port_mask(struct drm_device *dev,
18 				   struct device_node *port)
19 {
20 	unsigned int index = 0;
21 	struct drm_crtc *tmp;
22 
23 	drm_for_each_crtc(tmp, dev) {
24 		if (tmp->port == port)
25 			return 1 << index;
26 
27 		index++;
28 	}
29 
30 	return 0;
31 }
32 
33 /**
34  * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
35  * @dev: DRM device
36  * @port: encoder port to scan for endpoints
37  *
38  * Scan all endpoints attached to a port, locate their attached CRTCs,
39  * and generate the DRM mask of CRTCs which may be attached to this
40  * encoder.
41  *
42  * See Documentation/devicetree/bindings/graph.txt for the bindings.
43  */
44 uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
45 				    struct device_node *port)
46 {
47 	struct device_node *remote_port, *ep;
48 	uint32_t possible_crtcs = 0;
49 
50 	for_each_endpoint_of_node(port, ep) {
51 		remote_port = of_graph_get_remote_port(ep);
52 		if (!remote_port) {
53 			of_node_put(ep);
54 			return 0;
55 		}
56 
57 		possible_crtcs |= drm_crtc_port_mask(dev, remote_port);
58 
59 		of_node_put(remote_port);
60 	}
61 
62 	return possible_crtcs;
63 }
64 EXPORT_SYMBOL(drm_of_find_possible_crtcs);
65 
66 /**
67  * drm_of_component_probe - Generic probe function for a component based master
68  * @dev: master device containing the OF node
69  * @compare_of: compare function used for matching components
70  * @master_ops: component master ops to be used
71  *
72  * Parse the platform device OF node and bind all the components associated
73  * with the master. Interface ports are added before the encoders in order to
74  * satisfy their .bind requirements
75  * See Documentation/devicetree/bindings/graph.txt for the bindings.
76  *
77  * Returns zero if successful, or one of the standard error codes if it fails.
78  */
79 int drm_of_component_probe(struct device *dev,
80 			   int (*compare_of)(struct device *, void *),
81 			   const struct component_master_ops *m_ops)
82 {
83 	struct device_node *ep, *port, *remote;
84 	struct component_match *match = NULL;
85 	int i;
86 
87 	if (!dev->of_node)
88 		return -EINVAL;
89 
90 	/*
91 	 * Bind the crtc's ports first, so that drm_of_find_possible_crtcs()
92 	 * called from encoder's .bind callbacks works as expected
93 	 */
94 	for (i = 0; ; i++) {
95 		port = of_parse_phandle(dev->of_node, "ports", i);
96 		if (!port)
97 			break;
98 
99 		if (!of_device_is_available(port->parent)) {
100 			of_node_put(port);
101 			continue;
102 		}
103 
104 		component_match_add(dev, &match, compare_of, port);
105 		of_node_put(port);
106 	}
107 
108 	if (i == 0) {
109 		dev_err(dev, "missing 'ports' property\n");
110 		return -ENODEV;
111 	}
112 
113 	if (!match) {
114 		dev_err(dev, "no available port\n");
115 		return -ENODEV;
116 	}
117 
118 	/*
119 	 * For bound crtcs, bind the encoders attached to their remote endpoint
120 	 */
121 	for (i = 0; ; i++) {
122 		port = of_parse_phandle(dev->of_node, "ports", i);
123 		if (!port)
124 			break;
125 
126 		if (!of_device_is_available(port->parent)) {
127 			of_node_put(port);
128 			continue;
129 		}
130 
131 		for_each_child_of_node(port, ep) {
132 			remote = of_graph_get_remote_port_parent(ep);
133 			if (!remote || !of_device_is_available(remote)) {
134 				of_node_put(remote);
135 				continue;
136 			} else if (!of_device_is_available(remote->parent)) {
137 				dev_warn(dev, "parent device of %s is not available\n",
138 					 remote->full_name);
139 				of_node_put(remote);
140 				continue;
141 			}
142 
143 			component_match_add(dev, &match, compare_of, remote);
144 			of_node_put(remote);
145 		}
146 		of_node_put(port);
147 	}
148 
149 	return component_master_add_with_match(dev, m_ops, match);
150 }
151 EXPORT_SYMBOL(drm_of_component_probe);
152