All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/5] Unified fwnode endpoint parser
@ 2017-08-29 11:03 ` Sakari Ailus
  0 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media-u79uwXL29TY76Z2rM5mHXA
  Cc: niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

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

-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v5 0/5] Unified fwnode endpoint parser
@ 2017-08-29 11:03 ` Sakari Ailus
  0 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media
  Cc: niklas.soderlund, robh, hverkuil, laurent.pinchart, devicetree

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

-- 
2.11.0

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
  2017-08-29 11:03 ` Sakari Ailus
@ 2017-08-29 11:03     ` Sakari Ailus
  -1 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media-u79uwXL29TY76Z2rM5mHXA
  Cc: niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

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 <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
 drivers/media/v4l2-core/v4l2-fwnode.c | 75 --------------------------------
 include/media/v4l2-fwnode.h           | 81 ++++++++++++++++++++++++++++++++++-
 2 files changed, 80 insertions(+), 76 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 40b2fbfe8865..706f9e7b90f1 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -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);
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
index 7adec9851d9e..68eb22ba571b 100644
--- a/include/media/v4l2-fwnode.h
+++ b/include/media/v4l2-fwnode.h
@@ -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 */
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
@ 2017-08-29 11:03     ` Sakari Ailus
  0 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media
  Cc: niklas.soderlund, robh, hverkuil, laurent.pinchart, devicetree

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 <sakari.ailus@linux.intel.com>
---
 drivers/media/v4l2-core/v4l2-fwnode.c | 75 --------------------------------
 include/media/v4l2-fwnode.h           | 81 ++++++++++++++++++++++++++++++++++-
 2 files changed, 80 insertions(+), 76 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 40b2fbfe8865..706f9e7b90f1 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -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);
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
index 7adec9851d9e..68eb22ba571b 100644
--- a/include/media/v4l2-fwnode.h
+++ b/include/media/v4l2-fwnode.h
@@ -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 */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 2/5] v4l: async: Add V4L2 async documentation to the documentation build
  2017-08-29 11:03 ` Sakari Ailus
@ 2017-08-29 11:03     ` Sakari Ailus
  -1 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media-u79uwXL29TY76Z2rM5mHXA
  Cc: niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

The V4L2 async wasn't part of the documentation build. Fix this.

Signed-off-by: Sakari Ailus <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
 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

diff --git a/Documentation/media/kapi/v4l2-async.rst b/Documentation/media/kapi/v4l2-async.rst
new file mode 100644
index 000000000000..523ff9eb09a0
--- /dev/null
+++ b/Documentation/media/kapi/v4l2-async.rst
@@ -0,0 +1,3 @@
+V4L2 async kAPI
+^^^^^^^^^^^^^^^
+.. kernel-doc:: include/media/v4l2-async.h
diff --git a/Documentation/media/kapi/v4l2-core.rst b/Documentation/media/kapi/v4l2-core.rst
index c7434f38fd9c..5cf292037a48 100644
--- a/Documentation/media/kapi/v4l2-core.rst
+++ b/Documentation/media/kapi/v4l2-core.rst
@@ -19,6 +19,7 @@ Video4Linux devices
     v4l2-mc
     v4l2-mediabus
     v4l2-mem2mem
+    v4l2-async
     v4l2-fwnode
     v4l2-rect
     v4l2-tuner
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 2/5] v4l: async: Add V4L2 async documentation to the documentation build
@ 2017-08-29 11:03     ` Sakari Ailus
  0 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media
  Cc: niklas.soderlund, robh, hverkuil, laurent.pinchart, devicetree

The V4L2 async wasn't part of the documentation build. Fix this.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 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

diff --git a/Documentation/media/kapi/v4l2-async.rst b/Documentation/media/kapi/v4l2-async.rst
new file mode 100644
index 000000000000..523ff9eb09a0
--- /dev/null
+++ b/Documentation/media/kapi/v4l2-async.rst
@@ -0,0 +1,3 @@
+V4L2 async kAPI
+^^^^^^^^^^^^^^^
+.. kernel-doc:: include/media/v4l2-async.h
diff --git a/Documentation/media/kapi/v4l2-core.rst b/Documentation/media/kapi/v4l2-core.rst
index c7434f38fd9c..5cf292037a48 100644
--- a/Documentation/media/kapi/v4l2-core.rst
+++ b/Documentation/media/kapi/v4l2-core.rst
@@ -19,6 +19,7 @@ Video4Linux devices
     v4l2-mc
     v4l2-mediabus
     v4l2-mem2mem
