From 9ca4653121329595443df4e7a458154e8f745edf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 17 Aug 2017 11:28:21 -0400 Subject: media: v4l: fwnode: Support generic parsing of graph endpoints in a device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two functions for parsing devices graph endpoints: v4l2_async_notifier_parse_fwnode_endpoints and v4l2_async_notifier_parse_fwnode_endpoints_by_port. The former iterates over all endpoints whereas the latter only iterates over the endpoints in a given port. The former is mostly useful for existing drivers that currently implement the iteration over all the endpoints themselves whereas the latter is especially intended for devices with both sinks and sources: async sub-devices for external devices connected to the device's sources will have already been set up, or the external sub-devices are part of the master device. Depends-on: ("device property: preserve usecount for node passed to of_fwnode_graph_get_port_parent()") Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Acked-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 196 ++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) (limited to 'drivers/media/v4l2-core/v4l2-fwnode.c') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 40b2fbfe8865..df0695b7bbcc 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -19,6 +19,7 @@ */ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #include enum v4l2_fwnode_bus_type { @@ -388,6 +390,200 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) } EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link); +static int v4l2_async_notifier_realloc(struct v4l2_async_notifier *notifier, + unsigned int max_subdevs) +{ + struct v4l2_async_subdev **subdevs; + + if (max_subdevs <= notifier->max_subdevs) + return 0; + + subdevs = kvmalloc_array( + max_subdevs, sizeof(*notifier->subdevs), + GFP_KERNEL | __GFP_ZERO); + if (!subdevs) + return -ENOMEM; + + if (notifier->subdevs) { + memcpy(subdevs, notifier->subdevs, + sizeof(*subdevs) * notifier->num_subdevs); + + kvfree(notifier->subdevs); + } + + notifier->subdevs = subdevs; + notifier->max_subdevs = max_subdevs; + + return 0; +} + +static int v4l2_async_notifier_fwnode_parse_endpoint( + struct device *dev, struct v4l2_async_notifier *notifier, + struct fwnode_handle *endpoint, unsigned int asd_struct_size, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)) +{ + struct v4l2_async_subdev *asd; + struct v4l2_fwnode_endpoint *vep; + int ret = 0; + + asd = kzalloc(asd_struct_size, GFP_KERNEL); + if (!asd) + return -ENOMEM; + + asd->match_type = V4L2_ASYNC_MATCH_FWNODE; + asd->match.fwnode.fwnode = + fwnode_graph_get_remote_port_parent(endpoint); + if (!asd->match.fwnode.fwnode) { + dev_warn(dev, "bad remote port parent\n"); + ret = -EINVAL; + goto out_err; + } + + vep = v4l2_fwnode_endpoint_alloc_parse(endpoint); + if (IS_ERR(vep)) { + ret = PTR_ERR(vep); + dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n", + ret); + goto out_err; + } + + ret = parse_endpoint ? parse_endpoint(dev, vep, asd) : 0; + if (ret == -ENOTCONN) + dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep->base.port, + vep->base.id); + else if (ret < 0) + dev_warn(dev, + "driver could not parse port@%u/endpoint@%u (%d)\n", + vep->base.port, vep->base.id, ret); + v4l2_fwnode_endpoint_free(vep); + if (ret < 0) + goto out_err; + + notifier->subdevs[notifier->num_subdevs] = asd; + notifier->num_subdevs++; + + return 0; + +out_err: + fwnode_handle_put(asd->match.fwnode.fwnode); + kfree(asd); + + return ret == -ENOTCONN ? 0 : ret; +} + +static int __v4l2_async_notifier_parse_fwnode_endpoints( + struct device *dev, struct v4l2_async_notifier *notifier, + size_t asd_struct_size, unsigned int port, bool has_port, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)) +{ + struct fwnode_handle *fwnode; + unsigned int max_subdevs = notifier->max_subdevs; + int ret; + + if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev))) + return -EINVAL; + + for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint( + dev_fwnode(dev), fwnode)); ) { + struct fwnode_handle *dev_fwnode; + bool is_available; + + dev_fwnode = fwnode_graph_get_port_parent(fwnode); + is_available = fwnode_device_is_available(dev_fwnode); + fwnode_handle_put(dev_fwnode); + if (!is_available) + continue; + + if (has_port) { + struct fwnode_endpoint ep; + + ret = fwnode_graph_parse_endpoint(fwnode, &ep); + if (ret) { + fwnode_handle_put(fwnode); + return ret; + } + + if (ep.port != port) + continue; + } + max_subdevs++; + } + + /* No subdevs to add? Return here. */ + if (max_subdevs == notifier->max_subdevs) + return 0; + + ret = v4l2_async_notifier_realloc(notifier, max_subdevs); + if (ret) + return ret; + + for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint( + dev_fwnode(dev), fwnode)); ) { + struct fwnode_handle *dev_fwnode; + bool is_available; + + dev_fwnode = fwnode_graph_get_port_parent(fwnode); + is_available = fwnode_device_is_available(dev_fwnode); + fwnode_handle_put(dev_fwnode); + + if (!fwnode_device_is_available(dev_fwnode)) + continue; + + if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) { + ret = -EINVAL; + break; + } + + if (has_port) { + struct fwnode_endpoint ep; + + ret = fwnode_graph_parse_endpoint(fwnode, &ep); + if (ret) + break; + + if (ep.port != port) + continue; + } + + ret = v4l2_async_notifier_fwnode_parse_endpoint( + dev, notifier, fwnode, asd_struct_size, parse_endpoint); + if (ret < 0) + break; + } + + fwnode_handle_put(fwnode); + + return ret; +} + +int v4l2_async_notifier_parse_fwnode_endpoints( + struct device *dev, struct v4l2_async_notifier *notifier, + size_t asd_struct_size, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)) +{ + return __v4l2_async_notifier_parse_fwnode_endpoints( + dev, notifier, asd_struct_size, 0, false, parse_endpoint); +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints); + +int v4l2_async_notifier_parse_fwnode_endpoints_by_port( + struct device *dev, struct v4l2_async_notifier *notifier, + size_t asd_struct_size, unsigned int port, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)) +{ + return __v4l2_async_notifier_parse_fwnode_endpoints( + dev, notifier, asd_struct_size, port, true, parse_endpoint); +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); -- cgit From baf249e40fddd1793c7970b5afe7f10945dcfb0c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 29 Aug 2017 06:13:19 -0400 Subject: media: v4l: fwnode: Move KernelDoc documentation to the header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In V4L2 the practice is to have the KernelDoc documentation in the header and not in .c source code files. This consequently makes the V4L2 fwnode function documentation part of the Media documentation build. Also correct the link related function and argument naming in documentation and add an asterisk to v4l2_fwnode_endpoint_free() documentation to make it proper KernelDoc documentation. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund Acked-by: Hans Verkuil Acked-by: Pavel Machek Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 75 -------------------------------- include/media/v4l2-fwnode.h | 81 ++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 76 deletions(-) (limited to 'drivers/media/v4l2-core/v4l2-fwnode.c') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index df0695b7bbcc..65bdcd59744a 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -183,25 +183,6 @@ v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode, vep->bus_type = V4L2_MBUS_CSI1; } -/** - * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties - * @fwnode: pointer to the endpoint's fwnode handle - * @vep: pointer to the V4L2 fwnode data structure - * - * All properties are optional. If none are found, we don't set any flags. This - * means the port has a static configuration and no properties have to be - * specified explicitly. If any properties that identify the bus as parallel - * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if - * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we - * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a - * reference to @fwnode. - * - * NOTE: This function does not parse properties the size of which is variable - * without a low fixed limit. Please use v4l2_fwnode_endpoint_alloc_parse() in - * new drivers instead. - * - * Return: 0 on success or a negative error code on failure. - */ int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep) { @@ -241,14 +222,6 @@ int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse); -/* - * v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by - * v4l2_fwnode_endpoint_alloc_parse() - * @vep - the V4L2 fwnode the resources of which are to be released - * - * It is safe to call this function with NULL argument or on a V4L2 fwnode the - * parsing of which failed. - */ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep) { if (IS_ERR_OR_NULL(vep)) @@ -259,29 +232,6 @@ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep) } EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free); -/** - * v4l2_fwnode_endpoint_alloc_parse() - parse all fwnode node properties - * @fwnode: pointer to the endpoint's fwnode handle - * - * All properties are optional. If none are found, we don't set any flags. This - * means the port has a static configuration and no properties have to be - * specified explicitly. If any properties that identify the bus as parallel - * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if - * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we - * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a - * reference to @fwnode. - * - * v4l2_fwnode_endpoint_alloc_parse() has two important differences to - * v4l2_fwnode_endpoint_parse(): - * - * 1. It also parses variable size data. - * - * 2. The memory it has allocated to store the variable size data must be freed - * using v4l2_fwnode_endpoint_free() when no longer needed. - * - * Return: Pointer to v4l2_fwnode_endpoint if successful, on an error pointer - * on error. - */ struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( struct fwnode_handle *fwnode) { @@ -324,24 +274,6 @@ out_err: } EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse); -/** - * v4l2_fwnode_endpoint_parse_link() - parse a link between two endpoints - * @__fwnode: pointer to the endpoint's fwnode at the local end of the link - * @link: pointer to the V4L2 fwnode link data structure - * - * Fill the link structure with the local and remote nodes and port numbers. - * The local_node and remote_node fields are set to point to the local and - * remote port's parent nodes respectively (the port parent node being the - * parent node of the port node if that node isn't a 'ports' node, or the - * grand-parent node of the port node otherwise). - * - * A reference is taken to both the local and remote nodes, the caller must use - * v4l2_fwnode_endpoint_put_link() to drop the references when done with the - * link. - * - * Return: 0 on success, or -ENOLINK if the remote endpoint fwnode can't be - * found. - */ int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode, struct v4l2_fwnode_link *link) { @@ -376,13 +308,6 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode, } EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link); -/** - * v4l2_fwnode_put_link() - drop references to nodes in a link - * @link: pointer to the V4L2 fwnode link data structure - * - * Drop references to the local and remote nodes in the link. This function - * must be called on every link parsed with v4l2_fwnode_parse_link(). - */ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) { fwnode_handle_put(link->local_node); diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index ac605af9b877..105cfeee44ef 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -115,13 +115,92 @@ struct v4l2_fwnode_link { unsigned int remote_port; }; +/** + * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties + * @fwnode: pointer to the endpoint's fwnode handle + * @vep: pointer to the V4L2 fwnode data structure + * + * All properties are optional. If none are found, we don't set any flags. This + * means the port has a static configuration and no properties have to be + * specified explicitly. If any properties that identify the bus as parallel + * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if + * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we + * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a + * reference to @fwnode. + * + * NOTE: This function does not parse properties the size of which is variable + * without a low fixed limit. Please use v4l2_fwnode_endpoint_alloc_parse() in + * new drivers instead. + * + * Return: 0 on success or a negative error code on failure. + */ int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep); + +/** + * v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by + * v4l2_fwnode_endpoint_alloc_parse() + * @vep: the V4L2 fwnode the resources of which are to be released + * + * It is safe to call this function with NULL argument or on a V4L2 fwnode the + * parsing of which failed. + */ +void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); + +/** + * v4l2_fwnode_endpoint_alloc_parse() - parse all fwnode node properties + * @fwnode: pointer to the endpoint's fwnode handle + * + * All properties are optional. If none are found, we don't set any flags. This + * means the port has a static configuration and no properties have to be + * specified explicitly. If any properties that identify the bus as parallel + * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if + * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we + * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a + * reference to @fwnode. + * + * v4l2_fwnode_endpoint_alloc_parse() has two important differences to + * v4l2_fwnode_endpoint_parse(): + * + * 1. It also parses variable size data. + * + * 2. The memory it has allocated to store the variable size data must be freed + * using v4l2_fwnode_endpoint_free() when no longer needed. + * + * Return: Pointer to v4l2_fwnode_endpoint if successful, on an error pointer + * on error. + */ struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( struct fwnode_handle *fwnode); -void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); + +/** + * v4l2_fwnode_parse_link() - parse a link between two endpoints + * @fwnode: pointer to the endpoint's fwnode at the local end of the link + * @link: pointer to the V4L2 fwnode link data structure + * + * Fill the link structure with the local and remote nodes and port numbers. + * The local_node and remote_node fields are set to point to the local and + * remote port's parent nodes respectively (the port parent node being the + * parent node of the port node if that node isn't a 'ports' node, or the + * grand-parent node of the port node otherwise). + * + * A reference is taken to both the local and remote nodes, the caller must use + * v4l2_fwnode_put_link() to drop the references when done with the + * link. + * + * Return: 0 on success, or -ENOLINK if the remote endpoint fwnode can't be + * found. + */ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, struct v4l2_fwnode_link *link); + +/** + * v4l2_fwnode_put_link() - drop references to nodes in a link + * @link: pointer to the V4L2 fwnode link data structure + * + * Drop references to the local and remote nodes in the link. This function + * must be called on every link parsed with v4l2_fwnode_parse_link(). + */ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); /** -- cgit From d84285390f0722fb6844880c48fbe7db8a183fc1 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 20 Jun 2017 07:47:24 -0400 Subject: media: v4l: fwnode: Add a helper function for parsing generic references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add function v4l2_fwnode_reference_parse() for parsing them as async sub-devices. This can be done on e.g. flash or lens async sub-devices that are not part of but are associated with a sensor. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Acked-by: Niklas Söderlund Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 69 +++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'drivers/media/v4l2-core/v4l2-fwnode.c') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 65bdcd59744a..edd2e8d983a1 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -509,6 +509,75 @@ int v4l2_async_notifier_parse_fwnode_endpoints_by_port( } EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port); +/* + * v4l2_fwnode_reference_parse - parse references for async sub-devices + * @dev: the device node the properties of which are parsed for references + * @notifier: the async notifier where the async subdevs will be added + * @prop: the name of the property + * + * Return: 0 on success + * -ENOENT if no entries were found + * -ENOMEM if memory allocation failed + * -EINVAL if property parsing failed + */ +static int v4l2_fwnode_reference_parse( + struct device *dev, struct v4l2_async_notifier *notifier, + const char *prop) +{ + struct fwnode_reference_args args; + unsigned int index; + int ret; + + for (index = 0; + !(ret = fwnode_property_get_reference_args( + dev_fwnode(dev), prop, NULL, 0, index, &args)); + index++) + fwnode_handle_put(args.fwnode); + + if (!index) + return -ENOENT; + + /* + * Note that right now both -ENODATA and -ENOENT may signal + * out-of-bounds access. Return the error in cases other than that. + */ + if (ret != -ENOENT && ret != -ENODATA) + return ret; + + ret = v4l2_async_notifier_realloc(notifier, + notifier->num_subdevs + index); + if (ret) + return ret; + + for (index = 0; !fwnode_property_get_reference_args( + dev_fwnode(dev), prop, NULL, 0, index, &args); + index++) { + struct v4l2_async_subdev *asd; + + if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) { + ret = -EINVAL; + goto error; + } + + asd = kzalloc(sizeof(*asd), GFP_KERNEL); + if (!asd) { + ret = -ENOMEM; + goto error; + } + + notifier->subdevs[notifier->num_subdevs] = asd; + asd->match.fwnode.fwnode = args.fwnode; + asd->match_type = V4L2_ASYNC_MATCH_FWNODE; + notifier->num_subdevs++; + } + + return 0; + +error: + fwnode_handle_put(args.fwnode); + return ret; +} + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); -- cgit From a1699a4e525dedd2ecd03ce5f92e80c058e14e28 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 22 Jun 2017 08:03:28 -0400 Subject: media: v4l: fwnode: Add a helper function to obtain device / integer references v4l2_fwnode_reference_parse_int_prop() will find an fwnode such that under the device's own fwnode, it will follow child fwnodes with the given property-value pair and return the resulting fwnode. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 287 ++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) (limited to 'drivers/media/v4l2-core/v4l2-fwnode.c') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index edd2e8d983a1..f8cd88f791c4 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -578,6 +578,293 @@ error: return ret; } +/* + * v4l2_fwnode_reference_get_int_prop - parse a reference with integer + * arguments + * @fwnode: fwnode to read @prop from + * @notifier: notifier for @dev + * @prop: the name of the property + * @index: the index of the reference to get + * @props: the array of integer property names + * @nprops: the number of integer property names in @nprops + * + * First find an fwnode referred to by the reference at @index in @prop. + * + * Then under that fwnode, @nprops times, for each property in @props, + * iteratively follow child nodes starting from fwnode such that they have the + * property in @props array at the index of the child node distance from the + * root node and the value of that property matching with the integer argument + * of the reference, at the same index. + * + * The child fwnode reched at the end of the iteration is then returned to the + * caller. + * + * The core reason for this is that you cannot refer to just any node in ACPI. + * So to refer to an endpoint (easy in DT) you need to refer to a device, then + * provide a list of (property name, property value) tuples where each tuple + * uniquely identifies a child node. The first tuple identifies a child directly + * underneath the device fwnode, the next tuple identifies a child node + * underneath the fwnode identified by the previous tuple, etc. until you + * reached the fwnode you need. + * + * An example with a graph, as defined in Documentation/acpi/dsd/graph.txt: + * + * Scope (\_SB.PCI0.I2C2) + * { + * Device (CAM0) + * { + * Name (_DSD, Package () { + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + * Package () { + * Package () { + * "compatible", + * Package () { "nokia,smia" } + * }, + * }, + * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), + * Package () { + * Package () { "port0", "PRT0" }, + * } + * }) + * Name (PRT0, Package() { + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + * Package () { + * Package () { "port", 0 }, + * }, + * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), + * Package () { + * Package () { "endpoint0", "EP00" }, + * } + * }) + * Name (EP00, Package() { + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + * Package () { + * Package () { "endpoint", 0 }, + * Package () { + * "remote-endpoint", + * Package() { + * \_SB.PCI0.ISP, 4, 0 + * } + * }, + * } + * }) + * } + * } + * + * Scope (\_SB.PCI0) + * { + * Device (ISP) + * { + * Name (_DSD, Package () { + * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), + * Package () { + * Package () { "port4", "PRT4" }, + * } + * }) + * + * Name (PRT4, Package() { + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + * Package () { + * Package () { "port", 4 }, + * }, + * ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), + * Package () { + * Package () { "endpoint0", "EP40" }, + * } + * }) + * + * Name (EP40, Package() { + * ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + * Package () { + * Package () { "endpoint", 0 }, + * Package () { + * "remote-endpoint", + * Package () { + * \_SB.PCI0.I2C2.CAM0, + * 0, 0 + * } + * }, + * } + * }) + * } + * } + * + * From the EP40 node under ISP device, you could parse the graph remote + * endpoint using v4l2_fwnode_reference_get_int_prop with these arguments: + * + * @fwnode: fwnode referring to EP40 under ISP. + * @prop: "remote-endpoint" + * @index: 0 + * @props: "port", "endpoint" + * @nprops: 2 + * + * And you'd get back fwnode referring to EP00 under CAM0. + * + * The same works the other way around: if you use EP00 under CAM0 as the + * fwnode, you'll get fwnode referring to EP40 under ISP. + * + * The same example in DT syntax would look like this: + * + * cam: cam0 { + * compatible = "nokia,smia"; + * + * port { + * port = <0>; + * endpoint { + * endpoint = <0>; + * remote-endpoint = <&isp 4 0>; + * }; + * }; + * }; + * + * isp: isp { + * ports { + * port@4 { + * port = <4>; + * endpoint { + * endpoint = <0>; + * remote-endpoint = <&cam 0 0>; + * }; + * }; + * }; + * }; + * + * Return: 0 on success + * -ENOENT if no entries (or the property itself) were found + * -EINVAL if property parsing otherwise failed + * -ENOMEM if memory allocation failed + */ +static struct fwnode_handle *v4l2_fwnode_reference_get_int_prop( + struct fwnode_handle *fwnode, const char *prop, unsigned int index, + const char * const *props, unsigned int nprops) +{ + struct fwnode_reference_args fwnode_args; + unsigned int *args = fwnode_args.args; + struct fwnode_handle *child; + int ret; + + /* + * Obtain remote fwnode as well as the integer arguments. + * + * Note that right now both -ENODATA and -ENOENT may signal + * out-of-bounds access. Return -ENOENT in that case. + */ + ret = fwnode_property_get_reference_args(fwnode, prop, NULL, nprops, + index, &fwnode_args); + if (ret) + return ERR_PTR(ret == -ENODATA ? -ENOENT : ret); + + /* + * Find a node in the tree under the referred fwnode corresponding to + * the integer arguments. + */ + fwnode = fwnode_args.fwnode; + while (nprops--) { + u32 val; + + /* Loop over all child nodes under fwnode. */ + fwnode_for_each_child_node(fwnode, child) { + if (fwnode_property_read_u32(child, *props, &val)) + continue; + + /* Found property, see if its value matches. */ + if (val == *args) + break; + } + + fwnode_handle_put(fwnode); + + /* No property found; return an error here. */ + if (!child) { + fwnode = ERR_PTR(-ENOENT); + break; + } + + props++; + args++; + fwnode = child; + } + + return fwnode; +} + +/* + * v4l2_fwnode_reference_parse_int_props - parse references for async + * sub-devices + * @dev: struct device pointer + * @notifier: notifier for @dev + * @prop: the name of the property + * @props: the array of integer property names + * @nprops: the number of integer properties + * + * Use v4l2_fwnode_reference_get_int_prop to find fwnodes through reference in + * property @prop with integer arguments with child nodes matching in properties + * @props. Then, set up V4L2 async sub-devices for those fwnodes in the notifier + * accordingly. + * + * While it is technically possible to use this function on DT, it is only + * meaningful on ACPI. On Device tree you can refer to any node in the tree but + * on ACPI the references are limited to devices. + * + * Return: 0 on success + * -ENOENT if no entries (or the property itself) were found + * -EINVAL if property parsing otherwisefailed + * -ENOMEM if memory allocation failed + */ +static int v4l2_fwnode_reference_parse_int_props( + struct device *dev, struct v4l2_async_notifier *notifier, + const char *prop, const char * const *props, unsigned int nprops) +{ + struct fwnode_handle *fwnode; + unsigned int index; + int ret; + + for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop( + dev_fwnode(dev), prop, index, props, + nprops))); index++) + fwnode_handle_put(fwnode); + + /* + * Note that right now both -ENODATA and -ENOENT may signal + * out-of-bounds access. Return the error in cases other than that. + */ + if (PTR_ERR(fwnode) != -ENOENT && PTR_ERR(fwnode) != -ENODATA) + return PTR_ERR(fwnode); + + ret = v4l2_async_notifier_realloc(notifier, + notifier->num_subdevs + index); + if (ret) + return -ENOMEM; + + for (index = 0; !IS_ERR((fwnode = v4l2_fwnode_reference_get_int_prop( + dev_fwnode(dev), prop, index, props, + nprops))); index++) { + struct v4l2_async_subdev *asd; + + if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) { + ret = -EINVAL; + goto error; + } + + asd = kzalloc(sizeof(struct v4l2_async_subdev), GFP_KERNEL); + if (!asd) { + ret = -ENOMEM; + goto error; + } + + notifier->subdevs[notifier->num_subdevs] = asd; + asd->match.fwnode.fwnode = fwnode; + asd->match_type = V4L2_ASYNC_MATCH_FWNODE; + notifier->num_subdevs++; + } + + return PTR_ERR(fwnode) == -ENOENT ? 0 : PTR_ERR(fwnode); + +error: + fwnode_handle_put(fwnode); + return ret; +} + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); -- cgit From 7a9ec808ad46d1e02e5e766a83adc5f88e5df264 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Sep 2017 08:35:42 -0400 Subject: media: v4l: fwnode: Add convenience function for parsing common external refs Add v4l2_fwnode_parse_reference_sensor_common for parsing common sensor properties that refer to adjacent devices such as flash or lens driver chips. As this is an association only, there's little a regular driver needs to know about these devices as such. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Acked-by: Pavel Machek Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 35 +++++++++++++++++++++++++++++++++++ include/media/v4l2-async.h | 3 ++- include/media/v4l2-fwnode.h | 21 +++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core/v4l2-fwnode.c') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index f8cd88f791c4..39387dc6cadd 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -865,6 +865,41 @@ error: return ret; } +int v4l2_async_notifier_parse_fwnode_sensor_common( + struct device *dev, struct v4l2_async_notifier *notifier) +{ + static const char * const led_props[] = { "led" }; + static const struct { + const char *name; + const char * const *props; + unsigned int nprops; + } props[] = { + { "flash-leds", led_props, ARRAY_SIZE(led_props) }, + { "lens-focus", NULL, 0 }, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(props); i++) { + int ret; + + if (props[i].props && is_acpi_node(dev_fwnode(dev))) + ret = v4l2_fwnode_reference_parse_int_props( + dev, notifier, props[i].name, + props[i].props, props[i].nprops); + else + ret = v4l2_fwnode_reference_parse( + dev, notifier, props[i].name); + if (ret && ret != -ENOENT) { + dev_warn(dev, "parsing property \"%s\" failed (%d)\n", + props[i].name, ret); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 17c4ac7c73e8..8d8cfc3f3100 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -156,7 +156,8 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier); * Release memory resources related to a notifier, including the async * sub-devices allocated for the purposes of the notifier but not the notifier * itself. The user is responsible for calling this function to clean up the - * notifier after calling @v4l2_async_notifier_parse_fwnode_endpoints. + * notifier after calling @v4l2_async_notifier_parse_fwnode_endpoints or + * @v4l2_fwnode_reference_parse_sensor_common. * * There is no harm from calling v4l2_async_notifier_cleanup in other * cases as long as its memory has been zeroed after it has been diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index 105cfeee44ef..ca50108dfd8f 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -319,4 +319,25 @@ int v4l2_async_notifier_parse_fwnode_endpoints_by_port( struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd)); +/** + * v4l2_fwnode_reference_parse_sensor_common - parse common references on + * sensors for async sub-devices + * @dev: the device node the properties of which are parsed for references + * @notifier: the async notifier where the async subdevs will be added + * + * Parse common sensor properties for remote devices related to the + * sensor and set up async sub-devices for them. + * + * Any notifier populated using this function must be released with a call to + * v4l2_async_notifier_release() after it has been unregistered and the async + * sub-devices are no longer in use, even in the case the function returned an + * error. + * + * Return: 0 on success + * -ENOMEM if memory allocation failed + * -EINVAL if property parsing failed + */ +int v4l2_async_notifier_parse_fwnode_sensor_common( + struct device *dev, struct v4l2_async_notifier *notifier); + #endif /* _V4L2_FWNODE_H */ -- cgit From aef69d54755d45edefbf347a51efd1673d7daed9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 24 Sep 2017 18:47:44 -0400 Subject: media: v4l: fwnode: Add a convenience function for registering sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a convenience function for parsing firmware for information on related devices using v4l2_async_notifier_parse_fwnode_sensor_common() registering the notifier and finally the async sub-device itself. This should be useful for sensor drivers that do not have device specific requirements related to firmware information parsing or the async framework. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Reviewed-by: Niklas Söderlund Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 19 ++++++++++++---- drivers/media/v4l2-core/v4l2-fwnode.c | 41 +++++++++++++++++++++++++++++++++++ include/media/v4l2-async.h | 22 +++++++++++++++++++ include/media/v4l2-subdev.h | 3 +++ 4 files changed, 81 insertions(+), 4 deletions(-) (limited to 'drivers/media/v4l2-core/v4l2-fwnode.c') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 57bd54016064..49f7eccc76db 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -474,19 +474,25 @@ int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd, } EXPORT_SYMBOL(v4l2_async_subdev_notifier_register); -void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) +static void __v4l2_async_notifier_unregister( + struct v4l2_async_notifier *notifier) { - if (!notifier->v4l2_dev && !notifier->sd) + if (!notifier || (!notifier->v4l2_dev && !notifier->sd)) return; - mutex_lock(&list_lock); - v4l2_async_notifier_unbind_all_subdevs(notifier); notifier->sd = NULL; notifier->v4l2_dev = NULL; list_del(¬ifier->list); +} + +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) +{ + mutex_lock(&list_lock); + + __v4l2_async_notifier_unregister(notifier); mutex_unlock(&list_lock); } @@ -596,6 +602,11 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) { mutex_lock(&list_lock); + __v4l2_async_notifier_unregister(sd->subdev_notifier); + v4l2_async_notifier_cleanup(sd->subdev_notifier); + kfree(sd->subdev_notifier); + sd->subdev_notifier = NULL; + if (sd->asd) { struct v4l2_async_notifier *notifier = sd->notifier; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 39387dc6cadd..3b9c6afb49a3 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -29,6 +29,7 @@ #include #include +#include enum v4l2_fwnode_bus_type { V4L2_FWNODE_BUS_TYPE_GUESS = 0, @@ -900,6 +901,46 @@ int v4l2_async_notifier_parse_fwnode_sensor_common( } EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common); +int v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd) +{ + struct v4l2_async_notifier *notifier; + int ret; + + if (WARN_ON(!sd->dev)) + return -ENODEV; + + notifier = kzalloc(sizeof(*notifier), GFP_KERNEL); + if (!notifier) + return -ENOMEM; + + ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev, + notifier); + if (ret < 0) + goto out_cleanup; + + ret = v4l2_async_subdev_notifier_register(sd, notifier); + if (ret < 0) + goto out_cleanup; + + ret = v4l2_async_register_subdev(sd); + if (ret < 0) + goto out_unregister; + + sd->subdev_notifier = notifier; + + return 0; + +out_unregister: + v4l2_async_notifier_unregister(notifier); + +out_cleanup: + v4l2_async_notifier_cleanup(notifier); + kfree(notifier); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 8d8cfc3f3100..6152434cbe82 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -173,6 +173,28 @@ void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier); */ int v4l2_async_register_subdev(struct v4l2_subdev *sd); +/** + * v4l2_async_register_subdev_sensor_common - registers a sensor sub-device to + * the asynchronous sub-device + * framework and parse set up common + * sensor related devices + * + * @sd: pointer to struct &v4l2_subdev + * + * This function is just like v4l2_async_register_subdev() with the exception + * that calling it will also parse firmware interfaces for remote references + * using v4l2_async_notifier_parse_fwnode_sensor_common() and registers the + * async sub-devices. The sub-device is similarly unregistered by calling + * v4l2_async_unregister_subdev(). + * + * While registered, the subdev module is marked as in-use. + * + * An error is returned if the module is no longer loaded on any attempts + * to register it. + */ +int __must_check v4l2_async_register_subdev_sensor_common( + struct v4l2_subdev *sd); + /** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous * subdevice framework diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index e83872078376..ec399c770301 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -793,6 +793,8 @@ struct v4l2_subdev_platform_data { * list. * @asd: Pointer to respective &struct v4l2_async_subdev. * @notifier: Pointer to the managing notifier. + * @subdev_notifier: A sub-device notifier implicitly registered for the sub- + * device using v4l2_device_register_sensor_subdev(). * @pdata: common part of subdevice platform data * * Each instance of a subdev driver should create this struct, either @@ -823,6 +825,7 @@ struct v4l2_subdev { struct list_head async_list; struct v4l2_async_subdev *asd; struct v4l2_async_notifier *notifier; + struct v4l2_async_notifier *subdev_notifier; struct v4l2_subdev_platform_data *pdata; }; -- cgit From 1acce5f72cfabcafee5e101b9ac7d71ebe1c7af9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 31 Oct 2017 14:18:03 -0400 Subject: media: v4l2-fwnode: use the cached value instead of getting again There is a get/put operation in order to get firmware is_available data there at the __v4l2_async_notifier_parse_fwnode_endpoints() function. However, instead of using it, the code just reads again without the lock. That's a bug, as dev_fwnode isn't guaranteed to be there once fwnode_handle_put() has been called on it. This solves this smatch warning: drivers/media/v4l2-core/v4l2-fwnode.c:453:8: warning: variable 'is_available' set but not used [-Wunused-but-set-variable] bool is_available; ^~~~~~~~~~~~ Fixes: 9ca465312132 ("media: v4l: fwnode: Support generic parsing of graph endpoints in a device") Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core/v4l2-fwnode.c') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 3b9c6afb49a3..681b192420d9 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -455,8 +455,7 @@ static int __v4l2_async_notifier_parse_fwnode_endpoints( dev_fwnode = fwnode_graph_get_port_parent(fwnode); is_available = fwnode_device_is_available(dev_fwnode); fwnode_handle_put(dev_fwnode); - - if (!fwnode_device_is_available(dev_fwnode)) + if (!is_available) continue; if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) { -- cgit