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 | ||
103 | bus->flags = flags; | |
104 | ||
105 | } | |
99fd133f GL |
106 | |
107 | /** | |
108 | * v4l2_of_parse_endpoint() - parse all endpoint node properties | |
109 | * @node: pointer to endpoint device_node | |
110 | * @endpoint: pointer to the V4L2 OF endpoint data structure | |
111 | * | |
112 | * All properties are optional. If none are found, we don't set any flags. | |
113 | * This means the port has a static configuration and no properties have | |
114 | * to be specified explicitly. | |
115 | * If any properties that identify the bus as parallel are found and | |
116 | * slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if we recognise | |
117 | * the bus as serial CSI-2 and clock-noncontinuous isn't set, we set the | |
118 | * V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. | |
119 | * The caller should hold a reference to @node. | |
120 | */ | |
121 | void v4l2_of_parse_endpoint(const struct device_node *node, | |
122 | struct v4l2_of_endpoint *endpoint) | |
123 | { | |
124 | struct device_node *port_node = of_get_parent(node); | |
125 | ||
126 | memset(endpoint, 0, offsetof(struct v4l2_of_endpoint, head)); | |
127 | ||
128 | endpoint->local_node = node; | |
129 | /* | |
130 | * It doesn't matter whether the two calls below succeed. | |
131 | * If they don't then the default value 0 is used. | |
132 | */ | |
133 | of_property_read_u32(port_node, "reg", &endpoint->port); | |
134 | of_property_read_u32(node, "reg", &endpoint->id); | |
135 | ||
136 | v4l2_of_parse_csi_bus(node, endpoint); | |
137 | /* | |
138 | * Parse the parallel video bus properties only if none | |
139 | * of the MIPI CSI-2 specific properties were found. | |
140 | */ | |
141 | if (endpoint->bus.mipi_csi2.flags == 0) | |
142 | v4l2_of_parse_parallel_bus(node, endpoint); | |
143 | ||
144 | of_node_put(port_node); | |
145 | } | |
146 | EXPORT_SYMBOL(v4l2_of_parse_endpoint); | |
147 | ||
148 | /** | |
149 | * v4l2_of_get_next_endpoint() - get next endpoint node | |
150 | * @parent: pointer to the parent device node | |
151 | * @prev: previous endpoint node, or NULL to get first | |
152 | * | |
153 | * Return: An 'endpoint' node pointer with refcount incremented. Refcount | |
154 | * of the passed @prev node is not decremented, the caller have to use | |
155 | * of_node_put() on it when done. | |
156 | */ | |
157 | struct device_node *v4l2_of_get_next_endpoint(const struct device_node *parent, | |
158 | struct device_node *prev) | |
159 | { | |
160 | struct device_node *endpoint; | |
161 | struct device_node *port = NULL; | |
162 | ||
163 | if (!parent) | |
164 | return NULL; | |
165 | ||
166 | if (!prev) { | |
167 | struct device_node *node; | |
168 | /* | |
169 | * It's the first call, we have to find a port subnode | |
170 | * within this node or within an optional 'ports' node. | |
171 | */ | |
172 | node = of_get_child_by_name(parent, "ports"); | |
173 | if (node) | |
174 | parent = node; | |
175 | ||
91f9241f LP |
176 | port = of_get_child_by_name(parent, "port"); |
177 | ||
99fd133f GL |
178 | if (port) { |
179 | /* Found a port, get an endpoint. */ | |
180 | endpoint = of_get_next_child(port, NULL); | |
181 | of_node_put(port); | |
182 | } else { | |
183 | endpoint = NULL; | |
184 | } | |
185 | ||
186 | if (!endpoint) | |
187 | pr_err("%s(): no endpoint nodes specified for %s\n", | |
188 | __func__, parent->full_name); | |
5045b495 | 189 | of_node_put(node); |
99fd133f GL |
190 | } else { |
191 | port = of_get_parent(prev); | |
192 | if (!port) | |
193 | /* Hm, has someone given us the root node ?... */ | |
194 | return NULL; | |
195 | ||
196 | /* Avoid dropping prev node refcount to 0. */ | |
197 | of_node_get(prev); | |
198 | endpoint = of_get_next_child(port, prev); | |
199 | if (endpoint) { | |
200 | of_node_put(port); | |
201 | return endpoint; | |
202 | } | |
203 | ||
204 | /* No more endpoints under this port, try the next one. */ | |
205 | do { | |
206 | port = of_get_next_child(parent, port); | |
207 | if (!port) | |
208 | return NULL; | |
209 | } while (of_node_cmp(port->name, "port")); | |
210 | ||
211 | /* Pick up the first endpoint in this port. */ | |
212 | endpoint = of_get_next_child(port, NULL); | |
213 | of_node_put(port); | |
214 | } | |
215 | ||
216 | return endpoint; | |
217 | } | |
218 | EXPORT_SYMBOL(v4l2_of_get_next_endpoint); | |
219 | ||
220 | /** | |
221 | * v4l2_of_get_remote_port_parent() - get remote port's parent node | |
222 | * @node: pointer to a local endpoint device_node | |
223 | * | |
224 | * Return: Remote device node associated with remote endpoint node linked | |
225 | * to @node. Use of_node_put() on it when done. | |
226 | */ | |
227 | struct device_node *v4l2_of_get_remote_port_parent( | |
228 | const struct device_node *node) | |
229 | { | |
230 | struct device_node *np; | |
231 | unsigned int depth; | |
232 | ||
233 | /* Get remote endpoint node. */ | |
234 | np = of_parse_phandle(node, "remote-endpoint", 0); | |
235 | ||
236 | /* Walk 3 levels up only if there is 'ports' node. */ | |
237 | for (depth = 3; depth && np; depth--) { | |
238 | np = of_get_next_parent(np); | |
239 | if (depth == 2 && of_node_cmp(np->name, "ports")) | |
240 | break; | |
241 | } | |
242 | return np; | |
243 | } | |
244 | EXPORT_SYMBOL(v4l2_of_get_remote_port_parent); | |
245 | ||
246 | /** | |
247 | * v4l2_of_get_remote_port() - get remote port node | |
248 | * @node: pointer to a local endpoint device_node | |
249 | * | |
250 | * Return: Remote port node associated with remote endpoint node linked | |
251 | * to @node. Use of_node_put() on it when done. | |
252 | */ | |
253 | struct device_node *v4l2_of_get_remote_port(const struct device_node *node) | |
254 | { | |
255 | struct device_node *np; | |
256 | ||
257 | /* Get remote endpoint node. */ | |
258 | np = of_parse_phandle(node, "remote-endpoint", 0); | |
259 | if (!np) | |
260 | return NULL; | |
261 | return of_get_parent(np); | |
262 | } | |
263 | EXPORT_SYMBOL(v4l2_of_get_remote_port); |