Unified fwnode endpoint parser
Subsystems (4)
v5 cover Unified fwnode endpoint parser
Hi folks,
We have a large influx of new, unmerged, drivers that are now parsing fwnode endpoints and each one of them is doing this a little bit differently. The needs are still exactly the same for the graph data structure is device independent. This is still a non-trivial task and the majority of the driver implementations are buggy, just buggy in different ways.
Facilitate parsing endpoints by adding a convenience function for parsing the endpoints, and make the omap3isp and rcar-vin drivers use them as an example.
since v4:
- Prepend the set with three documentation fixes.
- The driver's async struct must begin with struct v4l2_async_subdev. Fix this for omap3isp and document it.
- Improve documentation for new functions.
- Don't use devm_ family of functions for allocating memory. Introduce v4l2_async_notifier_release() to release memory resources.
- Rework both v4l2_async_notifier_fwnode_parse_endpoints() and v4l2_async_notifier_fwnode_parse_endpoint() and the local functions they call. This should make the code cleaner. Despite the name, for linking and typical usage reasons the functions remain in v4l2-fwnode.c.
- Convert rcar-vin to use v4l2_async_notifier_fwnode_parse_endpoint().
- Use kvmalloc() for allocating the notifier's subdevs array.
- max_subdevs argument for notifier_realloc is now the total maximum number of subdevs, not the number of available subdevs.
- Use fwnode_device_is_available() to make sure the device actually exists.
- Move the note telling v4l2_async_notifier_fwnode_parse_endpoints() should not be used by new drivers to the last patch adding v4l2_async_notifier_fwnode_parse_endpoint().
since v3:
- Rebase on current mediatree master.
since v2:
- Rebase on CCP2 support patches.
- Prepend a patch cleaning up omap3isp driver a little.
since v1:
- The first patch has been merged (it was a bugfix).
- In anticipation that the parsing can take place over several iterations, take the existing number of async sub-devices into account when re-allocating an array of async sub-devices.
- Rework the first patch to better anticipate parsing single endpoint at a time by a driver.
- Add a second patch that adds a function for parsing endpoints one at a time based on port and endpoint numbers.
Sakari Ailus (5):
v4l: fwnode: Move KernelDoc documentation to the header
v4l: async: Add V4L2 async documentation to the documentation build
docs-rst: v4l: Include Qualcomm CAMSS in documentation build
v4l: fwnode: Support generic parsing of graph endpoints in a device
v4l: fwnode: Support generic parsing of graph endpoints in a single
port
Documentation/media/kapi/v4l2-async.rst | 3 + Documentation/media/kapi/v4l2-core.rst | 1 + Documentation/media/v4l-drivers/index.rst | 1 + drivers/media/platform/omap3isp/isp.c | 115 ++++--------- drivers/media/platform/omap3isp/isp.h | 5 +- drivers/media/platform/rcar-vin/rcar-core.c | 111 ++++-------- drivers/media/platform/rcar-vin/rcar-dma.c | 10 +- drivers/media/platform/rcar-vin/rcar-v4l2.c | 14 +- drivers/media/platform/rcar-vin/rcar-vin.h | 4 +- drivers/media/v4l2-core/v4l2-async.c | 16 ++ drivers/media/v4l2-core/v4l2-fwnode.c | 254 ++++++++++++++++++++-------- include/media/v4l2-async.h | 20 ++- include/media/v4l2-fwnode.h | 159 ++++++++++++++++- 13 files changed, 459 insertions(+), 254 deletions(-) create mode 100644 Documentation/media/kapi/v4l2-async.rst
Signature · 6 lines
2.11.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): 20170829110313.19538-1-sakari.ailus-VuQAYsv1563Yd54FQh9/[email protected]
PATCH v5 1/5 v4l: fwnode: Move KernelDoc documentation to the header
In V4L2 the practice is to have the KernelDoc documentation in the header and not in .c source code files. This consequientally makes the V4L2 fwnode function documentation part of the Media documentation build.
Also correct the link related function and argument naming in documentation.
--- drivers/media/v4l2-core/v4l2-fwnode.c | 75 -------------------------------- include/media/v4l2-fwnode.h | 81 ++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 76 deletions(-)
drivers/media/v4l2-core/v4l2-fwnode.c +0 -75
@@ -181,25 +181,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) {
@@ -239,14 +220,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))
@@ -257,29 +230,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) {
@@ -322,24 +272,6 @@ struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( }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) {
@@ -374,13 +306,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);
include/media/v4l2-fwnode.h +80 -1
93 lines not on this page · raw message
Signature · 6 lines
2.11.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): 20170829110313.19538-2-sakari.ailus-VuQAYsv1563Yd54FQh9/[email protected]
Niklas Söderlund
Reviewed-byRe: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
Hi Sakari,
Thanks for your patch.
Quoted 8 lines · Sakari Ailus
In V4L2 the practice is to have the KernelDoc documentation in the header and not in .c source code files. This consequientally makes the V4L2 fwnode function documentation part of the Media documentation build.
Also correct the link related function and argument naming in documentation.
Quoted 220 lines
--- drivers/media/v4l2-core/v4l2-fwnode.c | 75 -------------------------------- include/media/v4l2-fwnode.h | 81 ++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 76 deletions(-)drivers/media/v4l2-core/v4l2-fwnode.c +0
-75@@ -181,25 +181,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) {@@ -239,14 +220,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))@@ -257,29 +230,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) {@@ -322,24 +272,6 @@ struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( }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) {@@ -374,13 +306,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);include/media/v4l2-fwnode.h +80
-1@@ -113,13 +113,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);#endif /* _V4L2_FWNODE_H */ Signature · 1 line
2.11.0
Signature · 6 lines
Regards, Niklas Söderlund -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
In reply to Sakari AilusHi Sakari,
Thank you for the patch.
Quoted 8 lines · On Tuesday, 29 August 2017 14:03:09 EEST Sakari Ailus wrote:
In V4L2 the practice is to have the KernelDoc documentation in the header and not in .c source code files. This consequientally makes the V4L2 fwnode function documentation part of the Media documentation build.
Also correct the link related function and argument naming in documentation.
Signed-off-by Sakari Ailus <[email protected]>
I prefer documenting functions in the C file. Documentation in header files will get out-of-sync with the implementation much more easily.
Quoted 220 lines
---
drivers/media/v4l2-core/v4l2-fwnode.c| 75 --------------------------------include/media/v4l2-fwnode.h| 81 ++++++++++++++++++++++++++++++++-2 files changed, 80 insertions(+), 76 deletions(-)(quoted diff) +0
-0
b/drivers/media/v4l2-core/v4l2-fwnode.cindex40b2fbfe8865..706f9e7b90f1100644drivers/media/v4l2-core/v4l2-fwnode.c +0
-6@@ -181,25 +181,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 theV4L2_MBUS_CSI2_CONTINUOUS_CLOCKflag. 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 usev4l2_fwnode_endpoint_alloc_parse()in - * new drivers instead.
- *
- * Return: 0 on success or a negative error code on failure.
- */
int v4l2_fwnode_endpoint_parse(structfwnode_handle*fwnode,drivers/media/v4l2-core/v4l2-fwnode.c +0
-6struct v4l2_fwnode_endpoint *vep) {@@ -239,14 +220,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.
- */
voidv4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep){drivers/media/v4l2-core/v4l2-fwnode.c +0
-5if (IS_ERR_OR_NULL (vep))@@ -257,29 +230,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 theV4L2_MBUS_CSI2_CONTINUOUS_CLOCKflag. 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 - * usingv4l2_fwnode_endpoint_free()when no longer needed.
- *
- * Return: Pointer tov4l2_fwnode_endpointif successful, on an error pointer - * on error.
- */
structv4l2_fwnode_endpoint*v4l2_fwnode_endpoint_alloc_parse(drivers/media/v4l2-core/v4l2-fwnode.c +0
-6struct fwnode_handle *fwnode) {@@ -322,24 +272,6 @@ struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( } 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_nodeandremote_nodefields 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-ENOLINKif the remote endpoint fwnode can't be
- * found.
- */
int v4l2_fwnode_parse_link(structfwnode_handle*__fwnode,drivers/media/v4l2-core/v4l2-fwnode.c +0
-6struct v4l2_fwnode_link *link) {@@ -374,13 +306,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);include/media/v4l2-fwnode.h +6
-0@@ -113,13 +113,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 theV4L2_MBUS_CSI2_CONTINUOUS_CLOCKflag. 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 usev4l2_fwnode_endpoint_alloc_parse()in + * new drivers instead.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
int v4l2_fwnode_endpoint_parse(structfwnode_handle*fwnode,include/media/v4l2-fwnode.h +7
-0struct 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.
+ */include/media/v4l2-fwnode.h +7
-0+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 theV4L2_MBUS_CSI2_CONTINUOUS_CLOCKflag. 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 + * usingv4l2_fwnode_endpoint_free()when no longer needed.
+ *
+ * Return: Pointer tov4l2_fwnode_endpointif successful, on an error pointer + * on error.
+ */
structv4l2_fwnode_endpoint*v4l2_fwnode_endpoint_alloc_parse(include/media/v4l2-fwnode.h +7
-1struct 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_nodeandremote_nodefields 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-ENOLINKif the remote endpoint fwnode can't be
+ * found.
+ */
int v4l2_fwnode_parse_link(structfwnode_handle*fwnode,include/media/v4l2-fwnode.h +7
-0struct 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);#endif /* _V4L2_FWNODE_H */
Signature · 3 lines
Regards, Laurent Pinchart
Re: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
Hi Laurent,
Quoted 9 lines · Laurent Pinchart
Hi Sakari,
Thank you for the patch.
Quoted 4 lines · On Tuesday, 29 August 2017 14:03:09 EEST Sakari Ailus wrote:
In V4L2 the practice is to have the KernelDoc documentation in the header and not in .c source code files. This consequientally makes the V4L2 fwnode function documentation part of the Media documentation build.
Quoted 7 lines
Quoted 4 lines
Also correct the link related function and argument naming in documentation.
I prefer documenting functions in the C file. Documentation in header files will get out-of-sync with the implementation much more easily.
The fact is that there's very little KernelDoc documentation left in the .c files in V4L2. This actually appears to be the only exception. And it seems to have been in the Sphinx build; I missed that earlier, so that part of the commit message doesn't apply.
Signature · 6 lines
Sakari Ailus sakari.ailus-VuQAYsv1563Yd54FQh9/[email protected] -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Sakari Ailus
maintainerRe: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
Quoted 17 lines · Sakari Ailus
Hi Laurent,
Quoted 14 lines · Laurent Pinchart
Hi Sakari,
Thank you for the patch.
Quoted 8 lines · On Tuesday, 29 August 2017 14:03:09 EEST Sakari Ailus wrote:
In V4L2 the practice is to have the KernelDoc documentation in the header and not in .c source code files. This consequientally makes the V4L2 fwnode function documentation part of the Media documentation build.
Also correct the link related function and argument naming in documentation.
Signed-off-by Sakari Ailus <[email protected]>
Quoted 7 lines
Quoted 2 lines
I prefer documenting functions in the C file. Documentation in header files will get out-of-sync with the implementation much more easily.
The fact is that there's very little KernelDoc documentation left in the .c files in V4L2. This actually appears to be the only exception. And it seems to have been in the Sphinx build; I missed that earlier, so that part of the commit message doesn't apply.
Oops. That was just a local change. So yes, the commit message is fine.
Signature · 2 lines
Sakari Ailus [email protected]
PATCH v5 2/5 v4l: async: Add V4L2 async documentation to the documentation build
In reply to a message not in the archiveThe V4L2 async wasn't part of the documentation build. Fix this.
--- Documentation/media/kapi/v4l2-async.rst | 3 +++ Documentation/media/kapi/v4l2-core.rst | 1 + 2 files changed, 4 insertions(+) create mode 100644 Documentation/media/kapi/v4l2-async.rst
Documentation/media/kapi/v4l2-async.rst new file +3 -0
@@ -0,0 +1,3 @@ +V4L2 async kAPI +^^^^^^^^^^^^^^^ +.. kernel-doc:: include/media/v4l2-async.h
Documentation/media/kapi/v4l2-core.rst +1 -0
@@ -19,6 +19,7 @@ Video4Linux devices v4l2-mc v4l2-mediabus v4l2-mem2mem + v4l2-async v4l2-fwnode v4l2-rect v4l2-tuner
Signature · 6 lines
2.11.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v5 2/5] v4l: async: Add V4L2 async documentation to the documentation build
Hi Sakari,
Thanks for your patch.
Quoted 3 lines · Sakari Ailus
The V4L2 async wasn't part of the documentation build. Fix this.
Signed-off-by Sakari Ailus <[email protected]>
Quoted 30 lines
--- Documentation/media/kapi/v4l2-async.rst | 3 +++ Documentation/media/kapi/v4l2-core.rst | 1 + 2 files changed, 4 insertions(+) create mode 100644 Documentation/media/kapi/v4l2-async.rstDocumentation/media/kapi/v4l2-async.rst new file +3
-0@@ -0,0 +1,3 @@ +V4L2 async kAPI +^^^^^^^^^^^^^^^ +.. kernel-doc:: include/media/v4l2-async.hDocumentation/media/kapi/v4l2-core.rst +1
-0@@ -19,6 +19,7 @@ Video4Linux devices v4l2-mc v4l2-mediabus v4l2-mem2mem + v4l2-async v4l2-fwnode v4l2-rect v4l2-tunerSignature · 1 line
2.11.0
Signature · 2 lines
Regards, Niklas Söderlund
PATCH v5 4/5 v4l: fwnode: Support generic parsing of graph endpoints in a device
In reply to a message not in the archiveThe current practice is that drivers iterate over their endpoints and parse each endpoint separately. This is very similar in a number of drivers, implement a generic function for the job. Driver specific matters can be taken into account in the driver specific callback.
Convert the omap3isp as an example.
--- drivers/media/platform/omap3isp/isp.c | 115 ++++++++++------------------- drivers/media/platform/omap3isp/isp.h | 5 +- drivers/media/v4l2-core/v4l2-async.c | 16 +++++ drivers/media/v4l2-core/v4l2-fwnode.c | 132 ++++++++++++++++++++++++++++++++++ include/media/v4l2-async.h | 20 +++++- include/media/v4l2-fwnode.h | 39 ++++++++++ 6 files changed, 242 insertions(+), 85 deletions(-)
drivers/media/platform/omap3isp/isp.c +36 -79
@@ -2001,6 +2001,7 @@ static int isp_remove(struct platform_device *pdev)__omap3isp_put (isp, false);media_entity_enum_cleanup (&isp->crashed); +v4l2_async_notifier_release (&isp->notifier);return 0 ; }
@@ -2011,44 +2012,41 @@ enum isp_of_phy { ISP_OF_PHY_CSIPHY2, }; -static int isp_fwnode_parse (struct device *dev,struct fwnode_handle *fwnode, -struct isp_async_subdev *isd) +static int isp_fwnode_parse (struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd) { +struct isp_async_subdev *isd = + container_of(asd, struct isp_async_subdev, asd);struct isp_bus_cfg *buscfg = &isd->bus; -struct v4l2_fwnode_endpoint vep; -unsigned int i; -int ret;bool csi1 = false; - - ret =v4l2_fwnode_endpoint_parse (fwnode, &vep); -if (ret) -return ret; +unsigned int i;dev_dbg (dev,"parsing endpoint %pOF, interface %u\n" , -to_of_node (fwnode), vep.base.port); +to_of_node (vep->base.local_fwnode), vep->base.port); -switch (vep.base.port) { +switch (vep->base.port) {case ISP_OF_PHY_PARALLEL : buscfg->interface =ISP_INTERFACE_PARALLEL ; buscfg->bus.parallel.data_lane_shift = - vep.bus.parallel.data_shift; + vep->bus.parallel.data_shift; buscfg->bus.parallel.clk_pol = - !!(vep.bus.parallel.flags + !!(vep->bus.parallel.flags &V4L2_MBUS_PCLK_SAMPLE_FALLING ); buscfg->bus.parallel.hs_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW ); buscfg->bus.parallel.vs_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW ); buscfg->bus.parallel.fld_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_FIELD_EVEN_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_FIELD_EVEN_LOW ); buscfg->bus.parallel.data_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_DATA_ACTIVE_LOW ); - buscfg->bus.parallel.bt656 = vep.bus_type ==V4L2_MBUS_BT656 ; + !!(vep->bus.parallel.flags &V4L2_MBUS_DATA_ACTIVE_LOW ); + buscfg->bus.parallel.bt656 = vep->bus_type ==V4L2_MBUS_BT656 ;break ;case ISP_OF_PHY_CSIPHY1 :case ISP_OF_PHY_CSIPHY2 : -switch (vep.bus_type) { +switch (vep->bus_type) {case V4L2_MBUS_CCP2 :case V4L2_MBUS_CSI1 :dev_dbg (dev,"CSI-1/CCP-2 configuration\n" );
@@ -2060,11 +2058,11 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode,break ;default :dev_err (dev,"unsupported bus type %u\n" , - vep.bus_type); + vep->bus_type);return -EINVAL ; } -switch (vep.base.port) { +switch (vep->base.port) {case ISP_OF_PHY_CSIPHY1 :if (csi1) buscfg->interface =ISP_INTERFACE_CCP2B_PHY1 ;
@@ -2080,47 +2078,47 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode, }if (csi1) { buscfg->bus.ccp2.lanecfg.clk.pos = - vep.bus.mipi_csi1.clock_lane; + vep->bus.mipi_csi1.clock_lane; buscfg->bus.ccp2.lanecfg.clk.pol = - vep.bus.mipi_csi1.lane_polarity[0 ]; + vep->bus.mipi_csi1.lane_polarity[0 ];dev_dbg (dev,"clock lane polarity %u, pos %u\n" , buscfg->bus.ccp2.lanecfg.clk.pol, buscfg->bus.ccp2.lanecfg.clk.pos); buscfg->bus.ccp2.lanecfg.data[0 ].pos = - vep.bus.mipi_csi1.data_lane; + vep->bus.mipi_csi1.data_lane; buscfg->bus.ccp2.lanecfg.data[0 ].pol = - vep.bus.mipi_csi1.lane_polarity[1 ]; + vep->bus.mipi_csi1.lane_polarity[1 ];dev_dbg (dev,"data lane polarity %u, pos %u\n" , buscfg->bus.ccp2.lanecfg.data[0 ].pol, buscfg->bus.ccp2.lanecfg.data[0 ].pos); buscfg->bus.ccp2.strobe_clk_pol = - vep.bus.mipi_csi1.clock_inv; - buscfg->bus.ccp2.phy_layer = vep.bus.mipi_csi1.strobe; + vep->bus.mipi_csi1.clock_inv; + buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe; buscfg->bus.ccp2.ccp2_mode = - vep.bus_type ==V4L2_MBUS_CCP2 ; + vep->bus_type ==V4L2_MBUS_CCP2 ; buscfg->bus.ccp2.vp_clk_pol =1 ; buscfg->bus.ccp2.crc =1 ; }else { buscfg->bus.csi2.lanecfg.clk.pos = - vep.bus.mipi_csi2.clock_lane; + vep->bus.mipi_csi2.clock_lane; buscfg->bus.csi2.lanecfg.clk.pol = - vep.bus.mipi_csi2.lane_polarities[0 ]; + vep->bus.mipi_csi2.lane_polarities[0 ];dev_dbg (dev,"clock lane polarity %u, pos %u\n" , buscfg->bus.csi2.lanecfg.clk.pol, buscfg->bus.csi2.lanecfg.clk.pos); buscfg->bus.csi2.num_data_lanes = - vep.bus.mipi_csi2.num_data_lanes; + vep->bus.mipi_csi2.num_data_lanes;for (i =0 ; i < buscfg->bus.csi2.num_data_lanes; i++) { buscfg->bus.csi2.lanecfg.data[i].pos = - vep.bus.mipi_csi2.data_lanes[i]; + vep->bus.mipi_csi2.data_lanes[i]; buscfg->bus.csi2.lanecfg.data[i].pol = - vep.bus.mipi_csi2.lane_polarities[i +1 ]; + vep->bus.mipi_csi2.lane_polarities[i +1 ]; dev_dbg(dev, "data lane %u polarity %u, pos %u\n", i, buscfg->bus.csi2.lanecfg.data[i].pol,
@@ -2137,57 +2135,13 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode,default :dev_warn (dev,"%pOF: invalid interface %u\n" , -to_of_node (fwnode), vep.base.port); +to_of_node (vep->base.local_fwnode), vep->base.port);return -EINVAL ; }return 0 ; } -static int isp_fwnodes_parse (struct device *dev, -struct v4l2_async_notifier *notifier) -{ -struct fwnode_handle *fwnode =NULL ; - - notifier->subdevs =devm_kcalloc ( - dev,ISP_MAX_SUBDEVS ,sizeof (*notifier->subdevs),GFP_KERNEL ); -if (!notifier->subdevs) -return -ENOMEM ; - -while (notifier->num_subdevs <ISP_MAX_SUBDEVS && - (fwnode =fwnode_graph_get_next_endpoint ( -of_fwnode_handle (dev->of_node), fwnode))) { -struct isp_async_subdev *isd; - - isd =devm_kzalloc (dev,sizeof (*isd),GFP_KERNEL ); -if (!isd) - goto error; - -if (isp_fwnode_parse (dev, fwnode, isd)) { -devm_kfree (dev, isd); -continue ; - } - - notifier->subdevs[notifier->num_subdevs] = &isd->asd; - - isd->asd.match.fwnode.fwnode = -fwnode_graph_get_remote_port_parent (fwnode); -if (!isd->asd.match.fwnode.fwnode) { -dev_warn (dev,"bad remote port parent\n" ); - goto error; - } - - isd->asd.match_type =V4L2_ASYNC_MATCH_FWNODE ; - notifier->num_subdevs++; - } - -return notifier->num_subdevs; - -error: -fwnode_handle_put (fwnode); -return -EINVAL ; -} -static int isp_subdev_notifier_complete (struct v4l2_async_notifier *async) { struct isp_device *isp = container_of(async, struct isp_device,
@@ -2256,7 +2210,9 @@ static int isp_probe(struct platform_device *pdev)if (ret)return ret; - ret =isp_fwnodes_parse (&pdev->dev, &isp->notifier); + ret =v4l2_async_notifier_parse_fwnode_endpoints ( + &pdev->dev, &isp->notifier,sizeof (struct isp_async_subdev ), + isp_fwnode_parse);if (ret <0 )return ret;
@@ -2407,6 +2363,7 @@ static int isp_probe(struct platform_device *pdev)__omap3isp_put (isp, false); error:mutex_destroy (&isp->isp_mutex); +v4l2_async_notifier_release (&isp->notifier);return ret; }
drivers/media/platform/omap3isp/isp.h +1 -4
@@ -220,14 +220,11 @@ struct isp_device {unsigned int sbl_resources;unsigned int subclk_resources; - -#define ISP_MAX_SUBDEVS 8 -struct v4l2_subdev *subdevs[ISP_MAX_SUBDEVS ]; };struct isp_async_subdev { -struct isp_bus_cfg bus;struct v4l2_async_subdev asd; +struct isp_bus_cfg bus; };#define v4l2_subdev_to_bus_cfg (sd) \
drivers/media/v4l2-core/v4l2-async.c +16 -0
@@ -22,6 +22,7 @@#include <media/v4l2-async.h> #include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> static bool match_i2c (struct v4l2_subdev *sd,struct v4l2_async_subdev *asd)
@@ -278,6 +279,21 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) }EXPORT_SYMBOL (v4l2_async_notifier_unregister); +void v4l2_async_notifier_release (struct v4l2_async_notifier *notifier) +{ +unsigned int i; + +if (!notifier->max_subdevs) +return ; + +for (i =0 ; i < notifier->num_subdevs; i++) +kfree (notifier->subdevs[i]); + +kvfree (notifier->subdevs); + notifier->max_subdevs =0 ; +} +EXPORT_SYMBOL_GPL (v4l2_async_notifier_release); +int v4l2_async_register_subdev (struct v4l2_subdev *sd) {struct v4l2_async_notifier *notifier;
drivers/media/v4l2-core/v4l2-fwnode.c +132 -0
150 lines not on this page · raw message
include/media/v4l2-async.h +18 -2
@@ -18,7 +18,6 @@ struct device;struct device_node ;struct v4l2_device ;struct v4l2_subdev ; -struct v4l2_async_notifier ;/* A random max subdevice number, used to allocate an array on stack */ #define V4L2_MAX_SUBDEVS 128U
@@ -78,7 +77,8 @@ struct v4l2_async_subdev {/** * struct v4l2_async_notifier - v4l2_device notifier data * -* @num_subdevs: number of subdevices +* @num_subdevs: number of subdevices used in subdevs array +* @max_subdevs: number of subdevices allocated in subdevs array * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers
@@ -90,6 +90,7 @@ struct v4l2_async_subdev {*/ struct v4l2_async_notifier {unsigned int num_subdevs; +unsigned int max_subdevs;struct v4l2_async_subdev **subdevs;struct v4l2_device *v4l2_dev;struct list_head waiting;
@@ -121,6 +122,21 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,void v4l2_async_notifier_unregister (struct v4l2_async_notifier *notifier);/** +* v4l2_async_notifier_release - release notifier resources +* @notifier: pointer to &struct v4l2_async_notifier +* +* Release memory resources related to a notifier, including the async +* sub-devices allocated for the purposes of the notifier. The user is +* responsible for releasing the notifier's resources after calling +* @v4l2_async_notifier_parse_fwnode_endpoints. +* +* There is no harm from calling v4l2_async_notifier_release in other +* cases as long as its memory has been zeroed after it has been +* allocated. +*/ +void v4l2_async_notifier_release (struct v4l2_async_notifier *notifier); + +/** * v4l2_async_register_subdev - registers a sub-device to the asynchronous * subdevice framework *
include/media/v4l2-fwnode.h +39 -0
@@ -25,6 +25,8 @@#include <media/v4l2-mediabus.h> struct fwnode_handle ; +struct v4l2_async_notifier ; +struct v4l2_async_subdev ;#define V4L2_FWNODE_CSI2_MAX_DATA_LANES 4
@@ -201,4 +203,41 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode,*/ void v4l2_fwnode_put_link (struct v4l2_fwnode_link *link); +/** +* v4l2_async_notifier_parse_fwnode_endpoints - Parse V4L2 fwnode endpoints in a +* device node +* @dev: @struct device pointer +* @notifier: pointer to &struct v4l2_async_notifier +* @asd_struct_size: size of the driver's async sub-device struct, including +* sizeof(struct v4l2_async_subdev). The &struct +* v4l2_async_subdev shall be the first member of +* the driver's async sub-device struct, i.e. both +* begin at the same memory address. +* @parse_single: driver's callback function called on each V4L2 fwnode endpoint +* +* Allocate async sub-device array and sub-devices for each fwnode endpoint, +* parse the related fwnode endpoints and finally call driver's callback +* function to that V4L2 fwnode endpoint. +* +* The function may not be called on a registered notifier. +* +* Once the user has called this function, the resources released by it need to +* be released by callin v4l2_async_notifier_release after the notifier has been +* unregistered and the sub-devices are no longer in use. +* +* A driver supporting fwnode (currently Devicetree and ACPI) should call this +* function as part of its probe function before it registers the notifier. +* +* Return: %0 on success, including when no async sub-devices are found +* %-ENOMEM if memory allocation failed +* %-EINVAL if graph or endpoint parsing failed +* Other error codes as returned by @parse_single +*/ +int v4l2_async_notifier_parse_fwnode_endpoints ( +struct device *dev,struct v4l2_async_notifier *notifier, +size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)); +#endif /* _V4L2_FWNODE_H */
Signature · 6 lines
2.11.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): 20170829110313.19538-5-sakari.ailus-VuQAYsv1563Yd54FQh9/[email protected]
Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
Hi Sakari,
Thank you for the patch.
Quoted 323 lines · On Tuesday, 29 August 2017 14:03:12 EEST Sakari Ailus wrote:
The current practice is that drivers iterate over their endpoints and parse each endpoint separately. This is very similar in a number of drivers, implement a generic function for the job. Driver specific matters can be taken into account in the driver specific callback.
Convert the omap3isp as an example.
Signed-off-by Sakari Ailus <[email protected]>--- drivers/media/platform/omap3isp/isp.c | 115 ++++++++++------------------- drivers/media/platform/omap3isp/isp.h | 5 +- drivers/media/v4l2-core/v4l2-async.c | 16 +++++ drivers/media/v4l2-core/v4l2-fwnode.c | 132 ++++++++++++++++++++++++++++++++++ include/media/v4l2-async.h | 20 +++++- include/media/v4l2-fwnode.h | 39 ++++++++++ 6 files changed, 242 insertions(+), 85 deletions(-)(quoted diff) +0
-0
b/drivers/media/platform/omap3isp/isp.cindex1a428fe9f070..a546cf774d40100644drivers/media/platform/omap3isp/isp.c +32
-77@@ -2001,6 +2001,7 @@ static int isp_remove(struct platform_device *pdev)__omap3isp_put (isp, false);media_entity_enum_cleanup (&isp->crashed); +v4l2_async_notifier_release (&isp->notifier);return 0 ; }@@ -2011,44 +2012,41 @@ enum isp_of_phy { ISP_OF_PHY_CSIPHY2, }; -static int isp_fwnode_parse (struct device *dev,struct fwnode_handle *fwnode, - struct isp_async_subdev *isd) +static int isp_fwnode_parse (struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd) { +struct isp_async_subdev *isd = + container_of(asd, struct isp_async_subdev, asd);struct isp_bus_cfg *buscfg = &isd->bus; -struct v4l2_fwnode_endpoint vep; -unsigned int i; -int ret;bool csi1 = false; - - ret =v4l2_fwnode_endpoint_parse (fwnode, &vep); -if (ret) -return ret; +unsigned int i;dev_dbg (dev,"parsing endpoint %pOF, interface %u\n" , -to_of_node (fwnode), vep.base.port); +to_of_node (vep->base.local_fwnode), vep->base.port); -switch (vep.base.port) { +switch (vep->base.port) {case ISP_OF_PHY_PARALLEL : buscfg->interface =ISP_INTERFACE_PARALLEL ; buscfg->bus.parallel.data_lane_shift = - vep.bus.parallel.data_shift; + vep->bus.parallel.data_shift; buscfg->bus.parallel.clk_pol = - !!(vep.bus.parallel.flags + !!(vep->bus.parallel.flags &V4L2_MBUS_PCLK_SAMPLE_FALLING ); buscfg->bus.parallel.hs_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW ); buscfg->bus.parallel.vs_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW ); buscfg->bus.parallel.fld_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_FIELD_EVEN_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_FIELD_EVEN_LOW ); buscfg->bus.parallel.data_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_DATA_ACTIVE_LOW ); - buscfg->bus.parallel.bt656 = vep.bus_type ==V4L2_MBUS_BT656 ; + !!(vep->bus.parallel.flags &V4L2_MBUS_DATA_ACTIVE_LOW ); + buscfg->bus.parallel.bt656 = vep->bus_type ==V4L2_MBUS_BT656 ;break ;case ISP_OF_PHY_CSIPHY1 :case ISP_OF_PHY_CSIPHY2 : -switch (vep.bus_type) { +switch (vep->bus_type) {case V4L2_MBUS_CCP2 :case V4L2_MBUS_CSI1 :dev_dbg (dev,"CSI-1/CCP-2 configuration\n" );@@ -2060,11 +2058,11 @@ static int isp_fwnode_parse(struct device *dev,struct fwnode_handle *fwnode, break;default :dev_err (dev,"unsupported bus type %u\n" , - vep.bus_type); + vep->bus_type);return -EINVAL ; } -switch (vep.base.port) { +switch (vep->base.port) {case ISP_OF_PHY_CSIPHY1 :if (csi1) buscfg->interface =ISP_INTERFACE_CCP2B_PHY1 ;@@ -2080,47 +2078,47 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode, }if (csi1) { buscfg->bus.ccp2.lanecfg.clk.pos = - vep.bus.mipi_csi1.clock_lane; + vep->bus.mipi_csi1.clock_lane; buscfg->bus.ccp2.lanecfg.clk.pol = - vep.bus.mipi_csi1.lane_polarity[0 ]; + vep->bus.mipi_csi1.lane_polarity[0 ];dev_dbg (dev,"clock lane polarity %u, pos %u\n" , buscfg->bus.ccp2.lanecfg.clk.pol, buscfg->bus.ccp2.lanecfg.clk.pos); buscfg->bus.ccp2.lanecfg.data[0 ].pos = - vep.bus.mipi_csi1.data_lane; + vep->bus.mipi_csi1.data_lane; buscfg->bus.ccp2.lanecfg.data[0 ].pol = - vep.bus.mipi_csi1.lane_polarity[1 ]; + vep->bus.mipi_csi1.lane_polarity[1 ];dev_dbg (dev,"data lane polarity %u, pos %u\n" , buscfg->bus.ccp2.lanecfg.data[0 ].pol, buscfg->bus.ccp2.lanecfg.data[0 ].pos); buscfg->bus.ccp2.strobe_clk_pol = - vep.bus.mipi_csi1.clock_inv; - buscfg->bus.ccp2.phy_layer = vep.bus.mipi_csi1.strobe; + vep->bus.mipi_csi1.clock_inv; + buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe; buscfg->bus.ccp2.ccp2_mode = - vep.bus_type ==V4L2_MBUS_CCP2 ; + vep->bus_type ==V4L2_MBUS_CCP2 ; buscfg->bus.ccp2.vp_clk_pol =1 ; buscfg->bus.ccp2.crc =1 ; }else { buscfg->bus.csi2.lanecfg.clk.pos = - vep.bus.mipi_csi2.clock_lane; + vep->bus.mipi_csi2.clock_lane; buscfg->bus.csi2.lanecfg.clk.pol = - vep.bus.mipi_csi2.lane_polarities[0 ]; + vep->bus.mipi_csi2.lane_polarities[0 ];dev_dbg (dev,"clock lane polarity %u, pos %u\n" , buscfg->bus.csi2.lanecfg.clk.pol, buscfg->bus.csi2.lanecfg.clk.pos); buscfg->bus.csi2.num_data_lanes = - vep.bus.mipi_csi2.num_data_lanes; + vep->bus.mipi_csi2.num_data_lanes;for (i =0 ; i < buscfg->bus.csi2.num_data_lanes; i++) { buscfg->bus.csi2.lanecfg.data[i].pos = - vep.bus.mipi_csi2.data_lanes[i]; + vep->bus.mipi_csi2.data_lanes[i]; buscfg->bus.csi2.lanecfg.data[i].pol = - vep.bus.mipi_csi2.lane_polarities[i +1 ]; + vep->bus.mipi_csi2.lane_polarities[i +1 ]; dev_dbg(dev, "data lane %u polarity %u, pos %u\n", i, buscfg->bus.csi2.lanecfg.data[i].pol,@@ -2137,57 +2135,13 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode, default: dev_warn(dev, "%pOF: invalid interface %u\n", - to_of_node(fwnode), vep.base.port); + to_of_node(vep->base.local_fwnode), vep->base.port);return -EINVAL ; }return 0 ; } -static int isp_fwnodes_parse (struct device *dev, -struct v4l2_async_notifier *notifier) -{ -struct fwnode_handle *fwnode =NULL ; - - notifier->subdevs =devm_kcalloc ( - dev,ISP_MAX_SUBDEVS ,sizeof (*notifier->subdevs),GFP_KERNEL ); -if (!notifier->subdevs) -return -ENOMEM ; - -while (notifier->num_subdevs <ISP_MAX_SUBDEVS && - (fwnode =fwnode_graph_get_next_endpoint ( -of_fwnode_handle (dev->of_node), fwnode))) { -struct isp_async_subdev *isd; - - isd =devm_kzalloc (dev,sizeof (*isd),GFP_KERNEL ); -if (!isd) - goto error; - -if (isp_fwnode_parse (dev, fwnode, isd)) { -devm_kfree (dev, isd); -continue ; - } - - notifier->subdevs[notifier->num_subdevs] = &isd->asd; - - isd->asd.match.fwnode.fwnode = -fwnode_graph_get_remote_port_parent (fwnode); -if (!isd->asd.match.fwnode.fwnode) { -dev_warn (dev,"bad remote port parent\n" ); - goto error; - } - - isd->asd.match_type =V4L2_ASYNC_MATCH_FWNODE ; - notifier->num_subdevs++; - } - -return notifier->num_subdevs; - -error: -fwnode_handle_put (fwnode); -return -EINVAL ; -} -static int isp_subdev_notifier_complete (struct v4l2_async_notifier *async) {drivers/media/platform/omap3isp/isp.c +4
-1struct isp_device *isp = container_of(async, struct isp_device,@@ -2256,7 +2210,9 @@ static int isp_probe(struct platform_device *pdev)if (ret)return ret; - ret =isp_fwnodes_parse (&pdev->dev, &isp->notifier); + ret =v4l2_async_notifier_parse_fwnode_endpoints ( + &pdev->dev, &isp->notifier,sizeof (struct isp_async_subdev ), + isp_fwnode_parse);if (ret <0 )return ret;@@ -2407,6 +2363,7 @@ static int isp_probe(struct platform_device *pdev)__omap3isp_put (isp, false); error:mutex_destroy (&isp->isp_mutex); +v4l2_async_notifier_release (&isp->notifier);return ret; }(quoted diff) +0
-0
b/drivers/media/platform/omap3isp/isp.hindexe528df6efc09..8b9043db94b3100644drivers/media/platform/omap3isp/isp.h +1
-4@@ -220,14 +220,11 @@ struct isp_device {unsigned int sbl_resources;unsigned int subclk_resources; - -#define ISP_MAX_SUBDEVS 8 -struct v4l2_subdev *subdevs[ISP_MAX_SUBDEVS ]; };struct isp_async_subdev { -struct isp_bus_cfg bus;struct v4l2_async_subdev asd; +struct isp_bus_cfg bus; };#define v4l2_subdev_to_bus_cfg (sd) \(quoted diff) +0
-0
b/drivers/media/v4l2-core/v4l2-async.cindex851f128eba22..c490acf5ae82100644drivers/media/v4l2-core/v4l2-async.c +1
-0@@ -22,6 +22,7 @@#include <media/v4l2-async.h> #include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev*asd) @@ -278,6 +279,21 @@ void
v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)}
EXPORT_SYMBOL(v4l2_async_notifier_unregister);drivers/media/v4l2-core/v4l2-async.c +15
-0+void v4l2_async_notifier_release (struct v4l2_async_notifier *notifier) +{ +unsigned int i; + +if (!notifier->max_subdevs) +return ; + +for (i =0 ; i < notifier->num_subdevs; i++) +kfree (notifier->subdevs[i]); + +kvfree (notifier->subdevs); + notifier->max_subdevs =0 ; +} +EXPORT_SYMBOL_GPL (v4l2_async_notifier_release); +int v4l2_async_register_subdev (struct v4l2_subdev *sd) {struct v4l2_async_notifier *notifier;(quoted diff) +0
-0
b/drivers/media/v4l2-core/v4l2-fwnode.cindex706f9e7b90f1..39a587c6992a100644drivers/media/v4l2-core/v4l2-fwnode.c +2
-0@@ -19,6 +19,7 @@*/ #include <linux/acpi.h> #include <linux/kernel.h> +#include <linux/mm.h> #include <linux/module.h> #include <linux/of.h> #include <linux/property.h> @@ -26,6 +27,7 @@#include <linux/string.h> #include <linux/types.h> +#include <media/v4l2-async.h> #include <media/v4l2-fwnode.h> enum v4l2_fwnode_bus_type {
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link · line 319
Quoted 5 lines
drivers/media/v4l2-core/v4l2-fwnode.c +2
-0@@ -313,6 +315,136 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) }EXPORT_SYMBOL_GPL (v4l2_fwnode_put_link); +static int notifier_realloc (struct v4l2_async_notifier *notifier, +unsigned int max_subdevs)
I'd prefix static functions with v4l2_async_ to avoid namespace clashes.
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 329
Quoted 10 lines
drivers/media/v4l2-core/v4l2-fwnode.c +10
-0+{ +struct v4l2_async_subdev **subdevs; +unsigned int i; + +if (max_subdevs <= notifier->max_subdevs) +return 0 ; + + subdevs =kvmalloc_array ( + max_subdevs,sizeof (*notifier->subdevs), +GFP_KERNEL | __GFP_ZERO);
Should it be mentioned in the documentation that the address of the subdevs array will change during parsing and should not be stored by drivers ? It might be overkill.
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 335
Quoted 6 lines
drivers/media/v4l2-core/v4l2-fwnode.c +6
-0+if (!subdevs) +return -ENOMEM ; + +if (notifier->subdevs) { +for (i =0 ; i < notifier->num_subdevs; i++) + subdevs[i] = notifier->subdevs[i];
To answer your previous question, yes, I would find
memcpy(subdevs, notifier->subdevs, sizeof(*subdevs) * num_subdevs);
easier to read :-)
Quoted 23 lines
drivers/media/v4l2-core/v4l2-fwnode.c +23
-0+kvfree (notifier->subdevs); + } + + notifier->subdevs = subdevs; + notifier->max_subdevs = max_subdevs; + +return 0 ; +} + +static int parse_endpoint ( +struct device *dev,struct v4l2_async_notifier *notifier, +struct fwnode_handle *endpoint,unsigned int asd_struct_size, +int (*parse_single)(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 ;
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 369
Quoted 10 lines
drivers/media/v4l2-core/v4l2-fwnode.c +10
-0+ + 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; + } + +/* Ignore endpoints the parsing of which failed. */
You don't ignore them anymore, the comment should be updated.
Quoted 112 lines
drivers/media/v4l2-core/v4l2-fwnode.c +78
-0+ 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_single (dev, vep, asd); +v4l2_fwnode_endpoint_free (vep); +if (ret) { +dev_warn (dev,"driver could not parse endpoint (%d)\n" , ret); + goto out_err; + } + + asd->match_type =V4L2_ASYNC_MATCH_FWNODE ; + notifier->subdevs[notifier->num_subdevs] = asd; + notifier->num_subdevs++; + +return 0 ; + +out_err: +fwnode_handle_put (asd->match.fwnode.fwnode); +kfree (asd); + +return ret; +} + +int v4l2_async_notifier_parse_fwnode_endpoints ( +struct device *dev,struct v4l2_async_notifier *notifier, +size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)) +{ +struct fwnode_handle *fwnode =NULL ; +unsigned int max_subdevs = notifier->max_subdevs; +int ret; + +if (asd_struct_size <sizeof (struct v4l2_async_subdev ) || + notifier->v4l2_dev) +return -EINVAL ; + +for (fwnode =NULL ; (fwnode =fwnode_graph_get_next_endpoint ( +dev_fwnode (dev), fwnode)); ) +if (fwnode_device_is_available ( +fwnode_graph_get_port_parent (fwnode))) + max_subdevs++; + +/* No subdevs to add? Return here. */ +if (max_subdevs == notifier->max_subdevs) +return 0 ; + + ret =notifier_realloc (notifier, max_subdevs); +if (ret) +return ret; + +for (fwnode =NULL ; (fwnode =fwnode_graph_get_next_endpoint ( +dev_fwnode (dev), fwnode)); ) { +if (!fwnode_device_is_available ( +fwnode_graph_get_port_parent (fwnode))) +continue ; + +if (WARN_ON (notifier->num_subdevs >= notifier->max_subdevs)) +break ; + + ret =parse_endpoint (dev, notifier, fwnode, asd_struct_size, + parse_single); +if (ret <0 ) +break ; + } + +fwnode_handle_put (fwnode); + +return ret; +} +EXPORT_SYMBOL_GPL (v4l2_async_notifier_parse_fwnode_endpoints); +MODULE_LICENSE ("GPL" );MODULE_AUTHOR ("Sakari Ailus <[email protected]>" );MODULE_AUTHOR ("Sylwester Nawrocki <[email protected]>" );include/media/v4l2-async.h +3
-2@@ -18,7 +18,6 @@ struct device;struct device_node ;struct v4l2_device ;struct v4l2_subdev ; -struct v4l2_async_notifier ;/* A random max subdevice number, used to allocate an array on stack */ #define V4L2_MAX_SUBDEVS 128U@@ -78,7 +77,8 @@ struct v4l2_async_subdev {/** * struct v4l2_async_notifier - v4l2_device notifier data * -* @num_subdevs: number of subdevices +* @num_subdevs: number of subdevices used in subdevs array +* @max_subdevs: number of subdevices allocated in subdevs array * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers @@ -90,6 +90,7 @@ struct v4l2_async_subdev {*/ struct v4l2_async_notifier {unsigned int num_subdevs; +unsigned int max_subdevs;struct v4l2_async_subdev **subdevs;struct v4l2_device *v4l2_dev;struct list_head waiting;
include/media/v4l2-async.h int v4l2_async_notifier_register(struct v4l2_device · line 127
Quoted 6 lines
include/media/v4l2-async.h +2
-0@@ -121,6 +122,21 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);/** +* v4l2_async_notifier_release - release notifier resources +* @notifier: pointer to &struct v4l2_async_notifier
That's quite obvious given the type of the argument. It would be much more useful to tell which notifier pointer this function expects (although in this case it should be obvious too): "(pointer to )?the notifier whose resources will be released".
include/media/v4l2-async.h int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, · line 135
Quoted 9 lines
include/media/v4l2-async.h +9
-0+* +* Release memory resources related to a notifier, including the async +* sub-devices allocated for the purposes of the notifier. The user is +* responsible for releasing the notifier's resources after calling +* @v4l2_async_notifier_parse_fwnode_endpoints. +* +* There is no harm from calling v4l2_async_notifier_release in other +* cases as long as its memory has been zeroed after it has been +* allocated.
Zeroing the memory is pretty much a requirement, as v4l2_async_notifier_parse_fwnode_endpoints() won't operate correctly if memory contains random data anyway. Maybe we should introduce v4l2_async_notifier_init() and make v4l2_async_notifier_release() mandatory, but that's out of scope for this patch.
Quoted 26 lines
include/media/v4l2-async.h +4
-0+*/ +void v4l2_async_notifier_release (struct v4l2_async_notifier *notifier); + +/** * v4l2_async_register_subdev - registers a sub-device to the asynchronous * subdevice framework *
include/media/v4l2-fwnode.h +4
-0@@ -25,6 +25,8 @@#include <media/v4l2-mediabus.h> struct fwnode_handle ; +struct v4l2_async_notifier ; +struct v4l2_async_subdev ;#define V4L2_FWNODE_CSI2_MAX_DATA_LANES 4@@ -201,4 +203,41 @@ int v4l2_fwnode_parse_link(struct fwnode_handle*fwnode, */ void v4l2_fwnode_put_link (struct v4l2_fwnode_link *link); +/** +* v4l2_async_notifier_parse_fwnode_endpoints - Parse V4L2 fwnode endpoints
Quoted 3 lines
in a
+ * device node
+ * @dev: @struct device pointer
Similarly to my previous comment (and my comments to v3), you should tell which device the function expects.
Quoted 7 lines
+ * @notifier: pointer to &struct
v4l2_async_notifier
+ * @asd_struct_size: size of the driver's async sub-device struct, including+ * sizeof(struct v4l2_async_subdev). The &struct + * v4l2_async_subdev shall be the first member of + * the driver's async sub-device struct, i.e. both + * begin at the same memory address.
Should this be documented in the kerneldoc of the v4l2_async_subdev structure ?
Quoted 7 lines
+ * @parse_single: driver's callback function called on each V4L2 fwnode endpoint
+ *
+ * Allocate async sub-device array and sub-devices for each fwnode endpoint,
+ * parse the related fwnode endpoints and finally call driver's callback
+ * function to that V4L2 fwnode endpoint.
I'd document this from the notifier point of view.
"Parse the fwnode endpoints of the @dev device and populate the async sub- devices array of the notifier. The @parse_endpoint callback function is called for each endpoint with the corresponding async sub-device pointer to let the caller initialize the driver-specific part of the async sub-device structure."
Quoted 1 line
+ * The function may not be called on a registered notifier.
You should mention that the function may be called multiple times on an unregistered notifier.
"The function can be called multiple times to populate the same notifier from endpoints of different @dev devices before registering the notifier. It can't be called anymore once the notifier has been registered."
Quoted 6 lines
+ *
+ * Once the user has called this function, the resources released by it need to
+ * be released by callinv4l2_async_notifier_releaseafter the notifier has been
+ * unregistered and the sub-devices are no longer in use.
"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."
Quoted 18 lines
+ *
+ * A driver supporting fwnode (currently Devicetree and ACPI) should call this
+ * function as part of its probe function before it registers the notifier.
+ *
+ * Return: %0 on success, including when no async sub-devices are found+ * %-ENOMEM if memory allocation failed + * %-EINVAL if graph or endpoint parsing failed + * Other error codes as returned by @parse_single+ */
include/media/v4l2-fwnode.h +7
-0+int v4l2_async_notifier_parse_fwnode_endpoints ( +struct device *dev,struct v4l2_async_notifier *notifier, +size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)); +#endif /* _V4L2_FWNODE_H */
Signature · 3 lines
Regards, Laurent Pinchart
Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
Hi Laurent,
Quoted 3 lines · Laurent Pinchart
Hi Sakari,
Thank you for the patch.
Thanks for the review!
Quoted 328 lines · On Tuesday, 29 August 2017 14:03:12 EEST Sakari Ailus wrote:
Quoted 326 lines
The current practice is that drivers iterate over their endpoints and parse each endpoint separately. This is very similar in a number of drivers, implement a generic function for the job. Driver specific matters can be taken into account in the driver specific callback.
Convert the omap3isp as an example.
--- drivers/media/platform/omap3isp/isp.c | 115 ++++++++++------------------- drivers/media/platform/omap3isp/isp.h | 5 +- drivers/media/v4l2-core/v4l2-async.c | 16 +++++ drivers/media/v4l2-core/v4l2-fwnode.c | 132 ++++++++++++++++++++++++++++++++++ include/media/v4l2-async.h | 20 +++++- include/media/v4l2-fwnode.h | 39 ++++++++++ 6 files changed, 242 insertions(+), 85 deletions(-)(quoted diff) +0
-0
b/drivers/media/platform/omap3isp/isp.cindex1a428fe9f070..a546cf774d40100644drivers/media/platform/omap3isp/isp.c +32
-77@@ -2001,6 +2001,7 @@ static int isp_remove(struct platform_device *pdev)__omap3isp_put (isp, false);media_entity_enum_cleanup (&isp->crashed); +v4l2_async_notifier_release (&isp->notifier);return 0 ; }@@ -2011,44 +2012,41 @@ enum isp_of_phy { ISP_OF_PHY_CSIPHY2, }; -static int isp_fwnode_parse (struct device *dev,struct fwnode_handle *fwnode, - struct isp_async_subdev *isd) +static int isp_fwnode_parse (struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd) { +struct isp_async_subdev *isd = + container_of(asd, struct isp_async_subdev, asd);struct isp_bus_cfg *buscfg = &isd->bus; -struct v4l2_fwnode_endpoint vep; -unsigned int i; -int ret;bool csi1 = false; - - ret =v4l2_fwnode_endpoint_parse (fwnode, &vep); -if (ret) -return ret; +unsigned int i;dev_dbg (dev,"parsing endpoint %pOF, interface %u\n" , -to_of_node (fwnode), vep.base.port); +to_of_node (vep->base.local_fwnode), vep->base.port); -switch (vep.base.port) { +switch (vep->base.port) {case ISP_OF_PHY_PARALLEL : buscfg->interface =ISP_INTERFACE_PARALLEL ; buscfg->bus.parallel.data_lane_shift = - vep.bus.parallel.data_shift; + vep->bus.parallel.data_shift; buscfg->bus.parallel.clk_pol = - !!(vep.bus.parallel.flags + !!(vep->bus.parallel.flags &V4L2_MBUS_PCLK_SAMPLE_FALLING ); buscfg->bus.parallel.hs_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW ); buscfg->bus.parallel.vs_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW ); buscfg->bus.parallel.fld_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_FIELD_EVEN_LOW ); + !!(vep->bus.parallel.flags &V4L2_MBUS_FIELD_EVEN_LOW ); buscfg->bus.parallel.data_pol = - !!(vep.bus.parallel.flags &V4L2_MBUS_DATA_ACTIVE_LOW ); - buscfg->bus.parallel.bt656 = vep.bus_type ==V4L2_MBUS_BT656 ; + !!(vep->bus.parallel.flags &V4L2_MBUS_DATA_ACTIVE_LOW ); + buscfg->bus.parallel.bt656 = vep->bus_type ==V4L2_MBUS_BT656 ;break ;case ISP_OF_PHY_CSIPHY1 :case ISP_OF_PHY_CSIPHY2 : -switch (vep.bus_type) { +switch (vep->bus_type) {case V4L2_MBUS_CCP2 :case V4L2_MBUS_CSI1 :dev_dbg (dev,"CSI-1/CCP-2 configuration\n" );@@ -2060,11 +2058,11 @@ static int isp_fwnode_parse(struct device *dev,struct fwnode_handle *fwnode, break;default :dev_err (dev,"unsupported bus type %u\n" , - vep.bus_type); + vep->bus_type);return -EINVAL ; } -switch (vep.base.port) { +switch (vep->base.port) {case ISP_OF_PHY_CSIPHY1 :if (csi1) buscfg->interface =ISP_INTERFACE_CCP2B_PHY1 ;@@ -2080,47 +2078,47 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode, }if (csi1) { buscfg->bus.ccp2.lanecfg.clk.pos = - vep.bus.mipi_csi1.clock_lane; + vep->bus.mipi_csi1.clock_lane; buscfg->bus.ccp2.lanecfg.clk.pol = - vep.bus.mipi_csi1.lane_polarity[0 ]; + vep->bus.mipi_csi1.lane_polarity[0 ];dev_dbg (dev,"clock lane polarity %u, pos %u\n" , buscfg->bus.ccp2.lanecfg.clk.pol, buscfg->bus.ccp2.lanecfg.clk.pos); buscfg->bus.ccp2.lanecfg.data[0 ].pos = - vep.bus.mipi_csi1.data_lane; + vep->bus.mipi_csi1.data_lane; buscfg->bus.ccp2.lanecfg.data[0 ].pol = - vep.bus.mipi_csi1.lane_polarity[1 ]; + vep->bus.mipi_csi1.lane_polarity[1 ];dev_dbg (dev,"data lane polarity %u, pos %u\n" , buscfg->bus.ccp2.lanecfg.data[0 ].pol, buscfg->bus.ccp2.lanecfg.data[0 ].pos); buscfg->bus.ccp2.strobe_clk_pol = - vep.bus.mipi_csi1.clock_inv; - buscfg->bus.ccp2.phy_layer = vep.bus.mipi_csi1.strobe; + vep->bus.mipi_csi1.clock_inv; + buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe; buscfg->bus.ccp2.ccp2_mode = - vep.bus_type ==V4L2_MBUS_CCP2 ; + vep->bus_type ==V4L2_MBUS_CCP2 ; buscfg->bus.ccp2.vp_clk_pol =1 ; buscfg->bus.ccp2.crc =1 ; }else { buscfg->bus.csi2.lanecfg.clk.pos = - vep.bus.mipi_csi2.clock_lane; + vep->bus.mipi_csi2.clock_lane; buscfg->bus.csi2.lanecfg.clk.pol = - vep.bus.mipi_csi2.lane_polarities[0 ]; + vep->bus.mipi_csi2.lane_polarities[0 ];dev_dbg (dev,"clock lane polarity %u, pos %u\n" , buscfg->bus.csi2.lanecfg.clk.pol, buscfg->bus.csi2.lanecfg.clk.pos); buscfg->bus.csi2.num_data_lanes = - vep.bus.mipi_csi2.num_data_lanes; + vep->bus.mipi_csi2.num_data_lanes;for (i =0 ; i < buscfg->bus.csi2.num_data_lanes; i++) { buscfg->bus.csi2.lanecfg.data[i].pos = - vep.bus.mipi_csi2.data_lanes[i]; + vep->bus.mipi_csi2.data_lanes[i]; buscfg->bus.csi2.lanecfg.data[i].pol = - vep.bus.mipi_csi2.lane_polarities[i +1 ]; + vep->bus.mipi_csi2.lane_polarities[i +1 ]; dev_dbg(dev, "data lane %u polarity %u, pos %u\n", i, buscfg->bus.csi2.lanecfg.data[i].pol,@@ -2137,57 +2135,13 @@ static int isp_fwnode_parse(struct device *dev, struct fwnode_handle *fwnode, default: dev_warn(dev, "%pOF: invalid interface %u\n", - to_of_node(fwnode), vep.base.port); + to_of_node(vep->base.local_fwnode), vep->base.port);return -EINVAL ; }return 0 ; } -static int isp_fwnodes_parse (struct device *dev, -struct v4l2_async_notifier *notifier) -{ -struct fwnode_handle *fwnode =NULL ; - - notifier->subdevs =devm_kcalloc ( - dev,ISP_MAX_SUBDEVS ,sizeof (*notifier->subdevs),GFP_KERNEL ); -if (!notifier->subdevs) -return -ENOMEM ; - -while (notifier->num_subdevs <ISP_MAX_SUBDEVS && - (fwnode =fwnode_graph_get_next_endpoint ( -of_fwnode_handle (dev->of_node), fwnode))) { -struct isp_async_subdev *isd; - - isd =devm_kzalloc (dev,sizeof (*isd),GFP_KERNEL ); -if (!isd) - goto error; - -if (isp_fwnode_parse (dev, fwnode, isd)) { -devm_kfree (dev, isd); -continue ; - } - - notifier->subdevs[notifier->num_subdevs] = &isd->asd; - - isd->asd.match.fwnode.fwnode = -fwnode_graph_get_remote_port_parent (fwnode); -if (!isd->asd.match.fwnode.fwnode) { -dev_warn (dev,"bad remote port parent\n" ); - goto error; - } - - isd->asd.match_type =V4L2_ASYNC_MATCH_FWNODE ; - notifier->num_subdevs++; - } - -return notifier->num_subdevs; - -error: -fwnode_handle_put (fwnode); -return -EINVAL ; -} -static int isp_subdev_notifier_complete (struct v4l2_async_notifier *async) {drivers/media/platform/omap3isp/isp.c +4
-1struct isp_device *isp = container_of(async, struct isp_device,@@ -2256,7 +2210,9 @@ static int isp_probe(struct platform_device *pdev)if (ret)return ret; - ret =isp_fwnodes_parse (&pdev->dev, &isp->notifier); + ret =v4l2_async_notifier_parse_fwnode_endpoints ( + &pdev->dev, &isp->notifier,sizeof (struct isp_async_subdev ), + isp_fwnode_parse);if (ret <0 )return ret;@@ -2407,6 +2363,7 @@ static int isp_probe(struct platform_device *pdev)__omap3isp_put (isp, false); error:mutex_destroy (&isp->isp_mutex); +v4l2_async_notifier_release (&isp->notifier);return ret; }(quoted diff) +0
-0
b/drivers/media/platform/omap3isp/isp.hindexe528df6efc09..8b9043db94b3100644drivers/media/platform/omap3isp/isp.h +1
-4@@ -220,14 +220,11 @@ struct isp_device {unsigned int sbl_resources;unsigned int subclk_resources; - -#define ISP_MAX_SUBDEVS 8 -struct v4l2_subdev *subdevs[ISP_MAX_SUBDEVS ]; };struct isp_async_subdev { -struct isp_bus_cfg bus;struct v4l2_async_subdev asd; +struct isp_bus_cfg bus; };#define v4l2_subdev_to_bus_cfg (sd) \(quoted diff) +0
-0
b/drivers/media/v4l2-core/v4l2-async.cindex851f128eba22..c490acf5ae82100644drivers/media/v4l2-core/v4l2-async.c +1
-0@@ -22,6 +22,7 @@#include <media/v4l2-async.h> #include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev*asd) @@ -278,6 +279,21 @@ void
v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)}
EXPORT_SYMBOL(v4l2_async_notifier_unregister);drivers/media/v4l2-core/v4l2-async.c +15
-0+void v4l2_async_notifier_release (struct v4l2_async_notifier *notifier) +{ +unsigned int i; + +if (!notifier->max_subdevs) +return ; + +for (i =0 ; i < notifier->num_subdevs; i++) +kfree (notifier->subdevs[i]); + +kvfree (notifier->subdevs); + notifier->max_subdevs =0 ; +} +EXPORT_SYMBOL_GPL (v4l2_async_notifier_release); +int v4l2_async_register_subdev (struct v4l2_subdev *sd) {struct v4l2_async_notifier *notifier;(quoted diff) +0
-0
b/drivers/media/v4l2-core/v4l2-fwnode.cindex706f9e7b90f1..39a587c6992a100644drivers/media/v4l2-core/v4l2-fwnode.c +2
-0@@ -19,6 +19,7 @@*/ #include <linux/acpi.h> #include <linux/kernel.h> +#include <linux/mm.h> #include <linux/module.h> #include <linux/of.h> #include <linux/property.h> @@ -26,6 +27,7 @@#include <linux/string.h> #include <linux/types.h> +#include <media/v4l2-async.h> #include <media/v4l2-fwnode.h> enum v4l2_fwnode_bus_type {@@ -313,6 +315,136 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) }EXPORT_SYMBOL_GPL (v4l2_fwnode_put_link);
Quoted 4 lines
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 319
Quoted 2 lines
drivers/media/v4l2-core/v4l2-fwnode.c +2
-0+static int notifier_realloc (struct v4l2_async_notifier *notifier, +unsigned int max_subdevs)I'd prefix static functions with
v4l2_async_to avoid namespace clashes.
I can add that.
Quoted 15 lines
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 329
Quoted 11 lines
drivers/media/v4l2-core/v4l2-fwnode.c +10
-0+{ +struct v4l2_async_subdev **subdevs; +unsigned int i; + +if (max_subdevs <= notifier->max_subdevs) +return 0 ; + + subdevs =kvmalloc_array ( + max_subdevs,sizeof (*notifier->subdevs), +GFP_KERNEL | __GFP_ZERO);Should it be mentioned in the documentation that the address of the subdevs array will change during parsing and should not be stored by drivers ? It might be overkill.
It won't hurt. I'll add that.
Quoted 10 lines
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 335
Quoted 7 lines
drivers/media/v4l2-core/v4l2-fwnode.c +6
-0+if (!subdevs) +return -ENOMEM ; + +if (notifier->subdevs) { +for (i =0 ; i < notifier->num_subdevs; i++) + subdevs[i] = notifier->subdevs[i];To answer your previous question, yes, I would find
Quoted 3 lines
memcpy(subdevs, notifier->subdevs, sizeof(*subdevs) * num_subdevs);easier to read :-)
I don't agree but I don't have a strong opinion about this, I can change it.
Quoted 28 lines
Quoted 28 lines
drivers/media/v4l2-core/v4l2-fwnode.c +27
-0+kvfree (notifier->subdevs); + } + + notifier->subdevs = subdevs; + notifier->max_subdevs = max_subdevs; + +return 0 ; +} + +static int parse_endpoint ( +struct device *dev,struct v4l2_async_notifier *notifier, +struct fwnode_handle *endpoint,unsigned int asd_struct_size, +int (*parse_single)(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.fwnode.fwnode = +fwnode_graph_get_remote_port_parent (endpoint); +if (!asd->match.fwnode.fwnode) {
Quoted 8 lines
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 369
Quoted 6 lines
drivers/media/v4l2-core/v4l2-fwnode.c +6
-0+dev_warn (dev,"bad remote port parent\n" ); + ret = -EINVAL ; + goto out_err; + } + +/* Ignore endpoints the parsing of which failed. */ You don't ignore them anymore, the comment should be updated.
Hmm. I actually intended to do something else about this. :-) As there's a single error code, handling that would need to be done a little bit differently right now.
I'd print a warning and proceed. What do you think?
Even if there's a bad DT endpoint, that shouldn't prevent the rest from working, right?
Quoted 116 lines
Quoted 116 lines
drivers/media/v4l2-core/v4l2-fwnode.c +78
-0+ 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_single (dev, vep, asd); +v4l2_fwnode_endpoint_free (vep); +if (ret) { +dev_warn (dev,"driver could not parse endpoint (%d)\n" , ret); + goto out_err; + } + + asd->match_type =V4L2_ASYNC_MATCH_FWNODE ; + notifier->subdevs[notifier->num_subdevs] = asd; + notifier->num_subdevs++; + +return 0 ; + +out_err: +fwnode_handle_put (asd->match.fwnode.fwnode); +kfree (asd); + +return ret; +} + +int v4l2_async_notifier_parse_fwnode_endpoints ( +struct device *dev,struct v4l2_async_notifier *notifier, +size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)) +{ +struct fwnode_handle *fwnode =NULL ; +unsigned int max_subdevs = notifier->max_subdevs; +int ret; + +if (asd_struct_size <sizeof (struct v4l2_async_subdev ) || + notifier->v4l2_dev) +return -EINVAL ; + +for (fwnode =NULL ; (fwnode =fwnode_graph_get_next_endpoint ( +dev_fwnode (dev), fwnode)); ) +if (fwnode_device_is_available ( +fwnode_graph_get_port_parent (fwnode))) + max_subdevs++; + +/* No subdevs to add? Return here. */ +if (max_subdevs == notifier->max_subdevs) +return 0 ; + + ret =notifier_realloc (notifier, max_subdevs); +if (ret) +return ret; + +for (fwnode =NULL ; (fwnode =fwnode_graph_get_next_endpoint ( +dev_fwnode (dev), fwnode)); ) { +if (!fwnode_device_is_available ( +fwnode_graph_get_port_parent (fwnode))) +continue ; + +if (WARN_ON (notifier->num_subdevs >= notifier->max_subdevs)) +break ; + + ret =parse_endpoint (dev, notifier, fwnode, asd_struct_size, + parse_single); +if (ret <0 ) +break ; + } + +fwnode_handle_put (fwnode); + +return ret; +} +EXPORT_SYMBOL_GPL (v4l2_async_notifier_parse_fwnode_endpoints); +MODULE_LICENSE ("GPL" );MODULE_AUTHOR ("Sakari Ailus <sakari.ailus-VuQAYsv1563Yd54FQh9/[email protected]>" );MODULE_AUTHOR ("Sylwester Nawrocki <[email protected]>" );include/media/v4l2-async.h +3
-2@@ -18,7 +18,6 @@ struct device;struct device_node ;struct v4l2_device ;struct v4l2_subdev ; -struct v4l2_async_notifier ;/* A random max subdevice number, used to allocate an array on stack */ #define V4L2_MAX_SUBDEVS 128U@@ -78,7 +77,8 @@ struct v4l2_async_subdev {/** * struct v4l2_async_notifier - v4l2_device notifier data * -* @num_subdevs: number of subdevices +* @num_subdevs: number of subdevices used in subdevs array +* @max_subdevs: number of subdevices allocated in subdevs array * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers @@ -90,6 +90,7 @@ struct v4l2_async_subdev {*/ struct v4l2_async_notifier {unsigned int num_subdevs; +unsigned int max_subdevs;struct v4l2_async_subdev **subdevs;struct v4l2_device *v4l2_dev;struct list_head waiting;@@ -121,6 +122,21 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier);
Quoted 8 lines
include/media/v4l2-async.h int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, · line 126
Quoted 3 lines
include/media/v4l2-async.h +2
-0/** +* v4l2_async_notifier_release - release notifier resources +* @notifier: pointer to &struct v4l2_async_notifier That's quite obvious given the type of the argument. It would be much more useful to tell which notifier pointer this function expects (although in this case it should be obvious too): "(pointer to )?the notifier whose resources will be released".
This fully matches to the documentation elsewhere in the same file. :-)
I'll replace the text with yours.
Quoted 11 lines
include/media/v4l2-async.h int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, · line 135
Quoted 10 lines
include/media/v4l2-async.h +9
-0+* +* Release memory resources related to a notifier, including the async +* sub-devices allocated for the purposes of the notifier. The user is +* responsible for releasing the notifier's resources after calling +* @v4l2_async_notifier_parse_fwnode_endpoints. +* +* There is no harm from calling v4l2_async_notifier_release in other +* cases as long as its memory has been zeroed after it has been +* allocated.
Quoted 5 lines
Zeroing the memory is pretty much a requirement, as
v4l2_async_notifier_parse_fwnode_endpoints()won't operate correctly if memory contains random data anyway. Maybe we should introducev4l2_async_notifier_init()and makev4l2_async_notifier_release()mandatory, but that's out of scope for this patch.
Notifiers are typically allocated as part of a driver specific struct which is zeroed by the driver.
Registering the notifier won't work either if the rest of the struct wasn't zeroed.
Quoted 25 lines
Quoted 25 lines
include/media/v4l2-async.h +4
-0+*/ +void v4l2_async_notifier_release (struct v4l2_async_notifier *notifier); + +/** * v4l2_async_register_subdev - registers a sub-device to the asynchronous * subdevice framework *
include/media/v4l2-fwnode.h +2
-0@@ -25,6 +25,8 @@#include <media/v4l2-mediabus.h> struct fwnode_handle ; +struct v4l2_async_notifier ; +struct v4l2_async_subdev ;#define V4L2_FWNODE_CSI2_MAX_DATA_LANES 4@@ -201,4 +203,41 @@ int v4l2_fwnode_parse_link(struct fwnode_handle*fwnode, */ void v4l2_fwnode_put_link (struct v4l2_fwnode_link *link);
Quoted 8 lines
Quoted 5 lines
include/media/v4l2-fwnode.h +2
-0+/** +* v4l2_async_notifier_parse_fwnode_endpoints - Parse V4L2 fwnode endpoints in a
+ * device node
+ * @dev: @struct device pointerSimilarly to my previous comment (and my comments to v3), you should tell which device the function expects.
Will fix for the next version.
Quoted 11 lines
Quoted 8 lines
+ * @notifier: pointer to &struct
v4l2_async_notifier
+ * @asd_struct_size: size of the driver's async sub-device struct, including+ * sizeof(struct v4l2_async_subdev). The &struct + * v4l2_async_subdev shall be the first member of + * the driver's async sub-device struct, i.e. both + * begin at the same memory address.Should this be documented in the kerneldoc of the
v4l2_async_subdevstructure ?
Yes, I'll add that. It won't hurt to make it a requirement even if the helper functions weren't used.
Quoted 9 lines
Quoted 8 lines
+ * @parse_single: driver's callback function called on each V4L2 fwnode endpoint
+ *
+ * Allocate async sub-device array and sub-devices for each fwnode endpoint,
+ * parse the related fwnode endpoints and finally call driver's callback
+ * function to that V4L2 fwnode endpoint.
Quoted 6 lines
I'd document this from the notifier point of view.
"Parse the fwnode endpoints of the @dev device and populate the async sub- devices array of the notifier. The @parse_endpoint callback function is called for each endpoint with the corresponding async sub-device pointer to let the caller initialize the driver-specific part of the async sub-device structure."
Works for me.
Quoted 5 lines
Quoted 2 lines
+ * The function may not be called on a registered notifier.
You should mention that the function may be called multiple times on an unregistered notifier.
There's no point in calling it more than once as the endpoints wouldn't end up being different from the previous time. Actually it'd make sense to document this.
Quoted 4 lines
"The function can be called multiple times to populate the same notifier from endpoints of different @dev devices before registering the notifier. It can't be called anymore once the notifier has been registered."
I don't think there's really a use case for calling this for more than one device, is there?
Quoted 11 lines
Quoted 7 lines
+ *
+ * Once the user has called this function, the resources released by it need to
+ * be released by callinv4l2_async_notifier_releaseafter the notifier has been
+ * unregistered and the sub-devices are no longer in use."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."
Agreed.
Quoted 21 lines
Quoted 19 lines
+ *
+ * A driver supporting fwnode (currently Devicetree and ACPI) should call this
+ * function as part of its probe function before it registers the notifier.
+ *
+ * Return: %0 on success, including when no async sub-devices are found+ * %-ENOMEM if memory allocation failed + * %-EINVAL if graph or endpoint parsing failed + * Other error codes as returned by @parse_single+ */
include/media/v4l2-fwnode.h +7
-0+int v4l2_async_notifier_parse_fwnode_endpoints ( +struct device *dev,struct v4l2_async_notifier *notifier, +size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)); +#endif /* _V4L2_FWNODE_H */
Signature · 8 lines
Kind regards, Sakari Ailus e-mail: [email protected] -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): 20170829143121.6sjdx3lgcoxm6mva-S+BSfZ9RZZmRSg0ZkenSGLdO1Tsj/[email protected]
Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
Hi Sakari,
Quoted 19 lines · On Tuesday, 29 August 2017 17:31:21 EEST Sakari Ailus wrote:
Quoted 18 lines · Laurent Pinchart
Quoted 17 lines · On Tuesday, 29 August 2017 14:03:12 EEST Sakari Ailus wrote:
The current practice is that drivers iterate over their endpoints and parse each endpoint separately. This is very similar in a number of drivers, implement a generic function for the job. Driver specific matters can be taken into account in the driver specific callback.
Convert the omap3isp as an example.
Signed-off-by Sakari Ailus <[email protected]>---
drivers/media/platform/omap3isp/isp.c | 115 ++++++++++----------------- drivers/media/platform/omap3isp/isp.h | 5 +- drivers/media/v4l2-core/v4l2-async.c | 16 +++++ drivers/media/v4l2-core/v4l2-fwnode.c | 132 ++++++++++++++++++++++++++++ include/media/v4l2-async.h | > 20 +++++- include/media/v4l2-fwnode.h | 39 ++++++++++ 6 files changed, 242 insertions(+), 85 deletions(-)
[snip]
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
(quoted diff) +0
-0
b/drivers/media/v4l2-core/v4l2-fwnode.cindex706f9e7b90f1..39a587c6992a100644drivers/media/v4l2-core/v4l2-fwnode.c +0
-0
[snip]
Quoted 27 lines
Quoted 18 lines
Quoted 18 lines
drivers/media/v4l2-core/v4l2-fwnode.c +18
-0+static int parse_endpoint ( +struct device *dev,struct v4l2_async_notifier *notifier, +struct fwnode_handle *endpoint,unsigned int asd_struct_size, +int (*parse_single)(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.fwnode.fwnode = +fwnode_graph_get_remote_port_parent (endpoint); +if (!asd->match.fwnode.fwnode) {Quoted 8 lines
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 369
Quoted 6 lines
drivers/media/v4l2-core/v4l2-fwnode.c +6
-0+dev_warn (dev,"bad remote port parent\n" ); + ret = -EINVAL ; + goto out_err; + } + +/* Ignore endpoints the parsing of which failed. */ You don't ignore them anymore, the comment should be updated.
Quoted 8 lines
Hmm. I actually intended to do something else about this. :-) As there's a single error code, handling that would need to be done a little bit differently right now.
I'd print a warning and proceed. What do you think?
Even if there's a bad DT endpoint, that shouldn't prevent the rest from working, right?
I think it should, to make sure we catch DT issues. We both know how many vendors don't care about warnings, so I'd rather fail completely to ensure DT will be fixed (and even ten I wouldn't be surprised if some vendors patched the code to remove the check instead of fixing their DT ;-)).
Quoted 27 lines
Quoted 27 lines
Quoted 27 lines
drivers/media/v4l2-core/v4l2-fwnode.c +27
-0+ 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_single (dev, vep, asd); +v4l2_fwnode_endpoint_free (vep); +if (ret) { +dev_warn (dev,"driver could not parse endpoint (%d)\n" , ret); + goto out_err; + } + + asd->match_type =V4L2_ASYNC_MATCH_FWNODE ; + notifier->subdevs[notifier->num_subdevs] = asd; + notifier->num_subdevs++; + +return 0 ; + +out_err: +fwnode_handle_put (asd->match.fwnode.fwnode); +kfree (asd); + +return ret; +}
[snip]
Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
include/media/v4l2-async.h +0
-0@@ -121,6 +122,21 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, void v4l2_async_notifier_unregister(structv4l2_async_notifier *notifier);Quoted 8 lines
include/media/v4l2-async.h int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, · line 126
Quoted 3 lines
include/media/v4l2-async.h +2
-0/** +* v4l2_async_notifier_release - release notifier resources +* @notifier: pointer to &struct v4l2_async_notifier That's quite obvious given the type of the argument. It would be much more useful to tell which notifier pointer this function expects (although in this case it should be obvious too): "(pointer to )?the notifier whose resources will be released".
Quoted 1 line
This fully matches to the documentation elsewhere in the same file. :-)
Feel free to fix the rest of the file :-)
Quoted 36 lines
I'll replace the text with yours.
Quoted 10 lines
include/media/v4l2-async.h int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, · line 135
Quoted 9 lines
include/media/v4l2-async.h +9
-0+* +* Release memory resources related to a notifier, including the async +* sub-devices allocated for the purposes of the notifier. The user is +* responsible for releasing the notifier's resources after calling +* @v4l2_async_notifier_parse_fwnode_endpoints. +* +* There is no harm from calling v4l2_async_notifier_release in other +* cases as long as its memory has been zeroed after it has been +* allocated. Quoted 5 lines
Zeroing the memory is pretty much a requirement, as
v4l2_async_notifier_parse_fwnode_endpoints()won't operate correctly if memory contains random data anyway. Maybe we should introducev4l2_async_notifier_init()and makev4l2_async_notifier_release()mandatory, but that's out of scope for this patch.Notifiers are typically allocated as part of a driver specific struct which is zeroed by the driver.
Registering the notifier won't work either if the rest of the struct wasn't zeroed.
Quoted 12 lines
Quoted 12 lines
include/media/v4l2-async.h +4
-0+*/ +void v4l2_async_notifier_release (struct v4l2_async_notifier *notifier); + +/** * v4l2_async_register_subdev - registers a sub-device to the asynchronous * subdevice framework
*include/media/v4l2-fwnode.h +0
-0
[snip]
Quoted 58 lines
Quoted 5 lines
Quoted 5 lines
include/media/v4l2-fwnode.h +0
-0@@ -201,4 +203,41 @@ int v4l2_fwnode_parse_link(struct fwnode_handle*fwnode, */ void v4l2_fwnode_put_link (struct v4l2_fwnode_link *link);Quoted 8 lines
Quoted 5 lines
include/media/v4l2-fwnode.h +2
-0+/** +* v4l2_async_notifier_parse_fwnode_endpoints - Parse V4L2 fwnode endpoints in a
+ * device node
+ * @dev: @struct device pointerSimilarly to my previous comment (and my comments to v3), you should tell which device the function expects.
Will fix for the next version.
Quoted 10 lines
Quoted 7 lines
+ * @notifier: pointer to &struct
v4l2_async_notifier
+ * @asd_struct_size: size of the driver's async sub-device struct, including+ * sizeof(struct v4l2_async_subdev). The &struct + * v4l2_async_subdev shall be the first member of + * the driver's async sub-device struct, i.e. both + * begin at the same memory address.Should this be documented in the kerneldoc of the
v4l2_async_subdevstructure ?Yes, I'll add that. It won't hurt to make it a requirement even if the helper functions weren't used.
Quoted 9 lines
Quoted 8 lines
+ * @parse_single: driver's callback function called on each V4L2 fwnode endpoint
+ *
+ * Allocate async sub-device array and sub-devices for each fwnode endpoint,
+ * parse the related fwnode endpoints and finally call driver's callback
+ * function to that V4L2 fwnode endpoint.Quoted 7 lines
I'd document this from the notifier point of view.
"Parse the fwnode endpoints of the @dev device and populate the async sub- devices array of the notifier. The @parse_endpoint callback function is called for each endpoint with the corresponding async sub-device pointer to let the caller initialize the driver-specific part of the async sub-device structure."
Works for me.
Quoted 4 lines
Quoted 1 line
+ * The function may not be called on a registered notifier.
You should mention that the function may be called multiple times on an unregistered notifier.
There's no point in calling it more than once as the endpoints wouldn't end up being different from the previous time. Actually it'd make sense to document this.
Quoted 6 lines
Quoted 3 lines
"The function can be called multiple times to populate the same notifier from endpoints of different @dev devices before registering the notifier. It can't be called anymore once the notifier has been registered."
I don't think there's really a use case for calling this for more than one device, is there?
I don't have one in mind, but I was wondering. If there isn't then you don't need notifier_realloc(), which could be moved to the next patch.
Quoted 32 lines
Quoted 10 lines
Quoted 6 lines
+ *
+ * Once the user has called this function, the resources released by it need to
+ * be released by callinv4l2_async_notifier_releaseafter the notifier has been
+ * unregistered and the sub-devices are no longer in use."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."Agreed.
Quoted 19 lines
Quoted 19 lines
+ *
+ * A driver supporting fwnode (currently Devicetree and ACPI) should call this
+ * function as part of its probe function before it registers the notifier.
+ *
+ * Return: %0 on success, including when no async sub-devices are found+ * %-ENOMEM if memory allocation failed + * %-EINVAL if graph or endpoint parsing failed + * Other error codes as returned by @parse_single+ */
include/media/v4l2-fwnode.h +7
-0+int v4l2_async_notifier_parse_fwnode_endpoints ( +struct device *dev,struct v4l2_async_notifier *notifier, +size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)); +#endif /* _V4L2_FWNODE_H */
Signature · 3 lines
Regards, Laurent Pinchart
Sakari Ailus
maintainerRe: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
Hi Laurent,
On Fri, Sep 01, 2017 at 11:55:43AM +0300, Laurent Pinchart wrote:
...
Quoted 33 lines
Quoted 33 lines
Quoted 18 lines
Quoted 18 lines
drivers/media/v4l2-core/v4l2-fwnode.c +18
-0+static int parse_endpoint ( +struct device *dev,struct v4l2_async_notifier *notifier, +struct fwnode_handle *endpoint,unsigned int asd_struct_size, +int (*parse_single)(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.fwnode.fwnode = +fwnode_graph_get_remote_port_parent (endpoint); +if (!asd->match.fwnode.fwnode) {Quoted 8 lines
drivers/media/v4l2-core/v4l2-fwnode.c void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) · line 369
Quoted 6 lines
drivers/media/v4l2-core/v4l2-fwnode.c +6
-0+dev_warn (dev,"bad remote port parent\n" ); + ret = -EINVAL ; + goto out_err; + } + +/* Ignore endpoints the parsing of which failed. */ You don't ignore them anymore, the comment should be updated.
Hmm. I actually intended to do something else about this. :-) As there's a single error code, handling that would need to be done a little bit differently right now.
I'd print a warning and proceed. What do you think?
Quoted 7 lines
Quoted 2 lines
Even if there's a bad DT endpoint, that shouldn't prevent the rest from working, right?
I think it should, to make sure we catch DT issues. We both know how many vendors don't care about warnings, so I'd rather fail completely to ensure DT will be fixed (and even ten I wouldn't be surprised if some vendors patched the code to remove the check instead of fixing their DT ;-)).
If they test the DTS, they should find out that the device does not work.
If they do not, some devices will work even if others fail.
Therefore I don't see why everything should fail when a part of faulty. Extending that a little, you should also halt the system to make sure the problem will be noticed. :-)
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines
include/media/v4l2-async.h int v4l2_async_notifier_register(struct v4l2_device · line 127
Quoted 7 lines
include/media/v4l2-async.h +2
-0@@ -121,6 +122,21 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, void v4l2_async_notifier_unregister(structv4l2_async_notifier *notifier);/** +* v4l2_async_notifier_release - release notifier resources +* @notifier: pointer to &struct v4l2_async_notifier That's quite obvious given the type of the argument. It would be much more useful to tell which notifier pointer this function expects (although in this case it should be obvious too): "(pointer to )?the notifier whose resources will be released".
Quoted 3 lines
Quoted 1 line
This fully matches to the documentation elsewhere in the same file. :-)
Feel free to fix the rest of the file :-)
That's out of scope of this patch.
Quoted 9 lines
Quoted 6 lines
Quoted 3 lines
"The function can be called multiple times to populate the same notifier from endpoints of different @dev devices before registering the notifier. It can't be called anymore once the notifier has been registered."
I don't think there's really a use case for calling this for more than one device, is there?
I don't have one in mind, but I was wondering. If there isn't then you don't need
notifier_realloc(), which could be moved to the next patch.
I don't think there's even benefit from moving it to the next patch, it just adds to the reviewable code, nothing else.
Signature · 4 lines
Regards, Sakari Ailus [email protected]
Sakari Ailus
maintainerPATCH v5 3/5 docs-rst: v4l: Include Qualcomm CAMSS in documentation build
In reply to Sakari AilusQualcomm CAMSS was left out from documentation build. Fix this.
--- Documentation/media/v4l-drivers/index.rst | 1 + 1 file changed, 1 insertion(+)
Documentation/media/v4l-drivers/index.rst +1 -0
@@ -50,6 +50,7 @@ For more details see the file COPYING in the source distribution of Linux. philips pvrusb2 pxa_camera + qcom_camss radiotrack rcar-fdp1 saa7134
Signature · 1 line
2.11.0
A message that is not in the archive (sent off-list or not yet received): 20170829110313.19538-4-sakari.ailus-VuQAYsv1563Yd54FQh9/[email protected]
Laurent Pinchart
Reviewed-byRe: [PATCH v5 3/5] docs-rst: v4l: Include Qualcomm CAMSS in documentation build
Hi Sakari,
Thank you for the patch.
Quoted 3 lines · On Tuesday, 29 August 2017 14:03:11 EEST Sakari Ailus wrote:
Qualcomm CAMSS was left out from documentation build. Fix this.
Signed-off-by Sakari Ailus <[email protected]>
I've tested the qcom_camss documentation build and haven't noticed any new warning or error, so
Quoted 17 lines
--- Documentation/media/v4l-drivers/index.rst | 1 + 1 file changed, 1 insertion(+)(quoted diff) +0
-0
b/Documentation/media/v4l-drivers/index.rstindex10f2ce42ece2..5c202e23616b100644Documentation/media/v4l-drivers/index.rst +0
-0@@ -50,6 +50,7 @@ For more details see the file COPYING in the sourcedistribution of Linux. philips
Documentation/media/v4l-drivers/index.rst +1
-0pvrusb2 pxa_camera + qcom_camss radiotrack rcar-fdp1 saa7134
Signature · 3 lines
Regards, Laurent Pinchart
Sakari Ailus
maintainerPATCH v5 5/5 v4l: fwnode: Support generic parsing of graph endpoints in a single port
In reply to Sakari AilusThis is the preferred way to parse the endpoints.
Comment rcar-vin as an example.
--- drivers/media/platform/rcar-vin/rcar-core.c | 111 ++++++++-------------------- drivers/media/platform/rcar-vin/rcar-dma.c | 10 +-- drivers/media/platform/rcar-vin/rcar-v4l2.c | 14 ++-- drivers/media/platform/rcar-vin/rcar-vin.h | 4 +- drivers/media/v4l2-core/v4l2-fwnode.c | 47 ++++++++++++ include/media/v4l2-fwnode.h | 39 ++++++++++ 6 files changed, 132 insertions(+), 93 deletions(-)
drivers/media/platform/rcar-vin/rcar-core.c +32 -79
@@ -21,6 +21,7 @@#include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <media/v4l2-async.h> #include <media/v4l2-fwnode.h> #include "rcar-vin.h"
@@ -77,14 +78,14 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)int ret;/* Verify subdevices mbus format */ -if (!rvin_mbus_supported (&vin->digital)) { +if (!rvin_mbus_supported (vin->digital)) {vin_err (vin,"Unsupported media bus format for %s\n" , - vin->digital.subdev->name); + vin->digital->subdev->name);return -EINVAL ; }vin_dbg (vin,"Found media bus format for %s: %d\n" , - vin->digital.subdev->name, vin->digital.code); + vin->digital->subdev->name, vin->digital->code); ret =v4l2_device_register_subdev_nodes (&vin->v4l2_dev);if (ret <0 ) {
@@ -103,7 +104,7 @@ static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,vin_dbg (vin,"unbind digital subdev %s\n" , subdev->name);rvin_v4l2_remove (vin); - vin->digital.subdev =NULL ; + vin->digital->subdev =NULL ; } static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,
@@ -120,117 +121,67 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, ret =rvin_find_pad (subdev,MEDIA_PAD_FL_SOURCE );if (ret <0 )return ret; - vin->digital.source_pad = ret; + vin->digital->source_pad = ret; ret =rvin_find_pad (subdev,MEDIA_PAD_FL_SINK ); - vin->digital.sink_pad = ret <0 ?0 : ret; + vin->digital->sink_pad = ret <0 ?0 : ret; - vin->digital.subdev = subdev; + vin->digital->subdev = subdev;vin_dbg (vin,"bound subdev %s source pad: %u sink pad: %u\n" , - subdev->name, vin->digital.source_pad, - vin->digital.sink_pad); + subdev->name, vin->digital->source_pad, + vin->digital->sink_pad);return 0 ; } -static int rvin_digitial_parse_v4l2 (struct rvin_dev *vin, -struct device_node *ep, -struct v4l2_mbus_config *mbus_cfg) +static int rvin_digital_parse_v4l2 (struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd) { -struct v4l2_fwnode_endpoint v4l2_ep; -int ret; - - ret =v4l2_fwnode_endpoint_parse (of_fwnode_handle (ep), &v4l2_ep); -if (ret) { -vin_err (vin,"Could not parse v4l2 endpoint\n" ); -return -EINVAL ; - } +struct rvin_dev *vin =dev_get_drvdata (dev); +struct rvin_graph_entity *rvge = + container_of(asd, struct rvin_graph_entity, asd); - mbus_cfg->type = v4l2_ep.bus_type; + rvge->mbus_cfg.type = vep->bus_type; -switch (mbus_cfg->type) { +switch (rvge->mbus_cfg.type) {case V4L2_MBUS_PARALLEL :vin_dbg (vin,"Found PARALLEL media bus\n" ); - mbus_cfg->flags = v4l2_ep.bus.parallel.flags; + rvge->mbus_cfg.flags = vep->bus.parallel.flags;break ;case V4L2_MBUS_BT656 :vin_dbg (vin,"Found BT656 media bus\n" ); - mbus_cfg->flags =0 ; + rvge->mbus_cfg.flags =0 ;break ;default :vin_err (vin,"Unknown media bus type\n" );return -EINVAL ; } -return 0 ; -} - -static int rvin_digital_graph_parse (struct rvin_dev *vin) -{ -struct device_node *ep, *np; -int ret; - - vin->digital.asd.match.fwnode.fwnode =NULL ; - vin->digital.subdev =NULL ; - -/* -* Port 0 id 0 is local digital input, try to get it. -* Not all instances can or will have this, that is OK -*/ - ep =of_graph_get_endpoint_by_regs (vin->dev->of_node,0 ,0 ); -if (!ep) -return 0 ; - - np =of_graph_get_remote_port_parent (ep); -if (!np) { -vin_err (vin,"No remote parent for digital input\n" ); -of_node_put (ep); -return -EINVAL ; - } -of_node_put (np); - - ret =rvin_digitial_parse_v4l2 (vin, ep, &vin->digital.mbus_cfg); -of_node_put (ep); -if (ret) -return ret; - - vin->digital.asd.match.fwnode.fwnode =of_fwnode_handle (np); - vin->digital.asd.match_type =V4L2_ASYNC_MATCH_FWNODE ; + vin->digital = rvge;return 0 ; }static int rvin_digital_graph_init (struct rvin_dev *vin) { -struct v4l2_async_subdev **subdevs =NULL ;int ret; - ret =rvin_digital_graph_parse (vin); + ret =v4l2_async_notifier_parse_fwnode_endpoint ( + vin->dev, &vin->notifier,0 ,0 , +sizeof (struct rvin_graph_entity ), rvin_digital_parse_v4l2);if (ret)return ret; -if (!vin->digital.asd.match.fwnode.fwnode) { -vin_dbg (vin,"No digital subdevice found\n" ); -return -ENODEV ; - } - -/* Register the subdevices notifier. */ - subdevs =devm_kzalloc (vin->dev,sizeof (*subdevs),GFP_KERNEL ); -if (subdevs ==NULL ) -return -ENOMEM ; +if (vin->notifier.num_subdevs >0 ) +vin_dbg (vin,"Found digital subdevice %pOF\n" , +to_of_node ( + vin->notifier.subdevs[0 ]->match.fwnode.fwnode)); - subdevs[0 ] = &vin->digital.asd; - -vin_dbg (vin,"Found digital subdevice %pOF\n" , -to_of_node (subdevs[0 ]->match.fwnode.fwnode)); - - vin->notifier.num_subdevs =1 ; - vin->notifier.subdevs = subdevs; vin->notifier.bound = rvin_digital_notify_bound; vin->notifier.unbind = rvin_digital_notify_unbind; vin->notifier.complete = rvin_digital_notify_complete; - ret =v4l2_async_notifier_register (&vin->v4l2_dev, &vin->notifier);if (ret <0 ) {vin_err (vin,"Notifier registration failed\n" );
@@ -290,6 +241,8 @@ static int rcar_vin_probe(struct platform_device *pdev)if (ret)return ret; +platform_set_drvdata (pdev, vin); + ret =rvin_digital_graph_init (vin);if (ret <0 ) goto error;
@@ -297,11 +250,10 @@ static int rcar_vin_probe(struct platform_device *pdev)pm_suspend_ignore_children (&pdev->dev, true);pm_runtime_enable (&pdev->dev); -platform_set_drvdata (pdev, vin); -return 0 ; error:rvin_dma_remove (vin); +v4l2_async_notifier_release (&vin->notifier);return ret; }
@@ -313,6 +265,7 @@ static int rcar_vin_remove(struct platform_device *pdev)pm_runtime_disable (&pdev->dev);v4l2_async_notifier_unregister (&vin->notifier); +v4l2_async_notifier_release (&vin->notifier);rvin_dma_remove (vin);
drivers/media/platform/rcar-vin/rcar-dma.c +5 -5
@@ -183,7 +183,7 @@ static int rvin_setup(struct rvin_dev *vin)/* * Input interface */ -switch (vin->digital.code) { +switch (vin->digital->code) {case MEDIA_BUS_FMT_YUYV8_1X16 :/* BT.601/BT.1358 16bit YCbCr422 */ vnmc |=VNMC_INF_YUV16 ;
@@ -191,7 +191,7 @@ static int rvin_setup(struct rvin_dev *vin)break ;case MEDIA_BUS_FMT_UYVY8_2X8 :/* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= vin->digital.mbus_cfg.type ==V4L2_MBUS_BT656 ? + vnmc |= vin->digital->mbus_cfg.type ==V4L2_MBUS_BT656 ?VNMC_INF_YUV8_BT656 :VNMC_INF_YUV8_BT601 ; input_is_yuv = true;break ;
@@ -200,7 +200,7 @@ static int rvin_setup(struct rvin_dev *vin)break ;case MEDIA_BUS_FMT_UYVY10_2X10 :/* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= vin->digital.mbus_cfg.type ==V4L2_MBUS_BT656 ? + vnmc |= vin->digital->mbus_cfg.type ==V4L2_MBUS_BT656 ?VNMC_INF_YUV10_BT656 :VNMC_INF_YUV10_BT601 ; input_is_yuv = true;break ;
@@ -212,11 +212,11 @@ static int rvin_setup(struct rvin_dev *vin) dmr2 =VNDMR2_FTEV |VNDMR2_VLV (1 );/* Hsync Signal Polarity Select */ -if (!(vin->digital.mbus_cfg.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW )) +if (!(vin->digital->mbus_cfg.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW )) dmr2 |=VNDMR2_HPS ;/* Vsync Signal Polarity Select */ -if (!(vin->digital.mbus_cfg.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW )) +if (!(vin->digital->mbus_cfg.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW )) dmr2 |=VNDMR2_VPS ;/*
drivers/media/platform/rcar-vin/rcar-v4l2.c +7 -7
@@ -111,7 +111,7 @@ static int rvin_reset_format(struct rvin_dev *vin)struct v4l2_mbus_framefmt *mf = &fmt.format;int ret; - fmt.pad = vin->digital.source_pad; + fmt.pad = vin->digital->source_pad; ret =v4l2_subdev_call (vin_to_source (vin), pad, get_fmt,NULL , &fmt);if (ret)
@@ -172,13 +172,13 @@ static int __rvin_try_format_source(struct rvin_dev *vin, sd =vin_to_source (vin); -v4l2_fill_mbus_format (&format.format, pix, vin->digital.code); +v4l2_fill_mbus_format (&format.format, pix, vin->digital->code); pad_cfg =v4l2_subdev_alloc_pad_config (sd);if (pad_cfg ==NULL )return -ENOMEM ; - format.pad = vin->digital.source_pad; + format.pad = vin->digital->source_pad; field = pix->field;
@@ -555,7 +555,7 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh,if (timings->pad)return -EINVAL ; - timings->pad = vin->digital.sink_pad; + timings->pad = vin->digital->sink_pad; ret =v4l2_subdev_call (sd, pad, enum_dv_timings, timings);
@@ -607,7 +607,7 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh,if (cap->pad)return -EINVAL ; - cap->pad = vin->digital.sink_pad; + cap->pad = vin->digital->sink_pad; ret =v4l2_subdev_call (sd, pad, dv_timings_cap, cap);
@@ -625,7 +625,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)if (edid->pad)return -EINVAL ; - edid->pad = vin->digital.sink_pad; + edid->pad = vin->digital->sink_pad; ret =v4l2_subdev_call (sd, pad, get_edid, edid);
@@ -643,7 +643,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)if (edid->pad)return -EINVAL ; - edid->pad = vin->digital.sink_pad; + edid->pad = vin->digital->sink_pad; ret =v4l2_subdev_call (sd, pad, set_edid, edid);
drivers/media/platform/rcar-vin/rcar-vin.h +2 -2
@@ -126,7 +126,7 @@ struct rvin_dev {struct v4l2_device v4l2_dev;struct v4l2_ctrl_handler ctrl_handler;struct v4l2_async_notifier notifier; -struct rvin_graph_entity digital; +struct rvin_graph_entity *digital;struct mutex lock;struct vb2_queue queue;
@@ -145,7 +145,7 @@ struct rvin_dev {struct v4l2_rect compose; }; -#define vin_to_source (vin) vin->digital.subdev +#define vin_to_source (vin) ((vin)->digital->subdev)/* Debug */ #define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg)
drivers/media/v4l2-core/v4l2-fwnode.c +47 -0
53 lines not on this page · raw message
include/media/v4l2-fwnode.h +39 -0
@@ -240,4 +240,43 @@ int v4l2_async_notifier_parse_fwnode_endpoints(struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd)); +/** +* v4l2_async_notifier_parse_fwnode_endpoint - Set up async notifier for an +* fwnode based sub-device +* @dev: @struct device pointer +* @notifier: pointer to &struct v4l2_async_notifier +* @port_id: port number +* @endpoint_id: endpoint number +* @asd_struct_size: size of the driver's async sub-device struct, including +* sizeof(struct v4l2_async_subdev). The &struct +* v4l2_async_subdev shall be the first member of +* the driver's async sub-device struct, i.e. both +* begin at the same memory address. +* @parse_single: driver's callback function called on each V4L2 fwnode endpoint +* +* Find an endpoint node for the given port and endpoint IDs and allocate an +* async sub-device struct for it. Parse the V4L2 fwnode endpoint and call the +* provided callback function. +* +* The function may not be called on a registered notifier. +* +* Once the user has called this function, the resources released by it need to +* be released by callin v4l2_async_notifier_release after the notifier has been +* unregistered and the sub-devices are no longer in use. +* +* A driver supporting fwnode (currently Devicetree and ACPI) should call this +* function as part of its probe function before it registers the notifier. +* +* Return: %0 on success, including when no async sub-devices are found +* %-ENOMEM if memory allocation failed +* %-EINVAL if graph or endpoint parsing failed +* Other error codes as returned by @parse_single +*/ +int v4l2_async_notifier_parse_fwnode_endpoint ( +struct device *dev,struct v4l2_async_notifier *notifier, +unsigned int port_id,unsigned int endpoint_id,size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)); +#endif /* _V4L2_FWNODE_H */
Signature · 1 line
2.11.0
Re: [PATCH v5 5/5] v4l: fwnode: Support generic parsing of graph endpoints in a single port
Hi Sakari,
Thanks for your patch, I like how this turned out :-)
Quoted 5 lines · Sakari Ailus
This is the preferred way to parse the endpoints.
Comment rcar-vin as an example.
Signed-off-by Sakari Ailus <[email protected]>
Quoted 460 lines
--- drivers/media/platform/rcar-vin/rcar-core.c | 111 ++++++++-------------------- drivers/media/platform/rcar-vin/rcar-dma.c | 10 +-- drivers/media/platform/rcar-vin/rcar-v4l2.c | 14 ++-- drivers/media/platform/rcar-vin/rcar-vin.h | 4 +- drivers/media/v4l2-core/v4l2-fwnode.c | 47 ++++++++++++ include/media/v4l2-fwnode.h | 39 ++++++++++ 6 files changed, 132 insertions(+), 93 deletions(-)drivers/media/platform/rcar-vin/rcar-core.c +32
-79@@ -21,6 +21,7 @@#include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <media/v4l2-async.h> #include <media/v4l2-fwnode.h> #include "rcar-vin.h" @@ -77,14 +78,14 @@ static int rvin_digital_notify_complete(struct v4l2_async_notifier *notifier)int ret;/* Verify subdevices mbus format */ -if (!rvin_mbus_supported (&vin->digital)) { +if (!rvin_mbus_supported (vin->digital)) {vin_err (vin,"Unsupported media bus format for %s\n" , - vin->digital.subdev->name); + vin->digital->subdev->name);return -EINVAL ; }vin_dbg (vin,"Found media bus format for %s: %d\n" , - vin->digital.subdev->name, vin->digital.code); + vin->digital->subdev->name, vin->digital->code); ret =v4l2_device_register_subdev_nodes (&vin->v4l2_dev);if (ret <0 ) {@@ -103,7 +104,7 @@ static void rvin_digital_notify_unbind(struct v4l2_async_notifier *notifier,vin_dbg (vin,"unbind digital subdev %s\n" , subdev->name);rvin_v4l2_remove (vin); - vin->digital.subdev =NULL ; + vin->digital->subdev =NULL ; } static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier,@@ -120,117 +121,67 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, ret =rvin_find_pad (subdev,MEDIA_PAD_FL_SOURCE );if (ret <0 )return ret; - vin->digital.source_pad = ret; + vin->digital->source_pad = ret; ret =rvin_find_pad (subdev,MEDIA_PAD_FL_SINK ); - vin->digital.sink_pad = ret <0 ?0 : ret; + vin->digital->sink_pad = ret <0 ?0 : ret; - vin->digital.subdev = subdev; + vin->digital->subdev = subdev;vin_dbg (vin,"bound subdev %s source pad: %u sink pad: %u\n" , - subdev->name, vin->digital.source_pad, - vin->digital.sink_pad); + subdev->name, vin->digital->source_pad, + vin->digital->sink_pad);return 0 ; } -static int rvin_digitial_parse_v4l2 (struct rvin_dev *vin, -struct device_node *ep, -struct v4l2_mbus_config *mbus_cfg) +static int rvin_digital_parse_v4l2 (struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd) { -struct v4l2_fwnode_endpoint v4l2_ep; -int ret; - - ret =v4l2_fwnode_endpoint_parse (of_fwnode_handle (ep), &v4l2_ep); -if (ret) { -vin_err (vin,"Could not parse v4l2 endpoint\n" ); -return -EINVAL ; - } +struct rvin_dev *vin =dev_get_drvdata (dev); +struct rvin_graph_entity *rvge = + container_of(asd, struct rvin_graph_entity, asd); - mbus_cfg->type = v4l2_ep.bus_type; + rvge->mbus_cfg.type = vep->bus_type; -switch (mbus_cfg->type) { +switch (rvge->mbus_cfg.type) {case V4L2_MBUS_PARALLEL :vin_dbg (vin,"Found PARALLEL media bus\n" ); - mbus_cfg->flags = v4l2_ep.bus.parallel.flags; + rvge->mbus_cfg.flags = vep->bus.parallel.flags;break ;case V4L2_MBUS_BT656 :vin_dbg (vin,"Found BT656 media bus\n" ); - mbus_cfg->flags =0 ; + rvge->mbus_cfg.flags =0 ;break ;default :vin_err (vin,"Unknown media bus type\n" );return -EINVAL ; } -return 0 ; -} - -static int rvin_digital_graph_parse (struct rvin_dev *vin) -{ -struct device_node *ep, *np; -int ret; - - vin->digital.asd.match.fwnode.fwnode =NULL ; - vin->digital.subdev =NULL ; - -/* -* Port 0 id 0 is local digital input, try to get it. -* Not all instances can or will have this, that is OK -*/ - ep =of_graph_get_endpoint_by_regs (vin->dev->of_node,0 ,0 ); -if (!ep) -return 0 ; - - np =of_graph_get_remote_port_parent (ep); -if (!np) { -vin_err (vin,"No remote parent for digital input\n" ); -of_node_put (ep); -return -EINVAL ; - } -of_node_put (np); - - ret =rvin_digitial_parse_v4l2 (vin, ep, &vin->digital.mbus_cfg); -of_node_put (ep); -if (ret) -return ret; - - vin->digital.asd.match.fwnode.fwnode =of_fwnode_handle (np); - vin->digital.asd.match_type =V4L2_ASYNC_MATCH_FWNODE ; + vin->digital = rvge;return 0 ; }static int rvin_digital_graph_init (struct rvin_dev *vin) { -struct v4l2_async_subdev **subdevs =NULL ;int ret; - ret =rvin_digital_graph_parse (vin); + ret =v4l2_async_notifier_parse_fwnode_endpoint ( + vin->dev, &vin->notifier,0 ,0 , +sizeof (struct rvin_graph_entity ), rvin_digital_parse_v4l2);if (ret)return ret; -if (!vin->digital.asd.match.fwnode.fwnode) { -vin_dbg (vin,"No digital subdevice found\n" ); -return -ENODEV ; - } - -/* Register the subdevices notifier. */ - subdevs =devm_kzalloc (vin->dev,sizeof (*subdevs),GFP_KERNEL ); -if (subdevs ==NULL ) -return -ENOMEM ; +if (vin->notifier.num_subdevs >0 ) +vin_dbg (vin,"Found digital subdevice %pOF\n" , +to_of_node ( + vin->notifier.subdevs[0 ]->match.fwnode.fwnode)); - subdevs[0 ] = &vin->digital.asd; - -vin_dbg (vin,"Found digital subdevice %pOF\n" , -to_of_node (subdevs[0 ]->match.fwnode.fwnode)); - - vin->notifier.num_subdevs =1 ; - vin->notifier.subdevs = subdevs; vin->notifier.bound = rvin_digital_notify_bound; vin->notifier.unbind = rvin_digital_notify_unbind; vin->notifier.complete = rvin_digital_notify_complete; - ret =v4l2_async_notifier_register (&vin->v4l2_dev, &vin->notifier);if (ret <0 ) {vin_err (vin,"Notifier registration failed\n" );@@ -290,6 +241,8 @@ static int rcar_vin_probe(struct platform_device *pdev)if (ret)return ret; +platform_set_drvdata (pdev, vin); + ret =rvin_digital_graph_init (vin);if (ret <0 ) goto error;@@ -297,11 +250,10 @@ static int rcar_vin_probe(struct platform_device *pdev)pm_suspend_ignore_children (&pdev->dev, true);pm_runtime_enable (&pdev->dev); -platform_set_drvdata (pdev, vin); -return 0 ; error:rvin_dma_remove (vin); +v4l2_async_notifier_release (&vin->notifier);return ret; }@@ -313,6 +265,7 @@ static int rcar_vin_remove(struct platform_device *pdev)pm_runtime_disable (&pdev->dev);v4l2_async_notifier_unregister (&vin->notifier); +v4l2_async_notifier_release (&vin->notifier);rvin_dma_remove (vin);drivers/media/platform/rcar-vin/rcar-dma.c +5
-5@@ -183,7 +183,7 @@ static int rvin_setup(struct rvin_dev *vin)/* * Input interface */ -switch (vin->digital.code) { +switch (vin->digital->code) {case MEDIA_BUS_FMT_YUYV8_1X16 :/* BT.601/BT.1358 16bit YCbCr422 */ vnmc |=VNMC_INF_YUV16 ;@@ -191,7 +191,7 @@ static int rvin_setup(struct rvin_dev *vin)break ;case MEDIA_BUS_FMT_UYVY8_2X8 :/* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ - vnmc |= vin->digital.mbus_cfg.type ==V4L2_MBUS_BT656 ? + vnmc |= vin->digital->mbus_cfg.type ==V4L2_MBUS_BT656 ?VNMC_INF_YUV8_BT656 :VNMC_INF_YUV8_BT601 ; input_is_yuv = true;break ;@@ -200,7 +200,7 @@ static int rvin_setup(struct rvin_dev *vin)break ;case MEDIA_BUS_FMT_UYVY10_2X10 :/* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ - vnmc |= vin->digital.mbus_cfg.type ==V4L2_MBUS_BT656 ? + vnmc |= vin->digital->mbus_cfg.type ==V4L2_MBUS_BT656 ?VNMC_INF_YUV10_BT656 :VNMC_INF_YUV10_BT601 ; input_is_yuv = true;break ;@@ -212,11 +212,11 @@ static int rvin_setup(struct rvin_dev *vin) dmr2 =VNDMR2_FTEV |VNDMR2_VLV (1 );/* Hsync Signal Polarity Select */ -if (!(vin->digital.mbus_cfg.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW )) +if (!(vin->digital->mbus_cfg.flags &V4L2_MBUS_HSYNC_ACTIVE_LOW )) dmr2 |=VNDMR2_HPS ;/* Vsync Signal Polarity Select */ -if (!(vin->digital.mbus_cfg.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW )) +if (!(vin->digital->mbus_cfg.flags &V4L2_MBUS_VSYNC_ACTIVE_LOW )) dmr2 |=VNDMR2_VPS ;/* drivers/media/platform/rcar-vin/rcar-v4l2.c +7
-7@@ -111,7 +111,7 @@ static int rvin_reset_format(struct rvin_dev *vin)struct v4l2_mbus_framefmt *mf = &fmt.format;int ret; - fmt.pad = vin->digital.source_pad; + fmt.pad = vin->digital->source_pad; ret =v4l2_subdev_call (vin_to_source (vin), pad, get_fmt,NULL , &fmt);if (ret)@@ -172,13 +172,13 @@ static int __rvin_try_format_source(struct rvin_dev *vin, sd =vin_to_source (vin); -v4l2_fill_mbus_format (&format.format, pix, vin->digital.code); +v4l2_fill_mbus_format (&format.format, pix, vin->digital->code); pad_cfg =v4l2_subdev_alloc_pad_config (sd);if (pad_cfg ==NULL )return -ENOMEM ; - format.pad = vin->digital.source_pad; + format.pad = vin->digital->source_pad; field = pix->field;@@ -555,7 +555,7 @@ static int rvin_enum_dv_timings(struct file *file, void *priv_fh,if (timings->pad)return -EINVAL ; - timings->pad = vin->digital.sink_pad; + timings->pad = vin->digital->sink_pad; ret =v4l2_subdev_call (sd, pad, enum_dv_timings, timings);@@ -607,7 +607,7 @@ static int rvin_dv_timings_cap(struct file *file, void *priv_fh,if (cap->pad)return -EINVAL ; - cap->pad = vin->digital.sink_pad; + cap->pad = vin->digital->sink_pad; ret =v4l2_subdev_call (sd, pad, dv_timings_cap, cap);@@ -625,7 +625,7 @@ static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)if (edid->pad)return -EINVAL ; - edid->pad = vin->digital.sink_pad; + edid->pad = vin->digital->sink_pad; ret =v4l2_subdev_call (sd, pad, get_edid, edid);@@ -643,7 +643,7 @@ static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)if (edid->pad)return -EINVAL ; - edid->pad = vin->digital.sink_pad; + edid->pad = vin->digital->sink_pad; ret =v4l2_subdev_call (sd, pad, set_edid, edid);drivers/media/platform/rcar-vin/rcar-vin.h +2
-2@@ -126,7 +126,7 @@ struct rvin_dev {struct v4l2_device v4l2_dev;struct v4l2_ctrl_handler ctrl_handler;struct v4l2_async_notifier notifier; -struct rvin_graph_entity digital; +struct rvin_graph_entity *digital;struct mutex lock;struct vb2_queue queue;@@ -145,7 +145,7 @@ struct rvin_dev {struct v4l2_rect compose; }; -#define vin_to_source (vin) vin->digital.subdev +#define vin_to_source (vin) ((vin)->digital->subdev)/* Debug */ #define vin_dbg(d, fmt, arg...) dev_dbg(d->dev, fmt, ##arg)drivers/media/v4l2-core/v4l2-fwnode.c +47
-0@@ -445,6 +445,53 @@ int v4l2_async_notifier_parse_fwnode_endpoints( }EXPORT_SYMBOL_GPL (v4l2_async_notifier_parse_fwnode_endpoints); +int v4l2_async_notifier_parse_fwnode_endpoint ( +struct device *dev,struct v4l2_async_notifier *notifier, +unsigned int port_id,unsigned int endpoint_id,size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)) +{ +struct fwnode_handle *fwnode =NULL ; +int ret; + +while ((fwnode =fwnode_graph_get_next_endpoint ( +dev_fwnode (dev), fwnode))) { +struct fwnode_endpoint ep; + + ret =fwnode_graph_parse_endpoint (fwnode, &ep); +if (ret <0 ) +continue ; + +if (!fwnode_device_is_available ( +fwnode_graph_get_port_parent (fwnode))) +continue ; + +if (ep.port == port_id && ep.id == endpoint_id) +break ; + } + +if (!fwnode) +return -ENOENT ; + + ret =notifier_realloc (notifier, notifier->num_subdevs +1 ); +if (ret) + goto out_err; + + ret =parse_endpoint (dev, notifier, fwnode, asd_struct_size, + parse_single); +if (ret) + goto out_err; + +return 0 ; + +out_err: +fwnode_handle_put (fwnode); + +return ret; +} +EXPORT_SYMBOL_GPL (v4l2_async_notifier_parse_fwnode_endpoint); +MODULE_LICENSE ("GPL" );MODULE_AUTHOR ("Sakari Ailus <[email protected]>" );MODULE_AUTHOR ("Sylwester Nawrocki <[email protected]>" );include/media/v4l2-fwnode.h +39
-0@@ -240,4 +240,43 @@ int v4l2_async_notifier_parse_fwnode_endpoints(struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd)); +/** +* v4l2_async_notifier_parse_fwnode_endpoint - Set up async notifier for an +* fwnode based sub-device +* @dev: @struct device pointer +* @notifier: pointer to &struct v4l2_async_notifier +* @port_id: port number +* @endpoint_id: endpoint number +* @asd_struct_size: size of the driver's async sub-device struct, including +* sizeof(struct v4l2_async_subdev). The &struct +* v4l2_async_subdev shall be the first member of +* the driver's async sub-device struct, i.e. both +* begin at the same memory address. +* @parse_single: driver's callback function called on each V4L2 fwnode endpoint +* +* Find an endpoint node for the given port and endpoint IDs and allocate an +* async sub-device struct for it. Parse the V4L2 fwnode endpoint and call the +* provided callback function. +* +* The function may not be called on a registered notifier. +* +* Once the user has called this function, the resources released by it need to +* be released by callin v4l2_async_notifier_release after the notifier has been +* unregistered and the sub-devices are no longer in use. +* +* A driver supporting fwnode (currently Devicetree and ACPI) should call this +* function as part of its probe function before it registers the notifier. +* +* Return: %0 on success, including when no async sub-devices are found +* %-ENOMEM if memory allocation failed +* %-EINVAL if graph or endpoint parsing failed +* Other error codes as returned by @parse_single +*/ +int v4l2_async_notifier_parse_fwnode_endpoint ( +struct device *dev,struct v4l2_async_notifier *notifier, +unsigned int port_id,unsigned int endpoint_id,size_t asd_struct_size, +int (*parse_single)(struct device *dev, +struct v4l2_fwnode_endpoint *vep, +struct v4l2_async_subdev *asd)); +#endif /* _V4L2_FWNODE_H */ Signature · 1 line
2.11.0
Signature · 2 lines
Regards, Niklas Söderlund