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> | |
6 | #include <drm/drmP.h> | |
1f2db303 | 7 | #include <drm/drm_bridge.h> |
7e435aad | 8 | #include <drm/drm_crtc.h> |
9338203c | 9 | #include <drm/drm_encoder.h> |
1f2db303 | 10 | #include <drm/drm_panel.h> |
7e435aad RK |
11 | #include <drm/drm_of.h> |
12 | ||
7f9e7ec9 DV |
13 | /** |
14 | * DOC: overview | |
15 | * | |
16 | * A set of helper functions to aid DRM drivers in parsing standard DT | |
17 | * properties. | |
18 | */ | |
19 | ||
97ac0e47 RK |
20 | static void drm_release_of(struct device *dev, void *data) |
21 | { | |
22 | of_node_put(data); | |
23 | } | |
24 | ||
7e435aad | 25 | /** |
8b5f7a62 | 26 | * drm_of_crtc_port_mask - find the mask of a registered CRTC by port OF node |
7e435aad RK |
27 | * @dev: DRM device |
28 | * @port: port OF node | |
29 | * | |
30 | * Given a port OF node, return the possible mask of the corresponding | |
31 | * CRTC within a device's list of CRTCs. Returns zero if not found. | |
32 | */ | |
8b5f7a62 JS |
33 | uint32_t drm_of_crtc_port_mask(struct drm_device *dev, |
34 | struct device_node *port) | |
7e435aad RK |
35 | { |
36 | unsigned int index = 0; | |
37 | struct drm_crtc *tmp; | |
38 | ||
6295d607 | 39 | drm_for_each_crtc(tmp, dev) { |
7e435aad RK |
40 | if (tmp->port == port) |
41 | return 1 << index; | |
42 | ||
43 | index++; | |
44 | } | |
45 | ||
46 | return 0; | |
47 | } | |
8b5f7a62 | 48 | EXPORT_SYMBOL(drm_of_crtc_port_mask); |
7e435aad RK |
49 | |
50 | /** | |
51 | * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port | |
52 | * @dev: DRM device | |
53 | * @port: encoder port to scan for endpoints | |
54 | * | |
55 | * Scan all endpoints attached to a port, locate their attached CRTCs, | |
56 | * and generate the DRM mask of CRTCs which may be attached to this | |
57 | * encoder. | |
58 | * | |
59 | * See Documentation/devicetree/bindings/graph.txt for the bindings. | |
60 | */ | |
61 | uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, | |
62 | struct device_node *port) | |
63 | { | |
7416f4e3 | 64 | struct device_node *remote_port, *ep; |
7e435aad RK |
65 | uint32_t possible_crtcs = 0; |
66 | ||
7416f4e3 | 67 | for_each_endpoint_of_node(port, ep) { |
7e435aad RK |
68 | remote_port = of_graph_get_remote_port(ep); |
69 | if (!remote_port) { | |
70 | of_node_put(ep); | |
71 | return 0; | |
72 | } | |
73 | ||
8b5f7a62 | 74 | possible_crtcs |= drm_of_crtc_port_mask(dev, remote_port); |
7e435aad RK |
75 | |
76 | of_node_put(remote_port); | |
7416f4e3 | 77 | } |
7e435aad RK |
78 | |
79 | return possible_crtcs; | |
80 | } | |
81 | EXPORT_SYMBOL(drm_of_find_possible_crtcs); | |
df785aa8 | 82 | |
97ac0e47 RK |
83 | /** |
84 | * drm_of_component_match_add - Add a component helper OF node match rule | |
85 | * @master: master device | |
86 | * @matchptr: component match pointer | |
87 | * @compare: compare function used for matching component | |
88 | * @node: of_node | |
89 | */ | |
90 | void drm_of_component_match_add(struct device *master, | |
91 | struct component_match **matchptr, | |
92 | int (*compare)(struct device *, void *), | |
93 | struct device_node *node) | |
94 | { | |
95 | of_node_get(node); | |
96 | component_match_add_release(master, matchptr, drm_release_of, | |
97 | compare, node); | |
98 | } | |
99 | EXPORT_SYMBOL_GPL(drm_of_component_match_add); | |
100 | ||
df785aa8 LD |
101 | /** |
102 | * drm_of_component_probe - Generic probe function for a component based master | |
103 | * @dev: master device containing the OF node | |
104 | * @compare_of: compare function used for matching components | |
7f9e7ec9 | 105 | * @m_ops: component master ops to be used |
df785aa8 LD |
106 | * |
107 | * Parse the platform device OF node and bind all the components associated | |
108 | * with the master. Interface ports are added before the encoders in order to | |
109 | * satisfy their .bind requirements | |
110 | * See Documentation/devicetree/bindings/graph.txt for the bindings. | |
111 | * | |
112 | * Returns zero if successful, or one of the standard error codes if it fails. | |
113 | */ | |
114 | int drm_of_component_probe(struct device *dev, | |
115 | int (*compare_of)(struct device *, void *), | |
116 | const struct component_master_ops *m_ops) | |
117 | { | |
118 | struct device_node *ep, *port, *remote; | |
119 | struct component_match *match = NULL; | |
120 | int i; | |
121 | ||
122 | if (!dev->of_node) | |
123 | return -EINVAL; | |
124 | ||
125 | /* | |
126 | * Bind the crtc's ports first, so that drm_of_find_possible_crtcs() | |
127 | * called from encoder's .bind callbacks works as expected | |
128 | */ | |
129 | for (i = 0; ; i++) { | |
130 | port = of_parse_phandle(dev->of_node, "ports", i); | |
131 | if (!port) | |
132 | break; | |
133 | ||
2efa8392 BS |
134 | if (of_device_is_available(port->parent)) |
135 | drm_of_component_match_add(dev, &match, compare_of, | |
136 | port); | |
df785aa8 | 137 | |
df785aa8 LD |
138 | of_node_put(port); |
139 | } | |
140 | ||
141 | if (i == 0) { | |
142 | dev_err(dev, "missing 'ports' property\n"); | |
143 | return -ENODEV; | |
144 | } | |
145 | ||
146 | if (!match) { | |
147 | dev_err(dev, "no available port\n"); | |
148 | return -ENODEV; | |
149 | } | |
150 | ||
151 | /* | |
152 | * For bound crtcs, bind the encoders attached to their remote endpoint | |
153 | */ | |
154 | for (i = 0; ; i++) { | |
155 | port = of_parse_phandle(dev->of_node, "ports", i); | |
156 | if (!port) | |
157 | break; | |
158 | ||
159 | if (!of_device_is_available(port->parent)) { | |
160 | of_node_put(port); | |
161 | continue; | |
162 | } | |
163 | ||
164 | for_each_child_of_node(port, ep) { | |
165 | remote = of_graph_get_remote_port_parent(ep); | |
166 | if (!remote || !of_device_is_available(remote)) { | |
167 | of_node_put(remote); | |
168 | continue; | |
169 | } else if (!of_device_is_available(remote->parent)) { | |
4bf99144 RH |
170 | dev_warn(dev, "parent device of %pOF is not available\n", |
171 | remote); | |
df785aa8 LD |
172 | of_node_put(remote); |
173 | continue; | |
174 | } | |
175 | ||
97ac0e47 RK |
176 | drm_of_component_match_add(dev, &match, compare_of, |
177 | remote); | |
df785aa8 LD |
178 | of_node_put(remote); |
179 | } | |
180 | of_node_put(port); | |
181 | } | |
182 | ||
183 | return component_master_add_with_match(dev, m_ops, match); | |
184 | } | |
185 | EXPORT_SYMBOL(drm_of_component_probe); | |
4cacf91f PZ |
186 | |
187 | /* | |
188 | * drm_of_encoder_active_endpoint - return the active encoder endpoint | |
189 | * @node: device tree node containing encoder input ports | |
190 | * @encoder: drm_encoder | |
191 | * | |
192 | * Given an encoder device node and a drm_encoder with a connected crtc, | |
193 | * parse the encoder endpoint connecting to the crtc port. | |
194 | */ | |
195 | int drm_of_encoder_active_endpoint(struct device_node *node, | |
196 | struct drm_encoder *encoder, | |
197 | struct of_endpoint *endpoint) | |
198 | { | |
199 | struct device_node *ep; | |
200 | struct drm_crtc *crtc = encoder->crtc; | |
201 | struct device_node *port; | |
202 | int ret; | |
203 | ||
204 | if (!node || !crtc) | |
205 | return -EINVAL; | |
206 | ||
207 | for_each_endpoint_of_node(node, ep) { | |
208 | port = of_graph_get_remote_port(ep); | |
209 | of_node_put(port); | |
210 | if (port == crtc->port) { | |
211 | ret = of_graph_parse_endpoint(ep, endpoint); | |
212 | of_node_put(ep); | |
213 | return ret; | |
214 | } | |
215 | } | |
216 | ||
217 | return -EINVAL; | |
218 | } | |
219 | EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); | |
1f2db303 | 220 | |
3fbdfe99 | 221 | /** |
1f2db303 RH |
222 | * drm_of_find_panel_or_bridge - return connected panel or bridge device |
223 | * @np: device tree node containing encoder output ports | |
3fbdfe99 DV |
224 | * @port: port in the device tree node |
225 | * @endpoint: endpoint in the device tree node | |
1f2db303 RH |
226 | * @panel: pointer to hold returned drm_panel |
227 | * @bridge: pointer to hold returned drm_bridge | |
228 | * | |
229 | * Given a DT node's port and endpoint number, find the connected node and | |
230 | * return either the associated struct drm_panel or drm_bridge device. Either | |
231 | * @panel or @bridge must not be NULL. | |
232 | * | |
233 | * Returns zero if successful, or one of the standard error codes if it fails. | |
234 | */ | |
235 | int drm_of_find_panel_or_bridge(const struct device_node *np, | |
236 | int port, int endpoint, | |
237 | struct drm_panel **panel, | |
238 | struct drm_bridge **bridge) | |
239 | { | |
240 | int ret = -EPROBE_DEFER; | |
241 | struct device_node *remote; | |
242 | ||
243 | if (!panel && !bridge) | |
244 | return -EINVAL; | |
320e421e DC |
245 | if (panel) |
246 | *panel = NULL; | |
1f2db303 RH |
247 | |
248 | remote = of_graph_get_remote_node(np, port, endpoint); | |
249 | if (!remote) | |
250 | return -ENODEV; | |
251 | ||
2e64a174 BB |
252 | if (!of_device_is_available(remote)) { |
253 | of_node_put(remote); | |
254 | return -ENODEV; | |
255 | } | |
256 | ||
1f2db303 RH |
257 | if (panel) { |
258 | *panel = of_drm_find_panel(remote); | |
5fa8e4a2 | 259 | if (!IS_ERR(*panel)) |
1f2db303 | 260 | ret = 0; |
5fa8e4a2 BB |
261 | else |
262 | *panel = NULL; | |
1f2db303 RH |
263 | } |
264 | ||
265 | /* No panel found yet, check for a bridge next. */ | |
266 | if (bridge) { | |
267 | if (ret) { | |
268 | *bridge = of_drm_find_bridge(remote); | |
269 | if (*bridge) | |
270 | ret = 0; | |
271 | } else { | |
272 | *bridge = NULL; | |
273 | } | |
274 | ||
275 | } | |
276 | ||
277 | of_node_put(remote); | |
278 | return ret; | |
279 | } | |
280 | EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); |