+    v4l2-async
     v4l2-fwnode
     v4l2-rect
     v4l2-tuner
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 3/5] docs-rst: v4l: Include Qualcomm CAMSS in documentation build
  2017-08-29 11:03 ` Sakari Ailus
  (?)
  (?)
@ 2017-08-29 11:03 ` Sakari Ailus
       [not found]   ` <20170829110313.19538-4-sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
  -1 siblings, 1 reply; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media
  Cc: niklas.soderlund, robh, hverkuil, laurent.pinchart, devicetree

Qualcomm CAMSS was left out from documentation build. Fix this.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 Documentation/media/v4l-drivers/index.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/media/v4l-drivers/index.rst b/Documentation/media/v4l-drivers/index.rst
index 10f2ce42ece2..5c202e23616b 100644
--- a/Documentation/media/v4l-drivers/index.rst
+++ b/Documentation/media/v4l-drivers/index.rst
@@ -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
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
  2017-08-29 11:03 ` Sakari Ailus
@ 2017-08-29 11:03     ` Sakari Ailus
  -1 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media-u79uwXL29TY76Z2rM5mHXA
  Cc: niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

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 <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
 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(-)

diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 1a428fe9f070..a546cf774d40 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -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;
 }
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index e528df6efc09..8b9043db94b3 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -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) \
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 851f128eba22..c490acf5ae82 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -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;
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 706f9e7b90f1..39a587c6992a 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -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);
 
+static int notifier_realloc(struct v4l2_async_notifier *notifier,
+			    unsigned int max_subdevs)
+{
+	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);
+	if (!subdevs)
+		return -ENOMEM;
+
+	if (notifier->subdevs) {
+		for (i = 0; i < notifier->num_subdevs; i++)
+			subdevs[i] = notifier->subdevs[i];
+
+		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) {
+		dev_warn(dev, "bad remote port parent\n");
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	/* Ignore endpoints the parsing of which failed. */
+	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/CA@public.gmane.org>");
 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>");
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index c69d8c8a66d0..4a44ab47ab04 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -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
  *
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
index 68eb22ba571b..46521e8c8872 100644
--- a/include/media/v4l2-fwnode.h
+++ b/include/media/v4l2-fwnode.h
@@ -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 */
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
@ 2017-08-29 11:03     ` Sakari Ailus
  0 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media
  Cc: niklas.soderlund, robh, hverkuil, laurent.pinchart, devicetree

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 <sakari.ailus@linux.intel.com>
---
 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(-)

diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 1a428fe9f070..a546cf774d40 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -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;
 }
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index e528df6efc09..8b9043db94b3 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -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) \
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 851f128eba22..c490acf5ae82 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -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;
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 706f9e7b90f1..39a587c6992a 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -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);
 
+static int notifier_realloc(struct v4l2_async_notifier *notifier,
+			    unsigned int max_subdevs)
+{
+	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);
+	if (!subdevs)
+		return -ENOMEM;
+
+	if (notifier->subdevs) {
+		for (i = 0; i < notifier->num_subdevs; i++)
+			subdevs[i] = notifier->subdevs[i];
+
+		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) {
+		dev_warn(dev, "bad remote port parent\n");
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	/* Ignore endpoints the parsing of which failed. */
+	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@linux.intel.com>");
 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index c69d8c8a66d0..4a44ab47ab04 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -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
  *
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
index 68eb22ba571b..46521e8c8872 100644
--- a/include/media/v4l2-fwnode.h
+++ b/include/media/v4l2-fwnode.h
@@ -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 */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v5 5/5] v4l: fwnode: Support generic parsing of graph endpoints in a single port
  2017-08-29 11:03 ` Sakari Ailus
                   ` (2 preceding siblings ...)
  (?)
@ 2017-08-29 11:03 ` Sakari Ailus
  2017-08-29 12:57   ` Niklas Söderlund
  -1 siblings, 1 reply; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 11:03 UTC (permalink / raw)
  To: linux-media
  Cc: niklas.soderlund, robh, hverkuil, laurent.pinchart, devicetree

This is the preferred way to parse the endpoints.

Comment rcar-vin as an example.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 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(-)

diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
index 142de447aaaa..4378806be1d4 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/rcar-vin/rcar-core.c
@@ -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);
 
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
index b136844499f6..23fdff7a7370 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/rcar-vin/rcar-dma.c
@@ -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;
 
 	/*
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
index dd37ea811680..b479b882da12 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
@@ -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);
 
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
index 9bfb5a7c4dc4..5382078143fb 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/rcar-vin/rcar-vin.h
@@ -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)
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 39a587c6992a..e1a07916b9ca 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -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 <sakari.ailus@linux.intel.com>");
 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
index 46521e8c8872..6bc7b02b2f46 100644
--- a/include/media/v4l2-fwnode.h
+++ b/include/media/v4l2-fwnode.h
@@ -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 */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
  2017-08-29 11:03     ` Sakari Ailus
@ 2017-08-29 12:50         ` Niklas Söderlund
  -1 siblings, 0 replies; 27+ messages in thread
From: Niklas Söderlund @ 2017-08-29 12:50 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA, robh-DgEjT+Ai2ygdnm+yROfE0A,
	hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Sakari,

Thanks for your patch.

