From 76cb841cb886eef6b3bee341a2266c76578724ad Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 6 May 2024 03:02:30 +0200 Subject: Adding upstream version 4.19.249. Signed-off-by: Daniel Baumann --- drivers/gpu/drm/drm_of.c | 277 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 drivers/gpu/drm/drm_of.c (limited to 'drivers/gpu/drm/drm_of.c') diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c new file mode 100644 index 000000000..2763a5ec8 --- /dev/null +++ b/drivers/gpu/drm/drm_of.c @@ -0,0 +1,277 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: overview + * + * A set of helper functions to aid DRM drivers in parsing standard DT + * properties. + */ + +static void drm_release_of(struct device *dev, void *data) +{ + of_node_put(data); +} + +/** + * drm_of_crtc_port_mask - find the mask of a registered CRTC by port OF node + * @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. + */ +uint32_t drm_of_crtc_port_mask(struct drm_device *dev, + struct device_node *port) +{ + unsigned int index = 0; + struct drm_crtc *tmp; + + drm_for_each_crtc(tmp, dev) { + if (tmp->port == port) + return 1 << index; + + index++; + } + + return 0; +} +EXPORT_SYMBOL(drm_of_crtc_port_mask); + +/** + * 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) +{ + struct device_node *remote_port, *ep; + uint32_t possible_crtcs = 0; + + for_each_endpoint_of_node(port, ep) { + remote_port = of_graph_get_remote_port(ep); + if (!remote_port) { + of_node_put(ep); + return 0; + } + + possible_crtcs |= drm_of_crtc_port_mask(dev, remote_port); + + of_node_put(remote_port); + } + + return possible_crtcs; +} +EXPORT_SYMBOL(drm_of_find_possible_crtcs); + +/** + * 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); + +/** + * 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 + * @m_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 device_node *ep, *port, *remote; + struct component_match *match = NULL; + 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; + + if (of_device_is_available(port->parent)) + drm_of_component_match_add(dev, &match, compare_of, + port); + + of_node_put(port); + } + + if (i == 0) { + dev_err(dev, "missing 'ports' property\n"); + return -ENODEV; + } + + if (!match) { + dev_err(dev, "no available port\n"); + 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)) { + dev_warn(dev, "parent device of %pOF is not available\n", + remote); + of_node_put(remote); + continue; + } + + drm_of_component_match_add(dev, &match, compare_of, + remote); + of_node_put(remote); + } + of_node_put(port); + } + + return component_master_add_with_match(dev, m_ops, match); +} +EXPORT_SYMBOL(drm_of_component_probe); + +/* + * 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); + +/* + * drm_of_find_panel_or_bridge - return connected panel or bridge device + * @np: device tree node containing encoder output ports + * @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; + if (panel) + *panel = NULL; + + remote = of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + if (!of_device_is_available(remote)) { + of_node_put(remote); + return -ENODEV; + } + + if (panel) { + *panel = of_drm_find_panel(remote); + if (!IS_ERR(*panel)) + ret = 0; + else + *panel = NULL; + } + + /* 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); -- cgit v1.2.3