Commit | Line | Data |
---|---|---|
99fd133f GL |
1 | /* |
2 | * V4L2 OF binding parsing library | |
3 | * | |
4 | * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. | |
c6e8d96d | 5 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> |
99fd133f GL |
6 | * |
7 | * Copyright (C) 2012 Renesas Electronics Corp. | |
8 | * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of version 2 of the GNU General Public License as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/of.h> | |
17 | #include <linux/string.h> | |
18 | #include <linux/types.h> | |
19 | ||
20 | #include <media/v4l2-of.h> | |
21 | ||
22 | static void v4l2_of_parse_csi_bus(const struct device_node *node, | |
23 | struct v4l2_of_endpoint *endpoint) | |
24 | { | |
25 | struct v4l2_of_bus_mipi_csi2 *bus = &endpoint->bus.mipi_csi2; | |
26 | u32 data_lanes[ARRAY_SIZE(bus->data_lanes)]; | |
27 | struct property *prop; | |
28 | bool have_clk_lane = false; | |
29 | unsigned int flags = 0; | |
30 | u32 v; | |
31 | ||
32 | prop = of_find_property(node, "data-lanes", NULL); | |
33 | if (prop) { | |
34 | const __be32 *lane = NULL; | |
35 | int i; | |
36 | ||
37 | for (i = 0; i < ARRAY_SIZE(data_lanes); i++) { | |
38 | lane = of_prop_next_u32(prop, lane, &data_lanes[i]); | |
39 | if (!lane) | |
40 | break; | |
41 | } | |
42 | bus->num_data_lanes = i; | |
43 | while (i--) | |
44 | bus->data_lanes[i] = data_lanes[i]; | |
45 | } | |
46 | ||
47 | if (!of_property_read_u32(node, "clock-lanes", &v)) { | |
48 | bus->clock_lane = v; | |
49 | have_clk_lane = true; | |
50 | } | |
51 | ||
52 | if (of_get_property(node, "clock-noncontinuous", &v)) | |
53 | flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; | |
54 | else if (have_clk_lane || bus->num_data_lanes > 0) | |
55 | flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; | |
56 | ||
57 | bus->flags = flags; | |
58 | endpoint->bus_type = V4L2_MBUS_CSI2; | |
59 | } | |
60 | ||
61 | static void v4l2_of_parse_parallel_bus(const struct device_node *node, | |
62 | struct v4l2_of_endpoint *endpoint) | |
63 | { | |
64 | struct v4l2_of_bus_parallel *bus = &endpoint->bus.parallel; | |
65 | unsigned int flags = 0; | |
66 | u32 v; | |
67 | ||
68 | if (!of_property_read_u32(node, "hsync-active", &v)) | |
69 | flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH : | |
70 | V4L2_MBUS_HSYNC_ACTIVE_LOW; | |
71 | ||
72 | if (!of_property_read_u32(node, "vsync-active", &v)) | |
73 | flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH : | |
74 | V4L2_MBUS_VSYNC_ACTIVE_LOW; | |
75 | ||
76 | if (!of_property_read_u32(node, "pclk-sample", &v)) | |
77 | flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING : | |
78 | V4L2_MBUS_PCLK_SAMPLE_FALLING; | |
79 | ||
80 | if (!of_property_read_u32(node, "field-even-active", &v)) | |
81 | flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH : | |
82 | V4L2_MBUS_FIELD_EVEN_LOW; | |
83 | if (flags) | |
84 | endpoint->bus_type = V4L2_MBUS_PARALLEL; | |
85 | else | |
86 | endpoint->bus_type = V4L2_MBUS_BT656; | |
87 | ||
88 | if (!of_property_read_u32(node, "data-active", &v)) | |
89 | flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH : | |
90 | V4L2_MBUS_DATA_ACTIVE_LOW; | |
91 | ||
92 | if (of_get_property(node, "slave-mode", &v)) | |
93 | flags |= V4L2_MBUS_SLAVE; | |
94 | else | |
95 | flags |= V4L2_MBUS_MASTER; | |
96 | ||
97 | if (!of_property_read_u32(node, "bus-width", &v)) | |
98 | bus->bus_width = v; | |
99 | ||
100 | if (!of_property_read_u32(node, "data-shift", &v)) | |
101 | bus->data_shift = v; | |
102 | ||
d1d70aa6 LP |
103 | if (!of_property_read_u32(node, "sync-on-green-active", &v)) |
104 | flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : | |
105 | V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; | |
106 | ||
99fd133f GL |
107 | bus->flags = flags; |
108 | ||
109 | } | |
99fd133f GL |
110 | |
111 | /** | |
112 | * v4l2_of_parse_endpoint() - parse all endpoint node properties | |
113 | * @node: pointer to endpoint device_node | |
114 | * @endpoint: pointer to the V4L2 OF endpoint data structure | |
115 | * | |
116 | * All properties are optional. If none are found, we don't set any flags. | |
117 | * This means the port has a static configuration and no properties have | |
118 | * to be specified explicitly. | |
119 | * If any properties that identify the bus as parallel are found and | |
120 | * slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if we recognise | |
121 | * the bus as serial CSI-2 and clock-noncontinuous isn't set, we set the | |
122 | * V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. | |
123 | * The caller should hold a reference to @node. | |
9ff889b6 LP |
124 | * |
125 | * Return: 0. | |
99fd133f | 126 | */ |
9ff889b6 LP |
127 | int v4l2_of_parse_endpoint(const struct device_node *node, |
128 | struct v4l2_of_endpoint *endpoint) | |
99fd133f GL |
129 | { |
130 | struct device_node *port_node = of_get_parent(node); | |
131 | ||
132 | memset(endpoint, 0, offsetof(struct v4l2_of_endpoint, head)); | |
133 | ||
134 | endpoint->local_node = node; | |
135 | /* | |
136 | * It doesn't matter whether the two calls below succeed. | |
137 | * If they don't then the default value 0 is used. | |
138 | */ | |
139 | of_property_read_u32(port_node, "reg", &endpoint->port); | |
140 | of_property_read_u32(node, "reg", &endpoint->id); | |
141 | ||
142 | v4l2_of_parse_csi_bus(node, endpoint); | |
143 | /* | |
144 | * Parse the parallel video bus properties only if none | |
145 | * of the MIPI CSI-2 specific properties were found. | |
146 | */ | |
147 | if (endpoint->bus.mipi_csi2.flags == 0) | |
148 | v4l2_of_parse_parallel_bus(node, endpoint); | |
149 | ||
150 | of_node_put(port_node); | |
9ff889b6 LP |
151 | |
152 | return 0; | |
99fd133f GL |
153 | } |
154 | EXPORT_SYMBOL(v4l2_of_parse_endpoint); | |
155 | ||
156 | /** | |
157 | * v4l2_of_get_next_endpoint() - get next endpoint node | |
158 | * @parent: pointer to the parent device node | |
159 | * @prev: previous endpoint node, or NULL to get first | |
160 | * | |
161 | * Return: An 'endpoint' node pointer with refcount incremented. Refcount | |
162 | * of the passed @prev node is not decremented, the caller have to use | |
163 | * of_node_put() on it when done. | |
164 | */ | |
165 | struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent, | |
166 | struct device_node *prev) | |
167 | { | |
168 | struct device_node *endpoint; | |
169 | struct device_node *port = NULL; | |
170 | ||
171 | if (!parent) | |
172 | return NULL; | |
173 | ||
174 | if (!prev) { | |
175 | struct device_node *node; | |
176 | /* | |
177 | * It's the first call, we have to find a port subnode | |
178 | * within this node or within an optional 'ports' node. | |
179 | */ | |
180 | node = of_get_child_by_name(parent, "ports"); | |
181 | if (node) | |
182 | parent = node; | |
183 | ||
91f9241f LP |
184 | port = of_get_child_by_name(parent, "port"); |
185 | ||
99fd133f GL |
186 | if (port) { |
187 | /* Found a port, get an endpoint. */ | |
188 | endpoint = of_get_next_child(port, NULL); | |
189 | of_node_put(port); | |
190 | } else { | |
191 | endpoint = NULL; | |
192 | } | |
193 | ||
194 | if (!endpoint) | |
195 | pr_err("%s(): no endpoint nodes specified for %s\n", | |
196 | __func__, parent->full_name); | |
5045b495 | 197 | of_node_put(node); |
99fd133f GL |
198 | } else { |
199 | port = of_get_parent(prev); | |
200 | if (!port) | |
201 | /* Hm, has someone given us the root node ?... */ | |
202 | return NULL; | |
203 | ||
204 | /* Avoid dropping prev node refcount to 0. */ | |
205 | of_node_get(prev); | |
206 | endpoint = of_get_next_child(port, prev); | |
207 | if (endpoint) { | |
208 | of_node_put(port); | |
209 | return endpoint; | |
210 | } | |
211 | ||
212 | /* No more endpoints under this port, try the next one. */ | |
213 | do { | |
214 | port = of_get_next_child(parent, port); | |
215 | if (!port) | |
216 | return NULL; | |
217 | } while (of_node_cmp(port->name, "port")); | |
218 | ||
219 | /* Pick up the first endpoint in this port. */ | |
220 | endpoint = of_get_next_child(port, NULL); | |
221 | of_node_put(port); | |
222 | } | |
223 | ||
224 | return endpoint; | |
225 | } | |
226 | EXPORT_SYMBOL(v4l2_of_get_next_endpoint); | |
227 | ||
228 | /** | |
229 | * v4l2_of_get_remote_port_parent() - get remote port's parent node | |
230 | * @node: pointer to a local endpoint device_node | |
231 | * | |
232 | * Return: Remote device node associated with remote endpoint node linked | |
233 | * to @node. Use of_node_put() on it when done. | |
234 | */ | |
235 | struct device_node *v4l2_of_get_remote_port_parent( | |
236 | const struct device_node *node) | |
237 | { | |
238 | struct device_node *np; | |
239 | unsigned int depth; | |
240 | ||
241 | /* Get remote endpoint node. */ | |
242 | np = of_parse_phandle(node, "remote-endpoint", 0); | |
243 | ||
244 | /* Walk 3 levels up only if there is 'ports' node. */ | |
245 | for (depth = 3; depth && np; depth--) { | |
246 | np = of_get_next_parent(np); | |
247 | if (depth == 2 && of_node_cmp(np->name, "ports")) | |
248 | break; | |
249 | } | |
250 | return np; | |
251 | } | |
252 | EXPORT_SYMBOL(v4l2_of_get_remote_port_parent); | |
253 | ||
254 | /** | |
255 | * v4l2_of_get_remote_port() - get remote port node | |
256 | * @node: pointer to a local endpoint device_node | |
257 | * | |
258 | * Return: Remote port node associated with remote endpoint node linked | |
259 | * to @node. Use of_node_put() on it when done. | |
260 | */ | |
261 | struct device_node *v4l2_of_get_remote_port(const struct device_node *node) | |
262 | { | |
263 | struct device_node *np; | |
264 | ||
265 | /* Get remote endpoint node. */ | |
266 | np = of_parse_phandle(node, "remote-endpoint", 0); | |
267 | if (!np) | |
268 | return NULL; | |
269 | return of_get_parent(np); | |
270 | } | |
271 | EXPORT_SYMBOL(v4l2_of_get_remote_port); |