162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/component.h>
362306a36Sopenharmony_ci#include <linux/export.h>
462306a36Sopenharmony_ci#include <linux/list.h>
562306a36Sopenharmony_ci#include <linux/media-bus-format.h>
662306a36Sopenharmony_ci#include <linux/of.h>
762306a36Sopenharmony_ci#include <linux/of_graph.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <drm/drm_bridge.h>
1062306a36Sopenharmony_ci#include <drm/drm_crtc.h>
1162306a36Sopenharmony_ci#include <drm/drm_device.h>
1262306a36Sopenharmony_ci#include <drm/drm_encoder.h>
1362306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h>
1462306a36Sopenharmony_ci#include <drm/drm_of.h>
1562306a36Sopenharmony_ci#include <drm/drm_panel.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci * DOC: overview
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * A set of helper functions to aid DRM drivers in parsing standard DT
2162306a36Sopenharmony_ci * properties.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * drm_of_crtc_port_mask - find the mask of a registered CRTC by port OF node
2662306a36Sopenharmony_ci * @dev: DRM device
2762306a36Sopenharmony_ci * @port: port OF node
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Given a port OF node, return the possible mask of the corresponding
3062306a36Sopenharmony_ci * CRTC within a device's list of CRTCs.  Returns zero if not found.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ciuint32_t drm_of_crtc_port_mask(struct drm_device *dev,
3362306a36Sopenharmony_ci			    struct device_node *port)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	unsigned int index = 0;
3662306a36Sopenharmony_ci	struct drm_crtc *tmp;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	drm_for_each_crtc(tmp, dev) {
3962306a36Sopenharmony_ci		if (tmp->port == port)
4062306a36Sopenharmony_ci			return 1 << index;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci		index++;
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return 0;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_of_crtc_port_mask);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/**
5062306a36Sopenharmony_ci * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
5162306a36Sopenharmony_ci * @dev: DRM device
5262306a36Sopenharmony_ci * @port: encoder port to scan for endpoints
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Scan all endpoints attached to a port, locate their attached CRTCs,
5562306a36Sopenharmony_ci * and generate the DRM mask of CRTCs which may be attached to this
5662306a36Sopenharmony_ci * encoder.
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * See Documentation/devicetree/bindings/graph.txt for the bindings.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ciuint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
6162306a36Sopenharmony_ci				    struct device_node *port)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct device_node *remote_port, *ep;
6462306a36Sopenharmony_ci	uint32_t possible_crtcs = 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	for_each_endpoint_of_node(port, ep) {
6762306a36Sopenharmony_ci		remote_port = of_graph_get_remote_port(ep);
6862306a36Sopenharmony_ci		if (!remote_port) {
6962306a36Sopenharmony_ci			of_node_put(ep);
7062306a36Sopenharmony_ci			return 0;
7162306a36Sopenharmony_ci		}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		possible_crtcs |= drm_of_crtc_port_mask(dev, remote_port);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci		of_node_put(remote_port);
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return possible_crtcs;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_of_find_possible_crtcs);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/**
8362306a36Sopenharmony_ci * drm_of_component_match_add - Add a component helper OF node match rule
8462306a36Sopenharmony_ci * @master: master device
8562306a36Sopenharmony_ci * @matchptr: component match pointer
8662306a36Sopenharmony_ci * @compare: compare function used for matching component
8762306a36Sopenharmony_ci * @node: of_node
8862306a36Sopenharmony_ci */
8962306a36Sopenharmony_civoid drm_of_component_match_add(struct device *master,
9062306a36Sopenharmony_ci				struct component_match **matchptr,
9162306a36Sopenharmony_ci				int (*compare)(struct device *, void *),
9262306a36Sopenharmony_ci				struct device_node *node)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	of_node_get(node);
9562306a36Sopenharmony_ci	component_match_add_release(master, matchptr, component_release_of,
9662306a36Sopenharmony_ci				    compare, node);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_of_component_match_add);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/**
10162306a36Sopenharmony_ci * drm_of_component_probe - Generic probe function for a component based master
10262306a36Sopenharmony_ci * @dev: master device containing the OF node
10362306a36Sopenharmony_ci * @compare_of: compare function used for matching components
10462306a36Sopenharmony_ci * @m_ops: component master ops to be used
10562306a36Sopenharmony_ci *
10662306a36Sopenharmony_ci * Parse the platform device OF node and bind all the components associated
10762306a36Sopenharmony_ci * with the master. Interface ports are added before the encoders in order to
10862306a36Sopenharmony_ci * satisfy their .bind requirements
10962306a36Sopenharmony_ci * See Documentation/devicetree/bindings/graph.txt for the bindings.
11062306a36Sopenharmony_ci *
11162306a36Sopenharmony_ci * Returns zero if successful, or one of the standard error codes if it fails.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_ciint drm_of_component_probe(struct device *dev,
11462306a36Sopenharmony_ci			   int (*compare_of)(struct device *, void *),
11562306a36Sopenharmony_ci			   const struct component_master_ops *m_ops)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct device_node *ep, *port, *remote;
11862306a36Sopenharmony_ci	struct component_match *match = NULL;
11962306a36Sopenharmony_ci	int i;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!dev->of_node)
12262306a36Sopenharmony_ci		return -EINVAL;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/*
12562306a36Sopenharmony_ci	 * Bind the crtc's ports first, so that drm_of_find_possible_crtcs()
12662306a36Sopenharmony_ci	 * called from encoder's .bind callbacks works as expected
12762306a36Sopenharmony_ci	 */
12862306a36Sopenharmony_ci	for (i = 0; ; i++) {
12962306a36Sopenharmony_ci		port = of_parse_phandle(dev->of_node, "ports", i);
13062306a36Sopenharmony_ci		if (!port)
13162306a36Sopenharmony_ci			break;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		if (of_device_is_available(port->parent))
13462306a36Sopenharmony_ci			drm_of_component_match_add(dev, &match, compare_of,
13562306a36Sopenharmony_ci						   port);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		of_node_put(port);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (i == 0) {
14162306a36Sopenharmony_ci		dev_err(dev, "missing 'ports' property\n");
14262306a36Sopenharmony_ci		return -ENODEV;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (!match) {
14662306a36Sopenharmony_ci		dev_err(dev, "no available port\n");
14762306a36Sopenharmony_ci		return -ENODEV;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/*
15162306a36Sopenharmony_ci	 * For bound crtcs, bind the encoders attached to their remote endpoint
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	for (i = 0; ; i++) {
15462306a36Sopenharmony_ci		port = of_parse_phandle(dev->of_node, "ports", i);
15562306a36Sopenharmony_ci		if (!port)
15662306a36Sopenharmony_ci			break;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		if (!of_device_is_available(port->parent)) {
15962306a36Sopenharmony_ci			of_node_put(port);
16062306a36Sopenharmony_ci			continue;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		for_each_child_of_node(port, ep) {
16462306a36Sopenharmony_ci			remote = of_graph_get_remote_port_parent(ep);
16562306a36Sopenharmony_ci			if (!remote || !of_device_is_available(remote)) {
16662306a36Sopenharmony_ci				of_node_put(remote);
16762306a36Sopenharmony_ci				continue;
16862306a36Sopenharmony_ci			} else if (!of_device_is_available(remote->parent)) {
16962306a36Sopenharmony_ci				dev_warn(dev, "parent device of %pOF is not available\n",
17062306a36Sopenharmony_ci					 remote);
17162306a36Sopenharmony_ci				of_node_put(remote);
17262306a36Sopenharmony_ci				continue;
17362306a36Sopenharmony_ci			}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci			drm_of_component_match_add(dev, &match, compare_of,
17662306a36Sopenharmony_ci						   remote);
17762306a36Sopenharmony_ci			of_node_put(remote);
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci		of_node_put(port);
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return component_master_add_with_match(dev, m_ops, match);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_of_component_probe);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/*
18762306a36Sopenharmony_ci * drm_of_encoder_active_endpoint - return the active encoder endpoint
18862306a36Sopenharmony_ci * @node: device tree node containing encoder input ports
18962306a36Sopenharmony_ci * @encoder: drm_encoder
19062306a36Sopenharmony_ci *
19162306a36Sopenharmony_ci * Given an encoder device node and a drm_encoder with a connected crtc,
19262306a36Sopenharmony_ci * parse the encoder endpoint connecting to the crtc port.
19362306a36Sopenharmony_ci */
19462306a36Sopenharmony_ciint drm_of_encoder_active_endpoint(struct device_node *node,
19562306a36Sopenharmony_ci				   struct drm_encoder *encoder,
19662306a36Sopenharmony_ci				   struct of_endpoint *endpoint)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct device_node *ep;
19962306a36Sopenharmony_ci	struct drm_crtc *crtc = encoder->crtc;
20062306a36Sopenharmony_ci	struct device_node *port;
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (!node || !crtc)
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	for_each_endpoint_of_node(node, ep) {
20762306a36Sopenharmony_ci		port = of_graph_get_remote_port(ep);
20862306a36Sopenharmony_ci		of_node_put(port);
20962306a36Sopenharmony_ci		if (port == crtc->port) {
21062306a36Sopenharmony_ci			ret = of_graph_parse_endpoint(ep, endpoint);
21162306a36Sopenharmony_ci			of_node_put(ep);
21262306a36Sopenharmony_ci			return ret;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return -EINVAL;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/**
22162306a36Sopenharmony_ci * drm_of_find_panel_or_bridge - return connected panel or bridge device
22262306a36Sopenharmony_ci * @np: device tree node containing encoder output ports
22362306a36Sopenharmony_ci * @port: port in the device tree node
22462306a36Sopenharmony_ci * @endpoint: endpoint in the device tree node
22562306a36Sopenharmony_ci * @panel: pointer to hold returned drm_panel
22662306a36Sopenharmony_ci * @bridge: pointer to hold returned drm_bridge
22762306a36Sopenharmony_ci *
22862306a36Sopenharmony_ci * Given a DT node's port and endpoint number, find the connected node and
22962306a36Sopenharmony_ci * return either the associated struct drm_panel or drm_bridge device. Either
23062306a36Sopenharmony_ci * @panel or @bridge must not be NULL.
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * This function is deprecated and should not be used in new drivers. Use
23362306a36Sopenharmony_ci * devm_drm_of_get_bridge() instead.
23462306a36Sopenharmony_ci *
23562306a36Sopenharmony_ci * Returns zero if successful, or one of the standard error codes if it fails.
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_ciint drm_of_find_panel_or_bridge(const struct device_node *np,
23862306a36Sopenharmony_ci				int port, int endpoint,
23962306a36Sopenharmony_ci				struct drm_panel **panel,
24062306a36Sopenharmony_ci				struct drm_bridge **bridge)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	int ret = -EPROBE_DEFER;
24362306a36Sopenharmony_ci	struct device_node *remote;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (!panel && !bridge)
24662306a36Sopenharmony_ci		return -EINVAL;
24762306a36Sopenharmony_ci	if (panel)
24862306a36Sopenharmony_ci		*panel = NULL;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/*
25162306a36Sopenharmony_ci	 * of_graph_get_remote_node() produces a noisy error message if port
25262306a36Sopenharmony_ci	 * node isn't found and the absence of the port is a legit case here,
25362306a36Sopenharmony_ci	 * so at first we silently check whether graph presents in the
25462306a36Sopenharmony_ci	 * device-tree node.
25562306a36Sopenharmony_ci	 */
25662306a36Sopenharmony_ci	if (!of_graph_is_present(np))
25762306a36Sopenharmony_ci		return -ENODEV;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	remote = of_graph_get_remote_node(np, port, endpoint);
26062306a36Sopenharmony_ci	if (!remote)
26162306a36Sopenharmony_ci		return -ENODEV;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if (panel) {
26462306a36Sopenharmony_ci		*panel = of_drm_find_panel(remote);
26562306a36Sopenharmony_ci		if (!IS_ERR(*panel))
26662306a36Sopenharmony_ci			ret = 0;
26762306a36Sopenharmony_ci		else
26862306a36Sopenharmony_ci			*panel = NULL;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* No panel found yet, check for a bridge next. */
27262306a36Sopenharmony_ci	if (bridge) {
27362306a36Sopenharmony_ci		if (ret) {
27462306a36Sopenharmony_ci			*bridge = of_drm_find_bridge(remote);
27562306a36Sopenharmony_ci			if (*bridge)
27662306a36Sopenharmony_ci				ret = 0;
27762306a36Sopenharmony_ci		} else {
27862306a36Sopenharmony_ci			*bridge = NULL;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	of_node_put(remote);
28462306a36Sopenharmony_ci	return ret;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cienum drm_of_lvds_pixels {
28962306a36Sopenharmony_ci	DRM_OF_LVDS_EVEN = BIT(0),
29062306a36Sopenharmony_ci	DRM_OF_LVDS_ODD = BIT(1),
29162306a36Sopenharmony_ci};
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int drm_of_lvds_get_port_pixels_type(struct device_node *port_node)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	bool even_pixels =
29662306a36Sopenharmony_ci		of_property_read_bool(port_node, "dual-lvds-even-pixels");
29762306a36Sopenharmony_ci	bool odd_pixels =
29862306a36Sopenharmony_ci		of_property_read_bool(port_node, "dual-lvds-odd-pixels");
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return (even_pixels ? DRM_OF_LVDS_EVEN : 0) |
30162306a36Sopenharmony_ci	       (odd_pixels ? DRM_OF_LVDS_ODD : 0);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int drm_of_lvds_get_remote_pixels_type(
30562306a36Sopenharmony_ci			const struct device_node *port_node)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct device_node *endpoint = NULL;
30862306a36Sopenharmony_ci	int pixels_type = -EPIPE;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	for_each_child_of_node(port_node, endpoint) {
31162306a36Sopenharmony_ci		struct device_node *remote_port;
31262306a36Sopenharmony_ci		int current_pt;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		if (!of_node_name_eq(endpoint, "endpoint"))
31562306a36Sopenharmony_ci			continue;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		remote_port = of_graph_get_remote_port(endpoint);
31862306a36Sopenharmony_ci		if (!remote_port) {
31962306a36Sopenharmony_ci			of_node_put(endpoint);
32062306a36Sopenharmony_ci			return -EPIPE;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		current_pt = drm_of_lvds_get_port_pixels_type(remote_port);
32462306a36Sopenharmony_ci		of_node_put(remote_port);
32562306a36Sopenharmony_ci		if (pixels_type < 0)
32662306a36Sopenharmony_ci			pixels_type = current_pt;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		/*
32962306a36Sopenharmony_ci		 * Sanity check, ensure that all remote endpoints have the same
33062306a36Sopenharmony_ci		 * pixel type. We may lift this restriction later if we need to
33162306a36Sopenharmony_ci		 * support multiple sinks with different dual-link
33262306a36Sopenharmony_ci		 * configurations by passing the endpoints explicitly to
33362306a36Sopenharmony_ci		 * drm_of_lvds_get_dual_link_pixel_order().
33462306a36Sopenharmony_ci		 */
33562306a36Sopenharmony_ci		if (!current_pt || pixels_type != current_pt) {
33662306a36Sopenharmony_ci			of_node_put(endpoint);
33762306a36Sopenharmony_ci			return -EINVAL;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return pixels_type;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci/**
34562306a36Sopenharmony_ci * drm_of_lvds_get_dual_link_pixel_order - Get LVDS dual-link pixel order
34662306a36Sopenharmony_ci * @port1: First DT port node of the Dual-link LVDS source
34762306a36Sopenharmony_ci * @port2: Second DT port node of the Dual-link LVDS source
34862306a36Sopenharmony_ci *
34962306a36Sopenharmony_ci * An LVDS dual-link connection is made of two links, with even pixels
35062306a36Sopenharmony_ci * transitting on one link, and odd pixels on the other link. This function
35162306a36Sopenharmony_ci * returns, for two ports of an LVDS dual-link source, which port shall transmit
35262306a36Sopenharmony_ci * the even and odd pixels, based on the requirements of the connected sink.
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * The pixel order is determined from the dual-lvds-even-pixels and
35562306a36Sopenharmony_ci * dual-lvds-odd-pixels properties in the sink's DT port nodes. If those
35662306a36Sopenharmony_ci * properties are not present, or if their usage is not valid, this function
35762306a36Sopenharmony_ci * returns -EINVAL.
35862306a36Sopenharmony_ci *
35962306a36Sopenharmony_ci * If either port is not connected, this function returns -EPIPE.
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * @port1 and @port2 are typically DT sibling nodes, but may have different
36262306a36Sopenharmony_ci * parents when, for instance, two separate LVDS encoders carry the even and odd
36362306a36Sopenharmony_ci * pixels.
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci * Return:
36662306a36Sopenharmony_ci * * DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS - @port1 carries even pixels and @port2
36762306a36Sopenharmony_ci *   carries odd pixels
36862306a36Sopenharmony_ci * * DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS - @port1 carries odd pixels and @port2
36962306a36Sopenharmony_ci *   carries even pixels
37062306a36Sopenharmony_ci * * -EINVAL - @port1 and @port2 are not connected to a dual-link LVDS sink, or
37162306a36Sopenharmony_ci *   the sink configuration is invalid
37262306a36Sopenharmony_ci * * -EPIPE - when @port1 or @port2 are not connected
37362306a36Sopenharmony_ci */
37462306a36Sopenharmony_ciint drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
37562306a36Sopenharmony_ci					  const struct device_node *port2)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	int remote_p1_pt, remote_p2_pt;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (!port1 || !port2)
38062306a36Sopenharmony_ci		return -EINVAL;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	remote_p1_pt = drm_of_lvds_get_remote_pixels_type(port1);
38362306a36Sopenharmony_ci	if (remote_p1_pt < 0)
38462306a36Sopenharmony_ci		return remote_p1_pt;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	remote_p2_pt = drm_of_lvds_get_remote_pixels_type(port2);
38762306a36Sopenharmony_ci	if (remote_p2_pt < 0)
38862306a36Sopenharmony_ci		return remote_p2_pt;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/*
39162306a36Sopenharmony_ci	 * A valid dual-lVDS bus is found when one remote port is marked with
39262306a36Sopenharmony_ci	 * "dual-lvds-even-pixels", and the other remote port is marked with
39362306a36Sopenharmony_ci	 * "dual-lvds-odd-pixels", bail out if the markers are not right.
39462306a36Sopenharmony_ci	 */
39562306a36Sopenharmony_ci	if (remote_p1_pt + remote_p2_pt != DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD)
39662306a36Sopenharmony_ci		return -EINVAL;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return remote_p1_pt == DRM_OF_LVDS_EVEN ?
39962306a36Sopenharmony_ci		DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS :
40062306a36Sopenharmony_ci		DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_of_lvds_get_dual_link_pixel_order);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/**
40562306a36Sopenharmony_ci * drm_of_lvds_get_data_mapping - Get LVDS data mapping
40662306a36Sopenharmony_ci * @port: DT port node of the LVDS source or sink
40762306a36Sopenharmony_ci *
40862306a36Sopenharmony_ci * Convert DT "data-mapping" property string value into media bus format value.
40962306a36Sopenharmony_ci *
41062306a36Sopenharmony_ci * Return:
41162306a36Sopenharmony_ci * * MEDIA_BUS_FMT_RGB666_1X7X3_SPWG - data-mapping is "jeida-18"
41262306a36Sopenharmony_ci * * MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA - data-mapping is "jeida-24"
41362306a36Sopenharmony_ci * * MEDIA_BUS_FMT_RGB888_1X7X4_SPWG - data-mapping is "vesa-24"
41462306a36Sopenharmony_ci * * -EINVAL - the "data-mapping" property is unsupported
41562306a36Sopenharmony_ci * * -ENODEV - the "data-mapping" property is missing
41662306a36Sopenharmony_ci */
41762306a36Sopenharmony_ciint drm_of_lvds_get_data_mapping(const struct device_node *port)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	const char *mapping;
42062306a36Sopenharmony_ci	int ret;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	ret = of_property_read_string(port, "data-mapping", &mapping);
42362306a36Sopenharmony_ci	if (ret < 0)
42462306a36Sopenharmony_ci		return -ENODEV;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (!strcmp(mapping, "jeida-18"))
42762306a36Sopenharmony_ci		return MEDIA_BUS_FMT_RGB666_1X7X3_SPWG;
42862306a36Sopenharmony_ci	if (!strcmp(mapping, "jeida-24"))
42962306a36Sopenharmony_ci		return MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
43062306a36Sopenharmony_ci	if (!strcmp(mapping, "vesa-24"))
43162306a36Sopenharmony_ci		return MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return -EINVAL;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_of_lvds_get_data_mapping);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci/**
43862306a36Sopenharmony_ci * drm_of_get_data_lanes_count - Get DSI/(e)DP data lane count
43962306a36Sopenharmony_ci * @endpoint: DT endpoint node of the DSI/(e)DP source or sink
44062306a36Sopenharmony_ci * @min: minimum supported number of data lanes
44162306a36Sopenharmony_ci * @max: maximum supported number of data lanes
44262306a36Sopenharmony_ci *
44362306a36Sopenharmony_ci * Count DT "data-lanes" property elements and check for validity.
44462306a36Sopenharmony_ci *
44562306a36Sopenharmony_ci * Return:
44662306a36Sopenharmony_ci * * min..max - positive integer count of "data-lanes" elements
44762306a36Sopenharmony_ci * * -ve - the "data-lanes" property is missing or invalid
44862306a36Sopenharmony_ci * * -EINVAL - the "data-lanes" property is unsupported
44962306a36Sopenharmony_ci */
45062306a36Sopenharmony_ciint drm_of_get_data_lanes_count(const struct device_node *endpoint,
45162306a36Sopenharmony_ci				const unsigned int min, const unsigned int max)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	int ret;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	ret = of_property_count_u32_elems(endpoint, "data-lanes");
45662306a36Sopenharmony_ci	if (ret < 0)
45762306a36Sopenharmony_ci		return ret;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (ret < min || ret > max)
46062306a36Sopenharmony_ci		return -EINVAL;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	return ret;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci/**
46762306a36Sopenharmony_ci * drm_of_get_data_lanes_count_ep - Get DSI/(e)DP data lane count by endpoint
46862306a36Sopenharmony_ci * @port: DT port node of the DSI/(e)DP source or sink
46962306a36Sopenharmony_ci * @port_reg: identifier (value of reg property) of the parent port node
47062306a36Sopenharmony_ci * @reg: identifier (value of reg property) of the endpoint node
47162306a36Sopenharmony_ci * @min: minimum supported number of data lanes
47262306a36Sopenharmony_ci * @max: maximum supported number of data lanes
47362306a36Sopenharmony_ci *
47462306a36Sopenharmony_ci * Count DT "data-lanes" property elements and check for validity.
47562306a36Sopenharmony_ci * This variant uses endpoint specifier.
47662306a36Sopenharmony_ci *
47762306a36Sopenharmony_ci * Return:
47862306a36Sopenharmony_ci * * min..max - positive integer count of "data-lanes" elements
47962306a36Sopenharmony_ci * * -EINVAL - the "data-mapping" property is unsupported
48062306a36Sopenharmony_ci * * -ENODEV - the "data-mapping" property is missing
48162306a36Sopenharmony_ci */
48262306a36Sopenharmony_ciint drm_of_get_data_lanes_count_ep(const struct device_node *port,
48362306a36Sopenharmony_ci				   int port_reg, int reg,
48462306a36Sopenharmony_ci				   const unsigned int min,
48562306a36Sopenharmony_ci				   const unsigned int max)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct device_node *endpoint;
48862306a36Sopenharmony_ci	int ret;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	endpoint = of_graph_get_endpoint_by_regs(port, port_reg, reg);
49162306a36Sopenharmony_ci	ret = drm_of_get_data_lanes_count(endpoint, min, max);
49262306a36Sopenharmony_ci	of_node_put(endpoint);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return ret;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count_ep);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM_MIPI_DSI)
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/**
50162306a36Sopenharmony_ci * drm_of_get_dsi_bus - find the DSI bus for a given device
50262306a36Sopenharmony_ci * @dev: parent device of display (SPI, I2C)
50362306a36Sopenharmony_ci *
50462306a36Sopenharmony_ci * Gets parent DSI bus for a DSI device controlled through a bus other
50562306a36Sopenharmony_ci * than MIPI-DCS (SPI, I2C, etc.) using the Device Tree.
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * Returns pointer to mipi_dsi_host if successful, -EINVAL if the
50862306a36Sopenharmony_ci * request is unsupported, -EPROBE_DEFER if the DSI host is found but
50962306a36Sopenharmony_ci * not available, or -ENODEV otherwise.
51062306a36Sopenharmony_ci */
51162306a36Sopenharmony_cistruct mipi_dsi_host *drm_of_get_dsi_bus(struct device *dev)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct mipi_dsi_host *dsi_host;
51462306a36Sopenharmony_ci	struct device_node *endpoint, *dsi_host_node;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	/*
51762306a36Sopenharmony_ci	 * Get first endpoint child from device.
51862306a36Sopenharmony_ci	 */
51962306a36Sopenharmony_ci	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
52062306a36Sopenharmony_ci	if (!endpoint)
52162306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/*
52462306a36Sopenharmony_ci	 * Follow the first endpoint to get the DSI host node and then
52562306a36Sopenharmony_ci	 * release the endpoint since we no longer need it.
52662306a36Sopenharmony_ci	 */
52762306a36Sopenharmony_ci	dsi_host_node = of_graph_get_remote_port_parent(endpoint);
52862306a36Sopenharmony_ci	of_node_put(endpoint);
52962306a36Sopenharmony_ci	if (!dsi_host_node)
53062306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/*
53362306a36Sopenharmony_ci	 * Get the DSI host from the DSI host node. If we get an error
53462306a36Sopenharmony_ci	 * or the return is null assume we're not ready to probe just
53562306a36Sopenharmony_ci	 * yet. Release the DSI host node since we're done with it.
53662306a36Sopenharmony_ci	 */
53762306a36Sopenharmony_ci	dsi_host = of_find_mipi_dsi_host_by_node(dsi_host_node);
53862306a36Sopenharmony_ci	of_node_put(dsi_host_node);
53962306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(dsi_host))
54062306a36Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return dsi_host;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_of_get_dsi_bus);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci#endif /* CONFIG_DRM_MIPI_DSI */
547