On 2017-08-29 14:03:09 +0300, 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 <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>

Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas-1zkq55x86MTxsAP9Fp7wbw@public.gmane.org>

> ---
>  drivers/media/v4l2-core/v4l2-fwnode.c | 75 --------------------------------
>  include/media/v4l2-fwnode.h           | 81 ++++++++++++++++++++++++++++++++++-
>  2 files changed, 80 insertions(+), 76 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> index 40b2fbfe8865..706f9e7b90f1 100644
> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> @@ -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);
> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> index 7adec9851d9e..68eb22ba571b 100644
> --- a/include/media/v4l2-fwnode.h
> +++ b/include/media/v4l2-fwnode.h
> @@ -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 */
> -- 
> 2.11.0
> 

-- 
Regards,
Niklas Söderlund
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
@ 2017-08-29 12:50         ` Niklas Söderlund
  0 siblings, 0 replies; 27+ messages in thread
From: Niklas Söderlund @ 2017-08-29 12:50 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, robh, hverkuil, laurent.pinchart, devicetree

Hi Sakari,

Thanks for your patch.

On 2017-08-29 14:03:09 +0300, 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 <sakari.ailus@linux.intel.com>

Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

> ---
>  drivers/media/v4l2-core/v4l2-fwnode.c | 75 --------------------------------
>  include/media/v4l2-fwnode.h           | 81 ++++++++++++++++++++++++++++++++++-
>  2 files changed, 80 insertions(+), 76 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> index 40b2fbfe8865..706f9e7b90f1 100644
> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> @@ -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);
> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> index 7adec9851d9e..68eb22ba571b 100644
> --- a/include/media/v4l2-fwnode.h
> +++ b/include/media/v4l2-fwnode.h
> @@ -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 */
> -- 
> 2.11.0
> 

-- 
Regards,
Niklas Söderlund

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 2/5] v4l: async: Add V4L2 async documentation to the documentation build
  2017-08-29 11:03     ` Sakari Ailus
  (?)
@ 2017-08-29 12:52     ` Niklas Söderlund
  -1 siblings, 0 replies; 27+ messages in thread
From: Niklas Söderlund @ 2017-08-29 12:52 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, robh, hverkuil, laurent.pinchart, devicetree

Hi Sakari,

Thanks for your patch.

On 2017-08-29 14:03:10 +0300, Sakari Ailus wrote:
> The V4L2 async wasn't part of the documentation build. Fix this.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

> ---
>  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
> 
> diff --git a/Documentation/media/kapi/v4l2-async.rst b/Documentation/media/kapi/v4l2-async.rst
> new file mode 100644
> index 000000000000..523ff9eb09a0
> --- /dev/null
> +++ b/Documentation/media/kapi/v4l2-async.rst
> @@ -0,0 +1,3 @@
> +V4L2 async kAPI
> +^^^^^^^^^^^^^^^
> +.. kernel-doc:: include/media/v4l2-async.h
> diff --git a/Documentation/media/kapi/v4l2-core.rst b/Documentation/media/kapi/v4l2-core.rst
> index c7434f38fd9c..5cf292037a48 100644
> --- a/Documentation/media/kapi/v4l2-core.rst
> +++ b/Documentation/media/kapi/v4l2-core.rst
> @@ -19,6 +19,7 @@ Video4Linux devices
>      v4l2-mc
>      v4l2-mediabus
>      v4l2-mem2mem
> +    v4l2-async
>      v4l2-fwnode
>      v4l2-rect
>      v4l2-tuner
> -- 
> 2.11.0
> 

-- 
Regards,
Niklas Söderlund

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 5/5] v4l: fwnode: Support generic parsing of graph endpoints in a single port
  2017-08-29 11:03 ` [PATCH v5 5/5] v4l: fwnode: Support generic parsing of graph endpoints in a single port Sakari Ailus
@ 2017-08-29 12:57   ` Niklas Söderlund
  0 siblings, 0 replies; 27+ messages in thread
From: Niklas Söderlund @ 2017-08-29 12:57 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, robh, hverkuil, laurent.pinchart, devicetree

Hi Sakari,

Thanks for your patch, I like how this turned out :-)

On 2017-08-29 14:03:13 +0300, Sakari Ailus wrote:
> This is the preferred way to parse the endpoints.
> 
> Comment rcar-vin as an example.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Acked-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

> ---
>  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(-)
> 
> diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c
> index 142de447aaaa..4378806be1d4 100644
> --- a/drivers/media/platform/rcar-vin/rcar-core.c
> +++ b/drivers/media/platform/rcar-vin/rcar-core.c
> @@ -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);
>  
> diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/rcar-vin/rcar-dma.c
> index b136844499f6..23fdff7a7370 100644
> --- a/drivers/media/platform/rcar-vin/rcar-dma.c
> +++ b/drivers/media/platform/rcar-vin/rcar-dma.c
> @@ -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;
>  
>  	/*
> diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c
> index dd37ea811680..b479b882da12 100644
> --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
> +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c
> @@ -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);
>  
> diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/rcar-vin/rcar-vin.h
> index 9bfb5a7c4dc4..5382078143fb 100644
> --- a/drivers/media/platform/rcar-vin/rcar-vin.h
> +++ b/drivers/media/platform/rcar-vin/rcar-vin.h
> @@ -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)
> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
> index 39a587c6992a..e1a07916b9ca 100644
> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> @@ -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 <sakari.ailus@linux.intel.com>");
>  MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> index 46521e8c8872..6bc7b02b2f46 100644
> --- a/include/media/v4l2-fwnode.h
> +++ b/include/media/v4l2-fwnode.h
> @@ -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 */
> -- 
> 2.11.0
> 

-- 
Regards,
Niklas Söderlund

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
  2017-08-29 11:03     ` Sakari Ailus
  (?)
  (?)
