xref: /rk3399_rockchip-uboot/drivers/video/drm/drm_of.c (revision e60b026785da96e7717a915cc49de297f487d63f)
1*e60b0267SChaoyi Chen // SPDX-License-Identifier: GPL-2.0+
2*e60b0267SChaoyi Chen /*
3*e60b0267SChaoyi Chen  * (C) Copyright 2023 Rockchip Electronics Co., Ltd
4*e60b0267SChaoyi Chen  *
5*e60b0267SChaoyi Chen  */
6*e60b0267SChaoyi Chen #include <dm/device.h>
7*e60b0267SChaoyi Chen #include <dm/read.h>
8*e60b0267SChaoyi Chen 
9*e60b0267SChaoyi Chen #include "drm_of.h"
10*e60b0267SChaoyi Chen 
11*e60b0267SChaoyi Chen enum drm_of_lvds_pixels {
12*e60b0267SChaoyi Chen 	DRM_OF_LVDS_EVEN = BIT(0),
13*e60b0267SChaoyi Chen 	DRM_OF_LVDS_ODD = BIT(1),
14*e60b0267SChaoyi Chen 	DRM_OF_LVDS_LEFT = BIT(2),
15*e60b0267SChaoyi Chen 	DRM_OF_LVDS_RIGHT = BIT(3),
16*e60b0267SChaoyi Chen };
17*e60b0267SChaoyi Chen 
18*e60b0267SChaoyi Chen static int
drm_of_lvds_get_port_pixels_type(const struct device_node * port_node)19*e60b0267SChaoyi Chen drm_of_lvds_get_port_pixels_type(const struct device_node *port_node)
20*e60b0267SChaoyi Chen {
21*e60b0267SChaoyi Chen 	ofnode node = np_to_ofnode(port_node);
22*e60b0267SChaoyi Chen 
23*e60b0267SChaoyi Chen 	bool even_pixels =
24*e60b0267SChaoyi Chen 		ofnode_read_bool(node, "dual-lvds-even-pixels");
25*e60b0267SChaoyi Chen 	bool odd_pixels =
26*e60b0267SChaoyi Chen 		ofnode_read_bool(node, "dual-lvds-odd-pixels");
27*e60b0267SChaoyi Chen 	bool left_pixels =
28*e60b0267SChaoyi Chen 		ofnode_read_bool(node, "dual-lvds-left-pixels");
29*e60b0267SChaoyi Chen 	bool right_pixels =
30*e60b0267SChaoyi Chen 		ofnode_read_bool(node, "dual-lvds-right-pixels");
31*e60b0267SChaoyi Chen 
32*e60b0267SChaoyi Chen 	return (even_pixels ? DRM_OF_LVDS_EVEN : 0) |
33*e60b0267SChaoyi Chen 		   (odd_pixels ? DRM_OF_LVDS_ODD : 0) |
34*e60b0267SChaoyi Chen 		   (left_pixels ? DRM_OF_LVDS_LEFT : 0) |
35*e60b0267SChaoyi Chen 		   (right_pixels ? DRM_OF_LVDS_RIGHT : 0);
36*e60b0267SChaoyi Chen }
37*e60b0267SChaoyi Chen 
38*e60b0267SChaoyi Chen static int
drm_of_lvds_get_remote_pixels_type(const struct device_node * port_node)39*e60b0267SChaoyi Chen drm_of_lvds_get_remote_pixels_type(const struct device_node *port_node)
40*e60b0267SChaoyi Chen {
41*e60b0267SChaoyi Chen 	ofnode node = np_to_ofnode(port_node);
42*e60b0267SChaoyi Chen 	ofnode endpoint;
43*e60b0267SChaoyi Chen 	uint phandle;
44*e60b0267SChaoyi Chen 	int pixels_type = -EPIPE;
45*e60b0267SChaoyi Chen 
46*e60b0267SChaoyi Chen 	ofnode_for_each_subnode(endpoint, node) {
47*e60b0267SChaoyi Chen 		int current_pt;
48*e60b0267SChaoyi Chen 		const char *name;
49*e60b0267SChaoyi Chen 
50*e60b0267SChaoyi Chen 		if (!ofnode_is_available(endpoint))
51*e60b0267SChaoyi Chen 			continue;
52*e60b0267SChaoyi Chen 
53*e60b0267SChaoyi Chen 		name = ofnode_get_name(endpoint);
54*e60b0267SChaoyi Chen 		if (strncmp(name, "endpoint", 8) != 0)
55*e60b0267SChaoyi Chen 			continue;
56*e60b0267SChaoyi Chen 
57*e60b0267SChaoyi Chen 		if (ofnode_read_u32(endpoint, "remote-endpoint", &phandle))
58*e60b0267SChaoyi Chen 			continue;
59*e60b0267SChaoyi Chen 
60*e60b0267SChaoyi Chen 		endpoint = ofnode_get_by_phandle(phandle);
61*e60b0267SChaoyi Chen 		if (!ofnode_valid(endpoint) || !ofnode_is_available(endpoint))
62*e60b0267SChaoyi Chen 			continue;
63*e60b0267SChaoyi Chen 
64*e60b0267SChaoyi Chen 		endpoint = ofnode_get_parent(endpoint);
65*e60b0267SChaoyi Chen 		if (!ofnode_valid(endpoint))
66*e60b0267SChaoyi Chen 			continue;
67*e60b0267SChaoyi Chen 
68*e60b0267SChaoyi Chen 		current_pt =
69*e60b0267SChaoyi Chen 			drm_of_lvds_get_port_pixels_type(ofnode_to_np(endpoint
70*e60b0267SChaoyi Chen 				));
71*e60b0267SChaoyi Chen 		if (pixels_type < 0)
72*e60b0267SChaoyi Chen 			pixels_type = current_pt;
73*e60b0267SChaoyi Chen 
74*e60b0267SChaoyi Chen 		/*
75*e60b0267SChaoyi Chen 		 * Sanity check, ensure that all remote endpoints have the same
76*e60b0267SChaoyi Chen 		 * pixel type. We may lift this restriction later if we need to
77*e60b0267SChaoyi Chen 		 * support multiple sinks with different dual-link
78*e60b0267SChaoyi Chen 		 * configurations by passing the endpoints explicitly to
79*e60b0267SChaoyi Chen 		 * drm_of_lvds_get_dual_link_pixel_order().
80*e60b0267SChaoyi Chen 		 */
81*e60b0267SChaoyi Chen 		if (!current_pt || pixels_type != current_pt)
82*e60b0267SChaoyi Chen 			return -EINVAL;
83*e60b0267SChaoyi Chen 	}
84*e60b0267SChaoyi Chen 
85*e60b0267SChaoyi Chen 	return pixels_type;
86*e60b0267SChaoyi Chen }
87*e60b0267SChaoyi Chen 
88*e60b0267SChaoyi Chen /**
89*e60b0267SChaoyi Chen  * drm_of_lvds_get_dual_link_pixel_order - Get LVDS dual-link pixel order
90*e60b0267SChaoyi Chen  * @port1: First DT port node of the Dual-link LVDS source
91*e60b0267SChaoyi Chen  * @port2: Second DT port node of the Dual-link LVDS source
92*e60b0267SChaoyi Chen  *
93*e60b0267SChaoyi Chen  * An LVDS dual-link connection is made of two links, the two link can transmit
94*e60b0267SChaoyi Chen  * odd pixels and even pixels independently, or the two link can also transmit
95*e60b0267SChaoyi Chen  * left pixels and right pixels independently. This function returns for two
96*e60b0267SChaoyi Chen  * ports of an LVDS dual-link source, based on the requirements of the connected
97*e60b0267SChaoyi Chen  * sink.
98*e60b0267SChaoyi Chen  *
99*e60b0267SChaoyi Chen  * The pixel order is determined from the dual-lvds-even-pixels +
100*e60b0267SChaoyi Chen  * dual-lvds-odd-pixels or dual-lvds-left-pixels + dual-lvds-right-pixels
101*e60b0267SChaoyi Chen  * properties in the sink's DT port nodes. If those
102*e60b0267SChaoyi Chen  * properties are not present, or if their usage is not valid, this function
103*e60b0267SChaoyi Chen  * returns -EINVAL.
104*e60b0267SChaoyi Chen  *
105*e60b0267SChaoyi Chen  * If either port is not connected, this function returns -EPIPE.
106*e60b0267SChaoyi Chen  *
107*e60b0267SChaoyi Chen  * @port1 and @port2 are typically DT sibling nodes, but may have different
108*e60b0267SChaoyi Chen  * parents when, for instance, two separate LVDS encoders carry the even and
109*e60b0267SChaoyi Chen  * odd pixels.
110*e60b0267SChaoyi Chen  *
111*e60b0267SChaoyi Chen  * Return:
112*e60b0267SChaoyi Chen  * * DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS - @port1 carries even pixels and @port2
113*e60b0267SChaoyi Chen  *   carries odd pixels
114*e60b0267SChaoyi Chen  * * DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS - @port1 carries odd pixels and @port2
115*e60b0267SChaoyi Chen  *   carries even pixels
116*e60b0267SChaoyi Chen  * * DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS - @port1 carries left pixels and
117*e60b0267SChaoyi Chen  *   @port2 carries right pixels
118*e60b0267SChaoyi Chen  * * DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS - @port1 carries right pixels and
119*e60b0267SChaoyi Chen  *   @port2 carries left pixels
120*e60b0267SChaoyi Chen 
121*e60b0267SChaoyi Chen  * * -EINVAL - @port1 and @port2 are not connected to a dual-link LVDS sink, or
122*e60b0267SChaoyi Chen  *   the sink configuration is invalid
123*e60b0267SChaoyi Chen  * * -EPIPE - when @port1 or @port2 are not connected
124*e60b0267SChaoyi Chen  */
drm_of_lvds_get_dual_link_pixel_order(const struct device_node * port1,const struct device_node * port2)125*e60b0267SChaoyi Chen int drm_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
126*e60b0267SChaoyi Chen 					  const struct device_node *port2)
127*e60b0267SChaoyi Chen {
128*e60b0267SChaoyi Chen 	int remote_p1_pt, remote_p2_pt;
129*e60b0267SChaoyi Chen 
130*e60b0267SChaoyi Chen 	if (!port1 || !port2)
131*e60b0267SChaoyi Chen 		return -EINVAL;
132*e60b0267SChaoyi Chen 
133*e60b0267SChaoyi Chen 	remote_p1_pt = drm_of_lvds_get_remote_pixels_type(port1);
134*e60b0267SChaoyi Chen 	if (remote_p1_pt < 0)
135*e60b0267SChaoyi Chen 		return remote_p1_pt;
136*e60b0267SChaoyi Chen 
137*e60b0267SChaoyi Chen 	remote_p2_pt = drm_of_lvds_get_remote_pixels_type(port2);
138*e60b0267SChaoyi Chen 	if (remote_p2_pt < 0)
139*e60b0267SChaoyi Chen 		return remote_p2_pt;
140*e60b0267SChaoyi Chen 
141*e60b0267SChaoyi Chen 	/*
142*e60b0267SChaoyi Chen 	 * A valid dual-lVDS bus is found when one remote port is marked with
143*e60b0267SChaoyi Chen 	 * "dual-lvds-even-pixels" or "dual-lvds-left-pixels", and the other
144*e60b0267SChaoyi Chen 	 * remote port is marked with "dual-lvds-odd-pixels"or
145*e60b0267SChaoyi Chen 	 * "dual-lvds-right-pixels", bail out if the markers are not right.
146*e60b0267SChaoyi Chen 	 */
147*e60b0267SChaoyi Chen 	if ((remote_p1_pt + remote_p2_pt !=
148*e60b0267SChaoyi Chen 		DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD) &&
149*e60b0267SChaoyi Chen 		(remote_p1_pt + remote_p2_pt !=
150*e60b0267SChaoyi Chen 		DRM_OF_LVDS_LEFT + DRM_OF_LVDS_RIGHT))
151*e60b0267SChaoyi Chen 		return -EINVAL;
152*e60b0267SChaoyi Chen 
153*e60b0267SChaoyi Chen 	if (remote_p1_pt == DRM_OF_LVDS_EVEN)
154*e60b0267SChaoyi Chen 		return DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
155*e60b0267SChaoyi Chen 	else if (remote_p1_pt == DRM_OF_LVDS_ODD)
156*e60b0267SChaoyi Chen 		return DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
157*e60b0267SChaoyi Chen 	else if (remote_p1_pt == DRM_OF_LVDS_LEFT)
158*e60b0267SChaoyi Chen 		return DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS;
159*e60b0267SChaoyi Chen 	else
160*e60b0267SChaoyi Chen 		return DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS;
161*e60b0267SChaoyi Chen }
162