Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] v4l: fwnode: Support generic fwnode for parsing standardised properties

The fwnode_handle is a more generic way than OF device_node to describe
firmware nodes. Instead of the OF API, use more generic fwnode API to
obtain the same information.

As the V4L2 fwnode support will be required by a small minority of e.g.
ACPI based systems (the same might actually go for OF), make this a module
instead of embedding it in the videodev module.

The origins of the V4L2 fwnode framework is in the V4L2 OF framework.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Tested-by: Hans Verkuil <hans.verkuil@cisco.com>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>

authored by

Sakari Ailus and committed by
Mauro Carvalho Chehab
ca50c197 614d651e

+453
+3
drivers/media/v4l2-core/Kconfig
··· 55 55 56 56 When in doubt, say N. 57 57 58 + config V4L2_FWNODE 59 + tristate 60 + 58 61 # Used by drivers that need Videobuf modules 59 62 config VIDEOBUF_GEN 60 63 tristate
+1
drivers/media/v4l2-core/Makefile
··· 13 13 ifeq ($(CONFIG_OF),y) 14 14 videodev-objs += v4l2-of.o 15 15 endif 16 + obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o 16 17 ifeq ($(CONFIG_TRACEPOINTS),y) 17 18 videodev-objs += vb2-trace.o v4l2-trace.o 18 19 endif
+345
drivers/media/v4l2-core/v4l2-fwnode.c
··· 1 + /* 2 + * V4L2 fwnode binding parsing library 3 + * 4 + * The origins of the V4L2 fwnode library are in V4L2 OF library that 5 + * formerly was located in v4l2-of.c. 6 + * 7 + * Copyright (c) 2016 Intel Corporation. 8 + * Author: Sakari Ailus <sakari.ailus@linux.intel.com> 9 + * 10 + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. 11 + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 12 + * 13 + * Copyright (C) 2012 Renesas Electronics Corp. 14 + * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> 15 + * 16 + * This program is free software; you can redistribute it and/or modify 17 + * it under the terms of version 2 of the GNU General Public License as 18 + * published by the Free Software Foundation. 19 + */ 20 + #include <linux/acpi.h> 21 + #include <linux/kernel.h> 22 + #include <linux/module.h> 23 + #include <linux/of.h> 24 + #include <linux/property.h> 25 + #include <linux/slab.h> 26 + #include <linux/string.h> 27 + #include <linux/types.h> 28 + 29 + #include <media/v4l2-fwnode.h> 30 + 31 + static int v4l2_fwnode_endpoint_parse_csi_bus(struct fwnode_handle *fwnode, 32 + struct v4l2_fwnode_endpoint *vep) 33 + { 34 + struct v4l2_fwnode_bus_mipi_csi2 *bus = &vep->bus.mipi_csi2; 35 + bool have_clk_lane = false; 36 + unsigned int flags = 0, lanes_used = 0; 37 + unsigned int i; 38 + u32 v; 39 + int rval; 40 + 41 + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); 42 + if (rval > 0) { 43 + u32 array[ARRAY_SIZE(bus->data_lanes)]; 44 + 45 + bus->num_data_lanes = 46 + min_t(int, ARRAY_SIZE(bus->data_lanes), rval); 47 + 48 + fwnode_property_read_u32_array(fwnode, "data-lanes", array, 49 + bus->num_data_lanes); 50 + 51 + for (i = 0; i < bus->num_data_lanes; i++) { 52 + if (lanes_used & BIT(array[i])) 53 + pr_warn("duplicated lane %u in data-lanes\n", 54 + array[i]); 55 + lanes_used |= BIT(array[i]); 56 + 57 + bus->data_lanes[i] = array[i]; 58 + } 59 + } 60 + 61 + rval = fwnode_property_read_u32_array(fwnode, "lane-polarities", NULL, 62 + 0); 63 + if (rval > 0) { 64 + u32 array[ARRAY_SIZE(bus->lane_polarities)]; 65 + 66 + if (rval < 1 + bus->num_data_lanes /* clock + data */) { 67 + pr_warn("too few lane-polarities entries (need %u, got %u)\n", 68 + 1 + bus->num_data_lanes, rval); 69 + return -EINVAL; 70 + } 71 + 72 + fwnode_property_read_u32_array(fwnode, "lane-polarities", array, 73 + 1 + bus->num_data_lanes); 74 + 75 + for (i = 0; i < 1 + bus->num_data_lanes; i++) 76 + bus->lane_polarities[i] = array[i]; 77 + } 78 + 79 + if (!fwnode_property_read_u32(fwnode, "clock-lanes", &v)) { 80 + if (lanes_used & BIT(v)) 81 + pr_warn("duplicated lane %u in clock-lanes\n", v); 82 + lanes_used |= BIT(v); 83 + 84 + bus->clock_lane = v; 85 + have_clk_lane = true; 86 + } 87 + 88 + if (fwnode_property_present(fwnode, "clock-noncontinuous")) 89 + flags |= V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK; 90 + else if (have_clk_lane || bus->num_data_lanes > 0) 91 + flags |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; 92 + 93 + bus->flags = flags; 94 + vep->bus_type = V4L2_MBUS_CSI2; 95 + 96 + return 0; 97 + } 98 + 99 + static void v4l2_fwnode_endpoint_parse_parallel_bus( 100 + struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep) 101 + { 102 + struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel; 103 + unsigned int flags = 0; 104 + u32 v; 105 + 106 + if (!fwnode_property_read_u32(fwnode, "hsync-active", &v)) 107 + flags |= v ? V4L2_MBUS_HSYNC_ACTIVE_HIGH : 108 + V4L2_MBUS_HSYNC_ACTIVE_LOW; 109 + 110 + if (!fwnode_property_read_u32(fwnode, "vsync-active", &v)) 111 + flags |= v ? V4L2_MBUS_VSYNC_ACTIVE_HIGH : 112 + V4L2_MBUS_VSYNC_ACTIVE_LOW; 113 + 114 + if (!fwnode_property_read_u32(fwnode, "field-even-active", &v)) 115 + flags |= v ? V4L2_MBUS_FIELD_EVEN_HIGH : 116 + V4L2_MBUS_FIELD_EVEN_LOW; 117 + if (flags) 118 + vep->bus_type = V4L2_MBUS_PARALLEL; 119 + else 120 + vep->bus_type = V4L2_MBUS_BT656; 121 + 122 + if (!fwnode_property_read_u32(fwnode, "pclk-sample", &v)) 123 + flags |= v ? V4L2_MBUS_PCLK_SAMPLE_RISING : 124 + V4L2_MBUS_PCLK_SAMPLE_FALLING; 125 + 126 + if (!fwnode_property_read_u32(fwnode, "data-active", &v)) 127 + flags |= v ? V4L2_MBUS_DATA_ACTIVE_HIGH : 128 + V4L2_MBUS_DATA_ACTIVE_LOW; 129 + 130 + if (fwnode_property_present(fwnode, "slave-mode")) 131 + flags |= V4L2_MBUS_SLAVE; 132 + else 133 + flags |= V4L2_MBUS_MASTER; 134 + 135 + if (!fwnode_property_read_u32(fwnode, "bus-width", &v)) 136 + bus->bus_width = v; 137 + 138 + if (!fwnode_property_read_u32(fwnode, "data-shift", &v)) 139 + bus->data_shift = v; 140 + 141 + if (!fwnode_property_read_u32(fwnode, "sync-on-green-active", &v)) 142 + flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : 143 + V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; 144 + 145 + bus->flags = flags; 146 + 147 + } 148 + 149 + /** 150 + * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties 151 + * @fwnode: pointer to the endpoint's fwnode handle 152 + * @vep: pointer to the V4L2 fwnode data structure 153 + * 154 + * All properties are optional. If none are found, we don't set any flags. This 155 + * means the port has a static configuration and no properties have to be 156 + * specified explicitly. If any properties that identify the bus as parallel 157 + * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if 158 + * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we 159 + * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a 160 + * reference to @fwnode. 161 + * 162 + * NOTE: This function does not parse properties the size of which is variable 163 + * without a low fixed limit. Please use v4l2_fwnode_endpoint_alloc_parse() in 164 + * new drivers instead. 165 + * 166 + * Return: 0 on success or a negative error code on failure. 167 + */ 168 + int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, 169 + struct v4l2_fwnode_endpoint *vep) 170 + { 171 + int rval; 172 + 173 + fwnode_graph_parse_endpoint(fwnode, &vep->base); 174 + 175 + /* Zero fields from bus_type to until the end */ 176 + memset(&vep->bus_type, 0, sizeof(*vep) - 177 + offsetof(typeof(*vep), bus_type)); 178 + 179 + rval = v4l2_fwnode_endpoint_parse_csi_bus(fwnode, vep); 180 + if (rval) 181 + return rval; 182 + /* 183 + * Parse the parallel video bus properties only if none 184 + * of the MIPI CSI-2 specific properties were found. 185 + */ 186 + if (vep->bus.mipi_csi2.flags == 0) 187 + v4l2_fwnode_endpoint_parse_parallel_bus(fwnode, vep); 188 + 189 + return 0; 190 + } 191 + EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse); 192 + 193 + /* 194 + * v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by 195 + * v4l2_fwnode_endpoint_alloc_parse() 196 + * @vep - the V4L2 fwnode the resources of which are to be released 197 + * 198 + * It is safe to call this function with NULL argument or on a V4L2 fwnode the 199 + * parsing of which failed. 200 + */ 201 + void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep) 202 + { 203 + if (IS_ERR_OR_NULL(vep)) 204 + return; 205 + 206 + kfree(vep->link_frequencies); 207 + kfree(vep); 208 + } 209 + EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free); 210 + 211 + /** 212 + * v4l2_fwnode_endpoint_alloc_parse() - parse all fwnode node properties 213 + * @fwnode: pointer to the endpoint's fwnode handle 214 + * 215 + * All properties are optional. If none are found, we don't set any flags. This 216 + * means the port has a static configuration and no properties have to be 217 + * specified explicitly. If any properties that identify the bus as parallel 218 + * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if 219 + * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we 220 + * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a 221 + * reference to @fwnode. 222 + * 223 + * v4l2_fwnode_endpoint_alloc_parse() has two important differences to 224 + * v4l2_fwnode_endpoint_parse(): 225 + * 226 + * 1. It also parses variable size data. 227 + * 228 + * 2. The memory it has allocated to store the variable size data must be freed 229 + * using v4l2_fwnode_endpoint_free() when no longer needed. 230 + * 231 + * Return: Pointer to v4l2_fwnode_endpoint if successful, on an error pointer 232 + * on error. 233 + */ 234 + struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( 235 + struct fwnode_handle *fwnode) 236 + { 237 + struct v4l2_fwnode_endpoint *vep; 238 + int rval; 239 + 240 + vep = kzalloc(sizeof(*vep), GFP_KERNEL); 241 + if (!vep) 242 + return ERR_PTR(-ENOMEM); 243 + 244 + rval = v4l2_fwnode_endpoint_parse(fwnode, vep); 245 + if (rval < 0) 246 + goto out_err; 247 + 248 + rval = fwnode_property_read_u64_array(fwnode, "link-frequencies", 249 + NULL, 0); 250 + if (rval < 0) 251 + goto out_err; 252 + 253 + vep->link_frequencies = 254 + kmalloc_array(rval, sizeof(*vep->link_frequencies), GFP_KERNEL); 255 + if (!vep->link_frequencies) { 256 + rval = -ENOMEM; 257 + goto out_err; 258 + } 259 + 260 + vep->nr_of_link_frequencies = rval; 261 + 262 + rval = fwnode_property_read_u64_array(fwnode, "link-frequencies", 263 + vep->link_frequencies, 264 + vep->nr_of_link_frequencies); 265 + if (rval < 0) 266 + goto out_err; 267 + 268 + return vep; 269 + 270 + out_err: 271 + v4l2_fwnode_endpoint_free(vep); 272 + return ERR_PTR(rval); 273 + } 274 + EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse); 275 + 276 + /** 277 + * v4l2_fwnode_endpoint_parse_link() - parse a link between two endpoints 278 + * @__fwnode: pointer to the endpoint's fwnode at the local end of the link 279 + * @link: pointer to the V4L2 fwnode link data structure 280 + * 281 + * Fill the link structure with the local and remote nodes and port numbers. 282 + * The local_node and remote_node fields are set to point to the local and 283 + * remote port's parent nodes respectively (the port parent node being the 284 + * parent node of the port node if that node isn't a 'ports' node, or the 285 + * grand-parent node of the port node otherwise). 286 + * 287 + * A reference is taken to both the local and remote nodes, the caller must use 288 + * v4l2_fwnode_endpoint_put_link() to drop the references when done with the 289 + * link. 290 + * 291 + * Return: 0 on success, or -ENOLINK if the remote endpoint fwnode can't be 292 + * found. 293 + */ 294 + int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode, 295 + struct v4l2_fwnode_link *link) 296 + { 297 + const char *port_prop = is_of_node(__fwnode) ? "reg" : "port"; 298 + struct fwnode_handle *fwnode; 299 + 300 + memset(link, 0, sizeof(*link)); 301 + 302 + fwnode = fwnode_get_parent(__fwnode); 303 + fwnode_property_read_u32(fwnode, port_prop, &link->local_port); 304 + fwnode = fwnode_get_next_parent(fwnode); 305 + if (is_of_node(fwnode) && 306 + of_node_cmp(to_of_node(fwnode)->name, "ports") == 0) 307 + fwnode = fwnode_get_next_parent(fwnode); 308 + link->local_node = fwnode; 309 + 310 + fwnode = fwnode_graph_get_remote_endpoint(__fwnode); 311 + if (!fwnode) { 312 + fwnode_handle_put(fwnode); 313 + return -ENOLINK; 314 + } 315 + 316 + fwnode = fwnode_get_parent(fwnode); 317 + fwnode_property_read_u32(fwnode, port_prop, &link->remote_port); 318 + fwnode = fwnode_get_next_parent(fwnode); 319 + if (is_of_node(fwnode) && 320 + of_node_cmp(to_of_node(fwnode)->name, "ports") == 0) 321 + fwnode = fwnode_get_next_parent(fwnode); 322 + link->remote_node = fwnode; 323 + 324 + return 0; 325 + } 326 + EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link); 327 + 328 + /** 329 + * v4l2_fwnode_put_link() - drop references to nodes in a link 330 + * @link: pointer to the V4L2 fwnode link data structure 331 + * 332 + * Drop references to the local and remote nodes in the link. This function 333 + * must be called on every link parsed with v4l2_fwnode_parse_link(). 334 + */ 335 + void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) 336 + { 337 + fwnode_handle_put(link->local_node); 338 + fwnode_handle_put(link->remote_node); 339 + } 340 + EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link); 341 + 342 + MODULE_LICENSE("GPL"); 343 + MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>"); 344 + MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 345 + MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+104
include/media/v4l2-fwnode.h
··· 1 + /* 2 + * V4L2 fwnode binding parsing library 3 + * 4 + * Copyright (c) 2016 Intel Corporation. 5 + * Author: Sakari Ailus <sakari.ailus@linux.intel.com> 6 + * 7 + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. 8 + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 9 + * 10 + * Copyright (C) 2012 Renesas Electronics Corp. 11 + * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> 12 + * 13 + * This program is free software; you can redistribute it and/or modify 14 + * it under the terms of version 2 of the GNU General Public License as 15 + * published by the Free Software Foundation. 16 + */ 17 + #ifndef _V4L2_FWNODE_H 18 + #define _V4L2_FWNODE_H 19 + 20 + #include <linux/errno.h> 21 + #include <linux/fwnode.h> 22 + #include <linux/list.h> 23 + #include <linux/types.h> 24 + 25 + #include <media/v4l2-mediabus.h> 26 + 27 + struct fwnode_handle; 28 + 29 + /** 30 + * struct v4l2_fwnode_bus_mipi_csi2 - MIPI CSI-2 bus data structure 31 + * @flags: media bus (V4L2_MBUS_*) flags 32 + * @data_lanes: an array of physical data lane indexes 33 + * @clock_lane: physical lane index of the clock lane 34 + * @num_data_lanes: number of data lanes 35 + * @lane_polarities: polarity of the lanes. The order is the same of 36 + * the physical lanes. 37 + */ 38 + struct v4l2_fwnode_bus_mipi_csi2 { 39 + unsigned int flags; 40 + unsigned char data_lanes[4]; 41 + unsigned char clock_lane; 42 + unsigned short num_data_lanes; 43 + bool lane_polarities[5]; 44 + }; 45 + 46 + /** 47 + * struct v4l2_fwnode_bus_parallel - parallel data bus data structure 48 + * @flags: media bus (V4L2_MBUS_*) flags 49 + * @bus_width: bus width in bits 50 + * @data_shift: data shift in bits 51 + */ 52 + struct v4l2_fwnode_bus_parallel { 53 + unsigned int flags; 54 + unsigned char bus_width; 55 + unsigned char data_shift; 56 + }; 57 + 58 + /** 59 + * struct v4l2_fwnode_endpoint - the endpoint data structure 60 + * @base: fwnode endpoint of the v4l2_fwnode 61 + * @bus_type: bus type 62 + * @bus: bus configuration data structure 63 + * @link_frequencies: array of supported link frequencies 64 + * @nr_of_link_frequencies: number of elements in link_frequenccies array 65 + */ 66 + struct v4l2_fwnode_endpoint { 67 + struct fwnode_endpoint base; 68 + /* 69 + * Fields below this line will be zeroed by 70 + * v4l2_fwnode_parse_endpoint() 71 + */ 72 + enum v4l2_mbus_type bus_type; 73 + union { 74 + struct v4l2_fwnode_bus_parallel parallel; 75 + struct v4l2_fwnode_bus_mipi_csi2 mipi_csi2; 76 + } bus; 77 + u64 *link_frequencies; 78 + unsigned int nr_of_link_frequencies; 79 + }; 80 + 81 + /** 82 + * struct v4l2_fwnode_link - a link between two endpoints 83 + * @local_node: pointer to device_node of this endpoint 84 + * @local_port: identifier of the port this endpoint belongs to 85 + * @remote_node: pointer to device_node of the remote endpoint 86 + * @remote_port: identifier of the port the remote endpoint belongs to 87 + */ 88 + struct v4l2_fwnode_link { 89 + struct fwnode_handle *local_node; 90 + unsigned int local_port; 91 + struct fwnode_handle *remote_node; 92 + unsigned int remote_port; 93 + }; 94 + 95 + int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, 96 + struct v4l2_fwnode_endpoint *vep); 97 + struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( 98 + struct fwnode_handle *fwnode); 99 + void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); 100 + int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, 101 + struct v4l2_fwnode_link *link); 102 + void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); 103 + 104 + #endif /* _V4L2_FWNODE_H */