@ 2017-08-29 13:15     ` Laurent Pinchart
  2017-08-29 13:20         ` Sakari Ailus
  -1 siblings, 1 reply; 27+ messages in thread
From: Laurent Pinchart @ 2017-08-29 13:15 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, niklas.soderlund, robh, hverkuil, devicetree

Hi Sakari,

Thank you for the patch.

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 <sakari.ailus@linux.intel.com>

I prefer documenting functions in the C file. Documentation in header files 
will get out-of-sync with the implementation much more easily.

> ---
> drivers/media/v4l2-core/v4l2-fwnode.c | 75 --------------------------------
> include/media/v4l2-fwnode.h           | 81 ++++++++++++++++++++++++++++++++-
> 2 files changed, 80 insertions(+), 76 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c
> b/drivers/media/v4l2-core/v4l2-fwnode.c index 40b2fbfe8865..706f9e7b90f1
> 100644
> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> @@ -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);
> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> index 7adec9851d9e..68eb22ba571b 100644
> --- a/include/media/v4l2-fwnode.h
> +++ b/include/media/v4l2-fwnode.h
> @@ -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 */


-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
  2017-08-29 13:15     ` Laurent Pinchart
@ 2017-08-29 13:20         ` Sakari Ailus
  0 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 13:20 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
	niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Laurent,

On Tue, Aug 29, 2017 at 04:15:22PM +0300, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> 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 <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> 
> 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.

-- 
Sakari Ailus
sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
@ 2017-08-29 13:20         ` Sakari Ailus
  0 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 13:20 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linux-media, niklas.soderlund, robh, hverkuil, devicetree

Hi Laurent,

On Tue, Aug 29, 2017 at 04:15:22PM +0300, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.
> 
> 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 <sakari.ailus@linux.intel.com>
> 
> 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.

-- 
Sakari Ailus
sakari.ailus@linux.intel.com

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header
  2017-08-29 13:20         ` Sakari Ailus
  (?)
@ 2017-08-29 13:22         ` Sakari Ailus
  -1 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 13:22 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: linux-media, niklas.soderlund, robh, hverkuil, devicetree

On Tue, Aug 29, 2017 at 04:20:10PM +0300, Sakari Ailus wrote:
> Hi Laurent,
> 
> On Tue, Aug 29, 2017 at 04:15:22PM +0300, Laurent Pinchart wrote:
> > Hi Sakari,
> > 
> > Thank you for the patch.
> > 
> > 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 <sakari.ailus@linux.intel.com>
> > 
> > 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.

-- 
Sakari Ailus
sakari.ailus@linux.intel.com

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 3/5] docs-rst: v4l: Include Qualcomm CAMSS in documentation build
  2017-08-29 11:03 ` [PATCH v5 3/5] docs-rst: v4l: Include Qualcomm CAMSS in documentation build Sakari Ailus
@ 2017-08-29 13:32       ` Laurent Pinchart
  0 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2017-08-29 13:32 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
	niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Sakari,

Thank you for the patch.

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 <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>

I've tested the qcom_camss documentation build and haven't noticed any new 
warning or error, so

Reviewed-by: Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>

> ---
>  Documentation/media/v4l-drivers/index.rst | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/Documentation/media/v4l-drivers/index.rst
> b/Documentation/media/v4l-drivers/index.rst index
> 10f2ce42ece2..5c202e23616b 100644
> --- a/Documentation/media/v4l-drivers/index.rst
> +++ b/Documentation/media/v4l-drivers/index.rst
> @@ -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


-- 
Regards,

Laurent Pinchart

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 3/5] docs-rst: v4l: Include Qualcomm CAMSS in documentation build
@ 2017-08-29 13:32       ` Laurent Pinchart
  0 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2017-08-29 13:32 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, niklas.soderlund, robh, hverkuil, devicetree

Hi Sakari,

Thank you for the patch.

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 <sakari.ailus@linux.intel.com>

