Re: [PATCH 03/18] media: v4l: async: Simplify async sub-device fwnode matching

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jacopo,

On Thu, Apr 13, 2023 at 06:50:04PM +0200, Jacopo Mondi wrote:
> Hi Sakari
> 
> On Thu, Mar 30, 2023 at 02:58:38PM +0300, Sakari Ailus wrote:
> > V4L2 async sub-device matching originally used the device nodes only.
> > Endpoint nodes were taken into use instead as using the device nodes was
> > problematic for it was in some cases ambiguous which link might have been
> > in question.
> >
> > There is however no need to use endpoint nodes on both sides, as the async
> > sub-device's fwnode can always be trivially obtained using
> > fwnode_graph_get_remote_endpoint() when needed while what counts is
> > whether or not the link is between two device nodes, i.e. the device nodes
> > match.
> >
> 
> As you know I'm a bit debated.
> 
> Strict endpoint-matching requires one subdev to be registed per each
> endpoint, and this is tedious for drivers that have to register a
> subdev for each of its endpoints
> 
> Allowing a subdev to be matched multiple times on different endpoints
> gives a way for lazy drivers to take a shortcut and simplify their
> topologies to a single subdev, when they would actually need more.

I'd say this is really about interface design, not being "lazy". It depends
on the sub-device. Ideally the framework should be also as easy for drivers
drivers to use as possible.

What is not supported, though, is multiple sub-devices with a single device
node. Do we need that? At least I don't think I came across a driver that
would.

Although it would be relatively easy add this in form of a list of
endpoints, if there's a need to.

Also cc Niklas.

