hc
2024-03-22 f63cd4c03ea42695d5f9b0e1798edd196923aae6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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
// SPDX-License-Identifier: GPL-2.0+
/*
 * (C) Copyright 2023 Rockchip Electronics Co., Ltd
 *
 */
#include <dm/device.h>
#include <dm/read.h>
 
#include "drm_of.h"
 
enum drm_of_lvds_pixels {
   DRM_OF_LVDS_EVEN = BIT(0),
   DRM_OF_LVDS_ODD = BIT(1),
   DRM_OF_LVDS_LEFT = BIT(2),
   DRM_OF_LVDS_RIGHT = BIT(3),
};
 
static int
drm_of_lvds_get_port_pixels_type(const struct device_node *port_node)
{
   ofnode node = np_to_ofnode(port_node);
 
   bool even_pixels =
       ofnode_read_bool(node, "dual-lvds-even-pixels");
   bool odd_pixels =
       ofnode_read_bool(node, "dual-lvds-odd-pixels");
   bool left_pixels =
       ofnode_read_bool(node, "dual-lvds-left-pixels");
   bool right_pixels =
       ofnode_read_bool(node, "dual-lvds-right-pixels");
 
   return (even_pixels ? DRM_OF_LVDS_EVEN : 0) |
          (odd_pixels ? DRM_OF_LVDS_ODD : 0) |
          (left_pixels ? DRM_OF_LVDS_LEFT : 0) |
          (right_pixels ? DRM_OF_LVDS_RIGHT : 0);
}
 
static int
drm_of_lvds_get_remote_pixels_type(const struct device_node *port_node)
{
   ofnode node = np_to_ofnode(port_node);
   ofnode endpoint;
   uint phandle;
   int pixels_type = -EPIPE;
 
   ofnode_for_each_subnode(endpoint, node) {
       int current_pt;
       const char *name;
 
       if (!ofnode_is_available(endpoint))
           continue;
 
       name = ofnode_get_name(endpoint);
       if (strncmp(name, "endpoint", 8) != 0)
           continue;
 
       if (ofnode_read_u32(endpoint, "remote-endpoint", &phandle))
           continue;
 
       endpoint = ofnode_get_by_phandle(phandle);
       if (!ofnode_valid(endpoint) || !ofnode_is_available(endpoint))
           continue;
 
       endpoint = ofnode_get_parent(endpoint);
       if (!ofnode_valid(endpoint))
           continue;
 
       current_pt =
           drm_of_lvds_get_port_pixels_type(ofnode_to_np(endpoint
               ));
       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().
        */
       if (!current_pt || pixels_type != current_pt)
           return -EINVAL;
   }
 
   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, the two link can transmit
 * odd pixels and even pixels independently, or the two link can also transmit
 * left pixels and right pixels independently. This function returns for two
 * ports of an LVDS dual-link source, based on the requirements of the connected
 * sink.
 *
 * The pixel order is determined from the dual-lvds-even-pixels +
 * dual-lvds-odd-pixels or dual-lvds-left-pixels + dual-lvds-right-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
 * * DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS - @port1 carries left pixels and
 *   @port2 carries right pixels
 * * DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS - @port1 carries right pixels and
 *   @port2 carries left 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" or "dual-lvds-left-pixels", and the other
    * remote port is marked with "dual-lvds-odd-pixels"or
    * "dual-lvds-right-pixels", bail out if the markers are not right.
    */
   if ((remote_p1_pt + remote_p2_pt !=
       DRM_OF_LVDS_EVEN + DRM_OF_LVDS_ODD) &&
       (remote_p1_pt + remote_p2_pt !=
       DRM_OF_LVDS_LEFT + DRM_OF_LVDS_RIGHT))
       return -EINVAL;
 
   if (remote_p1_pt == DRM_OF_LVDS_EVEN)
       return DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
   else if (remote_p1_pt == DRM_OF_LVDS_ODD)
       return DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
   else if (remote_p1_pt == DRM_OF_LVDS_LEFT)
       return DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS;
   else
       return DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS;
}