I've tested the qcom_camss documentation build and haven't noticed any new 
warning or error, so

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  Documentation/media/v4l-drivers/index.rst | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/Documentation/media/v4l-drivers/index.rst
> b/Documentation/media/v4l-drivers/index.rst index
> 10f2ce42ece2..5c202e23616b 100644
> --- a/Documentation/media/v4l-drivers/index.rst
> +++ b/Documentation/media/v4l-drivers/index.rst
> @@ -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


-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
  2017-08-29 11:03     ` Sakari Ailus
@ 2017-08-29 14:02         ` Laurent Pinchart
  -1 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2017-08-29 14:02 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
	niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Sakari,

Thank you for the patch.

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 <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> ---
>  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(-)
> 
> diff --git a/drivers/media/platform/omap3isp/isp.c
> b/drivers/media/platform/omap3isp/isp.c index 1a428fe9f070..a546cf774d40
> 100644
> --- a/drivers/media/platform/omap3isp/isp.c
> +++ b/drivers/media/platform/omap3isp/isp.c
> @@ -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;
>  }
> diff --git a/drivers/media/platform/omap3isp/isp.h
> b/drivers/media/platform/omap3isp/isp.h index e528df6efc09..8b9043db94b3
> 100644
> --- a/drivers/media/platform/omap3isp/isp.h
> +++ b/drivers/media/platform/omap3isp/isp.h
> @@ -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) \
> diff --git a/drivers/media/v4l2-core/v4l2-async.c
> b/drivers/media/v4l2-core/v4l2-async.c index 851f128eba22..c490acf5ae82
> 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -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;
> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c
> b/drivers/media/v4l2-core/v4l2-fwnode.c index 706f9e7b90f1..39a587c6992a
> 100644
> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> @@ -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);
> 
> +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.

> +{
> +	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.

> +	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 :-)

> +		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) {
> +		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.

> +	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/CA@public.gmane.org>");
>  MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>");
> diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> index c69d8c8a66d0..4a44ab47ab04 100644
> --- a/include/media/v4l2-async.h
> +++ b/include/media/v4l2-async.h
> @@ -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

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".

> + *
> + * 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.

> + */
> +void v4l2_async_notifier_release(struct v4l2_async_notifier *notifier);
> +
> +/**
>   * v4l2_async_register_subdev - registers a sub-device to the asynchronous
>   * 	subdevice framework
>   *
> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> index 68eb22ba571b..46521e8c8872 100644
> --- a/include/media/v4l2-fwnode.h
> +++ b/include/media/v4l2-fwnode.h
> @@ -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

Similarly to my previous comment (and my comments to v3), you should tell 
which device the function expects.

> + * @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 
?

> + * @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."

> + * 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."

> + *
> + * 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.

"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."

> + *
> + * 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 */


-- 
Regards,

Laurent Pinchart

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
@ 2017-08-29 14:02         ` Laurent Pinchart
  0 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2017-08-29 14:02 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, niklas.soderlund, robh, hverkuil, devicetree

Hi Sakari,

Thank you for the patch.

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 <sakari.ailus@linux.intel.com>
> ---
>  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(-)
> 
> diff --git a/drivers/media/platform/omap3isp/isp.c
> b/drivers/media/platform/omap3isp/isp.c index 1a428fe9f070..a546cf774d40
> 100644
> --- a/drivers/media/platform/omap3isp/isp.c
> +++ b/drivers/media/platform/omap3isp/isp.c
> @@ -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;
>  }
> diff --git a/drivers/media/platform/omap3isp/isp.h
> b/drivers/media/platform/omap3isp/isp.h index e528df6efc09..8b9043db94b3
> 100644
> --- a/drivers/media/platform/omap3isp/isp.h
> +++ b/drivers/media/platform/omap3isp/isp.h
> @@ -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) \
> diff --git a/drivers/media/v4l2-core/v4l2-async.c
> b/drivers/media/v4l2-core/v4l2-async.c index 851f128eba22..c490acf5ae82
> 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -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;
> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c
> b/drivers/media/v4l2-core/v4l2-fwnode.c index 706f9e7b90f1..39a587c6992a
> 100644
> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> @@ -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);
> 
> +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.

> +{
> +	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.

> +	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 :-)

> +		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) {
> +		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.

> +	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@linux.intel.com>");
>  MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
> diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> index c69d8c8a66d0..4a44ab47ab04 100644
> --- a/include/media/v4l2-async.h
> +++ b/include/media/v4l2-async.h
> @@ -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

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".

> + *
> + * 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.

> + */
> +void v4l2_async_notifier_release(struct v4l2_async_notifier *notifier);
> +
> +/**
>   * v4l2_async_register_subdev - registers a sub-device to the asynchronous
>   * 	subdevice framework
>   *
> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> index 68eb22ba571b..46521e8c8872 100644
> --- a/include/media/v4l2-fwnode.h
> +++ b/include/media/v4l2-fwnode.h
> @@ -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

Similarly to my previous comment (and my comments to v3), you should tell 
which device the function expects.

> + * @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 
?

> + * @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."

> + * 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."

> + *
> + * 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.

"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."

> + *
> + * 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 */


-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
  2017-08-29 14:02         ` Laurent Pinchart
@ 2017-08-29 14:31           ` Sakari Ailus
  -1 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 14:31 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Sakari Ailus, linux-media-u79uwXL29TY76Z2rM5mHXA,
	niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Laurent,

On Tue, Aug 29, 2017 at 05:02:54PM +0300, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.

Thanks for the review!

> 
> 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 <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> > ---
> >  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(-)
> > 
> > diff --git a/drivers/media/platform/omap3isp/isp.c
> > b/drivers/media/platform/omap3isp/isp.c index 1a428fe9f070..a546cf774d40
> > 100644
> > --- a/drivers/media/platform/omap3isp/isp.c
> > +++ b/drivers/media/platform/omap3isp/isp.c
> > @@ -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;
> >  }
> > diff --git a/drivers/media/platform/omap3isp/isp.h
> > b/drivers/media/platform/omap3isp/isp.h index e528df6efc09..8b9043db94b3
> > 100644
> > --- a/drivers/media/platform/omap3isp/isp.h
> > +++ b/drivers/media/platform/omap3isp/isp.h
> > @@ -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) \
> > diff --git a/drivers/media/v4l2-core/v4l2-async.c
> > b/drivers/media/v4l2-core/v4l2-async.c index 851f128eba22..c490acf5ae82
> > 100644
> > --- a/drivers/media/v4l2-core/v4l2-async.c
> > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > @@ -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;
> > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c
> > b/drivers/media/v4l2-core/v4l2-fwnode.c index 706f9e7b90f1..39a587c6992a
> > 100644
> > --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > @@ -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);
> > 
> > +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.

> 
> > +{
> > +	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.

> 
> > +	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 :-)

I don't agree but I don't have a strong opinion about this, I can change
it.

> 
> > +		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) {
> > +		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?

> 
> > +	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/CA@public.gmane.org>");
> >  MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>");
> > diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> > index c69d8c8a66d0..4a44ab47ab04 100644
> > --- a/include/media/v4l2-async.h
> > +++ b/include/media/v4l2-async.h
> > @@ -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
> 
> 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.

> 
> > + *
> > + * 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.

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.

> 
> > + */
> > +void v4l2_async_notifier_release(struct v4l2_async_notifier *notifier);
> > +
> > +/**
> >   * v4l2_async_register_subdev - registers a sub-device to the asynchronous
> >   * 	subdevice framework
> >   *
> > diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> > index 68eb22ba571b..46521e8c8872 100644
> > --- a/include/media/v4l2-fwnode.h
> > +++ b/include/media/v4l2-fwnode.h
> > @@ -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
> 
> Similarly to my previous comment (and my comments to v3), you should tell 
> which device the function expects.

Will fix for the next version.

> 
> > + * @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 
> ?

Yes, I'll add that. It won't hurt to make it a requirement even if the
helper functions weren't used.

> 
> > + * @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."

Works for me.

> 
> > + * 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.

> 
> "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?

> 
> > + *
> > + * 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.
> 
> "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.

> 
> > + *
> > + * 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 */
> 
> 

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus-X3B1VOXEql0@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
@ 2017-08-29 14:31           ` Sakari Ailus
  0 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-08-29 14:31 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Sakari Ailus, linux-media, niklas.soderlund, robh, hverkuil, devicetree

Hi Laurent,

On Tue, Aug 29, 2017 at 05:02:54PM +0300, Laurent Pinchart wrote:
> Hi Sakari,
> 
> Thank you for the patch.

Thanks for the review!

> 
> 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 <sakari.ailus@linux.intel.com>
> > ---
> >  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(-)
> > 
> > diff --git a/drivers/media/platform/omap3isp/isp.c
> > b/drivers/media/platform/omap3isp/isp.c index 1a428fe9f070..a546cf774d40
> > 100644
> > --- a/drivers/media/platform/omap3isp/isp.c
> > +++ b/drivers/media/platform/omap3isp/isp.c
> > @@ -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;
> >  }
> > diff --git a/drivers/media/platform/omap3isp/isp.h
> > b/drivers/media/platform/omap3isp/isp.h index e528df6efc09..8b9043db94b3
> > 100644
> > --- a/drivers/media/platform/omap3isp/isp.h
> > +++ b/drivers/media/platform/omap3isp/isp.h
> > @@ -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) \
> > diff --git a/drivers/media/v4l2-core/v4l2-async.c
> > b/drivers/media/v4l2-core/v4l2-async.c index 851f128eba22..c490acf5ae82
> > 100644
> > --- a/drivers/media/v4l2-core/v4l2-async.c
> > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > @@ -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;
> > diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c
> > b/drivers/media/v4l2-core/v4l2-fwnode.c index 706f9e7b90f1..39a587c6992a
> > 100644
> > --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> > +++ b/drivers/media/v4l2-core/v4l2-fwnode.c
> > @@ -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);
> > 
> > +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.