> 
> Also, knowing where this series is going, I wonder if this wouldn't be
> a good time to enforce all async subdevices to be registered with an
> endpoint. From a very quick look at the drivers we have in mainline
> only a few still use v4l2_async_nf_add_fwnode() and only 4 of them
> (camss, rcar_drif, am437x-vpfe, vpif_capture, xilinx_vipp) use as
> match.fwnode what the remote port parent.
> 
> I wonder if this would be a good occasione to enforce with a WARN() if
> !fwnode_graph_is_endpoint(fwnode) in v4l2_async_nf_add_fwnode() (or
> possibily, even remove that function and port all drivers to use
> vl2_async_nf_add_fwnode_remote(). I can help, if the above makes sense
> to you as well.

I prefer to keep things simple for drivers if possible, and this patch does
that by removing a noticeable number of lines from a few drivers.

> 
> > Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
> > ---
> >  drivers/media/i2c/adv748x/adv748x-csi2.c |  3 -
> >  drivers/media/i2c/max9286.c              | 14 +---
> >  drivers/media/i2c/rdacm20.c              | 15 +----
> >  drivers/media/i2c/rdacm21.c              | 15 +----
> >  drivers/media/i2c/tc358746.c             |  3 -
> >  drivers/media/v4l2-core/v4l2-async.c     | 86 ++++++------------------
> >  6 files changed, 24 insertions(+), 112 deletions(-)
> >
> > diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c
> > index bd4f3fe0e309..3d830816243f 100644
> > --- a/drivers/media/i2c/adv748x/adv748x-csi2.c
> > +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c
> > @@ -300,9 +300,6 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx)
> >  			    MEDIA_ENT_F_VID_IF_BRIDGE,
> >  			    is_txa(tx) ? "txa" : "txb");
> >
> > -	/* Ensure that matching is based upon the endpoint fwnodes */
> > -	tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]);
> > -
> >  	/* Register internal ops for incremental subdev registration */
> >  	tx->sd.internal_ops = &adv748x_csi2_internal_ops;
> >
> > diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> > index 701038d6d19b..2d0f43e3fb9f 100644
> > --- a/drivers/media/i2c/max9286.c
> > +++ b/drivers/media/i2c/max9286.c
> > @@ -1051,7 +1051,6 @@ static const struct v4l2_ctrl_ops max9286_ctrl_ops = {
> >  static int max9286_v4l2_register(struct max9286_priv *priv)
> >  {
> >  	struct device *dev = &priv->client->dev;
> > -	struct fwnode_handle *ep;
> >  	int ret;
> >  	int i;
> >
> > @@ -1093,25 +1092,14 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
> >  	if (ret)
> >  		goto err_async;
> >
> > -	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD,
> > -					     0, 0);
> > -	if (!ep) {
> > -		dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n");
> > -		ret = -ENOENT;
> > -		goto err_async;
> > -	}
> > -	priv->sd.fwnode = ep;
> > -
> 
> 
> You should also remove the fwnode_handle_put() call from
> 
> static void max9286_v4l2_unregister(struct max9286_priv *priv)
> {
> 	fwnode_handle_put(priv->sd.fwnode);
> 	v4l2_async_unregister_subdev(&priv->sd);
> 	max9286_v4l2_notifier_unregister(priv);
> }

Thanks, I'll address these in v2.

> 
> >  	ret = v4l2_async_register_subdev(&priv->sd);
> >  	if (ret < 0) {
> >  		dev_err(dev, "Unable to register subdevice\n");
> > -		goto err_put_node;
> > +		goto err_async;
> >  	}
> >
> >  	return 0;
> >
> > -err_put_node:
> > -	fwnode_handle_put(ep);
> >  err_async:
> >  	v4l2_ctrl_handler_free(&priv->ctrls);
> >  	max9286_v4l2_notifier_unregister(priv);
> > diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
> > index a2263fa825b5..ea1111152285 100644
> > --- a/drivers/media/i2c/rdacm20.c
> > +++ b/drivers/media/i2c/rdacm20.c
> > @@ -567,7 +567,6 @@ static int rdacm20_initialize(struct rdacm20_device *dev)
> >  static int rdacm20_probe(struct i2c_client *client)
> >  {
> >  	struct rdacm20_device *dev;
> > -	struct fwnode_handle *ep;
> >  	int ret;
> >
> >  	dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
> > @@ -616,24 +615,12 @@ static int rdacm20_probe(struct i2c_client *client)
> >  	if (ret < 0)
> >  		goto error_free_ctrls;
> >
> > -	ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
> > -	if (!ep) {
> > -		dev_err(&client->dev,
> > -			"Unable to get endpoint in node %pOF\n",
> > -			client->dev.of_node);
> > -		ret = -ENOENT;
> > -		goto error_free_ctrls;
> > -	}
> > -	dev->sd.fwnode = ep;
> 
> Same for this driver and for rdacm21
> 
> > -
> >  	ret = v4l2_async_register_subdev(&dev->sd);
> >  	if (ret)
> > -		goto error_put_node;
> > +		goto error_free_ctrls;
> >
> >  	return 0;
> >
> > -error_put_node:
> > -	fwnode_handle_put(ep);
> >  error_free_ctrls:
> >  	v4l2_ctrl_handler_free(&dev->ctrls);
> >  error:
> > diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c
> > index 9ccc56c30d3b..d67cfcb2e05a 100644
> > --- a/drivers/media/i2c/rdacm21.c
> > +++ b/drivers/media/i2c/rdacm21.c
> > @@ -543,7 +543,6 @@ static int rdacm21_initialize(struct rdacm21_device *dev)
> >  static int rdacm21_probe(struct i2c_client *client)
> >  {
> >  	struct rdacm21_device *dev;
> > -	struct fwnode_handle *ep;
> >  	int ret;
> >
> >  	dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
> > @@ -588,24 +587,12 @@ static int rdacm21_probe(struct i2c_client *client)
> >  	if (ret < 0)
> >  		goto error_free_ctrls;
> >
> > -	ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
> > -	if (!ep) {
> > -		dev_err(&client->dev,
> > -			"Unable to get endpoint in node %pOF\n",
> > -			client->dev.of_node);
> > -		ret = -ENOENT;
> > -		goto error_free_ctrls;
> > -	}
> > -	dev->sd.fwnode = ep;
> > -
> >  	ret = v4l2_async_register_subdev(&dev->sd);
> >  	if (ret)
> > -		goto error_put_node;
> > +		goto error_free_ctrls;
> >
> >  	return 0;
> >
> > -error_put_node:
> > -	fwnode_handle_put(dev->sd.fwnode);
> >  error_free_ctrls:
> >  	v4l2_ctrl_handler_free(&dev->ctrls);
> >  error:
> > diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c
> > index 4063754a6732..56f2b43d4edf 100644
> > --- a/drivers/media/i2c/tc358746.c
> > +++ b/drivers/media/i2c/tc358746.c
> > @@ -1476,9 +1476,6 @@ static int tc358746_async_register(struct tc358746 *tc358746)
> >  	if (err)
> >  		goto err_cleanup;
> >
> > -	tc358746->sd.fwnode = fwnode_graph_get_endpoint_by_id(
> > -		dev_fwnode(tc358746->sd.dev), TC358746_SOURCE, 0, 0);
> > -
> >  	err = v4l2_async_register_subdev(&tc358746->sd);
> >  	if (err)
> >  		goto err_unregister;
> > diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> > index 6dd426c2ca68..13fe0bdc70b6 100644
> > --- a/drivers/media/v4l2-core/v4l2-async.c
> > +++ b/drivers/media/v4l2-core/v4l2-async.c
> > @@ -86,84 +86,33 @@ match_fwnode_one(struct v4l2_async_notifier *notifier,
> >  		 struct v4l2_subdev *sd, struct fwnode_handle *sd_fwnode,
> >  		 struct v4l2_async_subdev *asd)
> >  {
> > -	struct fwnode_handle *other_fwnode;
> > -	struct fwnode_handle *dev_fwnode;
> > -	bool asd_fwnode_is_ep;
> > -	bool sd_fwnode_is_ep;
> > -	struct device *dev;
> > +	struct fwnode_handle *asd_dev_fwnode;
> > +	bool ret;
> >
> >  	dev_dbg(sd->dev, "async: fwnode match: need %pfw, trying %pfw\n",
> >  		sd_fwnode, asd->match.fwnode);
> >
> > -	/*
> > -	 * Both the subdev and the async subdev can provide either an endpoint
> > -	 * fwnode or a device fwnode. Start with the simple case of direct
> > -	 * fwnode matching.
> > -	 */
> >  	if (sd_fwnode == asd->match.fwnode) {
> >  		dev_dbg(sd->dev, "async: direct match found\n");
> >  		return true;
> >  	}
> >
> > -	/*
> > -	 * Otherwise, check if the sd fwnode and the asd fwnode refer to an
> > -	 * endpoint or a device. If they're of the same type, there's no match.
> > -	 * Technically speaking this checks if the nodes refer to a connected
> > -	 * endpoint, which is the simplest check that works for both OF and
> > -	 * ACPI. This won't make a difference, as drivers should not try to
> > -	 * match unconnected endpoints.
> > -	 */
> > -	sd_fwnode_is_ep = fwnode_graph_is_endpoint(sd_fwnode);
> > -	asd_fwnode_is_ep = fwnode_graph_is_endpoint(asd->match.fwnode);
> > -
> > -	if (sd_fwnode_is_ep == asd_fwnode_is_ep) {
> > -		dev_dbg(sd->dev, "async: matching node types\n");
> > +	if (!fwnode_graph_is_endpoint(asd->match.fwnode)) {
> > +		dev_dbg(sd->dev,
> > +			"async: async subdev fwnode not endpoint, no match\n");
> 
> As per the previous patch I would just say "direct match failed";
> 
> >  		return false;
> >  	}
> >
> > -	/*
> > -	 * The sd and asd fwnodes are of different types. Get the device fwnode
> > -	 * parent of the endpoint fwnode, and compare it with the other fwnode.
> > -	 */
> > -	if (sd_fwnode_is_ep) {
> > -		dev_fwnode = fwnode_graph_get_port_parent(sd_fwnode);
> > -		other_fwnode = asd->match.fwnode;
> > -	} else {
> > -		dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode);
> > -		other_fwnode = sd_fwnode;
> > -	}
> > -
> > -	dev_dbg(sd->dev, "async: fwnode compat match, need %pfw, trying %pfw\n",
> > -		dev_fwnode, other_fwnode);
> > +	asd_dev_fwnode = fwnode_graph_get_port_parent(asd->match.fwnode);
> >
> > -	fwnode_handle_put(dev_fwnode);
> > +	ret = sd_fwnode == asd_dev_fwnode;
> >
> > -	if (dev_fwnode != other_fwnode) {
> > -		dev_dbg(sd->dev, "async: compat match not found\n");
> > -		return false;
> > -	}
> > +	fwnode_handle_put(asd_dev_fwnode);
> >
> > -	/*
> > -	 * We have a heterogeneous match. Retrieve the struct device of the side
> > -	 * that matched on a device fwnode to print its driver name.
> > -	 */
> > -	if (sd_fwnode_is_ep)
> > -		dev = notifier->v4l2_dev ? notifier->v4l2_dev->dev
> > -		    : notifier->sd->dev;
> > -	else
> > -		dev = sd->dev;
> > -
> > -	if (dev && dev->driver) {
> > -		if (sd_fwnode_is_ep)
> > -			dev_warn(dev, "Driver %s uses device fwnode, incorrect match may occur\n",
> > -				 dev->driver->name);
> > -		dev_notice(dev, "Consider updating driver %s to match on endpoints\n",
> > -			   dev->driver->name);
> > -	}
> > -
> > -	dev_dbg(sd->dev, "async: compat match found\n");
> > +	dev_dbg(sd->dev, "async: device--endpoint match %sfound\n",
> 
> double '--'
> 
> However now that I think about it, a subdev will be matched agains a
> rather large number of async subdevs and most attempts will fail.. do
> we want a printout for each of them ?

Note that this will only happen if these debug prints are enabled. There
was recently a case where they would have been useful.

> 
> 
> > +		ret ? "" : "not ");
> >
> > -	return true;
> > +	return ret;
> >  }
> >
> >  static bool match_fwnode(struct v4l2_async_notifier *notifier,
> > @@ -804,12 +753,19 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd)
> >  	int ret;
> >
> >  	/*
> > -	 * No reference taken. The reference is held by the device
> > -	 * (struct v4l2_subdev.dev), and async sub-device does not
> > -	 * exist independently of the device at any point of time.
> > +	 * No reference taken. The reference is held by the device (struct
> > +	 * v4l2_subdev.dev), and async sub-device does not exist independently
> > +	 * of the device at any point of time.
> > +	 *
> > +	 * The async sub-device shall always be registered for its device node,
> > +	 * not the endpoint node. Issue a warning in that case. Once there is
> > +	 * certainty no driver no longer does this, remove the warning (and
> > +	 * compatibility code) below.
> >  	 */
> >  	if (!sd->fwnode && sd->dev)
> >  		sd->fwnode = dev_fwnode(sd->dev);
> > +	else if (WARN_ON(fwnode_graph_is_endpoint(sd->fwnode)))
> > +		sd->fwnode = fwnode_graph_get_port_parent(sd->fwnode);
> >
> >  	mutex_lock(&list_lock);
> >

-- 
Kind regards,

Sakari Ailus



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux