Blame view

drivers/gpu/drm/drm_of.c 11.7 KB
457c89965   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
df785aa87   Liviu Dudau   drm: Introduce ge...
2
  #include <linux/component.h>
7e435aad3   Russell King   drm: add of_graph...
3
4
5
  #include <linux/export.h>
  #include <linux/list.h>
  #include <linux/of_graph.h>
0500c04ea   Sam Ravnborg   drm: drop use of ...
6

1f2db3034   Rob Herring   drm: of: introduc...
7
  #include <drm/drm_bridge.h>
7e435aad3   Russell King   drm: add of_graph...
8
  #include <drm/drm_crtc.h>
0500c04ea   Sam Ravnborg   drm: drop use of ...
9
  #include <drm/drm_device.h>
9338203c4   Laurent Pinchart   drm: Don't includ...
10
  #include <drm/drm_encoder.h>
7e435aad3   Russell King   drm: add of_graph...
11
  #include <drm/drm_of.h>
0500c04ea   Sam Ravnborg   drm: drop use of ...
12
  #include <drm/drm_panel.h>
7e435aad3   Russell King   drm: add of_graph...
13

7f9e7ec92   Daniel Vetter   drm/doc: Include ...
14
15
16
17
18
19
  /**
   * DOC: overview
   *
   * A set of helper functions to aid DRM drivers in parsing standard DT
   * properties.
   */
97ac0e47a   Russell King   drm: convert DT c...
20
21
22
23
  static void drm_release_of(struct device *dev, void *data)
  {
  	of_node_put(data);
  }
7e435aad3   Russell King   drm: add of_graph...
24
  /**
8b5f7a624   Jernej Skrabec   drm: of: Export a...
25
   * drm_of_crtc_port_mask - find the mask of a registered CRTC by port OF node
7e435aad3   Russell King   drm: add of_graph...
26
27
28
29
30
31
   * @dev: DRM device
   * @port: port OF node
   *
   * Given a port OF node, return the possible mask of the corresponding
   * CRTC within a device's list of CRTCs.  Returns zero if not found.
   */
8b5f7a624   Jernej Skrabec   drm: of: Export a...
32
33
  uint32_t drm_of_crtc_port_mask(struct drm_device *dev,
  			    struct device_node *port)
7e435aad3   Russell King   drm: add of_graph...
34
35
36
  {
  	unsigned int index = 0;
  	struct drm_crtc *tmp;
6295d607a   Daniel Vetter   drm: Add modeset ...
37
  	drm_for_each_crtc(tmp, dev) {
7e435aad3   Russell King   drm: add of_graph...
38
39
40
41
42
43
44
45
  		if (tmp->port == port)
  			return 1 << index;
  
  		index++;
  	}
  
  	return 0;
  }
8b5f7a624   Jernej Skrabec   drm: of: Export a...
46
  EXPORT_SYMBOL(drm_of_crtc_port_mask);
7e435aad3   Russell King   drm: add of_graph...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  
  /**
   * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
   * @dev: DRM device
   * @port: encoder port to scan for endpoints
   *
   * Scan all endpoints attached to a port, locate their attached CRTCs,
   * and generate the DRM mask of CRTCs which may be attached to this
   * encoder.
   *
   * See Documentation/devicetree/bindings/graph.txt for the bindings.
   */
  uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
  				    struct device_node *port)
  {
7416f4e33   Philipp Zabel   drm: use for_each...
62
  	struct device_node *remote_port, *ep;
7e435aad3   Russell King   drm: add of_graph...
63
  	uint32_t possible_crtcs = 0;
7416f4e33   Philipp Zabel   drm: use for_each...
64
  	for_each_endpoint_of_node(port, ep) {
7e435aad3   Russell King   drm: add of_graph...
65
66
67
68
69
  		remote_port = of_graph_get_remote_port(ep);
  		if (!remote_port) {
  			of_node_put(ep);
  			return 0;
  		}
8b5f7a624   Jernej Skrabec   drm: of: Export a...
70
  		possible_crtcs |= drm_of_crtc_port_mask(dev, remote_port);
7e435aad3   Russell King   drm: add of_graph...
71
72
  
  		of_node_put(remote_port);
7416f4e33   Philipp Zabel   drm: use for_each...
73
  	}
7e435aad3   Russell King   drm: add of_graph...
74
75
76
77
  
  	return possible_crtcs;
  }
  EXPORT_SYMBOL(drm_of_find_possible_crtcs);
df785aa87   Liviu Dudau   drm: Introduce ge...
78
79
  
  /**
97ac0e47a   Russell King   drm: convert DT c...
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
   * drm_of_component_match_add - Add a component helper OF node match rule
   * @master: master device
   * @matchptr: component match pointer
   * @compare: compare function used for matching component
   * @node: of_node
   */
  void drm_of_component_match_add(struct device *master,
  				struct component_match **matchptr,
  				int (*compare)(struct device *, void *),
  				struct device_node *node)
  {
  	of_node_get(node);
  	component_match_add_release(master, matchptr, drm_release_of,
  				    compare, node);
  }
  EXPORT_SYMBOL_GPL(drm_of_component_match_add);
  
  /**
c2b04e2c1   Liu Ying   MLK-16290 drm: Ad...
98
99
   * drm_of_component_probe_with_match - Generic probe function with match
   *                                     entries for a component based master
df785aa87   Liviu Dudau   drm: Introduce ge...
100
   * @dev: master device containing the OF node
c2b04e2c1   Liu Ying   MLK-16290 drm: Ad...
101
   * @match: component match pointer provided to store matches
df785aa87   Liviu Dudau   drm: Introduce ge...
102
   * @compare_of: compare function used for matching components
7f9e7ec92   Daniel Vetter   drm/doc: Include ...
103
   * @m_ops: component master ops to be used
df785aa87   Liviu Dudau   drm: Introduce ge...
104
105
106
107
108
109
110
111
   *
   * Parse the platform device OF node and bind all the components associated
   * with the master. Interface ports are added before the encoders in order to
   * satisfy their .bind requirements
   * See Documentation/devicetree/bindings/graph.txt for the bindings.
   *
   * Returns zero if successful, or one of the standard error codes if it fails.
   */
