diff options
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-of.c')
-rw-r--r-- | drivers/media/v4l2-core/v4l2-of.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c new file mode 100644 index 000000000000..aa59639d013c --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-of.c | |||
@@ -0,0 +1,266 @@ | |||
1 | /* | ||
2 | * V4L2 OF binding parsing library | ||
3 | * | ||
4 | * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. | ||
5 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
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 | } | ||
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 | |||
176 | for_each_child_of_node(parent, node) { | ||
177 | if (!of_node_cmp(node->name, "port")) { | ||
178 | port = node; | ||
179 | break; | ||
180 | } | ||
181 | } | ||
182 | if (port) { | ||
183 | /* Found a port, get an endpoint. */ | ||
184 | endpoint = of_get_next_child(port, NULL); | ||
185 | of_node_put(port); | ||
186 | } else { | ||
187 | endpoint = NULL; | ||
188 | } | ||
189 | |||
190 | if (!endpoint) | ||
191 | pr_err("%s(): no endpoint nodes specified for %s\n", | ||
192 | __func__, parent->full_name); | ||
193 | } else { | ||
194 | port = of_get_parent(prev); | ||
195 | if (!port) | ||
196 | /* Hm, has someone given us the root node ?... */ | ||
197 | return NULL; | ||
198 | |||
199 | /* Avoid dropping prev node refcount to 0. */ | ||
200 | of_node_get(prev); | ||
201 | endpoint = of_get_next_child(port, prev); | ||
202 | if (endpoint) { | ||
203 | of_node_put(port); | ||
204 | return endpoint; | ||
205 | } | ||
206 | |||
207 | /* No more endpoints under this port, try the next one. */ | ||
208 | do { | ||
209 | port = of_get_next_child(parent, port); | ||
210 | if (!port) | ||
211 | return NULL; | ||
212 | } while (of_node_cmp(port->name, "port")); | ||
213 | |||
214 | /* Pick up the first endpoint in this port. */ | ||
215 | endpoint = of_get_next_child(port, NULL); | ||
216 | of_node_put(port); | ||
217 | } | ||
218 | |||
219 | return endpoint; | ||
220 | } | ||
221 | EXPORT_SYMBOL(v4l2_of_get_next_endpoint); | ||
222 | |||
223 | /** | ||
224 | * v4l2_of_get_remote_port_parent() - get remote port's parent node | ||
225 | * @node: pointer to a local endpoint device_node | ||
226 | * | ||
227 | * Return: Remote device node associated with remote endpoint node linked | ||
228 | * to @node. Use of_node_put() on it when done. | ||
229 | */ | ||
230 | struct device_node *v4l2_of_get_remote_port_parent( | ||
231 | const struct device_node *node) | ||
232 | { | ||
233 | struct device_node *np; | ||
234 | unsigned int depth; | ||
235 | |||
236 | /* Get remote endpoint node. */ | ||
237 | np = of_parse_phandle(node, "remote-endpoint", 0); | ||
238 | |||
239 | /* Walk 3 levels up only if there is 'ports' node. */ | ||
240 | for (depth = 3; depth && np; depth--) { | ||
241 | np = of_get_next_parent(np); | ||
242 | if (depth == 2 && of_node_cmp(np->name, "ports")) | ||
243 | break; | ||
244 | } | ||
245 | return np; | ||
246 | } | ||
247 | EXPORT_SYMBOL(v4l2_of_get_remote_port_parent); | ||
248 | |||
249 | /** | ||
250 | * v4l2_of_get_remote_port() - get remote port node | ||
251 | * @node: pointer to a local endpoint device_node | ||
252 | * | ||
253 | * Return: Remote port node associated with remote endpoint node linked | ||
254 | * to @node. Use of_node_put() on it when done. | ||
255 | */ | ||
256 | struct device_node *v4l2_of_get_remote_port(const struct device_node *node) | ||
257 | { | ||
258 | struct device_node *np; | ||
259 | |||
260 | /* Get remote endpoint node. */ | ||
261 | np = of_parse_phandle(node, "remote-endpoint", 0); | ||
262 | if (!np) | ||
263 | return NULL; | ||
264 | return of_get_parent(np); | ||
265 | } | ||
266 | EXPORT_SYMBOL(v4l2_of_get_remote_port); | ||