> 
> > +{
> > +	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.

> 
> > +	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 :-)

I don't agree but I don't have a strong opinion about this, I can change
it.

> 
> > +		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) {
> > +		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?

> 
> > +	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@linux.intel.com>");
> >  MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
> > diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> > index c69d8c8a66d0..4a44ab47ab04 100644
> > --- a/include/media/v4l2-async.h
> > +++ b/include/media/v4l2-async.h
> > @@ -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
> 
> 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.

> 
> > + *
> > + * 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.

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.

> 
> > + */
> > +void v4l2_async_notifier_release(struct v4l2_async_notifier *notifier);
> > +
> > +/**
> >   * v4l2_async_register_subdev - registers a sub-device to the asynchronous
> >   * 	subdevice framework
> >   *
> > diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> > index 68eb22ba571b..46521e8c8872 100644
> > --- a/include/media/v4l2-fwnode.h
> > +++ b/include/media/v4l2-fwnode.h
> > @@ -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
> 
> Similarly to my previous comment (and my comments to v3), you should tell 
> which device the function expects.

Will fix for the next version.

> 
> > + * @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 
> ?

Yes, I'll add that. It won't hurt to make it a requirement even if the
helper functions weren't used.

> 
> > + * @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."

Works for me.

> 
> > + * 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.

> 
> "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?

> 
> > + *
> > + * 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.
> 
> "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.

> 
> > + *
> > + * 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 */
> 
> 

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
  2017-08-29 14:31           ` Sakari Ailus
@ 2017-09-01  8:55               ` Laurent Pinchart
  -1 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2017-09-01  8:55 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Sakari Ailus, linux-media-u79uwXL29TY76Z2rM5mHXA,
	niklas.soderlund-1zkq55x86MTxsAP9Fp7wbw,
	robh-DgEjT+Ai2ygdnm+yROfE0A, hverkuil-qWit8jRvyhVmR6Xm/wNWPw,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Sakari,

On Tuesday, 29 August 2017 17:31:21 EEST Sakari Ailus wrote:
> On Tue, Aug 29, 2017 at 05:02:54PM +0300, Laurent Pinchart wrote:
> > 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 <sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> >> ---
> >> 
> >>  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]

> >> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c
> >> b/drivers/media/v4l2-core/v4l2-fwnode.c index 706f9e7b90f1..39a587c6992a
> >> 100644
> >> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> >> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c

[snip]

> >> +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) {
> >> +		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?

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 ;-)).

> >> +	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]

> >> diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> >> index c69d8c8a66d0..4a44ab47ab04 100644
> >> --- a/include/media/v4l2-async.h
> >> +++ b/include/media/v4l2-async.h


> >> @@ -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".
> 
> This fully matches to the documentation elsewhere in the same file. :-)

Feel free to fix the rest of the file :-)

> I'll replace the text with yours.
> 
> >> + *
> >> + * 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.
> 
> 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.
> 
> >> + */
> >> +void v4l2_async_notifier_release(struct v4l2_async_notifier *notifier);
> >> +
> >> +/**
> >>   * v4l2_async_register_subdev - registers a sub-device to the
> >>   asynchronous
> >>   * 	subdevice framework
> >>   *
> >> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> >> index 68eb22ba571b..46521e8c8872 100644
> >> --- a/include/media/v4l2-fwnode.h
> >> +++ b/include/media/v4l2-fwnode.h

[snip]

> >> @@ -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
> > 
> > Similarly to my previous comment (and my comments to v3), you should tell
> > which device the function expects.
> 
> Will fix for the next version.
> 
> >> + * @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 ?
> 
> Yes, I'll add that. It won't hurt to make it a requirement even if the
> helper functions weren't used.
> 
> >> + * @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."
> 
> Works for me.
> 
> > > + * 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.
> 
> > "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.

> >> + *
> >> + * 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.
> > 
> > "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.
> 
> >> + *
> >> + * 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 */

-- 
Regards,

