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