c2b04e2c1   Liu Ying   MLK-16290 drm: Ad...
112
113
  int drm_of_component_probe_with_match(struct device *dev,
  			   struct component_match *match,
df785aa87   Liviu Dudau   drm: Introduce ge...
114
115
116
117
  			   int (*compare_of)(struct device *, void *),
  			   const struct component_master_ops *m_ops)
  {
  	struct device_node *ep, *port, *remote;
df785aa87   Liviu Dudau   drm: Introduce ge...
118
119
120
121
122
123
124
125
126
127
128
129
130
  	int i;
  
  	if (!dev->of_node)
  		return -EINVAL;
  
  	/*
  	 * Bind the crtc's ports first, so that drm_of_find_possible_crtcs()
  	 * called from encoder's .bind callbacks works as expected
  	 */
  	for (i = 0; ; i++) {
  		port = of_parse_phandle(dev->of_node, "ports", i);
  		if (!port)
  			break;
2efa83920   Baruch Siach   drm: of: simplify...
131
132
133
  		if (of_device_is_available(port->parent))
  			drm_of_component_match_add(dev, &match, compare_of,
  						   port);
df785aa87   Liviu Dudau   drm: Introduce ge...
134

df785aa87   Liviu Dudau   drm: Introduce ge...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
  		of_node_put(port);
  	}
  
  	if (i == 0) {
  		dev_err(dev, "missing 'ports' property
  ");
  		return -ENODEV;
  	}
  
  	if (!match) {
  		dev_err(dev, "no available port
  ");
  		return -ENODEV;
  	}
  
  	/*
  	 * For bound crtcs, bind the encoders attached to their remote endpoint
  	 */
  	for (i = 0; ; i++) {
  		port = of_parse_phandle(dev->of_node, "ports", i);
  		if (!port)
  			break;
  
  		if (!of_device_is_available(port->parent)) {
  			of_node_put(port);
  			continue;
  		}
  
  		for_each_child_of_node(port, ep) {
  			remote = of_graph_get_remote_port_parent(ep);
  			if (!remote || !of_device_is_available(remote)) {
  				of_node_put(remote);
  				continue;
  			} else if (!of_device_is_available(remote->parent)) {
4bf99144d   Rob Herring   drm: Convert to u...
169
170
171
  				dev_warn(dev, "parent device of %pOF is not available
  ",
  					 remote);
df785aa87   Liviu Dudau   drm: Introduce ge...
172
173
174
  				of_node_put(remote);
  				continue;
  			}
97ac0e47a   Russell King   drm: convert DT c...
175
176
  			drm_of_component_match_add(dev, &match, compare_of,
  						   remote);
df785aa87   Liviu Dudau   drm: Introduce ge...
177
178
179
180
181
182
183
  			of_node_put(remote);
  		}
  		of_node_put(port);
  	}
  
  	return component_master_add_with_match(dev, m_ops, match);
  }
c2b04e2c1   Liu Ying   MLK-16290 drm: Ad...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  EXPORT_SYMBOL(drm_of_component_probe_with_match);
  
  /**
   * drm_of_component_probe - Generic probe function for a component based master
   * @dev: master device containing the OF node
   * @compare_of: compare function used for matching components
   * @master_ops: component master ops to be used
   *
   * Parse the platform device OF node and bind all the components associated
   * with the master. Interface ports are added before the encoders in order to
   * satisfy their .bind requirements
   * See Documentation/devicetree/bindings/graph.txt for the bindings.
   *
   * Returns zero if successful, or one of the standard error codes if it fails.
   */
  int drm_of_component_probe(struct device *dev,
  			   int (*compare_of)(struct device *, void *),
  			   const struct component_master_ops *m_ops)
  {
  	struct component_match *match = NULL;
  
  	return drm_of_component_probe_with_match(dev, match, compare_of, m_ops);
  }
df785aa87   Liviu Dudau   drm: Introduce ge...
207
  EXPORT_SYMBOL(drm_of_component_probe);
4cacf91fc   Philipp Zabel   drm: add drm_of_e...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  
  /*
   * drm_of_encoder_active_endpoint - return the active encoder endpoint
   * @node: device tree node containing encoder input ports
   * @encoder: drm_encoder
   *
   * Given an encoder device node and a drm_encoder with a connected crtc,
   * parse the encoder endpoint connecting to the crtc port.
   */
  int drm_of_encoder_active_endpoint(struct device_node *node,
  				   struct drm_encoder *encoder,
  				   struct of_endpoint *endpoint)
  {
  	struct device_node *ep;
  	struct drm_crtc *crtc = encoder->crtc;
  	struct device_node *port;
  	int ret;
  
  	if (!node || !crtc)
  		return -EINVAL;
  
  	for_each_endpoint_of_node(node, ep) {
  		port = of_graph_get_remote_port(ep);
  		of_node_put(port);
  		if (port == crtc->port) {
  			ret = of_graph_parse_endpoint(ep, endpoint);
  			of_node_put(ep);
  			return ret;
  		}
  	}
  
  	return -EINVAL;
  }
  EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint);
1f2db3034   Rob Herring   drm: of: introduc...
242

3fbdfe99f   Daniel Vetter   drm/of: Fix kerne...
243
  /**
1f2db3034   Rob Herring   drm: of: introduc...
244
245
   * drm_of_find_panel_or_bridge - return connected panel or bridge device
   * @np: device tree node containing encoder output ports
3fbdfe99f   Daniel Vetter   drm/of: Fix kerne...
246
247
   * @port: port in the device tree node
   * @endpoint: endpoint in the device tree node
1f2db3034   Rob Herring   drm: of: introduc...
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
   * @panel: pointer to hold returned drm_panel
   * @bridge: pointer to hold returned drm_bridge
   *
   * Given a DT node's port and endpoint number, find the connected node and
   * return either the associated struct drm_panel or drm_bridge device. Either
   * @panel or @bridge must not be NULL.
   *
   * Returns zero if successful, or one of the standard error codes if it fails.
   */
  int drm_of_find_panel_or_bridge(const struct device_node *np,
  				int port, int endpoint,
  				struct drm_panel **panel,
  				struct drm_bridge **bridge)
  {
  	int ret = -EPROBE_DEFER;
  	struct device_node *remote;
  
  	if (!panel && !bridge)
  		return -EINVAL;
320e421ea   Dan Carpenter   drm: of: always i...
267
268
  	if (panel)
  		*panel = NULL;
1f2db3034   Rob Herring   drm: of: introduc...
269

ea5bc3b15   Dmitry Osipenko   drm/of: Make drm_...
270
271
272
273
274
275
276
277
  	/*
  	 * of_graph_get_remote_node() produces a noisy error message if port
  	 * node isn't found and the absence of the port is a legit case here,
  	 * so at first we silently check whether graph presents in the
  	 * device-tree node.
  	 */
  	if (!of_graph_is_present(np))
  		return -ENODEV;
1f2db3034   Rob Herring   drm: of: introduc...
278
279
280
281
282
283
  	remote = of_graph_get_remote_node(np, port, endpoint);
  	if (!remote)
  		return -ENODEV;
  
  	if (panel) {
  		*panel = of_drm_find_panel(remote);
5fa8e4a22   Boris Brezillon   drm/panel: Make o...
284
  		if (!IS_ERR(*panel))
1f2db3034   Rob Herring   drm: of: introduc...
285
  			ret = 0;
5fa8e4a22   Boris Brezillon   drm/panel: Make o...
286
287
  		else
  			*panel = NULL;
1f2db3034   Rob Herring   drm: of: introduc...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
  	}
  
  	/* No panel found yet, check for a bridge next. */
  	if (bridge) {
  		if (ret) {
  			*bridge = of_drm_find_bridge(remote);
  			if (*bridge)
  				ret = 0;
  		} else {
  			*bridge = NULL;
  		}
  
  	}
  
  	of_node_put(remote);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
652900752   Fabrizio Castro   drm: of: Add drm_...
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  
  enum drm_of_lvds_pixels {
  	DRM_OF_LVDS_EVEN = BIT(0),
  	DRM_OF_LVDS_ODD = BIT(1),
  };
  
  static int drm_of_lvds_get_port_pixels_type(struct device_node *port_node)
  {
  	bool even_pixels =
  		of_property_read_bool(port_node, "dual-lvds-even-pixels");
  	bool odd_pixels =
  		of_property_read_bool(port_node, "dual-lvds-odd-pixels");
  
  	return (even_pixels ? DRM_OF_LVDS_EVEN : 0) |
  	       (odd_pixels ? DRM_OF_LVDS_ODD : 0);
  }
  
  static int drm_of_lvds_get_remote_pixels_type(
  			const struct device_node *port_node)
  {
  	struct device_node *endpoint = NULL;
  	int pixels_type = -EPIPE;
  
  	for_each_child_of_node(port_node, endpoint) {
  		struct device_node *remote_port;
  		int current_pt;
  
  		if (!of_node_name_eq(endpoint, "endpoint"))
  			continue;
  
  		remote_port = of_graph_get_remote_port(endpoint);
  		if (!remote_port) {
  			of_node_put(remote_port);
  			return -EPIPE;
  		}
  
  		current_pt = drm_of_lvds_get_port_pixels_type(remote_port);
  		of_node_put(remote_port);
  		if (pixels_type < 0)
  			pixels_type = current_pt;
  
  		/*
  		 * Sanity check, ensure that all remote endpoints have the same
  		 * pixel type. We may lift this restriction later if we need to
  		 * support multiple sinks with different dual-link
  		 * configurations by passing the endpoints explicitly to
  		 * drm_of_lvds_get_dual_link_pixel_order().
  		 */
4ee48cc55   Biju Das   drm: of: Fix doub...
354
  		if (!current_pt || pixels_type != current_pt)
652900752   Fabrizio Castro   drm: of: Add drm_...
355
  			return -EINVAL;
652900752   Fabrizio Castro   drm: of: Add drm_...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  	}
  
  	return pixels_type;
  }
  
  /**
   * drm_of_lvds_get_dual_link_pixel_order - Get LVDS dual-link pixel order
   * @port1: First DT port node of the Dual-link LVDS source
   * @port2: Second DT port node of the Dual-link LVDS source
   *
   * An LVDS dual-link connection is made of two links, with even pixels
   * transitting on one link, and odd pixels on the other link. This function
   * returns, for two ports of an LVDS dual-link source, which port shall transmit
   * the even and odd pixels, based on the requirements of the connected sink.
   *
   * The pixel order is determined from the dual-lvds-even-pixels and
   * dual-lvds-odd-pixels properties in the sink's DT port nodes. If those
   * properties are not present, or if their usage is not valid, this function
   * returns -EINVAL.
   *
   * If either port is not connected, this function returns -EPIPE.
   *
   * @port1 and @port2 are typically DT sibling nodes, but may have different
   * parents when, for instance, two separate LVDS encoders carry the even and odd
   * pixels.
   *
   * Return:
   * * DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS - @port1 carries even pixels and @port2
   *   carries odd pixels
   * * DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS - @port1 carries odd pixels and @port2
   *   carries even pixels
   * * -EINVAL - @port1 and @port2 are not connected to a dual-link LVDS sink, or
   *   the sink configuration is invalid
   * * -EPIPE - when @port1 or @port2 are not connected
   */
  int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
  					  const struct device_node *port2)
  {
  	int remote_p1_pt, remote_p2_pt;
  
  	if (!port1 || !port2)
  		return -EINVAL;
  
  	remote_p1_pt = drm_of_lvds_get_remote_pixels_type(port1);
  	if (remote_p1_pt < 0)
  		return remote_p1_pt;
  
  	remote_p2_pt = drm_of_lvds_get_remote_pixels_type(port2);
  	if (remote_p2_pt < 0)
  		return remote_p2_pt;
  
  	/*
  	 * 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 != DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD)
  		return -EINVAL;
  
  	return remote_p1_pt == DRM_OF_LVDS_EVEN ?
  		DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
  		DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
  }
  EXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_pixel_order);