Laurent Pinchart

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
@ 2017-09-01  8:55               ` Laurent Pinchart
  0 siblings, 0 replies; 27+ messages in thread
From: Laurent Pinchart @ 2017-09-01  8:55 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: Sakari Ailus, linux-media, niklas.soderlund, robh, hverkuil, devicetree

Hi Sakari,

On Tuesday, 29 August 2017 17:31:21 EEST Sakari Ailus wrote:
> On Tue, Aug 29, 2017 at 05:02:54PM +0300, Laurent Pinchart wrote:
> > 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 <sakari.ailus@linux.intel.com>
> >> ---
> >> 
> >>  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]

> >> diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c
> >> b/drivers/media/v4l2-core/v4l2-fwnode.c index 706f9e7b90f1..39a587c6992a
> >> 100644
> >> --- a/drivers/media/v4l2-core/v4l2-fwnode.c
> >> +++ b/drivers/media/v4l2-core/v4l2-fwnode.c

[snip]

> >> +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) {
> >> +		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?

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 ;-)).

> >> +	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]

> >> diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
> >> index c69d8c8a66d0..4a44ab47ab04 100644
> >> --- a/include/media/v4l2-async.h
> >> +++ b/include/media/v4l2-async.h


> >> @@ -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".
> 
> This fully matches to the documentation elsewhere in the same file. :-)

Feel free to fix the rest of the file :-)

> I'll replace the text with yours.
> 
> >> + *
> >> + * 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.
> 
> 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.
> 
> >> + */
> >> +void v4l2_async_notifier_release(struct v4l2_async_notifier *notifier);
> >> +
> >> +/**
> >>   * v4l2_async_register_subdev - registers a sub-device to the
> >>   asynchronous
> >>   * 	subdevice framework
> >>   *
> >> diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h
> >> index 68eb22ba571b..46521e8c8872 100644
> >> --- a/include/media/v4l2-fwnode.h
> >> +++ b/include/media/v4l2-fwnode.h

[snip]

> >> @@ -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
> > 
> > Similarly to my previous comment (and my comments to v3), you should tell
> > which device the function expects.
> 
> Will fix for the next version.
> 
> >> + * @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 ?
> 
> Yes, I'll add that. It won't hurt to make it a requirement even if the
> helper functions weren't used.
> 
> >> + * @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."
> 
> Works for me.
> 
> > > + * 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.
> 
> > "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.

> >> + *
> >> + * 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.
> > 
> > "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.
> 
> >> + *
> >> + * 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 */

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device
  2017-09-01  8:55               ` Laurent Pinchart
  (?)
@ 2017-09-01  9:04               ` Sakari Ailus
  -1 siblings, 0 replies; 27+ messages in thread
From: Sakari Ailus @ 2017-09-01  9:04 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Sakari Ailus, linux-media, niklas.soderlund, robh, hverkuil, devicetree

Hi Laurent,

On Fri, Sep 01, 2017 at 11:55:43AM +0300, Laurent Pinchart wrote:
...
> > >> +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) {
> > >> +		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?
> 
> 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. :-)

> > >> @@ -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".
> > 
> > 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.

> > > "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.

-- 
Regards,

Sakari Ailus
sakari.ailus@linux.intel.com

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2017-09-01  9:04 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-29 11:03 [PATCH v5 0/5] Unified fwnode endpoint parser Sakari Ailus
2017-08-29 11:03 ` Sakari Ailus
     [not found] ` <20170829110313.19538-1-sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2017-08-29 11:03   ` [PATCH v5 1/5] v4l: fwnode: Move KernelDoc documentation to the header Sakari Ailus
2017-08-29 11:03     ` Sakari Ailus
     [not found]     ` <20170829110313.19538-2-sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2017-08-29 12:50       ` Niklas Söderlund
2017-08-29 12:50         ` Niklas Söderlund
2017-08-29 13:15     ` Laurent Pinchart
2017-08-29 13:20       ` Sakari Ailus
2017-08-29 13:20         ` Sakari Ailus
2017-08-29 13:22         ` Sakari Ailus
2017-08-29 11:03   ` [PATCH v5 2/5] v4l: async: Add V4L2 async documentation to the documentation build Sakari Ailus
2017-08-29 11:03     ` Sakari Ailus
2017-08-29 12:52     ` Niklas Söderlund
2017-08-29 11:03   ` [PATCH v5 4/5] v4l: fwnode: Support generic parsing of graph endpoints in a device Sakari Ailus
2017-08-29 11:03     ` Sakari Ailus
     [not found]     ` <20170829110313.19538-5-sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2017-08-29 14:02       ` Laurent Pinchart
2017-08-29 14:02         ` Laurent Pinchart
2017-08-29 14:31         ` Sakari Ailus
2017-08-29 14:31           ` Sakari Ailus
     [not found]           ` <20170829143121.6sjdx3lgcoxm6mva-S+BSfZ9RZZmRSg0ZkenSGLdO1Tsj/99ntUK59QYPAWc@public.gmane.org>
2017-09-01  8:55             ` Laurent Pinchart
2017-09-01  8:55               ` Laurent Pinchart
2017-09-01  9:04               ` Sakari Ailus
2017-08-29 11:03 ` [PATCH v5 3/5] docs-rst: v4l: Include Qualcomm CAMSS in documentation build Sakari Ailus
     [not found]   ` <20170829110313.19538-4-sakari.ailus-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
2017-08-29 13:32     ` Laurent Pinchart
2017-08-29 13:32       ` Laurent Pinchart
2017-08-29 11:03 ` [PATCH v5 5/5] v4l: fwnode: Support generic parsing of graph endpoints in a single port Sakari Ailus
2017-08-29 12:57   ` Niklas Söderlund

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.